3D로 경로를 압출하는 방법?
-
08-07-2019 - |
문제
나는 3D로 경로를 압제하려고합니다. 어떤 점을 따르고 '튜브'를 위해 정기적 인 다각형을 사용하는 것은 아직 없습니다. 지금은 프로토 타입을 신속하게 프로토 타입으로 사용하고 있지만 나중에 코드를 OpenGL로 바꿀 것입니다.
내 문제는 직각으로 '조인트'를 회전시키는 것입니다. 나는 확실하지 않은 각도를 얻는 방법에 대한 대략적인 아이디어가 있다고 생각합니다.
Simon Greenwold (처리> 파일> 예제> 3D> 양식> 정점)의 샘플에서 시작했습니다.
업데이트> Refactored/Simplified Code
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;
}
그리고 여기에 압출 클래스가 있습니다.
가져 오기 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);
}
}
업데이트
내 스케치의 모습은 다음과 같습니다.
돌출 처리 http://doc.gold.ac.uk/~ma802gp/extrude.gif
문제는 관절이 직각이 아니기 때문에 투출이 잘못 보인다는 것입니다. 이것은 선반으로 달성 될 수 있기 때문에 아주 좋은 예는 아닙니다. 내가 선반에 임의의 포인트 세트와 축으로 일할 수 있다면 나는 liviu stoicoviciu의 기술을 기반으로 기하학적 몸을 만들려고 노력하고 있기 때문에 압출을 사용하고 있습니다.
몇 가지 샘플은 다음과 같습니다.
스타 그림 http://doc.gold.ac.uk/~ma802gp/star_painting.jpg
스타 페이퍼 조각 http://doc.gold.ac.uk/~ma802gp/star_paper_sculpture.jpg
삼각형 http://doc.gold.ac.uk/~ma802gp/triangles_pencil.jpg
품질이 좋지 않은 것에 대해 죄송합니다.
삼각형 이미지에서 볼 수 있듯이 압출로 달성됩니다.
업데이트
드로우 방법에서 Drhirsch의 도움을 사용하려는 시도는 다음과 같습니다.
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);
}
코드를 리팩토링 했으므로 이제 CSHAPE라고 불리는 클래스를 삭제라고하며 코드는 더 적고 희망적으로 간단하며 혼란 스러울 수있는 PVECTOR 객체의 벡터 대신 PVECTOR 객체 배열을 사용합니다.
다음은 몇 가지 Escher-esque 결과를 가진 또 다른 시도입니다.
업화 된 추첨
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
drhirsch에 의해 편집해야합니다.
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);
}
}
업데이트
다음은 내 문제에 대한 간단한 그림입니다.
설명 http://doc.gold.ac.uk/~ma802gp/description.gif
파란색 경로는 포인트 나누스 = 6 인 경우 내 코드의 지점 [] PVECTOR 배열과 동일합니다. 빨간색 경로는 내가 해결하기 위해 고군분투하는 것입니다. 녹색 경로는 내가 달성하고 싶은 것입니다.
업데이트
내가 생각하는 정점의 순서와 관련된 사소한 문제. 다음은 6 점 및 NO (IF/ELSE % 2) 별 조건을 사용한 일부 인쇄 화면입니다.
해결책
모양이 정상 벡터 S가 있다고 가정하면 예제에서 S가 XY로 평평하기 때문에 (0,0,1)입니다. 현재 경로 벡터 v (정규화)와 S 사이의 교차 제품을 사용하여 회전 축 벡터 R을 얻을 수 있습니다. 그래서:
R = S x V
a = arc cos(S . V)
이제 설정할 수 있습니다 회전 매트릭스 r과 a와 함께 모양을 회전시킵니다.
glrotate (...)를 사용하여 스택의 현재 매트릭스를 회전시킬 수 있지만 glbegin ()와 glend () 사이에 할 수는 없습니다. 따라서 직접 또는 라이브러리로 매트릭스 곱셈을해야합니다.
편집 : 사용중인 라이브러리를 짧게 살펴보면 회전 행렬을 설정할 수 있어야합니다.
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);
이제 모양의 각 요소에 썩음을 곱하고 현재 경로 벡터로 번역합니다.
PVector rotVec;
rot.mult((PVector)shape[i], rotVec);
rotVec.add(current);