Skip to content

Instantly share code, notes, and snippets.

@pixelpusher
Created May 10, 2015 11:48
Show Gist options
  • Save pixelpusher/d968cbbd8f18f5be7d87 to your computer and use it in GitHub Desktop.
Save pixelpusher/d968cbbd8f18f5be7d87 to your computer and use it in GitHub Desktop.
ProfilePathAlignmentSpline2D
/**
* <p>Based on a toxiclibs example, a 2D polygon profile created using
* a smoothed triangle shape generatd by a 2D spline curve is being swept
* along a 3D spline path and aligned to the path direction using quaternions.
* This generates also a triangular mesha along the path that can be exported.
* The example demonstrates the usage of the alignment quaternion
* in combination with a 4x4 transformation matrix, as well as the use
* of the toAxisAngle() method to compute a rotation axis from a quat,
* and some manual mesh creation functionality.
* </p>
* <p>
* It also demostrates the limitaitons of using quarternion rotations, as they
* lead to some tortured, overly-twisty geometry due to simplistic rotation
* calcuations along the path.
* </p>
*
* <p><strong>Usage:</strong><ul>
* <li>l: toggle between line/dot rendering</li>
* </ul></p>
*/
/*
* Original Copyright (c) 2011 Karsten Schmidt
* Modified 2015 by Evan Raskob (http://pixelist.info) to use splines
* and to create mesh geometry along the path.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* http://creativecommons.org/licenses/LGPL/2.1/
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
import toxi.geom.*;
import toxi.geom.mesh.*;
import toxi.processing.*;
import java.util.Iterator;
import java.util.List;
Spline2D spline;
List<Vec2D> verts, points;
int subDiv = 30;
int quality = 5;
TriangleMesh mesh = null;
ToxiclibsSupport gfx;
List<Vec3D> path;
ArrayList<Vec3D> tubeVertices=new ArrayList<Vec3D>();
int pathID=0;
boolean doRenderPoints;
void setup() {
size(1024, 720, P3D);
gfx=new ToxiclibsSupport(this);
int triSize = 40;
Polygon2D tri = (new Triangle2D(new Vec2D(-triSize, 0), new Vec2D(0,0), new Vec2D(-triSize/2, -triSize/2)))
.toPolygon2D();
//for (Vec2D p : tri.vertices) {
// println("vert: " + p);
//}
spline=new Spline2D(tri.vertices);
spline.updateCoefficients();
spline.computeVertices(subDiv);
verts = spline.getDecimatedVertices(quality, true);
println("quality["+quality+"] results in " + verts.size() + " points");
points = spline.pointList;
// compute spiral key points (every 45 degrees)
ArrayList spiralPoints = new ArrayList();
for (float theta=-TWO_PI, r=100; theta<8*TWO_PI; theta+=QUARTER_PI) {
Vec3D p=Vec3D.fromXYTheta(theta).scale(r).add(200, 0, 0).rotateY(theta/9);
spiralPoints.add(p);
}
// use points to compute a spline and sample at regular interval
Spline3D s=new Spline3D(spiralPoints);
s.computeVertices(10);
path=s.getDecimatedVertices(4);
println("spiral path has: " + path.size() + " vertices");
int totalVertsInMesh = path.size() * verts.size();
int totalFacesInMesh = totalVertsInMesh/2 + 2;
println("totalFacesInMesh: " + totalFacesInMesh);
println("totalVertsInMesh: " + totalVertsInMesh);
mesh = new TriangleMesh("sweepspiral", totalVertsInMesh, totalFacesInMesh);
}
void draw() {
background(51);
lights();
translate(width / 2, height / 2, 0);
rotateX(mouseY * 0.01f);
rotateY(mouseX * 0.01f);
noStroke();
gfx.origin(300);
stroke(255, 0, 255);
gfx.lineStrip3D(path);
if (pathID<path.size()-1) {
// compute current curve direction
Vec3D dir=path.get(pathID+1).sub(path.get(pathID)).normalize();
// calculate alignment orientation for direction
// our profile shape is in XY plane (2D) and
// so its "forward" direction is the positive Z axis
Quaternion alignment=Quaternion.getAlignmentQuat(dir, Vec3D.Z_AXIS);
// construct a matrix to move shape to current curve position
Matrix4x4 mat=new Matrix4x4().translateSelf(path.get(pathID));
// then combine with alignment matrix
mat.multiplySelf(alignment.toMatrix4x4());
// then apply matrix to (copies of) all profile shape vertices
// and append them to global vertex list
for (Vec2D p : verts) {
tubeVertices.add(mat.applyToSelf(p.to3DXY()));
}
if (tubeVertices.size() >= 2*verts.size())
{
int startIndex = tubeVertices.size() - 2*verts.size();
int endIndex = tubeVertices.size()- verts.size();
for (int i=startIndex; i<endIndex-1; i++)
{
Vec3D prevProfileVert1 = tubeVertices.get(i);
Vec3D prevProfileVert2 = tubeVertices.get(i+1);
Vec3D currProfileVert1 = tubeVertices.get(i+verts.size());
Vec3D currProfileVert2 = tubeVertices.get(i+verts.size()+1);
mesh.addFace(prevProfileVert1, prevProfileVert2, currProfileVert1);
mesh.addFace(prevProfileVert2, currProfileVert2, currProfileVert1);
}
// add last face a bit manually
// why? To avoid using % inside the above each loop, hopefully save some CPU cycles?
Vec3D prevProfileVert1 = tubeVertices.get(tubeVertices.size() - verts.size()-1);
Vec3D prevProfileVert2 = tubeVertices.get(tubeVertices.size() - 2*verts.size());
Vec3D currProfileVert1 = tubeVertices.get(tubeVertices.size()-1);
Vec3D currProfileVert2 = tubeVertices.get(tubeVertices.size() - verts.size());
mesh.addFace(prevProfileVert1, prevProfileVert2, currProfileVert1);
mesh.addFace(prevProfileVert2, currProfileVert2, currProfileVert1);
mesh.computeFaceNormals();
}
pathID++;
}
stroke(255);
if (!doRenderPoints) {
//gfx.points3D(tubeVertices);
gfx.mesh(mesh);
} else {
gfx.lineStrip3D(tubeVertices);
}
// draw coordinate system for current spline direction
int id=constrain(pathID-2, 0, path.size()-2);
// current curve direction
Vec3D dir=path.get(id+1).sub(path.get(id)).normalize();
// compute rotation axis and angle
float[] axis=Quaternion.getAlignmentQuat(dir, Vec3D.Z_AXIS).toAxisAngle();
// move to curr/last curve point
gfx.translate(path.get(id+1));
// rotate around computed axis
rotate(axis[0], axis[1], axis[2], axis[3]);
// draw rotated coordinate system
gfx.origin(new Vec3D(), 100);
}
void keyPressed() {
if (key=='l') {
doRenderPoints=!doRenderPoints;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment