Addon. Produces almost infinite many time-varying geometries with functions

No genius, this text was translated with DeepL Translate because my English is miserable!


I have just updated the alpha version with scaling functions to my page
http://sandboxthreep.threejs.hofk.de (GitHub will follow later.)

20171018-2037-24660

Videos are available on http://threejs.hofk.de .

For the hemispheres, scaling between start and end function is possible.

For the sphere, however, the scaling works only between the default values 0 and 1.

Several attempts with start and end function led to different positioning errors. Convex and concave functions react differently. If the evenings are getting longer again, I have to delve deeper into the mathematical theory, I’m not a genius. Maybe there is a simple solution - you just have to find it. :eyes:

THREEp’s latest update is now available at GitHub.

During tests I noticed that the sandboxes now also run under the latest browsers Chrome and Opera.


THREEf / sandbox I brought to the stand of three. js r87. This relates to FlatShading.

flatShading =	document.getElementById( "flatShading" ).checked;
	
	//shading =	flatshading ? THREE.FlatShading : THREE.SmoothShadig;  // old
	//	.flatShading: true or false; // three.js r87

In THREEf is now also the extended helper

function vertexFaceNumbersHelper (mesh, mode, size, color) {)

	mode: 0 nothing, 1 vertex, 2 face, 3 vertex & face

available. For Geometry and BufferGeometry. GitHub and my page.

I wrote some days ago:
If the evenings are getting longer again, I have to delve deeper into the mathematical theory, … Maybe there is a simple solution - you just have to find it.

I almost had the simple solution. No complicated mathematical theory is required.
Again and again I added the same small bug to my variants.:crazy_face:

The simple solution:

in function create()

if ( g.scaleH ) {

	//  ...HSE Hemisphere Start- End
	
	g.scalePoleHSE = function( u, v, t ) { return  1 - ( g.startPole( u, t ) + ( g.endPole( u, t ) - g.startPole(u, t ) ) * g.scalePoleH( v, t ) ) }
	
} else {

	// uses the decomposed scale function (South and North)

	g.scalePoleS = function( u, v, t ) { return 2 * ( g.startPole( u, t ) + ( g.endPole( u, t ) - g.startPole( u, t ) ) * g.scalePole(  v / 2, t ) ) }
	g.scalePoleN = function( u, v, t ) { return 2 * ( 1 - g.endPole( u, t ) + ( g.endPole( u, t ) - g.startPole( u, t ) ) * ( 1 - g.scalePole( 1 -  v / 2, t ) ) ) }
			
}

in function morphVertices( time )

if ( g.scaleH )  {
	
	// scaling between pole and equator (start- and endPole) per hemisphere --> south is like north
				
	theta = south_north * pi2 * g.scalePoleHSE( nji, nih, t );
				
} else {
	
	// scaling of the sphere between south and north (start- and endPole) with decomposed function
	
	theta = south_north * pi2 * ( 1 - ( south_north === SOUTH ? g.scalePoleS( nji, nih, t ) : g.scalePoleN( nji, nih, t ) ) );
	
}

Now on my side and GitHub.

20171028-2036-05801

1 Like

Just updated the sandbox. sandboxthreep.threejs at hofk.de In it you can now also create geometries from the three. js “triangle soup” non indexed BufferGeometry.

20171110-2111-17324

For non indexed the calculation of the vertex normals is still missing, for indexed the three. js internal function is currently used. However, it leads to unsightly seams and has to be replaced. That gives again an extensive computing as with THREEf.

Not yet on GitHub.

1 Like

The vertex normals are now available. I was able to copy some of THREEf’s stuff.


:new:

Now there is also an exploded view. Of course only for non indexed BufferGeometry.

And that was very easy thanks to the existing data.

Since I needed the face normals to calculate the vertex normals, they are stored in the data field g.faceNormal.
If you now add to each position a multiple of these face normals, the triangles will drift apart from the middle.

Since the triangles of bottom, top and wedge have identical normals (parallel) they are shifted in the same way and thus stay together.

Of course, the explode function only works with non-indexed. Note! explode only accepts the time parameter t.
See video at the moment http://threejs.hofk.de/ and below the sandbox. You can try it out there.

[ Not yet on GitHub.] UPDATE: Latest Versions on GitHub!

THREEf and THREEp with explode function

Even if THREE. Geometry doesn’t persist in the long run, it is still very much present in the documentation, the questions, the answers and examples. That’s why I extended THREEp to THREE. Geometry. This was not very complicated, because large blocks of indexed BufferGeometry and from THREEf could be copied. I only made the necessary changes.

However, the seams are not as easy to remove as with BufferGeometry. It’s a lot of effort. Or I don’t have the optimal idea yet.

The THREE. Geometry variant is now available in my sandbox - see links above.

20171120-2145-2423

Soon also on GitHub.

:b: E T :a:

I put the seams aside and added the specification of the material index per face by string data field. With THREEp it is quite easy to use the order of faces per hemisphere, the strings are for bottom or top (South/North, if available) for each spherical segment and for the top plane and wedge (if available).

Thus, the scope is now similar to THREEf and the beta phase is reached.

The beta version is available on my site and on GitHub.

Cool concepts @hofk. What do you plan to use it for? Do you have a specific application or use case you will apply this to?

Not yet.

It was created, as I described in the first article here.

Because I was a newcomer to three.js, I didn’t realize that only elementary geometries of the concept are meaningfully intended. That’s why I tried to do what I could, then it went on and on. Let’s see :eyes: what will become of it.


It happens to me sometimes.:crazy_face:
Some time ago I wanted to show my high school students how a microprocessor works. Then it became a complete simulation.

http://cpusimulation.de
https://xprofan.net/intl/de/anwendungen/8bit-cpu-simulation/

There is an .exe for windows and the source code
http://hofk.info/sourcecode/HC680_assembler.xprf
in a programming language I had used with the students.

But it’s only in German. Since I’m trying to improve my English, I’ll probably translate it into English.

3 Likes

THREEp has a fixed material for all faces.

:new:
For THREEf I have therefore added this option for the bottom and top.

The internal construction differs from THREEp and for this reason there are two extra properties.

fixedMatTop, // fixed given material index (string of digits for faces), overrides materialTop values
fixedMatBottom, // fixed given material index (string of digits for faces), overrides materialBottom values.

Available on my site.

20171130-1000-55403

GitHub will follow later.

fixed material - option for the bottom and the top now :new: also on GitHub | works with r89


THREEf creates morph BufferGeometry with a time parameter.

I wrote a small HTML/Script. :gear:

It outputs the elementary BufferGeometry definition for a selected time. The output can be easily copied with Ctrl+A and Ctrl+C. There is no further .innerHTML content on the page!

Try it out
http://sandboxthreef.threejs.hofk.de/modifyCreateGeo.html

The code:
Change or add the parameters - nearly at line 190. Here I have taken as default for radius and height 1/100 of THREEf. So 0.16 and 1, respectively!

(also on GitHub: THREEf.js/modifyCreateGeo.html at master · hofk/THREEf.js · GitHub)

updated Jan 18: decimals can be selected

<!DOCTYPE html>
<!--   *** create to modify  ***
	(for THREEf created buffer geometries)
/**
 * @author hofk / http://threejs.hofk.de/
*/
-->
<html lang="de">
<head>
	<title> create modify </title>
	<meta charset="utf-8" />
	
			<!-- designed for Firefox -->
	<style>
		input[type="number"] { width: 80px }
		input[type="range"] { width: 500px }
	</style>
	
	<script src="three.min.89.js"></script>
	<script src="OrbitControls.js"></script>
	<script src="THREEx.WindowResize.js"></script>
	<script src="THREEf.js"></script>
	
</head>
<body> 
	<button id="export_geo.js">export t=</button>
	
	<input type="radio" name="tInput" id="asNumber">
	<input type="number" id="tNumber" min="0" max="100" value="0" step="0.001" >
	<input type="radio" name="tInput" id="asRange"  checked="checked">
	<input type="range" id="tRange" min="0" max="100" value="0" step="0.001" > 
	
	<button id="dec">decimals=</button>
	<input type="number" id="nDec" min="1" max="21" value="3" step="1" >
	
	<button id="wirefr">wireframe</button>
	
	
	<div id="webGL" style="position: absolute; top: 40px; left: 0px; " >  </div>

	<div id="output" style="position: absolute; top: 600px; left: 5px; text-align: left;">
	
	NOTICE!<br />  
	<br />  
	If used functions with parameter t  <br />  
	choose a time before export. <br />
	<br /> 
	Then copy code with Ctrl+A Ctrl+C.
	
	</div> 								
	
</body>

<script>

'use strict'

var tNumber = document.getElementById( "tNumber" );
var tRange = document.getElementById( "tRange" );
var btnWireframe = document.getElementById( "wirefr" );

var scene = new THREE.Scene();

var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 2000 );

camera.position.set( 0, 1, 3 ); // possibly change if geometry is much larger than 1

var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xf1f1f1, 1 );	
var container = document.getElementById( 'webGL' );
container.appendChild( renderer.domElement ); 
THREEx.WindowResize( renderer, camera );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.enableZoom = true;

// material
var uvTex			= new THREE.TextureLoader().load( "uvgrid01.png" );			
var waterlilyTex	= new THREE.TextureLoader().load( "waterlily.png" );
//var earth			= new THREE.TextureLoader().load( "earth_nasa_map_900.png" );	
var side =  THREE.DoubleSide;
var wireframe = false;
var specular =  0x333333;
var flatShading = true;

	var materials = [
		
		new THREE.MeshBasicMaterial( {
			opacity: 0.15,	transparent: true, 	
			side: side, wireframe: wireframe } ),					//  0 transparent
			
		new THREE.MeshPhongMaterial( {
			color: 0x440033, emissive: 0x330033, specular: specular,
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  1 color
			
		new THREE.MeshPhongMaterial( {
			color: 0xff0000, emissive: 0xff0000, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  2 red
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ff00, emissive: 0x00ff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  3 green
			
		new THREE.MeshPhongMaterial( {
			color: 0x0000ff, emissive: 0x0000ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  4 blue
			
		new THREE.MeshPhongMaterial( {
			color: 0xffff00, emissive: 0xffff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  5 yellow
			
		new THREE.MeshPhongMaterial( {
			color: 0xff00ff, emissive: 0xff00ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  6 mgenta
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ffff, emissive: 0x00ffff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  7 cyan
			
		new THREE.MeshBasicMaterial( {
			map: uvTex,			
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  8 uv grid
			
		new THREE.MeshBasicMaterial( {
			map: waterlilyTex,	
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  9 photo
			
		new THREE.MeshPhongMaterial( {
			color: 0x444444, emissive: 0x444444, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } )	// 10 grey
		
	];

var sun1 = new THREE.DirectionalLight( );
sun1.intensity = 0.6;  
sun1.position.set(5, 20, 30 );
scene.add( sun1 );
var sun2 = new THREE.DirectionalLight( );
sun2.intensity = 0.5;  
sun2.position.set( -5, -20, 30 );
scene.add( sun2 );

//aviable parameters
/*
indexed: false,
radius:		0.16,
height:		1,
radiusSegments: ,
heightSegments: ,
circOpen:	true,	
withTop:	true,
fixedMatTop: "",
withBottom: true,
fixedMatBottom: "",
waffled:	true,
quadLine:	true,
quadColor: 0x000000,
style: 'cover',
rCircHeight:	function ( u, v, t ) { return },
centerX:		function ( v, t ) { return  },
centerY:		function ( v, t ) { return  },
centerZ:		function ( v, t ) { return  },
unrollCover:	function ( v, t ) { return },
waffleDeep:		function ( u, v, t ) { return  },
moveX:			function ( u, v, t ) { return  },
moveY:			function ( u, v, t ) { return  },
moveZ:			function ( u, v, t ) { return  },
endCircAngle:	function ( v, t ) { return  },	
startCircAngle:	function ( v, t ) { return  },	
scaleCircAngle:	function ( u, t ) { return  },	
topHeight:		function ( u, t ) { return  },
bottomHeight:	function ( u, t ) { return  },		
scaleHeight:	function ( v, t ) { return  },	
materialTop:	function ( u, t ) { return  },
materialBottom:	function ( u, t ) { return  },
materialCover:	function ( u, v, t ) { return  },	
fixedMaterial: [],	
centerPoints:	[ [ .6, 0, 0 ], [ .5, 0, 0 ], [ .4, 0, 0 ],[ .3, 0, 0 ], [ .2, 0, 0 ],[ .1, .01, 0 ], [ .02, .07, 0 ], [ 0, .1 , 0], [ .02, .13, 0], [ .1, .21, 0],  [ .2, .28, 0], [ .3, .35 , 0], [ .4, .42 , 0], [ .5, .5 , 0],[ 0.58, .6 , 0 ],[ .6, .7, 0 ], [ .58, .8, 0 ], [ .51, .9, 0], [ .44, .95, 0], [ .38, .98, 0], [ .3, 1, 0 ],[ .2, .99, 0 ], [ .1, .94, 0 ], [ 0, 0.88, 0 ] ], // (only example)
 
*/ 

// create a THREEf BufferGeometry with your parameters

var parameters = {

	// take, if possible approximate (or change the camera position at the top)
	radius: 0.16,	// default from THREEf / 100
	height: 1,		// default from THREEf / 100 
	
	// further parameters of your choice
	radiusSegments: 5,
	heightSegments: 6,
	quadLine:	true,
	quadColor: 0x110011,
	rCircHeight: function ( u, v, t ) { return  (0.03 * ( t - 50 ) - v ) * (0.02 * ( t - 50 ) - v ) },

		
}

var geo = new THREE.BufferGeometry();
geo.createMorphGeometry = THREEf.createMorphGeometry;
geo.createMorphGeometry( parameters );

// mesh
var mesh = new THREE.Mesh( geo, materials );
scene.add( mesh );

if ( parameters.quadLine ) mesh.add( geo.quadLine );

btnWireframe.onclick = showWireframe;
document.getElementById( "export_geo.js" ).onclick = outputJavaScript;

var time;
 
animate();

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

function showWireframe( ) {

	wirefr = !wirefr;
	btnWireframe.innerHTML = wirefr === true ? "no wirefr." : "wireframe";
	for ( var m = 0; m < materials.length; m ++ ) materials[ m ].wireframe = wirefr;
	
}

function outputJavaScript( ) {
	
	var vc3 = geo.vertices.length;
	var fic = geo.faceIndices.length;
	var fc = geo.faceIndices.length / 3;	
	var uvc2 = geo.uvs.length;	
	var out = "";
	var ffd = Math.pow( 10, nDec.value );
	
	function round( x ) {
	
		return Math.floor( x * ffd ) / ffd;
		
	}

	// --- generate JavaScript code ---
	
	output.innerHTML  = " /* t = " + time + " (static timestamp) 	 <br />  	BufferGeometry generated with addon THREEf  <br />	var geo = new THREE.BufferGeometry();  <br /> geo.createMorphGeometry = THREEf.createMorphGeometry; <br /> geo.createMorphGeometry( { ";
	
	Object.keys( parameters ).forEach ( function ( val, key ) {
	
		if( val !== 'quadColor' ) {
		
			output.innerHTML +=  val + ": " + parameters[ val ] + ", ";
		
		} else {
			
			output.innerHTML +=  val + ": 0x" + parameters[ val ].toString(16) + ", ";
		
		}
		
	
	} );
	
	output.innerHTML  += " } ); <br /> */ <br />";
	
	out += "var geo = new THREE.BufferGeometry(); <br />";
	
	out += "geo.faceIndices = new Uint32Array( [ ";
	
	for ( var i = 0; i < fic; i ++ ) {
	
		out += geo.faceIndices[ i ];
		out += i < fic - 1  ? ", " : "";
		
	}
	
	out += " ] ); <br /> ";
	
	out += "geo.vertices = new Float32Array( [ ";
	
	for ( var v = 0; v < vc3 ; v ++ ) {
		
		out += round( geo.vertices[ v ] );
		out += v < vc3 - 1  ? ", " : "";
		
	}
	
	out += " ] ); <br /> ";
	
	out += "geo.normals = new Float32Array( [ ";
	
	for ( var v = 0; v < vc3 ; v ++ ) {
		
		out +=  round( geo.normals[ v ] );
		out += v < vc3 - 1  ? ", " : "";
		
	}
	
	out += " ] ); <br /> "
	
	out += "geo.uvs = new Float32Array( [ ";
	
	for ( var v = 0; v < uvc2 ; v ++ ) {
		
		out +=  round( geo.uvs[ v ] );
		out += v < uvc2 - 1  ? ", " : "";
		
	}
	
	out += " ] ); <br /> "
	
	out += "geo.setIndex( new THREE.BufferAttribute( geo.faceIndices, 1 ) ); <br /> geo.addAttribute( 'position', new THREE.BufferAttribute( geo.vertices, 3 ).setDynamic( true ) ); <br />geo.addAttribute( 'normal', new THREE.BufferAttribute( geo.normals, 3 ).setDynamic( true ) ); <br /> geo.addAttribute( 'uv', new THREE.BufferAttribute( geo.uvs, 2 ) );<br /> ";
	
	out += "var geoGrp = [ ";
	
	for ( var f = 0, p = 0; f < fc ; f ++, p += 3 ) { 
		
		out += geo.groups[ f ].start + ", " + geo.groups[ f ].count + ", " + geo.groups[ f ].materialIndex ;
		out += f < fc - 1  ? ", " : "";
	}
	
	out += " ]; <br /> ";
	
	out +=  " for ( var f = 0, p = 0; f < " + fc + "; f ++, p += 3 ) { geo.addGroup( geoGrp[ p ], geoGrp[ p + 1 ], geoGrp[ p + 2 ] ); } <br /> ";
	
	if ( geo.quadLine ) {
		
		out += "geo.lineGeometry = new THREE.BufferGeometry(); <br />";
		out += "geo.quadColor = 0x" + geo.quadColor.toString(16) + "; <br />";
		out += "geo.quadLine = new THREE.Line( geo.lineGeometry, new THREE.LineBasicMaterial( { color: geo.quadColor } ) ); <br />";
		
		out += "geo.linePositions = new Float32Array( [ ";
		
		var glc = geo.linePositions.length;
		
		for ( var v = 0; v < glc; v ++ ) {
			
			out +=  round( geo.linePositions[ v ] );
			out += v < glc - 1  ? ", " : "";
			
		}
		
		out += " ] ); <br /> ";
		
		out += "geo.lineGeometry.addAttribute( 'position', new THREE.BufferAttribute( geo.linePositions, 3 ) );  <br />";
		
	}
	
	output.innerHTML += out;
	
}

function animate() {

	requestAnimationFrame( animate );
	
	if ( asNumber.checked ) {
	
		time = tNumber.value;
		tRange.value = time;
	}
		
	if ( asRange.checked ) {
		
		time = tRange.value;
		tNumber.value = time;
		
	}
	
	geo.morphVertices( time );			
		
	renderer.render( scene, camera );
	controls.update();
	
}
</script>

</html>
1 Like

Just a thought: instead of offering a user to press ctrl + A, ctrl + C, just leave there a button which copies output to clipboard.

Thanks for the tip.

I’m probably still in the time when there were buttons on the typewriter and the first computer keyboards. Otherwise only commands!

But there is a simple solution. Just four extra lines. If you click on the export button, the code will also be copied to the clipboard. The input "code " shows the timestamp. If you change the time, you have a comparison in mind.

<input type="text" value="" id="code" size="20"   >


	code.value = output.innerHTML;
	document.getElementById("code").select();
	document.execCommand("Copy");

I’m gonna put this in.


update:
tested disabled=“true” doesn’t work!

1 Like

I wrote “But there is a simple solution. Just four extra lines.”

Looking more closely, I noticed that br tags are copied and destroy the code.
But thanks to the help of prisoner849 :star:, the problem is solved.

This is from him:


String.prototype.replaceAll = function( search, replacement ) {
	var target = this;
	return target.split( search ).join( replacement );
};

Then I adapted the application.

New text:

The code is displayed here
and is also copied to the clipboard.

<textarea id="code" cols="1" rows="1" style="position: absolute; top: 0px; left: -10px; width: 1px; height: 1px"> </textarea>

output.innerHTML += out;
code.value = "";
code.value = output.innerHTML.replaceAll('<br>', '\r\n'); // from prisoner849
document.getElementById("code").select();
document.execCommand("Copy");

Tested Firefox, opera, chrome with left: -10px -ok!
Only edge has a little problem and shows a little bit.

My site and Github are updated.

In order to give edge a chance, prisoner849 has pointed me to this page.
https://codepen.io/Mestika/pen/NxLzNq

This doesn’t require any more effort and probably works for all browsers?
Even Android Browser (Tablet) can do it!
I can’t test Apple Safari!

output.innerHTML += out;	
code = document.createElement( "textarea" );
code.value = output.innerHTML.replaceAll( '<br>', '\r\n' ).replaceAll( '&lt;', '<' ).replaceAll( '&gt;', '>' );
document.body.appendChild( code );
code.select();
document.execCommand( "Copy" );
document.body.removeChild( code );

On my site and GitHub.

1 Like

:white_check_mark: THREEp finished


About a year ago, I started :arrow_forward: with the geometry generated by functions.

That was a first result.
20180127-1726-52920

With still well under 1000 lines :writing_hand: of code.

Now also the addon THREEp is finished. :stop_button::next_track_button:

Also some examples and a form library are now available on my site http://sandbox.threejs.hofk.de/ and GitHub hofk (Klaus Hoffmeister) · GitHub.

The library is still under construction.
form library THREEp.js

However, I have refrained from removing the seams on Geometry. The effort is disproportionately high. Especially since Geometry has no long-term future.


:heavy_plus_sign:
For THREEf there is a further tool :gear: at
Modify indexed BufferGeometry (mouse or input) available.

1 Like

The addons THREEf and THREEp were updated. :white_check_mark:


They now support two exploded view modes in r90 - as in the THREEg addon.
It runs with revision 90 of three.js.

  • available on GitHub

Inside the sandbox:

20180310-2110-4267

20180310-2110-23784

if non indexed 20180310-2110-07449

‘center’ is default now!

This simple example shows the difference.

waffled grid,
centerY is v * v (centerpoint y by height)
and an explode of 0.1.

‘center’ 20180311-2052-14272

‘normal’ 20180311-2051-52496

I have extended the HTML/Script modifyCreateGeo.html (see above here) to non-indexed BufferGeometry.

https://github.com/hofk/THREEf.js/tree/dev/THREEf_90
(old version: modifyCreateGeoOnlyIndexed.html
new version: modifyCreateGeo.html)

Try the new version on my site now: http://sandboxthreef.threejs.hofk.de/modifyCreateGeo.html


<!DOCTYPE html>
<!--   *** create to modify  ***
	(for THREEf created indexed or non indexed buffer geometries)
/**
 * @author hofk / http://threejs.hofk.de/
*/
-->
<html lang="de">
<head>
	<title> create modify </title>
	<meta charset="utf-8" />
	
			<!-- designed for Firefox -->
	<style>
		input[type="number"] { width: 80px }
		input[type="range"] { width: 500px }
	</style>
	
	<script src="three.min.90.js"></script>
	<script src="OrbitControls.js"></script>
	<script src="THREEx.WindowResize.js"></script>
	<script src="THREEf.js"></script>
	
</head>

<body> 
	
	<button id="export_geo.js">export t=</button>
	
	<input type="radio" name="tInput" id="asNumber">
	<input type="number" id="tNumber" min="0" max="100" value="0" step="0.001" >
	<input type="radio" name="tInput" id="asRange"  checked="checked">
	<input type="range" id="tRange" min="0" max="100" value="0" step="0.001" > 
	
	<button id="dec">decimals=</button>
	<input type="number" id="nDec" min="1" max="21" value="3" step="1" >
	
	<button id="wirefr">wireframe</button>
	
	<div id="webGL" style="position: absolute; top: 40px; left: 0px; " >  </div>
	
	<div id="output" style="position: absolute; top: 600px; left: 5px; text-align: left;">
	
	NOTICE!<br />
	<br />
	If used functions with parameter t  <br />
	choose a time before export. <br />
	<br />
	The code is displayed here <br />
	and is also copied to the clipboard.
	
	</div>
	
</body>

<script>
'use strict'
var tNumber = document.getElementById( "tNumber" );
var tRange = document.getElementById( "tRange" );
var btnWireframe = document.getElementById( "wirefr" );
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.01, 2000 );
camera.position.set( 0, 1, 3 ); // possibly change if geometry is much larger than 1
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xf1f1f1, 1 );
var container = document.getElementById( 'webGL' );
container.appendChild( renderer.domElement );
THREEx.WindowResize( renderer, camera );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
//controls.enableZoom = true;
// material
var uvTex			= new THREE.TextureLoader().load( "uvgrid01.png" );
var waterlilyTex	= new THREE.TextureLoader().load( "waterlily.png" );
//var earth			= new THREE.TextureLoader().load( "earth_nasa_map_900.png" );
var specular =  0x333333;
var side =  THREE.DoubleSide;
var wireframe = false;
var flatShading = true;
	var materials = [
		
		new THREE.MeshBasicMaterial( {
			opacity: 0.15,	transparent: true, 	
			side: side, wireframe: wireframe } ),					//  0 transparent
			
		new THREE.MeshPhongMaterial( {
			color: 0x440033, emissive: 0x330033, specular: specular,
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  1 color
			
		new THREE.MeshPhongMaterial( {
			color: 0xff0000, emissive: 0xff0000, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  2 red
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ff00, emissive: 0x00ff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  3 green
			
		new THREE.MeshPhongMaterial( {
			color: 0x0000ff, emissive: 0x0000ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  4 blue
			
		new THREE.MeshPhongMaterial( {
			color: 0xffff00, emissive: 0xffff00, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  5 yellow
			
		new THREE.MeshPhongMaterial( {
			color: 0xff00ff, emissive: 0xff00ff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  6 mgenta
			
		new THREE.MeshPhongMaterial( {
			color: 0x00ffff, emissive: 0x00ffff, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  7 cyan
			
		new THREE.MeshBasicMaterial( {
			map: uvTex,
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  8 uv grid
			
		new THREE.MeshBasicMaterial( {
			map: waterlilyTex,	
			side: side, wireframe: wireframe, flatShading: flatShading } ),	//  9 photo
			
		new THREE.MeshPhongMaterial( {
			color: 0x444444, emissive: 0x444444, specular: specular, 
			side: side, wireframe: wireframe, flatShading: flatShading } )	// 10 grey
		
	];
var sun1 = new THREE.DirectionalLight( );
sun1.intensity = 0.6;
sun1.position.set(5, 20, 30 );
scene.add( sun1 );
var sun2 = new THREE.DirectionalLight( );
sun2.intensity = 0.5;  
sun2.position.set( -5, -20, 30 );
scene.add( sun2 );
//aviable parameters
/*
indexed: false,
radius:		0.16,
height:		1,
radiusSegments: ,
heightSegments: ,
circOpen:	true,
withTop:	true,
fixedMatTop: "",
withBottom: true,
fixedMatBottom: "",
waffled:	true,
quadLine:	true,
quadColor: 0x000000,
style: 'cover',
rCircHeight:	function ( u, v, t ) { return },
centerX:		function ( v, t ) { return  },
centerY:		function ( v, t ) { return  },
centerZ:		function ( v, t ) { return  },
unrollCover:	function ( v, t ) { return },
waffleDeep:		function ( u, v, t ) { return  },
moveX:			function ( u, v, t ) { return  },
moveY:			function ( u, v, t ) { return  },
moveZ:			function ( u, v, t ) { return  },
endCircAngle:	function ( v, t ) { return  },
startCircAngle:	function ( v, t ) { return  },
scaleCircAngle:	function ( u, t ) { return  },
topHeight:		function ( u, t ) { return  },
bottomHeight:	function ( u, t ) { return  },
scaleHeight:	function ( v, t ) { return  },
materialTop:	function ( u, t ) { return  },
materialBottom:	function ( u, t ) { return  },
materialCover:	function ( u, v, t ) { return  },
fixedMaterial: [],
centerPoints:	[ [ .6, 0, 0 ], [ .5, 0, 0 ], [ .4, 0, 0 ],[ .3, 0, 0 ], [ .2, 0, 0 ],[ .1, .01, 0 ], [ .02, .07, 0 ], [ 0, .1 , 0], [ .02, .13, 0], [ .1, .21, 0],  [ .2, .28, 0], [ .3, .35 , 0], [ .4, .42 , 0], [ .5, .5 , 0],[ 0.58, .6 , 0 ],[ .6, .7, 0 ], [ .58, .8, 0 ], [ .51, .9, 0], [ .44, .95, 0], [ .38, .98, 0], [ .3, 1, 0 ],[ .2, .99, 0 ], [ .1, .94, 0 ], [ 0, 0.88, 0 ] ], // (only example)
 
*/
// create a THREEf BufferGeometry with your parameters
var parameters = {
	// take, if possible approximate (or change the camera position at the top)
	radius: 0.16,	// default from THREEf / 100
	height: 1,		// default from THREEf / 100 
	
	// further parameters of your choice
	indexed: false, // or take indexed: true (is also default)
	radiusSegments: 3,
	heightSegments: 3,
	quadLine: true,
	quadColor: 0x110011,
	rCircHeight: function ( u, v, t ) { return  v > 0.5 ? v - 0.04 * t : v + 0.02 * t },
}
var geo = new THREE.BufferGeometry();
geo.createMorphGeometry = THREEf.createMorphGeometry;
geo.createMorphGeometry( parameters );
// mesh
var mesh = new THREE.Mesh( geo, materials );
scene.add( mesh );
if ( parameters.quadLine ) mesh.add( geo.quadLine );
btnWireframe.onclick = showWireframe;
document.getElementById( "export_geo.js" ).onclick = outputJavaScript;
var time;
String.prototype.replaceAll = function( search, replacement ) {
	var target = this;
	return target.split( search ).join( replacement );
	
};
 
animate();
//.........................
function showWireframe( ) {
	wireframe = !wireframe;
	btnWireframe.innerHTML = wireframe ? "no wirefr." : "wireframe";
	for ( var m = 0; m < materials.length; m ++ ) materials[ m ].wireframe = wireframe;
	
}
function outputJavaScript( ) {
	
	var gc3 = geo.indexed ? geo.vertices.length : geo.positions.length;
	var fic = geo.indexed ? geo.faceIndices.length : null;
	var fc  = geo.indexed ? geo.faceIndices.length / 3 : geo.positions.length / 9;
	var uvc2 = geo.uvs.length;	
	var out = "";
	var ffd = Math.pow( 10, nDec.value );
	var code;
	
	function round( x ) {
		
		return Math.floor( x * ffd ) / ffd;
		
	}
	// --- generate JavaScript code ---
	var indextype = geo.indexed ? "indexed" : "non indexed";
	output.innerHTML  = "/* t = " + time + " (static timestamp)<br />" + indextype + " BufferGeometry generated with addon THREEf<br />var geo = new THREE.BufferGeometry();<br />geo.createMorphGeometry = THREEf.createMorphGeometry;<br />geo.createMorphGeometry( { ";
	
	Object.keys( parameters ).forEach ( function ( val, key ) {
	
		if( val !== 'quadColor' ) {
			
			output.innerHTML +=  val + ": " + parameters[ val ] + ", ";
			
		} else {
			
			output.innerHTML +=  val + ": 0x" + parameters[ val ].toString(16) + ", ";
			
		}
		
	
	} );
	
	output.innerHTML  += " } ); <br /> */<br />";
	
	out += "var geo = new THREE.BufferGeometry();<br />";
	
	out +=  geo.indexed ? "geo.faceIndices = new Uint32Array( [ " : "geo.positions = new Float32Array( [ ";
	
	if ( geo.indexed ) {
	
		for ( var i = 0; i < fic; i ++ ) {
			
			out += geo.faceIndices[ i ];
			out += i < fic - 1  ? ", " : "";
			
		}
		
		out += " ] );<br />";
		
		out += "geo.vertices = new Float32Array( [ ";
		
		for ( var v = 0; v < gc3 ; v ++ ) {
			
			out += round( geo.vertices[ v ] );
			out += v < gc3 - 1  ? ", " : "";
			
		}
		
	} else {	// non indexed
		
		for ( var p = 0; p < gc3 ; p ++ ) {
			
			out += round( geo.positions[ p ] );
			out += p < gc3 - 1  ? ", " : "";
			
		}
	
	}
	
	out += " ] );<br />";
	
	out += "geo.normals = new Float32Array( [ ";
	
	for ( var v = 0; v < gc3 ; v ++ ) {
		
		out +=  round( geo.normals[ v ] );
		out += v < gc3 - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />"
	
	out += "geo.uvs = new Float32Array( [ ";
	
	for ( var v = 0; v < uvc2 ; v ++ ) {
		
		out +=  round( geo.uvs[ v ] );
		out += v < uvc2 - 1  ? ", " : "";
		
	}
	
	out += " ] );<br />"
	
	if ( geo.indexed ) {
		
		out += "geo.setIndex( new THREE.BufferAttribute( geo.faceIndices, 1 ) );<br />geo.addAttribute( 'position', new THREE.BufferAttribute( geo.vertices, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'normal', new THREE.BufferAttribute( geo.normals, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'uv', new THREE.BufferAttribute( geo.uvs, 2 ) );<br />";
	
	} else { // non indexed
		
		out += "geo.addAttribute( 'position', new THREE.BufferAttribute( geo.positions, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'normal', new THREE.BufferAttribute( geo.normals, 3 ).setDynamic( true ) );<br />geo.addAttribute( 'uv', new THREE.BufferAttribute( geo.uvs, 2 ) );<br />";
		
	}
	
	out += "var geoGrp = [ ";
	
	for ( var f = 0, p = 0; f < fc ; f ++, p += 3 ) {
		
		out += geo.groups[ f ].start + ", " + geo.groups[ f ].count + ", " + geo.groups[ f ].materialIndex ;
		out += f < fc - 1  ? ", " : "";
		
	}
	
	out += " ]; <br />";
	
	out += "for ( var f = 0, p = 0; f < " + fc + "; f ++, p += 3 ) { geo.addGroup( geoGrp[ p ], geoGrp[ p + 1 ], geoGrp[ p + 2 ] ); }<br />";
	
	if ( geo.quadLine ) {
		
		out += "geo.lineGeometry = new THREE.BufferGeometry();<br />";
		out += "geo.quadColor = 0x" + geo.quadColor.toString(16) + ";<br />";
		out += "geo.quadLine = new THREE.Line( geo.lineGeometry, new THREE.LineBasicMaterial( { color: geo.quadColor } ) );<br />";
		
		out += "geo.linePositions = new Float32Array( [ ";
		
		var glc = geo.linePositions.length;
		
		for ( var v = 0; v < glc; v ++ ) {
			
			out +=  round( geo.linePositions[ v ] );
			out += v < glc - 1  ? ", " : "";
			
		}
		
		out += " ] );<br />";
		
		out += "geo.lineGeometry.addAttribute( 'position', new THREE.BufferAttribute( geo.linePositions, 3 ) );<br />";
		
	}	
	
	output.innerHTML += out;
	
	code = document.createElement( "textarea" );
	code.value = output.innerHTML.replaceAll( '<br>', '\r\n' ).replaceAll( '&lt;', '<' ).replaceAll( '&gt;', '>' );
	document.body.appendChild( code );
	code.select();
	document.execCommand( "Copy" );
	document.body.removeChild( code );
	
}
function animate() {
	
	requestAnimationFrame( animate );
	
	if ( asNumber.checked ) {
	
		time = tNumber.value;
		tRange.value = time;
	}
	
	if ( asRange.checked ) {
		
		time = tRange.value;
		tNumber.value = time;
		
	}
	
	geo.morphVertices( time );
	
	renderer.render( scene, camera );
	controls.update();
	
}
</script>

</html>

Profiled Contour Geometry

Whether your plugin can generate a time-varying geometry tube by passing in a series of path points, the official 3D tube looks like time-varying geometry, I saw your case THREEF or THREEG can achieve

Not sure what exactly you need.

In the answer to your last question
Any better implementation of ExtrudeGeometry?

in the example https://hofk.de/main/threejs/sandboxthreef/examplesTHREEf%20r90.html you see a coil spring.

// coil spring
radius: 2,
height: 160,
radiusSegments: 16,
heightSegments: 500,
centerX: function ( v, t ) { return 0.5 * Math.sin( 10 * Math.PI * v ) },
centerY: function ( v, t ) { return v * ( v + 0.4 * ( 1 + Math.sin( t ) ) ) },
centerZ: function ( v, t ) { return 0.5 * Math.cos( 10 * Math.PI * v ) }

This is only a possible example. Time functions (for x,y,z) must be specified for the points.

If you only specify points as coordinates, they are not time variable.

34