RoundedRectangle + Squircle

Function to create a rectangle with rounded corners.

RoundedRectangle - Mozilla Firefox 2021-08-04 12.1

See RoundedRectangle

RoundedRectangle - Mozilla Firefox 2021-08-04 12.3

Example of a video see Three js video with rounded corners - #14 by hofk
(slightly different code)

RoundedRectangleVideo - Mozilla Firefox 2021-08-03


// 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: :clipboard: :pencil2: :triangular_ruler: :straight_ruler: :scissors:

Another more efficient construction:

RectangleRounded

2022-11-29 20.18.03

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
2022-11-29 20.27.06

From behind (with side: THREE.FrontSide !)

2022-11-29 20.29.29

12 Likes

I have added a version with indexed BufferGeometry, see original post.

2 Likes

thanks @hofk today I am going to use it.

1 Like

I’m pleased :slightly_smiling_face: when my geometric gimmicks find application.

UPDATE 2022: For another construction using radial segments only, as with the circle, see original post above. :slightly_smiling_face:

1 Like

these are super useful! i wish we had something like a library that collects common shapes. btw, if you have time to kill, would a squircle pose much of a problem? it would come in handy, often looks better than a rounded square, apple is pretty much all squircles. :smile:

An attempt at a squircle. Or maybe a sphox is a better term to describe it.
It’s a lerp between a boxGeometry and a normalised copy.
Example : Squircle
image

2 Likes

here’s a squircle according to wiki

Superellipse_chamfered_square

1 Like

This is 3d like this construction Morph box sphere geometry (+shader)

If you can calculate the points of the outline, it is no problem to fill the area with triangles using my Addon for triangulation of implicit surfaces/ forms with holes .

But then you need many triangles and it is less efficient than a special construction.

With star tips there are problems if the triangles are too large in relation to the tip.

see InnerGeometryTHREEi_03

1 Like

here, have your squircle, Paul. sorry it is not scalable approach, but like w/e

1 Like

And with just a few tweaks of makc3ds version, it’s now 3d.
Example : 3D Squircle
image

4 Likes

for anyone interested, people already did the meth and there is a parameter formula to calculate x(t), y(t) without iterative error minimization procedure above

uno mas shader based squircle (oh, you might want to round 0.0625 down to 0.06 to make sure)

1 Like

Here is another squircle method. Well, it looks like a squircle.
It uses hyperbolic tangent.
I tried the superellipse methods, but couldn’t generate a nice clean 3d geometry.
Example : Squircle using Hyperbolic Tangent
image

image

image

4 Likes

feels like ~1.3 looks best

1 Like

@hofk the 2022 edition, is there a bug hidden or do i use it wrong: w=1 h=1 r=0.5 s=16, your link is blank as well.

and btw, would you be interested in collecting geometries here? maath/packages/maath at main · pmndrs/maath · GitHub this is a vanilla kitchen sink for math helpers.

:thinking: The link works.

RectangleRounded

Tested the values locally:
2023-01-02 14.09.43

2023-01-02 14.04.12

At the moment I have a somewhat larger project. It includes custom geometries, of course. The collection of discourse examples is running alongside.

maybe it’s a mac thing