Comment extruder un chemin en 3D?
-
08-07-2019 - |
Question
J'essaie d'extruder un chemin en 3D. Rien de spécial pour le moment, il suffit de suivre quelques points et d’utiliser un polygone régulier pour «tube». J'utilise actuellement Traitement en cours pour réaliser rapidement un prototype, mais le code deviendra plus tard OpenGL.
Mon problème est de faire tourner les "joints" à angle droit. Je pense avoir une idée approximative de la manière d’obtenir les angles, mais je ne suis pas sûr.
J'ai commencé à partir d'un échantillon de Simon Greenwold (Traitement> Fichier> Exemples> 3D> Formulaires> Sommets). Voici ma tentative jusqu'à présent:
MISE À JOUR > CODE REFACTORÉ / SIMPLIFIÉ
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;
}
Et voici la classe d'extrusion:
importer 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);
}
}
MISE À JOUR
Voici à quoi ressemble mon croquis:
traitement de l'extrusion http://doc.gold.ac.uk/~ma802gp/extrude.gif
Le problème est que les joints ne sont pas au bon angle et que l'extrusion ne semble pas être bonne. Ce n'est pas un très bon exemple, car cela pourrait être réalisé avec un tour. Si je peux obtenir un tour pour travailler avec un ensemble arbitraire de points et un axe, ce sera génial. J'utilise l'extrusion J'essaie de créer des corps géométriques basés sur l'art de Liviu Stoicoviciu.
Voici quelques exemples:
peinture d'étoile http://doc.gold.ac.uk/~ma802gp/star_painting.jpg
sculpture en papier étoiles http://doc.gold.ac.uk/~ma802gp/star_paper_sculpture.jpg
triangles http://doc.gold.ac.uk/~ma802gp/triangles_pencil.jpg
Désolé pour la qualité médiocre.
Comme vous pouvez le voir dans l'image des triangles, cela serait réalisé avec des extrusions.
MISE À JOUR
Voici ma tentative d'utiliser l'aide de drhirsch dans la méthode de tirage au sort:
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);
}
J'ai refactoré le code et la classe qui s'appelait auparavant CShape s'appelle Extrude, le code est inférieur et, espérons-le, simple, et j'utilise un tableau d'objets PVector au lieu d'un vecteur d'objets PVector, ce qui peut prêter à confusion. .
Voici une autre tentative avec quelques résultats escher-esque:
tirage amélioré
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
Modifier de drhirsch Cela devrait fonctionner:
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);
}
}
MISE À JOUR
Voici une illustration simple de mon problème:
description http://doc.gold.ac.uk/~ma802gp/description.gif
Le chemin bleu est équivalent au tableau PVector de points [] dans mon code, si pointsNum = 6. Le chemin rouge est ce que j'ai du mal à résoudre, le chemin vert est ce que je veux réaliser.
MISE À JOUR
Je pense que quelques problèmes mineurs avec l’ordre des vertices. Voici quelques écrans d’impression utilisant 6 points et aucune condition d’étoile (si / 2%).
La solution
En supposant que votre forme a un vecteur normal S. Dans votre exemple, S serait (0,0,1), car votre forme est plate dans xy. Vous pouvez utiliser le produit croisé entre le vecteur de chemin actuel V (normalisé) et S pour obtenir le vecteur d'axe de rotation R. Vous devez faire pivoter votre forme autour de R. L'angle de rotation peut être obtenu à partir du produit scalaire entre S et V. Donc:
R = S x V
a = arc cos(S . V)
Vous pouvez maintenant configurer une matrice de rotation avec R et a et faire pivoter la forme par celle-ci. .
Vous pouvez utiliser glRotate (...) pour faire pivoter la matrice actuelle sur la pile, mais cela ne peut pas être fait entre glBegin () et glEnd (). Donc, vous devez faire la multiplication de matrice par vous-même ou avec une bibliothèque.
Modifier: après un bref aperçu de la bibliothèque que vous utilisez, vous devriez pouvoir configurer la matrice de rotation avec
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);
et multipliez maintenant chaque élément de votre forme avec rot et traduisez-le par votre vecteur de chemin actuel:
PVector rotVec;
rot.mult((PVector)shape[i], rotVec);
rotVec.add(current);