Sphere without use of trigonometric functions

geometry
buffergeometry
sphere
#1

In general, trigonometric functions are used when calculating circles and spheres.

But it is also very easy without these functions.

The construction of the sphere is made of 8 parts similar to my magic sphere in the addon THREEg. Addon to create special / extended geometries
20190401-2107-53395 The basis is the logo of discourse. threejs. org
Here I have now used indexed BufferGeometry.20190401-2048-31168


Correction April 15th

Working on an example, I looked at the sphere again. I noticed a mistake. Since I often only worked with THREE.DoubleSide and wireframe: true, I didn’t notice it.
Half of the octants have the front inside! :frowning_face:

I have to reverse the order of the indices there.

I have corrected the function indicesPartSphere( p ) and use
spin = ( p === 0 || p === 2 || p === 5 || p === 7 ) ? true : false;


Here you can find it in the collection.
http://discourse.threejs.hofk.de/2019/SphereWithoutTrigonometry/SphereWithoutTrigonometry.html


The complete code:

<head>
	<title> SphereWithoutTrigonometry </title>
	<meta charset="utf-8" />
</head>
<body> 	</body>
<script src="../js/three.min.103.js"></script>
<script src="../js/OrbitControls.js"></script>

<script>

// @author hofk

'use strict';

const sumNN = ( n ) => ( n * ( n + 1 ) / 2 );	// Sum natural numbers
const sumON = ( n ) => ( n * n );		// Sum odd numbers

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.1, 100 );
camera.position.set( 0, 1, 8 );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xdddddd, 1 );
const container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement ); 
const controls = new THREE.OrbitControls( camera, renderer.domElement );

const tex1 = new THREE.TextureLoader().load( 'uvgrid01.png' );
//const material = new THREE.MeshBasicMaterial( { map: tex1, side: THREE.DoubleSide } );
const tex2 = new THREE.TextureLoader().load( 'dahlia.png' );

const material = [
	new THREE.MeshBasicMaterial( { map: tex1, side: THREE.DoubleSide } ),
	new THREE.MeshBasicMaterial( { color: 0xff0000, side: THREE.DoubleSide, wireframe: true } ),
	new THREE.MeshBasicMaterial( { color: 0xff00ff, side: THREE.DoubleSide } ),
	new THREE.MeshBasicMaterial( { color: 0x00ffff, side: THREE.DoubleSide, wireframe: true } ),
	new THREE.MeshBasicMaterial( { color: 0xffff00, side: THREE.DoubleSide } ),
	new THREE.MeshBasicMaterial( { map: tex2, side: THREE.DoubleSide, wireframe: false } ),
	new THREE.MeshBasicMaterial( { color: 0x33ff55, side: THREE.DoubleSide } ),
	new THREE.MeshBasicMaterial( { map: tex1, side: THREE.DoubleSide } )
];

const g = new THREE.BufferGeometry( );

//SphereWithoutTrigonometry( g, 2.5, 10, [ 1,1,0,0, 1,0,1,0 ] );  //  ( BufferGeometry, radius, equator, parts )
	//equator = half of height segments = quarter of equatorial segments
SphereWithoutTrigonometry( g, 3,18 );
const mesh = new THREE.Mesh( g, material );
scene.add( mesh );

animate();

function animate() {
	
	requestAnimationFrame( animate );
	renderer.render( scene, camera );
	controls.update();
	
}

function SphereWithoutTrigonometry( g, r, eqt, parts ) { //  ( BufferGeometry, optional: radius, equator, parts )

	r = r !== undefined ? r : 1;
	eqt = eqt !== undefined ? eqt : 8;
	
	// parts array, value 1 for octant, otherwise arbitrary - upper counterclockwise, lower clockwise seen from below 
	parts = parts !== undefined ? parts : [ 1,1,1,1, 1,1,1,1 ];
	
	let pCount = 0;
	
	for ( let p = 0; p < 8; p ++ ) {
		
		pCount += parts[ p ] === 1 ? 1 : 0; 
		
	}
	
	let posIdx = 0;	// position index
	let uvIdx = 0;	// uv index
	let fIdx = 0;	// face index
	
	const vertexCount = sumNN( eqt + 1 ) * pCount;
	const faceCount = sumON( eqt ) * pCount;
	
	g.faceIndices = new Uint32Array( faceCount * 3 );
	g.vertices = new Float32Array( vertexCount * 3 );
	g.uvs = new Float32Array( vertexCount * 2 );
	
	g.setIndex( new THREE.BufferAttribute( g.faceIndices, 1 ) );
	g.addAttribute( 'position', new THREE.BufferAttribute( g.vertices, 3 ) );
	g.addAttribute( 'uv', new THREE.BufferAttribute( g.uvs, 2 ) );
	
	pCount = 0;
	
	for ( let p = 0; p < 8; p ++ ) {
		
		if (  parts[ p ] === 1 ) {
			
			indicesPartSphere( p );
			verticesUVsPartSphere( p );
			pCount += 1;
			
		}
		
	}
	
	function indicesPartSphere( p ) {
		
		let a0, a1, b0, b1;	// indices
	let spin = ( p === 0 || p === 2 || p === 5 || p === 7 ) ? true : false;
	
		a0 = sumNN( eqt + 1 ) * pCount; // start vertex index per part
		
		g.addGroup( fIdx, sumON( eqt ) * 3, p ); // write groups for multi material
		
		for ( let i = 0; i < eqt; i ++ ) {
			
			a1 = a0 + 1;
			b0 = a0 + i + 1; //  below ( i ) 
			b1 = b0 + 1;
			
			// each two faces
			
			for ( let j = 0; j < i; j ++ ) {
			
			g.faceIndices[ fIdx     ] = j + a0;			// left face
			g.faceIndices[ fIdx + 1 ] = j + ( spin ? b1 : b0 );
			g.faceIndices[ fIdx + 2 ] = j + ( spin ? b0 : b1 );
			
			g.faceIndices[ fIdx + 3 ] = j + a0;			// right face
			g.faceIndices[ fIdx + 4 ] = j + ( spin ? a1 : b1 );
			g.faceIndices[ fIdx + 5 ] = j + ( spin ? b1 : a1 );	
				
				fIdx += 6;
				
			}
		
		g.faceIndices[ fIdx     ] = i + a0;	// last face in row ( like a left face )
		g.faceIndices[ fIdx + 1 ] = i + ( spin ? b1 : b0 );
		g.faceIndices[ fIdx + 2 ] = i + ( spin ? b0 : b1 );
			
			fIdx += 3;
			
			a0 += i + 1; // next start index
			
		}
	
	}
	
	function verticesUVsPartSphere( p ) {
		
		const signX = ( p === 0 || p === 3 || p === 4 || p === 7 ) ? 1 : -1;
		const signY = p < 4 ? 1 : -1; 
		const signZ = ( p === 2 || p === 3 || p === 6 || p === 7 ) ? 1 : -1;
		
		let xp, yp, zp, len;
		
		let ux = 0.5;	
		let du = 1 / eqt;
		let uy = 1 + du;	
		
		for ( let i = 0 ; i <= eqt; i ++ ) {
			
			yp =  1 -  i / eqt;
			
			ux =  0.5 + signX * signY * signZ * ( i + 2 ) * du / 2;
			uy -= du;
			
			for ( let j = 0; j <= i ; j ++ ) {
				
				xp =  ( i -  j ) / eqt;
				zp =  j / eqt;
				
				len = Math.sqrt( xp * xp + yp * yp + zp * zp ); // to normalize
				
				g.vertices[ posIdx     ] = r * signX * xp / len;
				g.vertices[ posIdx + 1 ] = r * signY * yp / len;
				g.vertices[ posIdx + 2 ] = r * signZ * zp / len;
				
				posIdx += 3;
				
				ux += -signX * signY * signZ * du;
				
				g.uvs[ uvIdx ] = ux;
				g.uvs[ uvIdx + 1 ] = uy;
				
				uvIdx  += 2;		
						
			}
				
		}
		
	}
	
 }

</script>

</html>
2 Likes
#2

You can do this with three.js:

const box = new THREE.BoxGeometry(1,1,1,12,12,12)
const sphere =  box.clone().vertices.forEach(vertex=>vertex.normalize())
1 Like
#3

Yes, but it’s not identical. It’s also not a buffer geometry.

const g = new THREE.BufferGeometry( );
SphereWithoutTrigonometry( g, 1,12 );
const mesh = new THREE.Mesh( g, material );
scene.add( mesh );

const box = new THREE.BoxGeometry(1,1,1,12,12,12);
//const sphere = box.clone().vertices.forEach(vertex=>vertex.normalize());
// console.log( sphere ); //  I get undefined

const sphere = box.clone(); //.vertices.forEach(vertex=>vertex.normalize());
sphere.vertices.forEach(vertex=>vertex.normalize());

const mesh1 = new THREE.Mesh( box, material );
scene.add( mesh1 );
mesh1.position.x = 2;

const mesh2 = new THREE.Mesh( sphere, material );
scene.add( mesh2 );
mesh2.position.x = -2;
#4

Right, but i was just illustrating that you don’t need trig to spherify something.