2009-11-30 10 views
12

Sto provando a estrudere un percorso in 3d. Niente di eccezionale, basta seguire alcuni punti e usare un poligono regolare per "tubing". Sto usando Processing per ora per creare rapidamente un prototipo, ma in seguito trasformerò il codice in OpenGL.come estrudere un percorso in 3d?

Il mio problema è la rotazione dei "giunti" ad angolo retto. Penso di avere una vaga idea di come ottenere gli angoli, non sono sicuro.

ho iniziato da un campione di Simon Greenwold (produzione> File> Esempi> 3D> Modulo> vertici) di .Qui il mio tentativo finora:

UPDATE> refactoring/SEMPLIFICATO CODICE

Here is the main sketch code: 
int pointsNum = 10; 
Extrusion star; 

int zoom = 0; 

void setup() { 
    size(500, 500, P3D); 

    PVector[] points = new PVector[pointsNum+1]; 
    for(int i = 0 ; i <= pointsNum ; i++){ 
    float angle = TWO_PI/pointsNum * i; 
    if(i % 2 == 0) 
     points[i] = new PVector(cos(angle) * 100,sin(angle) * 100,0); 
    else 
     points[i] = new PVector(cos(angle) * 50,sin(angle) * 50,0); 
    } 

    star = new Extrusion(10,10,points,3); 
} 

void draw() { 
    background(0); 
    lights(); 
    translate(width/2, height/2,zoom); 
    rotateY(map(mouseX, 0, width, 0, PI)); 
    rotateX(map(mouseY, 0, height, 0, PI)); 
    rotateZ(-HALF_PI); 
    noStroke(); 
    fill(255, 255, 255); 
    translate(0, -40, 0); 
    star.draw(); 
} 

void keyPressed(){ 
    if(key == 'a') zoom += 5; 
    if(key == 's') zoom -= 5; 
} 

Ed ecco la classe Extrusion:

import processing.core.PMatrix3D;

class Extrusion{ 

    float topRadius,bottomRadius,tall,sides; 
    int pointsNum; 
    PVector[] points; 

    Extrusion(){} 

    Extrusion(float topRadius, float bottomRadius, PVector[] points, int sides) { 
    this.topRadius = topRadius; 
    this.bottomRadius = bottomRadius; 
    this.points = points; 
    this.pointsNum = points.length; 
    this.sides = sides; 
    } 

    void draw() { 
    if(pointsNum >= 2){ 
     float angle = 0; 
     float angleIncrement = TWO_PI/sides; 

     //begin draw segments between caps 
     angle = 0; 
     for(int i = 1; i < pointsNum ; ++i){ 
     beginShape(QUAD_STRIP); 
     for(int j = 0; j < sides + 1; j++){ 
      vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius); 
      vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius); 

      angle += angleIncrement; 
      } 
     endShape(); 
     } 
     //begin draw segments between caps 
    }else println("Not enough points: " + pointsNum); 
    } 
} 

UPDATE

Ecco come il mio schizzo assomiglia:

processing extrude http://doc.gold.ac.uk/~ma802gp/extrude.gif

Il problema è che le articolazioni non sono ad angolo retto, per cui l'estrusione sembra sbagliato. Questo non è un ottimo esempio, poiché questo potrebbe essere ottenuto con un tornio. Se riesco a far funzionare un tornio con un insieme arbitrario di punti e un asse che sarà fantastico. Sto usando estrusione perché sto cercando di creare corpi geometrici basati sull'arte di Liviu Stoicoviciu.

Ecco alcuni esempi:

star painting http://doc.gold.ac.uk/~ma802gp/star_painting.jpg

star paper sculpture http://doc.gold.ac.uk/~ma802gp/star_paper_sculpture.jpg

triangles http://doc.gold.ac.uk/~ma802gp/triangles_pencil.jpg

Ci scusiamo per la scarsa qualità.

Come potete vedere nell'immagine dei triangoli, ciò si otterrebbe con le estrusioni.

UPDATE

Ecco il mio tentativo di utilizzare l'aiuto di drhirsch nel metodo draw:

void draw() { 
    if(pointsNum >= 2){ 
     float angle = 0; 
     float angleIncrement = TWO_PI/sides; 

     //begin draw segments between caps 
     angle = 0; 
     for(int i = 1; i < pointsNum ; ++i){ 
     beginShape(QUAD_STRIP); 
     for(int j = 0; j < sides + 1; j++){ 

      PVector s = new PVector(0,0,1); 
      PVector cn = new PVector(); 
      points[i].normalize(cn); 
      PVector r = s.cross(cn); 
      float a = acos(s.dot(cn)); 
      PMatrix3D rot = new PMatrix3D(1,0,0,0, 
             0,1,0,0, 
             0,0,1,0, 
             0,0,0,1); 
      rot.rotate(a,r.x,r.y,r.z); 
      PVector rotVec = new PVector(); 
      rot.mult(points[i],rotVec); 
      rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius)); 

      vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius); 
      vertex(rotVec.x,rotVec.y,rotVec.y); 

      //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius); 
      //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius); 

      angle += angleIncrement; 
      } 
     endShape(); 
     } 
     //begin draw segments between caps 
    }else println("Not enough points: " + pointsNum); 
    } 

ho refactoring del codice in modo ormai la classe che si chiamava CShape è chiamato Estrusione, il codice è di meno e si spera che sia semplice, e io uso una serie di oggetti PVector invece di un vettore di oggetti PVector che potrebbero confondere.

Ecco il mio ennesimo tentativo con alcune Escher-esque risultati:

upated disegnare

void draw() { 
    if(pointsNum >= 2){ 
     float angle = 0; 
     float angleIncrement = TWO_PI/sides; 

     //begin draw segments between caps 
     angle = 0; 
     for(int i = 1; i < pointsNum ; ++i){ 
     beginShape(QUAD_STRIP); 
     float angleBetweenNextAndPrevious = 0.0; 
     if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]); 

     for(int j = 0; j < sides + 1; j++){ 

      PVector s = new PVector(0,0,1); 
      PVector s2 = new PVector(0,0,1); 
      PVector cn = new PVector(); 
      PVector cn2 = new PVector(); 
      points[i-1].normalize(cn); 
      points[i].normalize(cn); 
      PVector r = s.cross(cn); 
      PVector r2 = s.cross(cn2); 
      PMatrix3D rot = new PMatrix3D(1,0,0,0, 
             0,1,0,0, 
             0,0,1,0, 
             0,0,0,1); 
      PMatrix3D rot2 = new PMatrix3D(1,0,0,0, 
             0,1,0,0, 
             0,0,1,0, 
             0,0,0,1); 

      rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z); 
      rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z); 

      PVector rotVec = new PVector(); 
      rot.mult(points[i-1],rotVec); 
      rotVec.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius)); 
      PVector rotVec2 = new PVector(); 
      rot2.mult(points[i],rotVec2); 
      rotVec2.add(new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius)); 

      vertex(rotVec.x,rotVec.y,rotVec.z); 
      vertex(rotVec2.x,rotVec2.y,rotVec2.z); 
      //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius); 
      //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius); 

      angle += angleIncrement; 
      } 
     endShape(); 
     } 
     //begin draw segments between caps 
    }else println("Not enough points: " + pointsNum); 
    } 
} 

fix_test http://doc.gold.ac.uk/~ma802gp/extrude2.gif

Edit by drhirsch Questo dovrebbe funzionare:

void draw() { 
    if(pointsNum >= 2){ 
     float angle = 0; 
     float angleIncrement = TWO_PI/sides; 

     //begin draw segments between caps 
     angle = 0; 
     for(int i = 1; i < pointsNum ; ++i){ 
     beginShape(QUAD_STRIP); 
     float angleBetweenNextAndPrevious = 0.0; 
     if(i < pointsNum - 1) angleBetweenNextAndPrevious = PVector.angleBetween(points[i],points[i+1]); 
     PVector s = new PVector(0,0,1); 
     PVector s2 = new PVector(0,0,1); 
     PVector cn = new PVector(); 
     PVector cn2 = new PVector(); 
     points[i-1].normalize(cn); 
     points[i].normalize(cn2); 
     PVector r = s.cross(cn); 
     PVector r2 = s.cross(cn2); 
     PMatrix3D rot = new PMatrix3D(1,0,0,0, 
             0,1,0,0, 
             0,0,1,0, 
             0,0,0,1); 
     PMatrix3D rot2 = new PMatrix3D(1,0,0,0, 
             0,1,0,0, 
             0,0,1,0, 
             0,0,0,1); 

     rot.rotate(angleBetweenNextAndPrevious,r.x,r.y,r.z); 
     rot2.rotate(angleBetweenNextAndPrevious,r2.x,r2.y,r2.z); 
     PVector rotVec = new PVector(); 
     PVector rotVec2 = new PVector(); 

     for(int j = 0; j < sides + 1; j++){ 
      // I am still not sure about this. Should the shape be in the xy plane 
      // if the extrusion is mainly along the z axis? If the shape is now in 
      // the xz plane, you need to use (0,1,0) as normal vector of the shape 
      // (this would be s and s2 above, don't use the short names I have 
      // used, sorry) 
      PVector shape = new PVector(cos(angle) * topRadius,0,sin(angle) * topRadius); 

      rot.mult(shape, rotVec); 
      rot2.mult(shape,rotVec2); 

      rotVec.add(points[i-1]); 
      rotVec2.add(points[i]); 

      vertex(rotVec.x,rotVec.y,rotVec.z); 
      vertex(rotVec2.x,rotVec2.y,rotVec2.z); 
      //vertex(points[i-1].x + cos(angle) * topRadius, points[i-1].y, points[i-1].z + sin(angle) * topRadius); 
      //vertex(points[i].x + cos(angle) * bottomRadius, points[i].y, points[i].z + sin(angle) * bottomRadius); 

      angle += angleIncrement; 
      } 
     endShape(); 
     } 
     //begin draw segments between caps 
    }else println("Not enough points: " + pointsNum); 
    } 
} 

UPDATE

Ecco una semplice illustrazione del mio problema:

description http://doc.gold.ac.uk/~ma802gp/description.gif

Il percorso blu è equivalente ai punti [] array PVector nel mio codice, se pointsNum = 6. Il percorso rosso è quello che sto cercando di risolvere, il percorso verde è ciò che voglio raggiungere.

UPDATE

Alcuni problemi minori con l'ordine dei vertici credo. Ecco alcune schermate di stampa che usano 6 punti e nessuna (se/meno% 2) condizione stella.

points1 http://doc.gold.ac.uk/~ma802gp/points1.gif

alt text http://doc.gold.ac.uk/~ma802gp/points2.gif

+0

L'unico motivo per calcolare gli angoli sarebbe generare le normali. Perché non fai l'estrusione con una traduzione lungo l'asse z? E potresti usare un QUAD_STRIP ai lati. – hirschhornsalz

+0

O vuoi eseguire un'estrusione lungo un percorso con una rotazione automatica per abbinare l'inclinazione alla tangente di questo percorso? – hirschhornsalz

+0

@drhirsch Grazie per il suggerimento su QUAD_STRIP, lo userò. Voglio un'estrusione lungo un percorso con una rotazione automatica per abbinare l'inclinazione alla tangente del percorso. Penso di averlo risolto in 2D, avendo problemi quando mi sono trasferito in 3d.per ottenere l'inclinazione per un secondo punto, utilizzo l'atan2 della differenza tra il terzo punto e il primo punto e aggiungo 90 gradi (una perpendicolare). Non so come applicarlo in 3d. –

risposta

3

Assumendo la forma ha un vettore normale S. Nel tuo esempio S sarebbe (0,0,1), perché la forma è piatta in xy. È possibile utilizzare il prodotto incrociato tra il vettore del percorso corrente V (normalizzato) e S per ottenere il vettore dell'asse di rotazione R. È necessario ruotare la forma attorno a R. L'angolo di rotazione può essere ottenuto dal prodotto punto tra S e V. Quindi:

R = S x V 
a = arc cos(S . V) 

ora è possibile impostare un rotation matrix con R e un e ruotare la forma da essa.

È possibile utilizzare glRotate (...) per ruotare la matrice corrente nello stack, ma ciò non può essere fatto tra glBegin() e glEnd(). Quindi devi fare la moltiplicazione della matrice da solo o con una libreria.

Edit: Dopo un breve sguardo alla biblioteca che si sta utilizzando, si dovrebbe essere in grado di impostare la matrice di rotazione con

PVector s = new PVector(0,0,1); // is already normalized (meaning is has length 1) 
PVector cn; 
current.normalize(cn); 
PVector r = s.cross(cn); 
float a = acos(s.dot(cn)); 
PMatrix rot = new PMatrix(1, 0, 0, 0, 
          0, 1, 0, 0, 
          0, 0, 1, 0, 
          0, 0, 0, 1); 
rot.rotate(a, r.x, r.y, r.z); 

e ora moltiplicare ogni elemento della vostra forma con putrefazione e tradurlo dal vostro attuale path vector:

PVector rotVec; 
rot.mult((PVector)shape[i], rotVec); 
rotVec.add(current); 
+0

Sto cercando di usare il tuo aiuto, e finora ho provato questo: PVector s = new PVector (0,0,1); s.normalize(); // forma il vettore normale PVector r = s oltrepassate (corrente); float a = acos (s.dot (corrente)); Ho capito bene? a è uguale all'arco coseno del prodotto punto tra S e V? –

+0

Sì, hai capito esattamente. Il prodotto incrociato è sempre perpendicolare a entrambi i vettori. C'è un caso particolare se entrambi i vettori hanno la stessa direzione (probabilmente all'inizio del percorso probabilmente), ma è facile da gestire. – hirschhornsalz

+0

Ho aggiunto il codice con il mio tentativo di usare le tue spiegazioni. Ancora alcune cose con cui mi sono perso. Sto cercando di capire 'moltiplicare ogni elemento della tua forma con marcire'. ogni celebrazione, significa ogni vettore o ogni componente di ciascun vettore? È l'ultimo bit di concatenazione delle rotazioni che mi dà mal di testa. tradurre dovrebbe essere solo ottenere i vertici ruotati e aggiungere un vettore di traduzione a quello. Grazie per tutta la pazienza, drhirsch! –