How to create a simple dynamically expanding rectangle geometry

Hi,

I want to create a simple rectangle geometry that can dynamically expand. To be exact:

  • The geometry must expand its height over time (e.g. while the user presses a certain key)
  • The geometry needs to have rounded corners with a fixed radius
  • The final mesh needs to have an n-pixel wide border (like the black line in the example below)
  • 2D only

piano-keys-expand

As I’m still a beginner ( :beginner:), I was searching the web and this forum for possible solutions but I’m still uncertain which approach is the correct one. I made a small prototype using THREE.Shape (see below) but with that approach, I’ve no guess how I can expand this shape in its height over time without completely recreating it over and over again (which sounds performance-wise not like a good idea or?). Initially, I was using simple plane geometries and used the mesh scale function to expand it but with this variant, I didn’t know how to create the round corners. Somewhere I was reading about using morph targets for such kind of animations but I didn’t test it yet. The name morph target implies to me somehow that I need to know the target height in advance but as it expands over time I don’t know the “final” height.

Any input or examples would be very helpful!

Thank you in advance!

function roundedRect(x, y, width, height, radius) {
    const shape = new THREE.Shape();
    shape.moveTo(x, y + radius);
    shape.lineTo(x, y + height - radius);
    shape.quadraticCurveTo(x, y + height, x + radius, y + height);
    shape.lineTo(x + width - radius, y + height);
    shape.quadraticCurveTo(
        x + width,
        y + height,
        x + width,
        y + height - radius
    );
    shape.lineTo(x + width, y + radius);
    shape.quadraticCurveTo(x + width, y, x + width - radius, y);
    shape.lineTo(x + radius, y);
    shape.quadraticCurveTo(x, y, x, y + radius);
    return shape;
}

You can create a self defined indexed BufferGeometry and then position the vertices as time dependent as you like.

https://threejs.org/docs/index.html#api/en/core/BufferGeometry

To apply changes
https://threejs.org/docs/index.html#manual/en/introduction/How-to-update-things

A simple example without time changes
https://hofk.de/main/discourse.threejs/2017/Indexed%20BufferGeometry/Indexed%20BufferGeometry.html
another
https://hofk.de/main/discourse.threejs/2019/ColorStripeChanging/ColorStripeChanging.html


From these examples you might be able to use parts.

From the Collection of examples from discourse.threejs.org

https://hofk.de/main/discourse.threejs/2017/index2017.html

https://hofk.de/main/discourse.threejs/2017/Round-edged%20box%203/Round-edged%20box%203.html

https://hofk.de/main/discourse.threejs/2017/Round-edged%20box%202/Round-edged%20box%202.html

https://hofk.de/main/discourse.threejs/2019/ShapeWithOutline/ShapeWithOutline.html


https://hofk.de/main/threejs/sandboxthreeg/MagicBoxTHREEg.html example No. 7, 15 are dynamic


A code snippet from my program where the verices are updated.
Beforehand you calculate the coordinates as desired.

g.faceIndices = new Uint32Array( faceCount * 3 );
g.vertices = new Float32Array( vertexCount * 3 ); 

g.setIndex( new THREE.BufferAttribute( g.faceIndices, 1 ) );	
g.setAttribute( 'position', new THREE.BufferAttribute( g.vertices, 3 ).setDynamic( true ) );

	function xyzSet( x, y, z ) {
			
			posIdx = vIdx * 3; // (vIdx vertex index)
			
			g.vertices[ posIdx ]  = x;		// set vertex position
			g.vertices[ posIdx + 1 ]  = y;
			g.vertices[ posIdx + 2 ]  = z;
			
	}
		
	g.attributes.position.needsUpdate = true;
	g.attributes.normal.needsUpdate = true;

adapted from https://hofk.de/main/threejs/sandboxthreef/

Wow, thanks a lot for all the links and the example :smile: I’ll give the indexed BufferGeometry a try and let you know if I had success or got stuck at some point :slight_smile:

Ok, one more question regarding the BufferGeometry:

Basically I’ve to create a geometry that looks like the following or? Is there a simple method to generate this circle geometries? I don’t want to implement that on my own… Seems like it’s possible to merge a plane geometry with a circle geometry but in that case, I’d lose the vertex indexes (red dots) which I need for my transformation or?

rounded-rect-wireframe

(sorry for the bad quality :see_no_evil:)

A mixture of techniques in such a simple example does not seem reasonable.

2020-04-24 20.07.43

The calculation of the points is quite elementary.
circle see e.g. https://www.mathopenref.com/coordparamcircle.html

In a loop with the number of passes corresponding to the desired fineness of the arcs, the points of the quarter circles are calculated. With a function where the start angle is used as a parameter.

If you use appropriate variables, you can easily realize the change of shape.

Look there
https://github.com/mrdoob/three.js/blob/7c1424c5819ab622a346dd630ee4e6431388021e/src/geometries/CircleGeometry.js

For the thick border you may be able to use this method (@Mugen87) ?

See https://hofk.de/main/discourse.threejs/2020/index2020.html
nearly at the bottom.

2020-04-24 20.44.22

The principle from this topic is applicable in this case: Cheap round-edged box (vertex shader)
And it’s even simpler, as there’s no need to compute normals in shaders.

A little update from my side. I’ve managed to create a custom geometry that I can resize dynamically in its width/height but keeps a constant corner radius. Basically, I just translate the corners and scale the rectangles.

image
image

Implementing the resize logic took me a while and code-wise it’s more than 400+ lines (complete geometry with resize logic). Even if you could do in the half amount of code, it still feels like a huge overhead to maintain. Let’s see how it feels like to use a simple plane geometry instead and apply a vertex shader as recommended by @prisoner849