Curved2Geometry - a twofold curved geometry

Construction

One defines with some 3D points the center curve and for some angles the orthogonal radial distances of the hull from these points.

By specifying the maximum values, one determines the vertical and radial fineness of the representation.

With a definition string of three characters one determines whether caps are to be generated and whether these are to be flat or smooth. The third character
allows radial connection without seam or symmetrical geometry. For symmetrical geometry the last angle must be 180°.

3 characters ‘string’ = ‘withTop withBottom conncted/symmetric’.

withTop/Bottom: x, f, s = no, flat, smooth
conncted/symmetric: x, c, s = no, connected, symmetric

By specifying deviations from the defined points of the center curve, a very organic movement of the geometry can be optionally created.

The parameters and values are summarized in a design matrix. The first row of this matrix is an array of the parameters, the further rows contain the values in an array each.

This way you can arrange the values in the columns exactly under the parameters.

const design = [   
                             // first0,.. angles.., max angle 
 	[ maxVert., 'string', maxRadial,  0, ϕ1, ϕ2, ... ϕmax ],	
// center x,y,z distance to center orthogonal-radial 
	[       x,         y,         z, r0, r1, r2, ...  r   ], // top 
 	...
  	[ ] // bottom
	
];

Example:

Since an exact match is not possible for arbitrarily defined centers and angles and likewise arbitrary maximum values of fineness, the closest values are determined and used for the calculation.

The optional values of the deviations are to be noted in corresponding structure to the center points. From these values further points are generated on a curve. Their number is to be specified in a separate parameter.

The motion is created using the geometry.morph( geometry.cPts[ ] ) method.

g.cPts = []; // array of morphed g.cPoints

where

g.cPoints = new THREE.CatmullRomCurve3( g.dsgnCenters, false ).getSpacedPoints( g.vertical );


Try it out: Curved2Geometry


on GitHub - hofk/threejsResources: Resources for three.js


<!DOCTYPE html>
<!-- https://discourse.threejs.org/t/curved2geometry-a-twofold-curved-geometry/25580 -->
<head>
	<title> Curved2Geometry </title>
	<meta charset="utf-8" />
	<style>
		body{
		overflow: hidden;
		margin: 0;
		}
	</style>
</head>

<body></body>

<script type="module">

// @author hofk

import * as THREE from "../jsm/three.module.127.js";
import { OrbitControls } from "../jsm/OrbitControls.127.js";
import { VertexNormalsHelper } from "../jsm/VertexNormalsHelper.127.js";

const scene = new THREE.Scene( );
const camera = new THREE.PerspectiveCamera( 55, innerWidth / innerHeight, 0.01, 1000 );
camera.position.set( 8, 12, 22 );
const renderer = new THREE.WebGLRenderer( );
renderer.setSize( innerWidth, innerHeight );
renderer.setClearColor( 0x111111 );
document.body.appendChild(renderer.domElement);

const light = new THREE.AmbientLight( 0x404040, 1.2 ); // soft white light
scene.add( light );

const pointLight1 = new THREE.PointLight( 0xffffff, 1.1 );
pointLight1.position.set( -2, 10, 5 );
scene.add( pointLight1 );

new  OrbitControls( camera, renderer.domElement );

scene.add( new THREE.GridHelper( 20, 20 ) );

const matT = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'uvgrid01.png' ) } );
const matT1 = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'fisch1Tex.png' ) } );
const matT2 = new THREE.MeshBasicMaterial( { wireframe: false, side: THREE.DoubleSide, map: new THREE.TextureLoader( ).load( 'fisch2Tex.png' ) } );
const mata = new THREE.MeshPhongMaterial( { wireframe: false, side: THREE.DoubleSide, color: 0xffffbb } );
const matb = new THREE.MeshPhongMaterial( {	wireframe: false, side: THREE.DoubleSide, color: 0xffaaff } );
const matc = new THREE.MeshPhongMaterial( {	wireframe: true, side: THREE.DoubleSide, color: 0xffffff } );

const mat = [ matT, mata, matb ];
const matw = [ matc, mata, matb ];
const mats = [ matb, mata, matb ];
const matf1 = [ matT1, mata, matb ];
const matf2 = [ matT2, mata, matb ];

//                     no  flat smooth                                            symmetric (180°)
//  string - with top:  x,  f,  s    | with bottom:  x,  f,  s | connected: x, c, s

const design = [   
//max vert,str,radial,0 first, ...      , max angle 
 	[ 96, 'ffc', 36,    0,  80, 188, 260, 290 ],
// center x, y,  z,   distance to center orthogonal-radial 
	[-0.5,  5.0, -2,  0.3, 0.3, 0.4, 0.3, 0.3 ], // top, max vert. ( y )
	[ 0.3,  4.5,  0,  1.8, 1.6, 0.0, 1.5, 1.9 ],
	[ 0.2,  1.0,  0,  0.3, 0.5, 0.1, 0.3, 0.4 ], // ... to some centers x,y,z
	[ 0.1,  0.0,  0,  0.7, 0.7, 1.1, 0.7, 0.7 ],
	[ 0.0, -1.5,  1,  0.3, 0.6, 0.8, 0.9, 0.6 ],
	[ 0.2, -5.0,  1,  0.8, 0.3, 0.5, 0.3, 0.5 ],
	[14.5, -2.5,  8,  0.1, 0.2, 0.2, 0.1, 0.1 ], // bottom  
	
];

const centerMorph = [ 
		//  x,   y,    z  difference to initial position
	[
		[ 0.1,  0.5, -0.2, ],
		[ 0.1,  0.3,  0.0, ],
		[ 0.0,  0.2,  0.0, ],
		[-0.1,  0.0,  0.0, ],
		[ 0.0,  0.0,  0.0, ],
		[ 0.3, -0.9,  0.5, ],
		[ 0.0,  0.0,  0.0, ],
	],
	[
		[-0.1,  0.7, -0.4, ],
		[-0.1,  0.5,  0.2, ],
		[ 0.0,  0.4,  0.1, ],
		[ 0.1,  0.2,  0.2, ],
		[ 0.0,  0.0,  0.0, ],
		[ 0.6, -1.3,  0.8, ],
		[ 0.0,  0.0,  0.0, ],
	],
	[
		[-0.2,  0.2,  0.1, ],
		[-0.3,  0.1,  0.2, ],
		[ 0.1,  0.1, -0.1, ],
		[ 0.2,  0.3, -0.1, ],
		[ 0.0,  0.0,  0.0, ],
		[ 0.4, -0.8, -0.6, ],
		[ 0.0,  0.1,  0.0, ],
	],
	
];
 
const morphCount = 20;
const geo = Curved2Geometry( design, centerMorph, morphCount );

let mesh = new THREE.Mesh( geo, mat );
scene.add( mesh );
mesh.position.x = 12;
mesh.rotation.y = Math.PI;
 
// .....................................................................

const design1 = [   
//max vert, str, radial, 0 first, .., max angle 
	[  60, 'fxc', 95,    0, 106, 220, 280 ],
// center x,  y,   z,  distance to center orthogonal-radial   
	[ 0.8,  2.4, 0.0,  1.0, 1.0, 1.2, 0.7 ], //  top
	[ 0.1,  1.2, 0.1,  1.2, 0.6, 0.6, 0.7 ], //  to some centers x,y,z
	[ 0.0, -1.0, 0.0,  0.2, 0.3, 0.2, 0.4 ],
	[ 0.0, -2.5, 0.4,  0.7,-0.2, 0.7, 0.6 ], //  bottom 
	
];
 
const geo1 = Curved2Geometry( design1 );

let mesh1 = new THREE.Mesh( geo1, matw );
scene.add( mesh1 );
mesh1.position.x = -2;
mesh1.position.z = 12;
 
// .....................................................................

const design2 = [   
//max vert,str, radial, 0 first, .., max angle   - if '..s' 0°, 180° fixed
	[ 15, 'fss', 72,    0, 18,  120, 180 ], 
// center x,  y,  z,  distance to center orthogonal-radial   
	[ -4,  1.2, 0.4,  1.4, 0.8, 1.2, 0.7 ], //  top 
	[ -1,  0.8, 0.3,  1.6, 1.6, 0.8, 0.7 ], //  to some centers x,y,z
	[  1, -1.1, 0.1,  1.7, 1.3, 0.8, 0.7 ],	
	[  4, -1.2, 0.2,  1.6, 1.8, 0.9, 0.7 ], //  bottom 
	
];
 
const geo2 = Curved2Geometry( design2 );

let mesh2 = new THREE.Mesh( geo2, mats );
scene.add( mesh2 );
mesh2.position.x = -14;
mesh2.position.z = 5;
 
const helper =new VertexNormalsHelper( mesh2, 0.3, 0x00ff00, 1 );
scene.add( helper );
 
// ...................................................................

const design3 = [   
//max vert, str, radial, 0 first, ..               , max angle 
	[ 180, 'xxx', 72,    0,  45,  90, 135, 180, 225, 270  ],
// center x,  y,   z, 	distance to center orthogonal-radial   
	[ 1.9,  6.0, 4.0,  0.8, 0.9, 0.8, 0.8, 0.9, 0.9, 0.9  ], //  top
	[ 0.0,  0.0, 0.0,  0.5, 3.0, 0.3, 0.2, 0.1, 0.3, 0.3  ],
	[-2.9, -3.0, 3.0,  0.2, 0.1, 0.3, 0.3, 0.6, 0.1, 0.2  ], //  bottom 
	
];

const centerMorph3 = [ 
	//      x,    y,    z  difference to initial position
	[	
		[ 1.0,  3.3, -0.8, ],
		[ 0.0,  0.2, -0.5, ],
		[ 1.0,  0.1, -0.1, ],
	],
	[	
		[ 0.6,  0.1, -0.9, ],
		[-3.4,  0.8,  0.1, ],
		[ 1.0,  0.5, -0.5, ],
	],
	[	
		[ 2.5,  3.0,  0.0, ],
		[ 0.0,  3.0,  0.1, ],
		[ 4.5, -2.0, -0.8, ],
	],
	[	
		[ 1.5,  0.0,  1.0, ],
		[ 0.0,  1.0,  0.1, ],
		[ 0.9, -0.7,  1.1, ],
	],

];

const morphCount3 = 80;
const geo3 = Curved2Geometry( design3, centerMorph3, morphCount3 );
let mesh3 = new THREE.Mesh( geo3, mat );
scene.add( mesh3 );
mesh3.position.x = -14;
mesh3.position.z = -4;

// .....................................................................

const designf1 = [
//max vert, str, radial, 0 first,.. , max angle  - if '..s' 0°, 180° fixed
	[  16, 'ffs', 34,    0,   7, 150, 180, ],
// center x, y,   z, distance to center orthogonal-radial
	[ 0.0, 1.0, 0.0,   0.4, 0.4, 0.4, 0.9, ], 
	[ 0.0, 2.0, 0.0,   1.0, 0.8, 0.9, 2.0, ],
	[ 0.0, 3.0, 0.0,   1.2, 1.1, 1.1, 2.7, ],
	[ 0.0, 4.0, 0.0,   1.6, 1.3, 1.5, 3.3, ],
	[ 0.0, 5.0, 0.0,   1.9, 1.5, 1.7, 3.8, ],
	[ 0.0, 6.0, 0.0,   2.1, 1.7, 2.0, 4.1, ], //  to some centers x,y,z
	[ 0.0, 7.0, 0.0,   2.3, 1.8, 2.0, 4.3, ],
	[ 0.0, 8.0, 0.0,   2.3, 1.8, 2.0, 4.3, ],
	[ 0.0, 9.0, 0.0,   2.2, 1.8, 2.0, 4.0, ],
	[ 0.0,10.0, 0.0,   2.1, 1.6, 1.8, 3.6, ],
	[ 0.0,11.0, 0.0,   2.0, 1.4, 1.6, 3.3, ],
	[ 0.0,12.0, 0.0,   1.8, 1.0, 1.2, 2.9, ],
	[ 0.0,13.0, 0.0,   1.3, 0.8, 1.0, 2.5, ],
	[ 0.0,14.0, 0.0,   0.6, 0.6, 0.7, 2.2, ],
	[ 0.0,15.0, 0.0,   0.6, 0.3, 0.4, 2.1, ],
	[ 0.0,16.0, 0.0,   0.7, 0.1, 0.2, 2.0, ], 
	
];

const centerMorphf1 = [ 
	//      x,    y,    z  difference to initial position
	[	
		[ 0.0,  0.0,  0.0, ],
		[ 0.0,  0.0, -0.1, ],
		[ 0.0,  0.0, -0.1, ],
		[ 0.0,  0.0, -0.1, ],
		[ 0.0,  0.0, -0.2, ],
		[ 0.0,  0.0, -0.2, ],
		[0.03,  0.0, -0.2, ],
		[0.03,  0.0, -0.3, ],
		[0.03,  0.0, -0.3, ],
		[0.03,  0.0, -0.2, ],
		[0.03,  0.0, -0.1, ],
		[ 0.0,  0.0,  0.1, ],
		[ 0.0,  0.0,  0.2, ],
		[ 0.0,  0.0,  0.3, ],
		[ 0.1,  0.0,  0.5, ],
		[ 0.2,  0.0,  0.7, ]
	],
 	[
		[ 0.0,  0.0,  0.0, ],
		[ 0.0,  0.0,  0.1, ],
		[ 0.0,  0.0,  0.1, ],
		[ 0.0,  0.0,  0.1, ],
		[ 0.0,  0.0,  0.2, ],
		[ 0.0, -0.0,  0.2, ],
		[ 0.0,  0.0,  0.2, ],
		[ 0.0,  0.0,  0.3, ],
		[ 0.0,  0.0,  0.3, ],
		[ 0.0,  0.0,  0.2, ],
		[ 0.0,  0.0,  0.1, ],
		[ 0.0,  0.0, -0.1, ],
		[ 0.0,  0.0, -0.2, ],
		[ 0.0, -0.0, -0.3, ],
		[-0.1,  0.0, -0.5, ],
		[-0.2,  0.0, -0.7, ]
	],
];

const morphCountf1 = 40;
const geof1 = Curved2Geometry( designf1, centerMorphf1, morphCountf1 );

let meshf1 = new THREE.Mesh( geof1, matf1 );
meshf1.rotation.z = Math. PI / 2;
meshf1.position.x = 8;
meshf1.position.z = -9;
scene.add(meshf1 );

// .....................................................................

const designf2 = [
//max vert, str,radial, 0 first,.., max angle  - if '..s' 0°, 180° fixed
	[  60, 'ffs', 34,   0,  90, 180, ],
// center x,  y,   z, distance to center orthogonal-radial	
	[  9.5, 0.0, 0.0, 0.2, 0.1, 0.2, ], //  top, max vert. ( top x )
	[  9.0, 0.0, 0.0, 0.9, 0.4, 0.7, ],
	[  8.0, 0.0, 0.0, 1.2, 0.6, 1.2, ],
	[  7.0, 0.0, 0.0, 1.5, 0.8, 1.5, ],
	[  6.0, 0.0, 0.0, 1.7, 0.9, 1.8, ],
	[  5.0, 0.0, 0.0, 1.8, 1.0, 2.0, ],
	[  4.0, 0.0, 0.0, 1.9, 1.1, 2.15,],
	[  3.0, 0.0, 0.0, 2.0, 1.2, 2.2, ],
	[  2.0, 0.0, 0.0, 2.1, 1.2, 2.25,],	
	[  1.0, 0.0, 0.0, 2.1, 1.3, 2.3, ],	
	[  0.0, 0.0, 0.0, 2.1, 1.3, 2.2, ], //  to some centers x,y,z
	[ -1.0, 0.0, 0.0, 2.0, 1.2, 2.1, ],
	[ -2.0, 0.0, 0.0, 1.9, 1.1, 2.0, ],	
	[ -3.0, 0.0, 0.1, 1.8, 1.0, 1.9, ],
	[ -4.0, 0.0, 0.2, 1.6, 0.9, 1.8, ],	
	[ -5.0, 0.0, 0.3, 1.3, 0.8, 1.6, ],
	[ -6.0, 0.0, 0.4, 1.1, 0.7, 1.4, ],	
	[ -7.0, 0.0, 0.5, 0.8, 0.6, 1.1, ],	
	[ -8.0, 0.0, 0.6, 0.6, 0.5, 0.8, ],
	[ -9.0, 0.0, 0.7, 0.7, 0.4, 0.5, ],
	[ -9.5, 0.0, 0.8, 0.8, 0.3, 0.3, ], //  bottom
	
];

const centerMorphf2 = [];

for ( let i = 0; i < 4; i ++ ) {
	
	centerMorphf2.push( [] );
	
	for ( let j = 0; j < designf2.length - 1; j ++ ) {	
		
		centerMorphf2[ i ].push( [] );
		
		const dy = 0.5 * Math.sin( 0.1 + i * j / 12 );
		const dz = 0.1 * Math.cos( 0.1 - i + j / 3 );
		
		centerMorphf2[ i ][ j ].push( 0, dy, dz );
		
	}
	
}
const morphCountf2 = 80;
const geof2 = Curved2Geometry( designf2, centerMorphf2, morphCountf2 );

let meshf2 = new THREE.Mesh( geof2, matf2 );
scene.add(meshf2 );
 
let t = 0;
let t1 = 0;

animate( );

// .......................................................................................

function animate( ) {

	requestAnimationFrame( animate );
			
	geo.morph( geo.cPts[ Math.trunc( t ) % ( morphCount ) ] );
	geo.attributes.position.needsUpdate = true;
		
	geo3.morph( geo3.cPts[ Math.trunc( t ) % ( morphCount3 ) ] );
	geo3.attributes.position.needsUpdate = true;
	
 	t += 0.1;
	
	geof1.morph( geof1.cPts[ Math.trunc( t1 ) % ( morphCountf1 ) ] );
	geof1.attributes.position.needsUpdate = true;
	
	geof2.morph( geof2.cPts[ Math.trunc( t1 ) % ( morphCountf2 ) ] );
	geof2.attributes.position.needsUpdate = true;
	
	t1 += 0.9;

	renderer.render( scene, camera );
	
}

// ...........................................................................................

function Curved2Geometry( design, centerMorph, morphCount ) { // optional: centerMorph, morphCount
	
	const g = new THREE.BufferGeometry( );
	
	const connected = design[ 0 ][ 1 ][2] === 'x' ? false : true;
	const symmetric = design[ 0 ][ 1 ][2] === 's' ? true : false;
	
	let dsgn = [];
	
	if ( symmetric ) {
		
		const len = design[ 0 ].length;
		
		for ( let i = 0; i < design.length; i ++ ) {
			
			dsgn.push( [] );
			
			for ( let j = 0; j < len; j ++ ) {
				
				dsgn[ i ].push( design[ i ][ j ] );	
				
			}
			
			if ( i === 0 ) {
				
				dsgn[ 0 ][ 3 ] = 0;
				
				
				for ( let j = len - 2; j >= 4; j -- ) {
					
					dsgn[ 0 ].push( 180 + 180 - design[ 0 ][ j ] );
					
				}
				
				dsgn[ 0 ][ len - 1 ] = 180;
				
			} else {
				
				for ( let j = len - 2; j >= 4; j -- ) {
					
					dsgn[ i ].push( design[ i ][ j ] );
					
				}
				
			}
			
		}
		
	} else {
		
		dsgn = design;
		
	}
	
	const rdefCount = dsgn[ 0 ].length - 3; // radial definition count
	
	g.radial = dsgn[ 0 ][ 2 ];		// radial max 
	g.vertical = dsgn[ 0 ][ 0 ];	// vertical max
	
	const wTop = dsgn[ 0 ][ 1 ][0] !== 'x' ? true : false;
	const flatTop = dsgn[ 0 ][ 1 ][0] === 'f' ? true : false;
	
	const wBtm = dsgn[ 0 ][ 1 ][1] !==  'x' ? true : false;
	const flatBtm = dsgn[ 0 ][ 1 ][1] === 'f' ? true : false;
	
	const angle = dsgn[ 0 ][ dsgn[ 0 ].length - 1 ];   
	const dAngle = angle / g.radial;	
		
	let angles = [ ]; // all radial angles in ° (g.radial + 1 many)
	let phi = []; // closest angle to the design in radiant (rdefCount many)
	
	for ( let j = 0; j <= g.radial; j ++ ) {
		
		angles[ j ] = dAngle * j; 
		
	}
	
	for ( let i = 0; i < rdefCount; i ++ ) { // angle adaptation
		
		for ( let j = 0; j <= g.radial; j ++ ) {
		
			if ( Math.abs( dsgn[ 0 ][ i + 3 ] - angles[ j ] ) < dAngle / 2 ) {
				
				phi.push( angles[ j ] * Math.PI / 180 ); // with degree to radiant
				
			}
			
		}
		
	}
	
	g.morphCount = morphCount;
	
	g.morph = function( pts ) {
		
		// tangent( direction),  normal, binormal, shape in space
		
		let v3a = new THREE.Vector3( ); 
		let v3b = new THREE.Vector3( );
		
		let tangent = new THREE.Vector3( );	
		let normal = new THREE.Vector3( 0, 0, -1 ); // first normal to after ... 
		let binormal = new THREE.Vector3( );
		
		let idx = 0;
		
		for( let i = 0; i <= g.vertical; i ++ ) {
			
			if ( i === 0 ) tangent.subVectors( pts[ 1 ], pts[ 0 ] );
			if ( i > 0 && i < g.vertical ) tangent.subVectors( pts[ i + 1 ], pts[ i - 1 ] );
			if ( i === g.vertical ) tangent.subVectors( pts[ i ], pts[ i - 1 ] );
			
			binormal.crossVectors( normal, tangent );
			normal.crossVectors( tangent, binormal );
			
			binormal.normalize( );
			normal.normalize( );
				
			for( let j = 0; j <= g.radial; j ++ ) {
				
				v3a.addVectors( binormal.clone( ).multiplyScalar( g.pts2D[ i ][ j ].x ), normal.clone( ).multiplyScalar( g.pts2D[ i ][ j ].z ) );
				v3b.addVectors( pts[ i ], v3a );
				
				g.attributes.position.setXYZ( idx ++, v3b.x, v3b.y, v3b.z );
				
			}
			
		}
		
		idx --; // idx = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1; // last index torso
		
		if( wTop ) {
			
			let x, y, z;
			
			g.attributes.position.setXYZ( ++ idx, pts[ 0 ].x, pts[ 0 ].y, pts[ 0 ].z ); // center top
			
			for( let j = 0; j <= g.radial ; j ++ ) {
				
				x = g.attributes.position.getX( j );
				y = g.attributes.position.getY( j );
				z = g.attributes.position.getZ( j );
				
				g.attributes.position.setXYZ( ++ idx, x, y, z );
				
			}
			
		}
		
		if( wBtm ) {
			
			let x, y, z, idxBtm;
			
			g.attributes.position.setXYZ( ++ idx, pts[ g.vertical ].x, pts[ g.vertical ].y, pts[ g.vertical ].z ); // center bottom
			
			for( let j = 0; j <= g.radial ; j ++ ) {
				
				idxBtm = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1 - g.radial + j; // last index torso - g.radial + j
				
				x = g.attributes.position.getX( idxBtm );
				y = g.attributes.position.getY( idxBtm);
				z = g.attributes.position.getZ( idxBtm );
				
				g.attributes.position.setXYZ( ++ idx, x, y, z );
				
			}
			
		}
		
		g.attributes.position.needsUpdate = true;
		g.computeVertexNormals( );
		
	}
	
	g.dsgnCenters = [];
	
	for ( let i = 1; i < dsgn.length; i ++ ) {
		
		g.dsgnCenters.push( new THREE.Vector3( dsgn[ i ][ 0 ], dsgn[ i ][ 1 ], dsgn[ i ][ 2 ] ) );
		
	}
	
	g.cPoints = new THREE.CatmullRomCurve3( g.dsgnCenters, false ).getSpacedPoints( g.vertical );
	
	if ( centerMorph !== undefined ) {
		
		g.centerMrph = []; // vectors (center morph)  to create curve 
		g.cMorph = []; // morphCount many points of the curve (for each defined center)
		g.cPts = []; // array of array of morphed g.cPoints
		
		for ( let i = 0; i < dsgn.length - 1; i ++ ) {
			
			g.centerMrph.push( [] );
			
			g.centerMrph[ i ].push( g.dsgnCenters[ i ] );  // Vector3,  from design array
			
			for ( let j = 0; j < centerMorph.length; j ++ ) {
				
				const x = dsgn[ i + 1 ][ 0 ] + centerMorph[ j ][ i ][ 0 ];
				const y = dsgn[ i + 1 ][ 1 ] + centerMorph[ j ][ i ][ 1 ];
				const z = dsgn[ i + 1 ][ 2 ] + centerMorph[ j ][ i ][ 2 ];
				
				g.centerMrph[ i ].push( new THREE.Vector3( x, y, z ) );
				
			}
			
			g.cMorph.push( new THREE.CatmullRomCurve3( g.centerMrph[ i ], true ).getSpacedPoints( g.morphCount ) );
			
		}
		
		let cMrph; // points resorted to center lines
		
		for ( let i = 0; i < g.morphCount; i ++ ) {
		
			cMrph = [];
			
			for ( let j = 0; j < dsgn.length - 1; j ++ ) {
				
				cMrph.push( g.cMorph[ j ][ i ] )
				
			}
			
			g.cPts.push( [] );
			g.cPts[ i ] = new THREE.CatmullRomCurve3( cMrph, false ).getSpacedPoints( g.vertical );
			
		}
		
	}
	
	let dist2; // distanceToSquared	
	let idxCp = [ ]; // indices of nearest center points (design, calculated)
	
	for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
		
		dist2 = Infinity;
		
		for ( let j = 0; j < g.cPoints.length; j ++ ) {
			
			const d = g.dsgnCenters[ i ].distanceToSquared( g.cPoints[ j ] );
			
			if ( d < dist2 ) {
				
				dist2 = d;
				idxCp[ i ] = j;
				
			}
			
		}	
		
	}
	
	let v3 = new THREE.Vector3( );
	let cLen;
	let arg2 = [];
	arg2[ 0 ] = 0;
	
	for( let i = 0; i < idxCp.length - 1; i ++ ) {
		
		cLen = 0;
		
		for( let j = idxCp[ i ]; j < idxCp[ i + 1 ]; j ++ ) {
			
			v3.subVectors( g.cPoints[ j + 1 ], g.cPoints[ j ] );
			cLen += v3.length( );
			
		}
		
		arg2[ i + 1 ] = arg2[ i ] + cLen;
		
	}
	
	/*
	let pr2 = []; // points radial, vector 2
	
	for ( let j = 0; j < rdefCount; j ++ ) {
		
		let V2 = [ ];
		
		for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
			
			V2.push( new THREE.Vector2( arg2[ i ], dsgn[ i + 1 ][ j + 3 ] ) );
			
		}
		
		pr2.push( new THREE.SplineCurve( V2 ).getPoints( g.vertical ) );
		
	}
	
	*/
	
	let pr3 = []; // points radial, vector 3, z = 0
	
	for ( let j = 0; j < rdefCount; j ++ ) {
		
		let V3a = [ ];
		
		for ( let i = 0; i < g.dsgnCenters.length; i ++ ) {
			
			V3a.push( new THREE.Vector3( arg2[ i ], dsgn[ i + 1 ][ j + 3 ], 0 ) );
			
		}
		
		pr3.push( new THREE.CatmullRomCurve3( V3a ).getPoints( g.vertical ) );
		
	}
	
	g.pts2D = []; // all points orthogonal cut, vector 3 in plane,  y is 0
	let V3 = [];
	
	for ( let i = 0; i <= g.vertical; i ++ ) {
		
		V3 = [];
		
		for ( let j = 0 ; j < rdefCount; j ++  ) {
			
			//V3.push( new THREE.Vector3( -Math.cos( phi[ j ] ) * pr2[ j ][ i ].y,  0,  Math.sin(  phi[ j ] ) * pr2[ j ][ i ].y ) );
			V3.push( new THREE.Vector3( -Math.cos( phi[ j ] ) * pr3[ j ][ i ].y,  0,  Math.sin(  phi[ j ] ) * pr3[ j ][ i ].y ) );
			
		}
		
		g.pts2D.push( new THREE.CatmullRomCurve3( V3, connected ).getSpacedPoints( g.radial ) );
		
	}
	
	BasicGeometry( g.radial, g.vertical, wTop, wBtm ); // create a rudimentary geometry
	
	let idx = ( g.radial + 1 ) * ( g.vertical + 1 ) - 1; // // last index torso
	
	if( wTop ) {
		
		let zMax = -Infinity;
		let xMax = -Infinity;
		
		for ( let j = 0; j <= g.radial; j ++ ) {
			
			zMax = Math.abs( g.pts2D[ 0 ][ j ].z ) > zMax ? Math.abs( g.pts2D[ 0 ][ j ].z ) : zMax;
			xMax = Math.abs( g.pts2D[ 0 ][ j ].x ) > xMax ? Math.abs( g.pts2D[ 0 ][ j ].x ) : xMax;
			
		}
		
		idx ++; // center uv in BasicGeometry
		
		for( let j = 0; j <= g.radial ; j ++ ) {
			
			g.attributes.uv.setXY( ++ idx, 0.5 - g.pts2D[ 0 ][ j ].z / zMax / 2, 0.5 - g.pts2D[ 0 ][ j ].x / xMax / 2 );
			
		}
		
	}
	
	if( wBtm ) {
		
		let zMax = -Infinity;
		let xMax = -Infinity;
		
		for ( let j = 0; j <= g.radial; j ++ ) {
			
			zMax = Math.abs( g.pts2D[ g.vertical ][ j ].z ) > zMax ? Math.abs( g.pts2D[ g.vertical ][ j ].z ) : zMax;
			xMax = Math.abs( g.pts2D[ g.vertical ][ j ].x ) > xMax ? Math.abs( g.pts2D[ g.vertical ][ j ].x ) : xMax;
			
		}
		
		idx ++; // center uv in BasicGeometry
		
		for( let j = 0; j <= g.radial ; j ++ ) {
			
			g.attributes.uv.setXY( ++ idx, 0.5 + g.pts2D[ g.vertical ][ j ].z / zMax / 2, 0.5 - g.pts2D[ g.vertical ][ j ].x / xMax / 2 );
			
		}
		
	}	
	
	g.morph( g.cPoints ); // initial morph of BasicGeometry
	
	g.computeVertexNormals( );
	
	if ( connected ) { // calculate new normals at mantle seam
		
		for( let i = 0; i <= g.vertical; i ++ ) {
			
			smoothEdge( ( g.radial + 1 ) * i, ( g.radial + 1 ) * i + g.radial );
			
		}
		
	}
	
	if ( wTop && !flatTop ) { // calculate new normals at top seam
		
		for( let j = 0; j <= g.radial; j ++ ) {
			
			smoothEdge( ( g.radial + 1 ) * ( g.vertical + 1 ) + 1 + j, j );
			
		}
		
	}
	
	if ( wBtm && !flatBtm ) { // calculate new normals at bottom seam
		
		for( let j = 0; j <= g.radial ; j ++ ) {
			
			const offs = wTop ? + g.radial + 2 : 0;
			
			smoothEdge( ( g.radial + 1 ) * g.vertical + j, ( g.radial + 1 ) * ( g.vertical + 1 ) + offs + 1 + j );
			
		}
		
	}
	
	g.attributes.normal.needsUpdate = true;
	
	return g;

	// ............................................................................
	
	function BasicGeometry( radialSegments, heightSegments, withTop, withBottom ) {
		
		let indices = [];
		let uvs = [];
		
		let index = 0;
		let indexArray = [];
		let groupStart = 0; 
		
		let groupCount = 0;
		
		for ( let y = 0; y <= heightSegments; y ++ ) {
			
			let indexRow = [];
			
			let v = y / heightSegments;
			
			if ( symmetric ) { // texture mirror image
				
				for ( let x = 0; x <= radialSegments / 2; x ++ ) {
					
					uvs.push( x / ( radialSegments / 2 ), 1 - v );
					indexRow.push( index ++ );
					
				}
				
				for ( let x = radialSegments / 2 + 1 ; x <= radialSegments; x ++ ) {
					
					uvs.push( ( radialSegments - x ) / ( radialSegments / 2 ), 1 - v );
					indexRow.push( index ++ );
					
				}
			
			} else { // only one texture 
			
				for ( let x = 0; x <= radialSegments; x ++ ) {
					
					uvs.push( x / radialSegments, 1 - v );
					indexRow.push( index ++ );
					
				}
			}
			
			indexArray.push( indexRow );
			
		}
		
		let a, b, c, d;
		
		for ( let i = 0; i < radialSegments; i ++ ) {
			
			for ( let j = 0; j < heightSegments; j ++ ) {
				
				a = indexArray[ j ][ i ];
				b = indexArray[ j + 1 ] [ i ];
				c = indexArray[ j + 1 ][ i + 1 ];
				d = indexArray[ j ] [ i + 1 ];
				
				indices.push( a, b, d );
				indices.push( b, c, d );
				
				groupCount += 6;
				
			}
			
		}
		
		g.addGroup( groupStart, groupCount, 0 );
		
		groupStart += groupCount;
		
		let verticesCount = ( radialSegments + 1 ) * ( heightSegments + 1 )
		
		if ( wTop ) generateCap( true );
		if ( wBtm ) generateCap( false );
		
		g.setIndex( new THREE.BufferAttribute( new Uint32Array( indices ), 1 ) );
		g.setAttribute( 'position', new THREE.BufferAttribute( new Float32Array( verticesCount * 3 ), 3 ) );
		g.setAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( uvs ), 2 ) );
		
		function generateCap( top ) {
			
			let groupCount = 0;
			
			uvs.push( 0.5, 0.5 );
			
			const centerIndex = index; 
			
			for ( let x = 0; x <= radialSegments; x ++ ) {
				
				uvs.push( 0, 0 );
				index ++;
				
			}
			
			index ++;
			
			for ( let x = 1; x <= radialSegments; x ++ ) {
				
				const c = centerIndex;
				const i = centerIndex + x;
				
				if ( top ) {
					
					indices.push( i, i + 1, c );	// face top
					
				} else {
					
					indices.push( i + 1, i, c ); 	// face bottom
					
				}
				
				groupCount += 3;
				
			}
			
			g.addGroup( groupStart, groupCount, top ? 1 : 2 );
			
			groupStart += groupCount;
			
			verticesCount += radialSegments + 2; // with center 
			
		}
		
	}
	
	function smoothEdge( idxa, idxb ) {
		
		let v3a = new THREE.Vector3( );
		let v3b = new THREE.Vector3( );
		
		v3a.set( g.attributes.normal.getX( idxa ), g.attributes.normal.getY( idxa ), g.attributes.normal.getZ( idxa ) );
		v3b.set( g.attributes.normal.getX( idxb ), g.attributes.normal.getY( idxb ), g.attributes.normal.getZ( idxb ) );
		
		v3.addVectors( v3a, v3b ).normalize( );
		
		g.attributes.normal.setXYZ( idxa, v3.x, v3.y, v3.z );
		g.attributes.normal.setXYZ( idxb, v3.x, v3.y, v3.z );
		
	}

}

</script>
</html>
6 Likes

Finally, three-snek.js :pleading_face::heart:

4 Likes