Deutsch
PHP, HTML & JavaScript- Forum

3D Grafik - WebGL mit three.js

 
- Seite 1 -



HofK
Auf einen heißen Tipp von IF hin, habe ich mir mal
three.js  [...]  angeschaut. Da  [...]  (ganz unten) die ersten Resultate.
 
31.01.2016  
 



 
- Seite 22 -



HofK
Die Kugel mit den Schnitten nun im Addon THREEg auf GitHub.  [...] 

Die beiden Varianten habe ich durch einen Parameter
symmetric// default is false

realisiert.
const g = new THREE.BufferGeometry( );
g.createSphereCut = THREEg.createSphereCut;

g.createSphereCut( {

    radius: 2,
    equator: 38,// is set to divisible by 4 (= 40) if symmetric: true
    cut: [ 6, 3, 10, 11, 12, 0 ],// if symmetric max. 1/2 equator, otherwise equator, non-overlapping
    parts: [ 1, 1, 1, 1, 1, 0, 0, 1 ],//  1 for create part, otherwise arbitrary
    //symmetric: true // default is false

} );

const mesh = new THREE.Mesh( g, material );// multimaterial array with 8 materials
scene.add( mesh );
 
29.04.2019  
 




HofK
Bei der Sichtung meiner Kugeldesigns ist mir aufgefallen, dass ich keine elementare Version für non-indexed BufferGeometry habe. Die im Addon benutzte Variante ist mit vielen Funktionen überladen und so ist das Grundprinzip kaum mehr erkennbar.

Deshalb habe ich eine Version erstellt, die mit minimalem Rechenaufwand auskommt und sofort das Float32Array der Positionen der unabhängigen Dreiecke ("Dreieckssuppe") füllt.

Dadurch ist die Hilfsfunktion zur Anzeige der Dreiecksnummern länger als die Funktion zur Erzeugung der Geometrie!



Dort zu betrachten:  [...]  bzw. unmittelbar  [...] 

Der komplette Code:
<!DOCTYPE html>
<head>
<title> SphereNonIndexed </title>
<meta charset="utf-8" />
<style>

body {

    margin: 0;

}

</style>
</head>
<body> 	</body>
<script src="../js/three.min.104.js"></script>
<script src="../js/OrbitControls.js"></script>
<script>
// @author hofk
'use strict';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.set( 1, -1, 6 );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(  0xaaaaaa, 1 );
const container = document.createElement('div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
const axesHelper = new THREE.AxesHelper( 2 );
scene.add( axesHelper );
var material1 = new THREE.MeshBasicMaterial( { color: 0x559955, transparent: true, opacity: 0.3, side: THREE.FrontSide, wireframe: false } );
var material2 = new THREE.MeshBasicMaterial( { color: 0x000000,  wireframe: true} );
const g = new THREE.BufferGeometry( );
createSphere( g, 2, 10 );
// BufferGeometry, radius, equator,
const mesh1 = new THREE.Mesh( g, material1 );
scene.add( mesh1 );
const mesh2 = new THREE.Mesh( g, material2 );
scene.add( mesh2 );
let numbersHelper = new vertexFaceNumbersHelper( mesh2, 2, 0.04, 0x0000aa );// only mode 2, no vertices defined
numbersHelper.update( 2 );
animate();

function animate() {

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

}

function createSphere( g, radius, equator ) {

    let eqt = equator;
    const sumON = ( n ) => ( n * n );// Sum odd numbers
    const pi2 = Math.PI / 2;
    let positionCount, posIdx, aIdx, bIdx, cx, cy, cz, theta, phi;
    positionCount = sumON( eqt ) * 6 * 9;// hemisphere
    g.positions = new Float32Array( positionCount );
    g.addAttribute('position', new THREE.BufferAttribute( g.positions, 3 ) );
    // previus ring
    let x0 = [];
    let y0 = [];
    let z0 = [];
    let x = [];
    let y = [];
    let z = [];
    theta = pi2 / eqt;
    x.push( radius * Math.sin( theta ) );
    y.push( radius * Math.cos( theta ) );
    z.push( 0 );
    posIdx = 0;

    for ( let p = 0; p < 6; p ++ ) {// pole faces, 6 parts

        phi = 2 * Math.PI * ( p + 1 ) / 6;// start next part
        g.positions[ posIdx     ] = 0;// pole
        g.positions[ posIdx + 1 ] = radius;
        g.positions[ posIdx + 2 ] = 0;
        g.positions[ posIdx + 3 ] = x[ p ];
        g.positions[ posIdx + 4 ] = y[ p ];
        g.positions[ posIdx + 5 ] = z[ p ];
        cx = radius * Math.sin( theta ) * Math.cos( phi );
        cy = radius * Math.cos( theta );
        cz = -radius * Math.sin( theta ) * Math.sin( phi );
        x.push( cx );
        y.push( cy );
        z.push( cz );
        g.positions[ posIdx + 6 ] = cx;
        g.positions[ posIdx + 7 ] = cy;
        g.positions[ posIdx + 8 ] = cz;
        posIdx += 9;

    }

    for ( let i = 1; i < eqt; i ++ ) {

        x0 = [];
        y0 = [];
        z0 = [];
        x0 = x.slice( );
        y0 = y.slice( );
        z0 = z.slice( );
        x = [];
        y = [];
        z = [];
        theta = pi2 * ( i + 1 ) / eqt;// refers to next ring

        for ( let p = 0; p < 6; p ++ ) {// 6 parts

            for ( let j = 0; j < i; j ++ ) {

                if ( p === 0 && j === 0 ) {// initial per ring

                    x.push( radius * Math.sin( theta ) );
                    y.push( radius * Math.cos( theta ) );
                    z.push( 0 );

                }

                aIdx = p * i + j;
                bIdx = p * ( i + 1 ) + j;
                // left face
                g.positions[ posIdx     ] = x0[ aIdx ];// a
                g.positions[ posIdx + 1 ] = y0[ aIdx ];
                g.positions[ posIdx + 2 ] = z0[ aIdx ];
                g.positions[ posIdx + 3 ] = x[ bIdx ];// b
                g.positions[ posIdx + 4 ] = y[ bIdx ];
                g.positions[ posIdx + 5 ] = z[ bIdx ];
                phi =  2 * Math.PI / 6 * ( p + ( j + 1 ) / ( i + 1) );
                cx = radius * Math.sin( theta ) * Math.cos( phi );
                cy = radius * Math.cos( theta );
                cz = -radius * Math.sin( theta ) * Math.sin( phi );
                x.push( cx );
                y.push( cy );
                z.push( cz );
                g.positions[ posIdx + 6 ] = cx;// c
                g.positions[ posIdx + 7 ] = cy;
                g.positions[ posIdx + 8 ] = cz;
                posIdx += 9;
                // right face
                g.positions[ posIdx     ] = x0[ aIdx ];// a
                g.positions[ posIdx + 1 ] = y0[ aIdx ];
                g.positions[ posIdx + 2 ] = z0[ aIdx ];
                g.positions[ posIdx + 3 ] = cx;// c
                g.positions[ posIdx + 4 ] = cy;
                g.positions[ posIdx + 5 ] = cz;
                g.positions[ posIdx + 6 ] = x0[ aIdx + 1 ];// a1
                g.positions[ posIdx + 7 ] = y0[ aIdx + 1 ];
                g.positions[ posIdx + 8 ] = z0[ aIdx + 1 ];
                posIdx += 9;

            }

            // last face, like a left face
            aIdx = p * i + i;
            g.positions[ posIdx     ] = x0[ aIdx ];// a, like j equals i
            g.positions[ posIdx + 1 ] = y0[ aIdx ];
            g.positions[ posIdx + 2 ] = z0[ aIdx ];
            g.positions[ posIdx + 3 ] = cx;// equals last c
            g.positions[ posIdx + 4 ] = cy;
            g.positions[ posIdx + 5 ] = cz;
            phi =  2 * Math.PI / 6 * ( p + 1 );// equals start next part
            cx = radius * Math.sin( theta ) * Math.cos( phi );
            cy = radius * Math.cos( theta );
            cz = -radius * Math.sin( theta ) * Math.sin( phi );
            x.push( cx );
            y.push( cy );
            z.push( cz );
            g.positions[ posIdx + 6 ] = cx;
            g.positions[ posIdx + 7 ] = cy;
            g.positions[ posIdx + 8 ] = cz;
            posIdx += 9;

        }

    }

}

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

    //  mode: 0 nothing, 1 vertex, 2 face, 3 vertex & face
    // for non indexed BufferGeometry
    var verticesCount;
    var facesCount;
    var vertexNumbers = [];
    var faceNumbers = [];
    var materialDigits = new THREE.LineBasicMaterial( { color: color } );
    var geometryDigit = [];
    var digit = [];
    var d100, d10, d1;// digits
    var coordDigit = [];// design of the digits
    var digitPositions = [];

    function numbering() {

        i1 ++;// starts with  -1 + 1 = 0

        if ( i1   === 10 ) {i1   = 0; i10 ++ }

        if ( i10  === 10 ) {i10  = 0; i100 ++ }

        if ( i100 === 10 ) {i100 = 0 }// hundreds (reset when overflow)

        if ( i100 > 0 ) {

            d100 = digit[ i100 ].clone();// digit for hundreds
            board.add( d100 );// on the board ...
            d100.position.x = -8 * 0.1 * size;// ... move slightly to the left

        }

        if ( ( i100 > 0 ) || ( ( i100 === 0 ) && ( i10 > 0 ) ) ) {// no preceding zeros tens

            d10 = digit[ i10 ].clone();// digit for tenth
            board.add( d10 );// on the board

        }

        d1 =   digit[ i1 ].clone();// digit
        board.add( d1 );//  on the board ...
        d1.position.x = 8 * 0.1 * size;// ... move slightly to the right

    }

    coordDigit[ 0 ] = [ 0,0, 0,9, 6,9, 6,0, 0,0 ];
    coordDigit[ 1 ] = [ 0,6, 3,9, 3,0 ];
    coordDigit[ 2 ] = [ 0,9, 6,9, 6,6, 0,0, 6,0 ];
    coordDigit[ 3 ] = [ 0,9, 6,9, 6,5, 3,5, 6,5, 6,0, 0,0 ];
    coordDigit[ 4 ] = [ 0,9, 0,5, 6,5, 3,5, 3,6, 3,0 ];
    coordDigit[ 5 ] = [ 6,9, 0,9, 0,5, 6,5, 6,0, 0,0 ];
    coordDigit[ 6 ] = [ 6,9, 0,9, 0,0, 6,0, 6,5, 0,5 ];
    coordDigit[ 7 ] = [ 0,9, 6,9, 6,6, 0,0 ];
    coordDigit[ 8 ] = [ 0,0, 0,9, 6,9, 6,5, 0,5, 6,5, 6,0, 0,0 ];
    coordDigit[ 9 ] = [ 6,5, 0,5, 0,9, 6,9, 6,0, 0,0 ];

    if ( mode === 1 || mode === 3 ) {

        verticesCount = mesh.geometry.vertexPositions.length;

    }

    if ( mode === 2 || mode === 3 ) {

        facesCount = mesh.geometry.positions.length / 9;

    }

    for ( var i = 0; i < 10; i ++ ) {

        geometryDigit[ i ] = new THREE.BufferGeometry();
        digitPositions[ i ] =  new Float32Array( coordDigit[ i ].length / 2 * 3 );
        geometryDigit[ i ].addAttribute('position', new THREE.BufferAttribute( digitPositions[ i ], 3 ) );

        for ( var j = 0; j < coordDigit[ i ].length/ 2; j ++ ) {

            digitPositions[ i ][ j * 3 ] =  0.1 * size * coordDigit[ i ][ 2 * j ];
            digitPositions[ i ][ j * 3 + 1 ] = 0.1 * size * coordDigit[ i ][ 2 * j + 1 ];
            digitPositions[ i ][ j * 3 + 2 ] = 0;

        }

        digit[ i ] = new THREE.Line( geometryDigit[ i ], materialDigits );

    }

    if ( mode === 1 || mode === 3 ) {

        var i100 =  0;
        var i10  =  0;
        var i1   = -1;

        for ( var i = 0; i < verticesCount ; i ++ ) {

            // Number on board, up to three digits are pinned there
            var board = new THREE.Mesh( new THREE.BufferGeometry() );
            numbering();// numbering the vertices, hundreds ...
            vertexNumbers.push( board );// place the table in the vertex numbering data field
            mesh.add( vertexNumbers[ i ] );

        }

    }

    if ( mode === 2 || mode === 3 ) {

        var i100 =  0;
        var i10  =  0;
        var i1   = -1;

        for ( var i = 0; i < facesCount ; i ++ ) {

            // Number on board, up to three digits are pinned there
            var board = new THREE.Mesh( new THREE.BufferGeometry() );
            numbering();// numbering the facesces, hundreds ...
            faceNumbers.push( board );// place the table in the face numbering data field
            mesh.add( faceNumbers[ i ] );

        }

    }

    // update helper

    this.update = function ( mode ) {

        var x, y, z;

        if ( mode === 1 || mode === 3 ) {

            for( var n = 0; n < vertexNumbers.length; n ++ ) {

                vertexNumbers[ n ].position.set( mesh.geometry.positions[ mesh.geometry.vertexPositions[ n ][ 0 ] ], mesh.geometry.positions[ mesh.geometry.vertexPositions[ n ][ 0 ] + 1 ], mesh.geometry.positions[ mesh.geometry.vertexPositions[ n ][ 0 ] + 2] );
                vertexNumbers[ n ].quaternion.copy(camera.quaternion);

            }

        }

        if ( mode === 2 || mode === 3 ) {

            for( var n = 0; n < faceNumbers.length; n ++ ) {

                x = 0;
                x += mesh.geometry.positions[ 9 * n ];
                x += mesh.geometry.positions[ 9 * n + 3 ];
                x += mesh.geometry.positions[ 9 * n + 6 ];
                x /= 3;
                y = 0;
                y += mesh.geometry.positions[ 9 * n + 1 ];
                y += mesh.geometry.positions[ 9 * n + 4 ];
                y += mesh.geometry.positions[ 9 * n + 7 ];
                y /= 3;
                z = 0;
                z += mesh.geometry.positions[ 9 * n + 2 ];
                z += mesh.geometry.positions[ 9 * n + 5 ];
                z += mesh.geometry.positions[ 9 * n + 8 ];
                z /= 3;
                faceNumbers[ n ].position.set( x, y, z );
                faceNumbers[ n ].quaternion.copy(camera.quaternion);

            }

        }

    }

}

</script>
</html>
 
03.05.2019  
 




HofK
Der obige Code ergibt nur eine Halbkugel mit 6 Teilen.

Ich habe auf n Teile verallgemeinert und durch einfache Kopieroperationen mit Vertauschen die zweite Halbkugel generiert.

Man kann dabei unmittelbar mit dem für die Darstellung verantwortlichem Datenfeld g.positions

positionCount = sumON( eqt ) * parts * 2 * 9;
g.positions = new Float32Array( positionCount );
g.addAttribute( 'position', new THREE.BufferAttribute( g.positions, 3 ) );


arbeiten.

Das ermöglicht eine sehr kompakte Realisierung.
// make south from north
let halfPosCount = positionCount / 2;
let cacheX, cacheY, cacheZ;

for ( let i = 0; i < halfPosCount; i ++ ) {

    g.positions[ halfPosCount + i ] = g.positions[ i ];// duplicate

}

for ( let i = halfPosCount; i < positionCount; i += 9 ) {

    cacheX = g.positions[ i + 3 ];// swap b, c positions (front, back),
    cacheY = g.positions[ i + 4 ];
    cacheZ = g.positions[ i + 5 ];
    g.positions[ i + 3 ]= g.positions[ i + 6 ];
    g.positions[ i + 4 ]= g.positions[ i + 7 ];
    g.positions[ i + 5 ]= g.positions[ i + 8 ];
    g.positions[ i + 6 ] = cacheX;
    g.positions[ i + 7 ] = cacheY;
    g.positions[ i + 8 ] = cacheZ;

}

for ( let i = halfPosCount + 1; i < positionCount; i += 3 ) {

    g.positions[ i ] = -g.positions[ i ];// make south (-)

}


Die Vertauschung der Ecken b und c ergibt den richtigen Umlaufsinn. Ansonsten wären Vor- und Rückseite vertauscht. Arbeitet man nur mit Material mit THREE.DoubleSide oder mit wireframe: true, ist das unerheblich und man könnte diesen Schritt noch einsparen.

Dort zu finden:  [...] 

Für den Nummernhelfer habe ich hier einfach mein Addon THREE.g eingebunden.

 
06.05.2019  
 




HofK
p.specht (27.04.2019)
Könnte man damit das Atomium  [...]  in Brüssel nachbauen?


Mit der vorhandene Version wie bereits angemerkt nicht, aber wenn man die Löcher beliebig platzieren kann geht es natürlich.

Die Sache ist naturgemäß etwas komplizierter als bei zu den Koordinatenebenen parallelen Löchern in der Kugel.

Deshalb habe ich erst einmal verschiedene Ansätze getestet. Mit einer non-indexed BufferGeometry verspricht es die günstigste Lösung.

Ein Loch bekommt man schnell in die Kugel, nur kreisförmig muss es noch gemacht werden. Dazu sollte der Ansatz wie bei der effizienten aber nicht symmetrischen Variante (siehe oben) funktionieren.



Zum Test wurden hier die Koordinaten der Punkte des Loches einfach auf 0 gesetzt. Das muss natürlich auch noch etwas bearbeitet werden.
 
12.05.2019  
 




HofK
Diesmal kein Fehler, sondern ein visueller Test mit noch unvollständiger Berechnung. Da kann man schon mal schauen, ob der eingeschlagene Weg richtig ist. Ein Loch ist zu erkennen. Sieht auch interessant aus.



Die Schwierigkeit beim "Loch machen" besteht darin, dass das Raster nicht äquidistant ist. Mit einer Skalierungsfunktion
const scale = ( k ) => ( k + 0.05 * Math.sin( 2 * Math.PI * k ) ); // j/count scale
kann man das Problem etwas entschärfen, aber nicht vollständig beseitigen. Somit passt der Schnittkreis nicht ins Raster und man muss geeignet ausgleichen. Da probiere ich noch einige Varianten.





 
14.05.2019  
 




HofK
Die obige Skalierung war rein empirisch. Macht man sich die Mühe und berechnet das Verhältnis von Winkel (Bogen) und entsprechendem Abschnitt der Sehne mit Hilfe des Sinussatzes, erhält man eine exakte Skalierung. Die so bestimmte Funktion sieht auf den ersten Blick der obigen recht ähnlich. Ich habe die Funktion mal mit matheretter.de/tools/funktionsplotter/ [...]  geplottet:



Übrigens benutzt matheretter.de für die Geometrie (geoservant) three.js  [...] 

Die Abstände der Knoten sind in jedem einzelnen Ring nun identisch, naturgemäß aber nicht in allen Ringen.



Hier ist Platz für die Einbindung des Schnittkreises und man sieht gerade am Rand den identischen Abstand sehr gut:



Der linke obere Teil passt schon und man erkennt, dass das Verfahren funktioniert. Der Rest ist Fleißarbeit und Code aufräumen.

 
15.05.2019  
 




p.specht

Na da hab´ ich mit meiner Atomium-Frage ja was angerichtet ...
 
XProfan 11
Computer: Gerät, daß es in Mikrosekunden erlaubt, 50.000 Fehler zu machen, zB 'daß' statt 'das'...
17.05.2019  
 




HofK
p.specht (17.05.2019)
Na da hab´ ich mit meiner Atomium-Frage ja was angerichtet ...


Anregungen können nie schaden!

Aber es kann leicht zu einer Lawine werden!

Es zeigt sich nämlich, dass die Verfahrensweise zwar für Löcher im mittleren Bereich des Kugelteils ( hier sind 3, 4, 5, 6 Teile pro Halbkugel sinnvoll) zu brauchbaren Ergebnissen führt,

gut



geht noch so



nicht aber am Rand.

schlecht



Dort kommt es durch das vorgegebene starre Raster zu starken Verzerrungen bei einigen Dreiecken. Das beeinträchtigt die die Gleichmäßigkeit der Oberfläche.

Abhilfe schaffen allgemeinere Verfahren zur Triangulation.
Allerdings ist das sehr anspruchsvoller Stoff [...]  Kapitel 6 (Seite 47)

Dazu gibt es zwar Programme als tar Datei - allerdings in Pascal. Habe mal reingeschaut, ist ein umfangreicheres System und nicht ganz leicht zu überschauen.
 
18.05.2019  
 




HofK


Nun kann man in jedes Teil der Halbkugel an gewünschter Stelle ein Loch "bohren".
 
20.05.2019  
 




p.specht

Coole Bowling-Kugel !
 
Computer: Gerät, daß es in Mikrosekunden erlaubt, 50.000 Fehler zu machen, zB 'daß' statt 'das'...
21.05.2019  
 




HofK
p.specht (21.05.2019)
Coole Bowling-Kugel !


Nur halbe Kugel!



Die Erweiterung auf eine komplette Kugel und die Berechnung von uv-Werten machen noch einmal einen erheblichen Aufwand. Deshalb lasse ich das erst einmal und beschäftige mich lieber mit allgemeineren Verfahren zur Triangulation wie oben angegeben.

Dort kann man die Halbkugel mit Löchern betrachten:  [...] 

Der gesamte Code:
<!DOCTYPE html>
<head>
<title> SphereCutAsWanted </title>
<meta charset="utf-8" />
<style>

body {

    margin: 0;

}

</style>
</head>
<body> 	</body>
<script src="../js/three.min.104.js"></script>
<script src="../js/OrbitControls.js"></script>
<script>
// @author hofk
'use strict'
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 55, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.set( 3, 1, 2 );
const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(  0xaaaaaa, 1 );
const container = document.createElement('div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
const axesHelper = new THREE.AxesHelper( 1 );
scene.add( axesHelper );
var material1 = new THREE.MeshBasicMaterial( { color: 0x559955, transparent: true, opacity: 0.3, side: THREE.FrontSide, wireframe: false } );
var material2 = new THREE.MeshBasicMaterial( { color: 0x000000,  wireframe: true} );
const g = new THREE.BufferGeometry( );
createSphereCut( g, 1.234, 40, 5, [ [ 28, 14, 12 ], [ 10, 6, 4 ], [ ], [ 25, 6, 6 ], [ 32, 6, 4 ] ] );
// BufferGeometry, radius, equator, parts, holes array [ center i, rotary j, radius (even) ] or empty, show circles
const mesh1 = new THREE.Mesh( g, material1 );
scene.add( mesh1 );
const mesh2 = new THREE.Mesh( g, material2 );
scene.add( mesh2 )
animate();

function animate() {

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

}

function createSphereCut( g, radius, equator, parts, holes, circles ) {

    // ..., holes: array [ center i, rotary j, radius (even) ] or empty,  optional circles: default is false
    // hole parameter from holes aray
    let iCenter;// center of hole
    let jCenter;// rotary
    let rr ;// radius i rings of hole, must be even
    let circ = circles !== undefined ? circles : false
    const sumON = ( n ) => ( n * n );// Sum odd numbers
    const scale = ( k ) => k + Math.sin( pi2 * k ) / Math.sin( 3 * Math.PI / 4  - pi2 * k ) - Math.sqrt(2) * k;
    const hCap = ( R ) => ( 1 - Math.sqrt( 1 - R * R ) );// height of spherical cap (cut radius R)
    const pi2 = Math.PI / 2;
    let eqt = equator;
    let hp;// hole part
    let	iTop, iBtm;
    let posIdx, cx, cy, cz, theta, phi ;
    let dx, dy, dz, vCount, len;
    let holeTopL, holeTopR, holeBtmL, holeBtmR, isHoleL, isHoleR;
    let jL = [];
    let jR = [];
    let positionCount = sumON( eqt ) * parts   * 9;// hemisphere
    g.positions = new Float32Array( positionCount );
    g.addAttribute('position', new THREE.BufferAttribute( g.positions, 3 ) );
    let x0 = [];// previus ring
    let y0 = [];
    let z0 = [];
    let x = [];//  ring
    let y = [];
    let z = [];
    let lftx = [];// left side positions x
    let lfty = [];// left side positions y
    let lftz = [];// left side positions z
    let rgtx = [];// right side positions x
    let rgty = [];// right side positions y
    let rgtz = [];// right side positions z

    for ( let i = 0; i <= eqt; i ++ ) {// initial (part 0 => left)

        theta = pi2 * i / eqt;
        rgtx.push( Math.sin( theta ) );
        rgty.push( Math.cos( theta ) );
        rgtz.push( 0 );

    }

    let cpLx = [];// circle points
    let cpLy = [];
    let cpLz = [];
    let cpRx = [];
    let cpRy = [];
    let cpRz = [];
    posIdx = 0;

    for ( let p = 0; p < parts; p ++ ) {

        hp = holes[ p ].length !== 0 ? p : -1;// hole part
        iCenter = holes[ p ][ 0 ];// center i of hole
        jCenter = holes[ p ][ 1 ];// rotary j
        rr = holes[ p ][ 2 ] % 2 === 0 ? holes[ p ][ 2 ] : holes[ p ][ 2 ] + 1;// radius i rings of hole, even
        lftx = rgtx.slice( );
        lfty = rgty.slice( );
        lftz = rgtz.slice( );
        rgtx = [];
        rgty = [];
        rgtz = [];
        phi = 2 * Math.PI * ( p + 1 ) / parts;// part right <=> next part left

        for ( let i = 0; i <= eqt; i ++ ) {

            theta = pi2 * i / eqt;
            rgtx.push( Math.sin( theta ) * Math.cos( phi ) );
            rgty.push( Math.cos( theta ) );
            rgtz.push( -Math.sin( theta ) * Math.sin( phi ) );

        }

        if ( p === hp ) {

            jL = [];
            jR = [];
            iTop = iCenter - rr;
            iBtm = iCenter + rr;

            for ( let i = 0; i <= eqt; i ++ ) {// hole structure

                areaBordersIndex( i );

            }

            holeCircle( );// calculate circle positions

        }

        poleFace( );
        x[ 0 ] = lftx[ 1 ];// initial, (first ring)
        y[ 0 ] = lfty[ 1 ];// only left
        z[ 0 ] = lftz[ 1 ];
        x[ 1 ] = rgtx[ 1 ];// and right
        y[ 1 ] = rgty[ 1 ];
        z[ 1 ] = rgtz[ 1 ];

        for ( let i = 1; i < eqt; i ++ ) {

            x0 = [];
            y0 = [];
            z0 = [];
            x0 = x.slice( );
            y0 = y.slice( );
            z0 = z.slice( );
            x = [];
            y = [];
            z = [];
            x.push( lftx[ i + 1 ] );
            y.push( lfty[ i + 1 ] );
            z.push( lftz[ i + 1 ] );

            for ( let j = 0; j < i; j ++ ) {

                checkHole( p, i, j );
                calculatePosition( p, i, j );// position c,  without radius

                if ( !isHoleL ) {

                    leftFace( j );

                } else {

                    x.push( cx );
                    y.push( cy );
                    z.push( cz );
                    makeHole( );

                }

                if ( !isHoleR ) {

                    rightFace( j );

                } else {

                    makeHole( );

                }

            }

            lastFace( i );// last face, like a left face

        }

    }

    // detail functions

    function poleFace( ) {

        g.positions[ posIdx     ] = radius * lftx[ 0 ];// pole
        g.positions[ posIdx + 1 ] = radius * lfty[ 0 ];
        g.positions[ posIdx + 2 ] = radius * lftz[ 0 ];
        g.positions[ posIdx + 3 ] = radius * lftx[ 1 ];
        g.positions[ posIdx + 4 ] = radius * lfty[ 1 ];
        g.positions[ posIdx + 5 ] = radius * lftz[ 1 ];
        g.positions[ posIdx + 6 ] = radius * rgtx[ 1 ];
        g.positions[ posIdx + 7 ] = radius * rgty[ 1 ];
        g.positions[ posIdx + 8 ] = radius * rgtz[ 1 ];
        posIdx += 9;

    }

    function leftFace( j ) {

        g.positions[ posIdx     ] = radius * x0[ j ];// a
        g.positions[ posIdx + 1 ] = radius * y0[ j ];
        g.positions[ posIdx + 2 ] = radius * z0[ j ];
        g.positions[ posIdx + 3 ] = radius * x[ j ];// b
        g.positions[ posIdx + 4 ] = radius * y[ j ];
        g.positions[ posIdx + 5 ] = radius * z[ j ];
        x.push( cx );
        y.push( cy );
        z.push( cz );
        g.positions[ posIdx + 6 ] = radius * cx;// c, new calculated
        g.positions[ posIdx + 7 ] = radius * cy;
        g.positions[ posIdx + 8 ] = radius * cz;
        posIdx += 9;

    }

    function rightFace( j ) {

        g.positions[ posIdx     ] = radius * x0[ j ];// a
        g.positions[ posIdx + 1 ] = radius * y0[ j ];
        g.positions[ posIdx + 2 ] = radius * z0[ j ];
        g.positions[ posIdx + 3 ] = radius * cx;// c, see left face
        g.positions[ posIdx + 4 ] = radius * cy;
        g.positions[ posIdx + 5 ] = radius * cz;
        g.positions[ posIdx + 6 ] = radius * x0[ j + 1 ];// a1 ( right from a)
        g.positions[ posIdx + 7 ] = radius * y0[ j + 1 ];
        g.positions[ posIdx + 8 ] = radius * z0[ j + 1 ];
        posIdx += 9;

    }

    function lastFace( i ) {

        // last face, like a left face
        g.positions[ posIdx     ] = radius * x0[ i ];// a, like j equals i
        g.positions[ posIdx + 1 ] = radius * y0[ i ];
        g.positions[ posIdx + 2 ] = radius * z0[ i ];
        g.positions[ posIdx + 3 ] = radius * cx;// equals last c
        g.positions[ posIdx + 4 ] = radius * cy;
        g.positions[ posIdx + 5 ] = radius * cz;
        cx = rgtx[ i + 1 ];// right side
        cy = rgty[ i + 1 ];
        cz = rgtz[ i + 1 ];
        x.push( cx );
        y.push( cy );
        z.push( cz );
        g.positions[ posIdx + 6 ] = radius * cx;
        g.positions[ posIdx + 7 ] = radius * cy;
        g.positions[ posIdx + 8 ] = radius * cz;
        posIdx += 9;

    }

    function checkHole( p, i, j ) {

        if ( p === hp ) {

            holeTopL = i >= iTop && i < iCenter && j >= jL[ i ] && j <= jR[ i ];
            holeTopR = i >= iTop && i < iCenter && j >= jL[ i ] && j < jR[ i ];
            holeBtmL = i >= iCenter && i < iBtm && j > jL[ i ] && j < jR[ i ];
            holeBtmR = i >= iCenter && i < iBtm && j >= jL[ i ] && j < jR[ i ];
            isHoleL = holeTopL || holeBtmL;
            isHoleR = holeTopR || holeBtmR;

        }

    }

    function makeHole( ) {

        for ( let k = 0; k < 9; k ++ ) {

            g.positions[ posIdx + k ] = 0;

        }

        posIdx += 9;

    }

    function holeCircle( ) {

        let lIdx;
        cpLx = [];
        cpLy = [];
        cpLz = [];
        cpRx = [];
        cpRy = [];
        cpRz = [];
        theta = theta = pi2 * iCenter / eqt;// refers to ring iCenter ..
        phi =  2 * Math.PI / parts * ( hp + jCenter / iCenter );// .. refers to rotary jCenter
        cx = Math.sin( theta ) * Math.cos( phi );
        cy = Math.cos( theta );
        cz = -Math.sin( theta ) * Math.sin( phi );
        let r = pi2 * rr / eqt;// without radius of sphere ( = 1 )
        let h = hCap( r );
        let hc = 1 - h;
        // hole circle center point
        cx = hc * cx;
        cy = hc * cy;
        cz = hc * cz;
        // unit vectors of cut plane
        let e0x = -cx;
        let e0y = ( cx * cx + cz * cz ) / cy;
        let e0z = -cz;
        // cross c , e0
        let e1x = cy * e0z - cz * e0y;
        let e1y = cz * e0x - cx * e0z;
        let e1z = cx * e0y - cy * e0x;
        len = Math.sqrt( e0x * e0x + e0y * e0y + e0z * e0z );
        e0x = e0x / len;
        e0y = e0y / len;
        e0z = e0z / len;
        len = Math.sqrt( e1x * e1x + e1y * e1y + e1z * e1z );
        e1x = e1x / len;
        e1y = e1y / len;
        e1z = e1z / len;
        phi = 0;

        if ( circ ) {

            let lineGeo = new THREE.BufferGeometry( );
            let linesPos = new Float32Array( rr * 4 * 3 );
            lineGeo.addAttribute('position', new THREE.BufferAttribute( linesPos, 3 ) );

        }

        for ( let k = 0; k <= rr * 2; k ++ ) {

            cpLx.push( cx + Math.cos( phi ) * r * e0x + Math.sin( phi ) * r * e1x );
            cpLy.push( cy + Math.cos( phi ) * r * e0y + Math.sin( phi ) * r * e1y );
            cpLz.push( cz + Math.cos( phi ) * r * e0z + Math.sin( phi ) * r * e1z );

            if ( circ ) {

                linesPos[ k * 3     ] = radius * cpLx[ k ];
                linesPos[ k * 3 + 1 ] = radius * cpLy[ k ];
                linesPos[ k * 3 + 2 ] = radius * cpLz[ k ];

            }

            phi += pi2 / rr;// equals 2 * PI / ( 4 * rr )

        }

        phi = 2 * Math.PI;

        for ( let k = 0; k <= rr * 2; k ++ ) {

            lIdx = ( 4 * rr - k - 1 ) * 3;
            cpRx.push( cx + Math.cos( phi ) * r * e0x + Math.sin( phi ) * r * e1x );
            cpRy.push( cy + Math.cos( phi ) * r * e0y + Math.sin( phi ) * r * e1y );
            cpRz.push( cz + Math.cos( phi ) * r * e0z + Math.sin( phi ) * r * e1z );

            if ( circ ) {

                linesPos[ lIdx + 3 ] = radius * cpRx[ k ];
                linesPos[ lIdx + 4 ] = radius * cpRy[ k ];
                linesPos[ lIdx + 5 ] = radius * cpRz[ k ];

            }

            phi -= pi2 / rr;// equals 2 * PI / ( 4 * rr )

        }

        if ( circ ) {

            let  lineMaterial = new THREE.LineBasicMaterial({ color: 0xff0000 });
            let  line = new THREE.LineLoop( lineGeo, lineMaterial );
            scene.add( line );

        }

    }

    function areaBordersIndex( i ) {

        if ( i < iTop ) {

            jL.push( i );
            jR.push( i );

        }

        if ( i >= iTop && i < iCenter ) {

            jL.push( jCenter - rr / 2 );
            jR.push( jCenter + rr / 2 + i - iCenter );

        }

        if ( i >= iCenter && i <= iBtm ) {

            jL.push( jCenter - rr / 2 + i - iCenter );
            jR.push( jCenter + rr / 2 );

        }

        if ( i > iBtm  ) {

            jL.push( i );
            jR.push( i );

        }

    }

    function calculatePosition( p, i, j ) {

        let dj1, dji, t;
        // refers to next ring
        let i1 = i + 1;
        let idx1 = i - iTop + 1;

        if ( i >= iTop - 1  &&  i <= iBtm - 1 && p === hp ) {

            if ( j < jL[ i1 ] ) {

                dx = cpLx[ idx1 ] - lftx[ i1 ];
                dy = cpLy[ idx1 ] - lfty[ i1 ];
                dz = cpLz[ idx1 ] - lftz[ i1 ];
                vCount = jL[ i1 ] + 1;
                dj1 = ( j + 1 ) / ( vCount - 1 );
                cx = lftx[ i1 ] + dj1 * dx;
                cy = lfty[ i1 ] + dj1 * dy;
                cz = lftz[ i1 ] + dj1 * dz;

            }

            if ( j >= jL[ i1 ] && j < jR[ i1 ] ) {

                dx = cpRx[ idx1 ] - cpLx[ idx1 ];
                dy = cpRy[ idx1 ] - cpLy[ idx1 ];
                dz = cpRz[ idx1 ] - cpLz[ idx1 ];
                vCount =  jR[ i1 ] - jL[ i1 ] + 1;
                dji = ( ( j - jL[ i1 ] + 1 ) ) / ( vCount - 1 );
                cx = cpLx[ idx1 ] + dji * dx;
                cy = cpLy[ idx1 ] + dji * dy;
                cz = cpLz[ idx1 ] + dji * dz;

            }

            if ( j >= jR[ i1 ] ) {

                dx = rgtx[ i1 ] - cpRx[ idx1 ];
                dy = rgty[ i1 ] - cpRy[ idx1 ];
                dz = rgtz[ i1 ] - cpRz[ idx1 ];
                vCount =  i1 - jR[ i1 ] + 1;
                dji = ( ( j - jR[ i1 ] + 1 ) ) / ( vCount - 1 );
                cx = cpRx[ idx1 ] + dji * dx;
                cy = cpRy[ idx1 ] + dji * dy;
                cz = cpRz[ idx1 ] + dji * dz;

            }

        }

        if ( i < iTop - 1 || i > iBtm - 1 || p !== hp ) {

            dx = rgtx[ i1 ] - lftx[ i1 ];
            dy = rgty[ i1 ] - lfty[ i1 ];
            dz = rgtz[ i1 ] - lftz[ i1 ];
            vCount = ( i1 ) + 1;
            // scale
            dj1 = scale( ( j + 1 ) / ( vCount - 1 ) );
            cx = lftx[ i1 ] + dj1 * dx;
            cy = lfty[ i1 ] + dj1 * dy;
            cz = lftz[ i1 ] + dj1 * dz;

        }

        t = Math.sqrt( ( 1 - cy * cy ) / ( cx * cx + cz * cz ) );// to move the point horizontally onto the sphere
        cx = cx * t;
        cz = cz * t;

    }

}

</script>
</html>
 
22.05.2019  
 




HofK
... und beschäftige mich lieber mit allgemeineren Verfahren zur Triangulation wie oben angegeben.

Das braucht etwas Zeit. Das Verfahren  [...] 
ist etwas kompliziert und muss tiefgründig durchdrungen werden und der Code  [...]  ist in Pascal. Damit habe ich mich letztmalig vor über 10 Jahren befasst.

Für den konkreten und einfachen Fall der Kugel möchte ich darüber hinaus eine angepasste und damit im Rechenaufwand reduzierte Variante erstellen. So ist bei einer Kugel der Normalenvektor stets identisch mit dem Ortsvektor des Kugelpunktes. Die Krümmung der Kugeloberfläche ist überall konstant.

Dadurch kann man einfachere Verfahren für bestimmte Schritte anwenden oder Schritte weglassen.

Einige Dinge muss ich noch testen, z.B. ob sie praktikabel sind und tatsächlich den Aufwand gegenüber dem originalen, allgemeinen Algorithmus verringern.

Bei den Tests erhält man ganz nebenbei aber auch einige interessante Erkenntnisse.

Ein Testbeispiel mit zwei Dreiecken und einigen Vektoren. Als Basis eine einfache Kugel nach three.js Definition (Gelb).

 
01.06.2019  
 




Antworten


Thementitel, max. 100 Zeichen.
 

Systemprofile:

Kein Systemprofil angelegt. [anlegen]

XProfan:

 Beitrag  Schrift  Smilies  ▼ 

Bitte anmelden um einen Beitrag zu verfassen.
 

Themenoptionen

332.840 Betrachtungen

Unbenanntvor 0 min.
HofK vor 23 Tagen
Rschnett24.08.2024
Michael W.28.03.2024
Thomas Zielinski17.02.2024
Mehr...

Themeninformationen



Admins  |  AGB  |  Anwendungen  |  Autoren  |  Chat  |  Datenschutz  |  Download  |  Eingangshalle  |  Hilfe  |  Händlerportal  |  Impressum  |  Mart  |  Schnittstellen  |  SDK  |  Services  |  Spiele  |  Suche  |  Support

Ein Projekt aller XProfaner, die es gibt!


Mein XProfan
Private Nachrichten
Eigenes Ablageforum
Themen-Merkliste
Eigene Beiträge
Eigene Themen
Zwischenablage
Abmelden
 Deutsch English Français Español Italia
Übersetzungen

Datenschutz


Wir verwenden Cookies nur als Session-Cookies wegen der technischen Notwendigkeit und bei uns gibt es keine Cookies von Drittanbietern.

Wenn du hier auf unsere Webseite klickst oder navigierst, stimmst du unserer Erfassung von Informationen in unseren Cookies auf XProfan.Net zu.

Weitere Informationen zu unseren Cookies und dazu, wie du die Kontrolle darüber behältst, findest du in unserer nachfolgenden Datenschutzerklärung.


einverstandenDatenschutzerklärung
Ich möchte keinen Cookie