Function to create a rectangle with rounded corners.
See RoundedRectangle
Example of a video see Three js video with rounded corners - #14 by hofk
(slightly different code)
// non indexed BufferGeometry
function RoundedRectangle( w, h, r, s ) { // width, height, radius corner, smoothness
// helper const's
const wi = w / 2 - r; // inner width
const hi = h / 2 - r; // inner height
const w2 = w / 2; // half width
const h2 = h / 2; // half height
const ul = r / w; // u left
const ur = ( w - r ) / w; // u right
const vl = r / h; // v low
const vh = ( h - r ) / h; // v high
let positions = [
-wi, -h2, 0, wi, -h2, 0, wi, h2, 0,
-wi, -h2, 0, wi, h2, 0, -wi, h2, 0,
-w2, -hi, 0, -wi, -hi, 0, -wi, hi, 0,
-w2, -hi, 0, -wi, hi, 0, -w2, hi, 0,
wi, -hi, 0, w2, -hi, 0, w2, hi, 0,
wi, -hi, 0, w2, hi, 0, wi, hi, 0
];
let uvs = [
ul, 0, ur, 0, ur, 1,
ul, 0, ur, 1, ul, 1,
0, vl, ul, vl, ul, vh,
0, vl, ul, vh, 0, vh,
ur, vl, 1, vl, 1, vh,
ur, vl, 1, vh, ur, vh
];
let phia = 0;
let phib, xc, yc, uc, vc, cosa, sina, cosb, sinb;
for ( let i = 0; i < s * 4; i ++ ) {
phib = Math.PI * 2 * ( i + 1 ) / ( 4 * s );
cosa = Math.cos( phia );
sina = Math.sin( phia );
cosb = Math.cos( phib );
sinb = Math.sin( phib );
xc = i < s || i >= 3 * s ? wi : - wi;
yc = i < 2 * s ? hi : -hi;
positions.push( xc, yc, 0, xc + r * cosa, yc + r * sina, 0, xc + r * cosb, yc + r * sinb, 0 );
uc = i < s || i >= 3 * s ? ur : ul;
vc = i < 2 * s ? vh : vl;
uvs.push( uc, vc, uc + ul * cosa, vc + vl * sina, uc + ul * cosb, vc + vl * sinb );
phia = phib;
}
const geometry = new THREE.BufferGeometry( );
geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
return geometry;
}
// indexed BufferGeometry
function RoundedRectangle( w, h, r, s ) { // width, height, radius corner, smoothness
// helper const's
const wi = w / 2 - r; // inner width
const hi = h / 2 - r; // inner height
const w2 = w / 2; // half width
const h2 = h / 2; // half height
const ul = r / w; // u left
const ur = ( w - r ) / w; // u right
const vl = r / h; // v low
const vh = ( h - r ) / h; // v high
let positions = [
wi, hi, 0, -wi, hi, 0, -wi, -hi, 0, wi, -hi, 0
];
let uvs = [
ur, vh, ul, vh, ul, vl, ur, vl
];
let n = [
3 * ( s + 1 ) + 3, 3 * ( s + 1 ) + 4, s + 4, s + 5,
2 * ( s + 1 ) + 4, 2, 1, 2 * ( s + 1 ) + 3,
3, 4 * ( s + 1 ) + 3, 4, 0
];
let indices = [
n[0], n[1], n[2], n[0], n[2], n[3],
n[4], n[5], n[6], n[4], n[6], n[7],
n[8], n[9], n[10], n[8], n[10], n[11]
];
let phi, cos, sin, xc, yc, uc, vc, idx;
for ( let i = 0; i < 4; i ++ ) {
xc = i < 1 || i > 2 ? wi : -wi;
yc = i < 2 ? hi : -hi;
uc = i < 1 || i > 2 ? ur : ul;
vc = i < 2 ? vh : vl;
for ( let j = 0; j <= s; j ++ ) {
phi = Math.PI / 2 * ( i + j / s );
cos = Math.cos( phi );
sin = Math.sin( phi );
positions.push( xc + r * cos, yc + r * sin, 0 );
uvs.push( uc + ul * cos, vc + vl * sin );
if ( j < s ) {
idx = ( s + 1 ) * i + j + 4;
indices.push( i, idx, idx + 1 );
}
}
}
const geometry = new THREE.BufferGeometry( );
geometry.setIndex( new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
return geometry;
}
UPDATE 2022:
Another more efficient construction:
The construction has 2 triangles less and a very tight code. The construction is similar to the circle and uses only radial segments.
function RectangleRounded( w, h, r, s ) { // width, height, radiusCorner, smoothness
const pi2 = Math.PI * 2;
const n = ( s + 1 ) * 4; // number of segments
let indices = [];
let positions = [];
let uvs = [];
let qu, sgx, sgy, x, y;
for ( let j = 1; j < n + 1; j ++ ) indices.push( 0, j, j + 1 ); // 0 is center
indices.push( 0, n, 1 );
positions.push( 0, 0, 0 ); // rectangle center
uvs.push( 0.5, 0.5 );
for ( let j = 0; j < n ; j ++ ) contour( j );
const geometry = new THREE.BufferGeometry( );
geometry.setIndex( new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
geometry.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( positions ), 3 ) );
geometry.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
return geometry;
function contour( j ) {
qu = Math.trunc( 4 * j / n ) + 1 ; // quadrant qu: 1..4
sgx = ( qu === 1 || qu === 4 ? 1 : -1 ) // signum left/right
sgy = qu < 3 ? 1 : -1; // signum top / bottom
x = sgx * ( w / 2 - r ) + r * Math.cos( pi2 * ( j - qu + 1 ) / ( n - 4 ) ); // corner center + circle
y = sgy * ( h / 2 - r ) + r * Math.sin( pi2 * ( j - qu + 1 ) / ( n - 4 ) );
positions.push( x, y, 0 );
uvs.push( 0.5 + x / w, 0.5 + y / h );
}
}
With exaggerated radius you can create something like this.
In frontside
From behind (with side: THREE.FrontSide
!)