Trucheteries : pour faire des trucheteries au plotter ou à la brodeuse, il faut joindre des chemins disjoints avec vpype :
vpype read truchet_094.svg linemerge linesimplify -t 0.05 write truchet_94_merge.svg
Broderie + upcycling : https://www.instagram.com/p/C58HraKiuYE/?img_index=1
Trucs à broder: https://dev.laurent-malys.fr/harmono-bro/
Profiter de ces beaux jours d'automne pour se chauffer au néons et ressortir la petite bobineuse pour créer un mirco
Dérive dans les images semi-graphiques. Avant le graphisme «hi-res». Journal : infra-graphique
Exploration des quaternions et de la modélisation procedurale avec Processing
Une vidéo qui m'a bien aidée à comprendre le ce truc : https://www.youtube.com/watch?v=bKd2lPjl92c
Les sketchs Processing ci-dessous dépendent de façon importante sur deux librairies non officielles:
Vous pouvez naviguer dans l'espace 3D avec les touches ZQSDA et E. Affichage en mode filaire avec W.
import queasycam.*; import com.badlogic.gdx.math.*; import nervoussystem.obj.*; QueasyCam cam; static final Vector3 xAxis = new Vector3(1.0, 0.0, 0.0); float rootSize = 8; int numSeg = 128; Segment root = new Segment(null, 10, new Quaternion()); public void setup() { fullScreen(P3D); cam = new QueasyCam(this, 1, 9999); cam.key_forward = 'o'; cam.key_left = 'k'; cam.key_backward = 'l'; cam.key_right = 'm'; cam.key_up = 'i'; cam.key_down = 'p'; sphereDetail(16); Segment c1 = root.branch(rootSize, new Quaternion().setEulerAngles(0, 0, -100)); Segment c2 = root.branch(rootSize, new Quaternion().setEulerAngles(120, 0, -100)); Segment c3 = root.branch(rootSize, new Quaternion().setEulerAngles(240, 0, -100)); for (int i=0; i<numSeg; i++) { c1 = c1.branch(rootSize * pow(1.0-i/(20.0*numSeg), i), new Quaternion()); c2 = c2.branch(rootSize * pow(1.0-i/(20.0*numSeg), i), new Quaternion()); c3 = c3.branch(rootSize * pow(1.0-i/(20.0*numSeg), i), new Quaternion()); } root.update(); } public void draw() { checkKeys(); background(255); lights(); noStroke(); translate(width/2, height/2); sphere(50); root.draw(); } void checkKeys() { if (keyPressed == false) return; float step = 0.1; Quaternion rotation = new Quaternion(); if (key == 'a') { rotation.setEulerAngles(-step, 0.0, 0.0); } if (key == 'z') { rotation.setEulerAngles(step, 0.0, 0.0); } if (key == 'q') { rotation.setEulerAngles(0.0, -step, 0.0); } if (key == 's') { rotation.setEulerAngles(0.0, step, 0.0); } if (key == 'w') { rotation.setEulerAngles(0.0, 0.0, -step); } if (key == 'x') { rotation.setEulerAngles(0.0, 0.0, step); } for (Segment seg : root.children) { while (seg.hasChildren()) { seg.localRot.mul(rotation).nor(); seg = seg.children.get(0); } seg.localRot.mul(rotation).nor(); } root.update(); } /***************************** ** class Segment ** *****************************/ public class Segment { static final int faces = 32; static final float l2b = 10.0; // Length to base ratio protected Segment parent = null; protected Vector3 head = new Vector3(); protected Vector3 rootWorldPos = null; private Quaternion localRot = new Quaternion(); // Rotation from parent Segment private Quaternion globalRot = new Quaternion(); // Total rotation in world space private float len; private float baseRadius; private Vector3[] points = new Vector3[faces]; private Vector3 tmpVec = new Vector3(); private ArrayList<Segment> children = new ArrayList(); public Segment(Segment parent, float len, Quaternion rot) { this.parent = parent; if (parent == null) { rootWorldPos = new Vector3(); globalRot = rot.cpy(); } else { rootWorldPos = parent.rootWorldPos; } this.localRot = rot.cpy(); this.len = len; baseRadius = len * l2b * 0.5; } public Segment branch(float len, Quaternion rot) { Segment child = new Segment(this, len, rot); children.add(child); return child; } public boolean hasChildren() { return !children.isEmpty(); } public void draw() { // Draw head line stroke(0); Vector3 up = new Vector3(0, len*0.4, 0); globalRot.transform(up); if (parent != null) line(parent.head.x, parent.head.y, parent.head.z, head.x, head.y, head.z); else line(0.0, 0.0, 0.0, head.x, head.y, head.z); // Draw UP line stroke(255, 0, 0); line(head.x, head.y, head.z, head.x + up.x, head.y + up.y, head.z + up.z); noStroke(); if (hasChildren()) drawSegment(); else drawTip(); for (Segment child : children) child.draw(); } private void drawSegment() { beginShape(QUAD_STRIP); for (int i=0; i<faces+1; i++) { if (parent == null) { Quaternion rot = new Quaternion(); rot.setFromAxisRad(xAxis, i * TWO_PI / faces); rot.mulLeft(globalRot); rot.transform(tmpVec); tmpVec.set(len, baseRadius, 0.0); } else { tmpVec.set(parent.points[i%faces]); } vertex(tmpVec.x, tmpVec.y, tmpVec.z); tmpVec.set(points[i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); } endShape(); } private void drawTip() { // Draw a capped tip if this segment has no children beginShape(TRIANGLE_FAN); vertex(head.x, head.y, head.z); for (int i=0; i<faces+1; i++) { if (parent == null) { Quaternion rot = new Quaternion(); rot.setFromAxisRad(xAxis, i * TWO_PI / faces); rot.mulLeft(globalRot); rot.transform(tmpVec); tmpVec.set(len, baseRadius, 0.0); } else { tmpVec.set(parent.points[i%faces]); } } endShape(); } public void update() { Vector3 pos = rootWorldPos.cpy(); globalRot.set(localRot); if (parent != null) { pos.add(parent.head); globalRot.mulLeft(parent.globalRot).nor(); } head.set(len, 0.0, 0.0); // Going right by default globalRot.transform(head); head.add(pos); float angle = 0.0; Quaternion rot = new Quaternion(); for (int i=0; i<faces; i++) { rot.setFromAxisRad(xAxis, angle); rot.mulLeft(globalRot); tmpVec.set(len, baseRadius, 0.0); rot.transform(tmpVec); points[i] = tmpVec.cpy().add(pos); angle += TWO_PI / faces; } for (Segment child : children) child.update(); } }
import queasycam.*; import com.badlogic.gdx.math.*; import nervoussystem.obj.*; import processing.pdf.*; static final Vector3 xAxis = new Vector3(1.0, 0.0, 0.0); static final Vector3 yAxis = new Vector3(0.0, 1.0, 0.0); static final Vector3 zAxis = new Vector3(0.0, 0.0, 1.0); QueasyCam cam; boolean wireframe = false; boolean record = false; ArrayList<Drawable> objects = new ArrayList(); public void setup() { fullScreen(P3D); //size(800, 600, P3D); cam = new QueasyCam(this, 1, 9999); cam.key_forward = 'z'; cam.key_left = 'q'; cam.key_backward = 's'; cam.key_right = 'd'; cam.key_up = 'e'; cam.key_down = 'a'; Pipe pipe = new Pipe( new Vector3(0, 0, 0), new Quaternion().setEulerAngles(10, 0, 40), 10, 32); objects.add(pipe); float size = 20; for (int i=0; i<8; i++) { pipe.changeSize(size); size += 10; pipe.addBend(90, 0.0, 45.0); } } public void draw() { background(255); lights(); strokeWeight(1); stroke(255, 0, 0); line(0, 0, 0, 100, 0, 0); stroke(0, 255, 0); line(0, 0, 0, 0, 100, 0); stroke(0, 0, 255); line(0, 0, 0, 0, 0, 100); if (wireframe) stroke(0); else noStroke(); strokeWeight(2); if (record) { beginRaw(PDF, "output.pdf"); noFill(); } for (Drawable o : objects) o.draw(); if (record) { record = false; endRaw(); } } void keyPressed() { if (key == 'w') wireframe = !wireframe; if (key == 'p') record = true; } public interface Drawable { public void draw(); public void update(); } public interface Chainable extends Drawable { public Vector3 getOutPos(); public Quaternion getOutOrientation(); } /***************************** ** class Pipe ** *****************************/ public class Pipe implements Drawable { private Vector3 pos; private Quaternion orientation; private float radius; private float nextRadius; private int faces; private ArrayList<Chainable> objects = new ArrayList(); private Vector3 currentPos; private Quaternion currentOrientation; public Pipe(Vector3 pos, Quaternion orientation, float radius, int faces) { this.pos = pos; this.orientation = orientation; this.radius = radius; this.nextRadius = radius; this.faces = faces; currentPos = this.pos.cpy(); currentOrientation = orientation.cpy(); } public void changeSize(float next) { this.nextRadius = next; } private void add(Chainable object) { objects.add(object); currentPos = object.getOutPos(); currentOrientation = object.getOutOrientation(); radius = nextRadius; } public void addTube(float len) { addTube(len, 0.0); } public void addTube(float len, float twist) { Tube tube = new Tube( currentPos, currentOrientation, len, radius, nextRadius, twist, 1, faces ); add(tube); } public void addBend(float angleBendDeg) { addBend(angleBendDeg, 0.0, 0.0); } public void addBend(float angleBendDeg, float angleRotDeg) { addBend(angleBendDeg, angleRotDeg, 0.0); } public void addBend(float angleBendDeg, float angleRotDeg, float twist) { int rings = 8; float radInt = 50; currentOrientation.mul(new Quaternion().setEulerAngles(0, angleRotDeg, 0)); Bend bend = new Bend( currentPos, currentOrientation, radius, nextRadius, radians(angleBendDeg), radInt, twist, rings, faces ); add(bend); } public void update() { for (Chainable o : objects) o.update(); } public void draw() { for (Chainable o : objects) { o.draw(); } Vector3 head = currentOrientation.transform(new Vector3(32, 0, 0)); head.add(currentPos); line(currentPos.x, currentPos.y, currentPos.z, head.x, head.y, head.z); } } /***************************** ** class Tube ** *****************************/ public class Tube implements Drawable, Chainable { private Vector3 pos; private Quaternion orientation; private float len; private float radiusIn; private float radiusOut; private float twist; private int rings; private int faces; private Vector3[] points; private Vector3 tmpVec = new Vector3(); private Vector3 outPos = new Vector3(); private Quaternion outOrientation = new Quaternion(); public Tube( Vector3 pos, Quaternion orientation, float len, float radiusIn, float radiusOut, float twist, // In degrees int rings, int faces ) { this.pos = pos.cpy(); this.orientation = orientation.cpy(); this.len = len; this.radiusIn = radiusIn; this.radiusOut = radiusOut; this.twist = radians(twist); this.rings = rings; this.faces = faces; points = new Vector3[faces * (rings+1)]; update(); } Vector3 getOutPos() { return outPos.cpy(); } Quaternion getOutOrientation() { return outOrientation.cpy(); } public void update() { Quaternion rot = new Quaternion(); int pointIdx = 0; for (int s=0; s<rings+1; s++) { float angle = s * twist/rings; for (int i=0; i<faces; i++) { tmpVec.set(s * len/rings, map(s, 0, rings, radiusIn, radiusOut), 0.0); rot.setFromAxisRad(xAxis, angle).mulLeft(orientation); rot.transform(tmpVec); points[pointIdx++] = tmpVec.cpy().add(pos); angle += TWO_PI / faces; } } outOrientation.set(orientation); outOrientation.mul(new Quaternion().setFromAxisRad(xAxis, twist)); outPos.set(len, 0.0, 0.0); outOrientation.transform(outPos); outPos.add(pos); } public void draw() { for (int s=0; s<rings; s++) { beginShape(QUAD_STRIP); for (int i=0; i<faces+1; i++) { tmpVec.set(points[s * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); tmpVec.set(points[(s+1) * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); } endShape(); } } } /***************************** ** class Bend ** *****************************/ public class Bend implements Drawable, Chainable { private Vector3 pos; private Quaternion orientation; private float radiusIn; private float radiusOut; private float bendAngle; private float radInt; private float twist; private int rings; private int faces; private Vector3[] points; private Vector3 tmpVec = new Vector3(); private Vector3 outPos = new Vector3(); private Quaternion outOrientation = new Quaternion(); public Bend( Vector3 pos, Quaternion orientation, float radiusIn, float radiusOut, float bendAngle, // In radians float radInt, // Internal radius float twist, int rings, int faces ) { this.pos = pos.cpy(); this.orientation = orientation.cpy(); this.radiusIn = radiusIn; this.radiusOut = radiusOut; this.bendAngle = bendAngle; this.radInt = radInt; this.twist = radians(twist); this.rings = rings; this.faces = faces; points = new Vector3[faces * (rings+1)]; update(); } Vector3 getOutPos() { return outPos.cpy(); } Quaternion getOutOrientation() { return outOrientation.cpy(); } public void update() { int pointIdx = 0; Quaternion segRot = new Quaternion(); Quaternion rot = new Quaternion(); Vector3 bendOffset = new Vector3(0.0, radInt + radiusIn, 0.0); orientation.transform(bendOffset); float angleSeg = 0.0; for (int s=0; s<rings+1; s++) { segRot.setFromAxisRad(zAxis, angleSeg); segRot.mulLeft(orientation); float tubeRadius = map(s, 0, rings, radiusIn, radiusOut); Vector3 segOffset = new Vector3(0.0, radInt + tubeRadius, 0.0); segRot.transform(segOffset); angleSeg += bendAngle / rings; float angle = s * twist/rings; for (int i=0; i<faces; i++) { tmpVec.set(0.0, tubeRadius, 0.0); rot.setFromAxisRad(xAxis, angle); rot.mulLeft(segRot); rot.transform(tmpVec); points[pointIdx++] = tmpVec.cpy().sub(segOffset).add(bendOffset).add(pos); angle += TWO_PI / faces; } } outPos.set(0, -radiusOut - radInt, 0); outOrientation.set(orientation); outOrientation.mul(new Quaternion().setFromAxisRad(zAxis, bendAngle)); outOrientation.transform(outPos); outOrientation.mul(new Quaternion().setFromAxisRad(xAxis, twist)); tmpVec.set(0.0, radInt + radiusIn, 0.0); orientation.transform(tmpVec); outPos.add(tmpVec).add(pos); } public void draw() { for (Vector3 p : points) { point(p.x, p.y, p.z); } for (int s=0; s<rings; s++) { beginShape(QUAD_STRIP); for (int i=0; i<faces+1; i++) { tmpVec.set(points[s * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); tmpVec.set(points[(s+1) * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); } endShape(); } } }
import queasycam.*; import com.badlogic.gdx.math.*; static final Vector3 xAxis = new Vector3(1.0, 0.0, 0.0); static final Vector3 yAxis = new Vector3(0.0, 1.0, 0.0); static final Vector3 zAxis = new Vector3(0.0, 0.0, 1.0); QueasyCam cam; boolean wireframe = false; ArrayList<Drawable> objects = new ArrayList(); public void setup() { fullScreen(P3D); cam = new QueasyCam(this, PConstants.PI/2.5f, 0.01, 9999); cam.key_forward = 'z'; cam.key_left = 'q'; cam.key_backward = 's'; cam.key_right = 'd'; cam.key_up = 'e'; cam.key_down = 'a'; // Center tower Pipe pipe = new Pipe( new Vector3(0.0, 800, 0.0), new Quaternion().setFromAxis(zAxis, -90), 550, 64); pipe.addTube(1000); pipe.changeSize(800); pipe.addTube(1000); pipe.changeSize(820); pipe.addTube(20); pipe.changeSize(2600); pipe.addTube(200); pipe.addTube(-400); objects.add(pipe); // Surrounding circus pipe = new Pipe( new Vector3(0.0, 800, 0.0), new Quaternion().setFromAxis(zAxis, -90), 800, 64); pipe.addTube(600); pipe.changeSize(2000); pipe.addTube(200); pipe.changeSize(2040); pipe.addTube(40); pipe.addTube(1000); objects.add(pipe); for (int r=0; r<4; r++) { Quaternion rot = new Quaternion().setEulerAngles(r * 90, 0, 9); for (int i=0; i<6; i++) { Vector3 pos = new Vector3(-2100, 298, (i-3)*34 + 17); rot.transform(pos); pipe = new Pipe(pos, rot, 16, 16); pipe.addTube(1480 + 4 * abs(i-3)); pipe.addBend(99, 180); pipe.addTube(300, (3-i) * 5); pipe.addBend(14); if (abs(i-2.5)<=1) { pipe.addTube(860); pipe.changeSize(24); pipe.addTube(18); pipe.changeSize(26); pipe.addTube(200); } else { pipe.addTube(1000); pipe.addBend(70); float l=0.0, ll=0.0; int state = 0; // -1: left; 0: center; 1: right while (l<1800) { int turn = floor(random(3.0)-1.0); if (state==0 && turn == -1) { pipe.addBend(45, 90); state = -1; } else if (state==0 && turn == 1) { pipe.addBend(45, -90); state = 1; } else if (state == -1 && turn == 1) { pipe.addBend(45, 180, -90); state = 0; } else if (state == 1 && turn == -1) { pipe.addBend(45, -180, 90); state = 0; } else if (state == 0) { pipe.addTube(ll); l += ll; ll = 0.0; } ll += 100; } } objects.add(pipe); } float deg = r * 90 + 45; rot.setFromAxis(zAxis, -90); rot.mul(new Quaternion().setFromAxis(xAxis, deg)); Vector3 pos = new Vector3(590 * cos(radians(deg)), 800, 590 * sin(radians(deg))); pipe = new Pipe(pos, rot, 34, 16); pipe.addTube(980); pipe.addBend(14); pipe.addTube(950); pipe.addBend(70); pipe.addTube(1800); objects.add(pipe); deg = r * 90 + 45; rot.setFromAxis(zAxis, -90); rot.mul(new Quaternion().setFromAxis(xAxis, deg)); pos = new Vector3(1700 * cos(radians(deg)), 50, 1700 * sin(radians(deg))); pipe = new Pipe(pos, rot, 140, 32); pipe.addBend(90, 0, 0, 300); objects.add(pipe); } } public void draw() { background(0); //lights(); //ambientLight(12, 4, 1); //pointLight(100, 100, 110, 140, -2000, 144); directionalLight(204, 204, 204, .5, 0.6, 0.2); emissive(50, 46, 51); //specular(204, 102, 0); shininess(1.0); strokeWeight(1); stroke(255, 0, 0); line(0, 0, 0, 100, 0, 0); stroke(0, 255, 0); line(0, 0, 0, 0, 100, 0); stroke(0, 0, 255); line(0, 0, 0, 0, 0, 100); if (wireframe) stroke(0); else noStroke(); strokeWeight(1); for (Drawable o : objects) o.draw(); } void keyPressed() { if (key == 'w') wireframe = !wireframe; } public interface Drawable { public void draw(); public void update(); } public interface Chainable extends Drawable { public Vector3 getOutPos(); public Quaternion getOutOrientation(); } /***************************** ** class Pipe ** *****************************/ public class Pipe implements Drawable { private Vector3 pos; private Quaternion orientation; private float radius; private float nextRadius; private int faces; private ArrayList<Chainable> objects = new ArrayList(); private Vector3 currentPos; private Quaternion currentOrientation; public Pipe(Vector3 pos, Quaternion orientation, float radius, int faces) { this.pos = pos; this.orientation = orientation; this.radius = radius; this.nextRadius = radius; this.faces = faces; currentPos = this.pos.cpy(); currentOrientation = orientation.cpy(); } public void changeSize(float next) { this.nextRadius = next; } private void add(Chainable object) { objects.add(object); currentPos = object.getOutPos(); currentOrientation = object.getOutOrientation(); radius = nextRadius; } public void addTube(float len) { addTube(len, 0.0); } public void addTube(float len, float twist) { Tube tube = new Tube( currentPos, currentOrientation, len, radius, nextRadius, twist, 1, faces ); add(tube); } public void addBend(float angleBendDeg) { addBend(angleBendDeg, 0.0, 0.0); } public void addBend(float angleBendDeg, float angleRotDeg) { addBend(angleBendDeg, angleRotDeg, 0.0); } public void addBend(float angleBendDeg, float angleRotDeg, float twist) { float radInt = 50; addBend(angleBendDeg, angleRotDeg, twist, radInt); } public void addBend(float angleBendDeg, float angleRotDeg, float twist, float radInt) { int rings = ceil(angleBendDeg * 64.0 / 360.0); currentOrientation.mul(new Quaternion().setEulerAngles(0, angleRotDeg, 0)); Bend bend = new Bend( currentPos, currentOrientation, radius, nextRadius, radians(angleBendDeg), radInt, twist, rings, faces ); add(bend); } public void update() { for (Chainable o : objects) o.update(); } public void draw() { for (Chainable o : objects) { o.draw(); } Vector3 head = currentOrientation.transform(new Vector3(32, 0, 0)); head.add(currentPos); line(currentPos.x, currentPos.y, currentPos.z, head.x, head.y, head.z); } } /***************************** ** class Tube ** *****************************/ public class Tube implements Drawable, Chainable { private Vector3 pos; private Quaternion orientation; private float len; private float radiusIn; private float radiusOut; private float twist; private int rings; private int faces; private Vector3[] points; private Vector3 tmpVec = new Vector3(); private Vector3 outPos = new Vector3(); private Quaternion outOrientation = new Quaternion(); public Tube( Vector3 pos, Quaternion orientation, float len, float radiusIn, float radiusOut, float twist, // In degrees int rings, int faces ) { this.pos = pos.cpy(); this.orientation = orientation.cpy(); this.len = len; this.radiusIn = radiusIn; this.radiusOut = radiusOut; this.twist = radians(twist); this.rings = rings; this.faces = faces; points = new Vector3[faces * (rings+1)]; update(); } Vector3 getOutPos() { return outPos.cpy(); } Quaternion getOutOrientation() { return outOrientation.cpy(); } public void update() { Quaternion rot = new Quaternion(); int pointIdx = 0; for (int s=0; s<rings+1; s++) { float angle = s * twist/rings; for (int i=0; i<faces; i++) { tmpVec.set(s * len/rings, map(s, 0, rings, radiusIn, radiusOut), 0.0); rot.setFromAxisRad(xAxis, angle).mulLeft(orientation); rot.transform(tmpVec); points[pointIdx++] = tmpVec.cpy().add(pos); angle += TWO_PI / faces; } } outOrientation.set(orientation); outOrientation.mul(new Quaternion().setFromAxisRad(xAxis, twist)); outPos.set(len, 0.0, 0.0); outOrientation.transform(outPos); outPos.add(pos); } public void draw() { for (int s=0; s<rings; s++) { beginShape(QUAD_STRIP); for (int i=0; i<faces+1; i++) { tmpVec.set(points[s * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); tmpVec.set(points[(s+1) * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); } endShape(); } } } /***************************** ** class Bend ** *****************************/ public class Bend implements Drawable, Chainable { private Vector3 pos; private Quaternion orientation; private float radiusIn; private float radiusOut; private float bendAngle; private float radInt; private float twist; private int rings; private int faces; private Vector3[] points; private Vector3 tmpVec = new Vector3(); private Vector3 outPos = new Vector3(); private Quaternion outOrientation = new Quaternion(); public Bend( Vector3 pos, Quaternion orientation, float radiusIn, float radiusOut, float bendAngle, // In radians float radInt, // Internal radius float twist, int rings, int faces ) { this.pos = pos.cpy(); this.orientation = orientation.cpy(); this.radiusIn = radiusIn; this.radiusOut = radiusOut; this.bendAngle = bendAngle; this.radInt = radInt; this.twist = radians(twist); this.rings = rings; this.faces = faces; points = new Vector3[faces * (rings+1)]; update(); } Vector3 getOutPos() { return outPos.cpy(); } Quaternion getOutOrientation() { return outOrientation.cpy(); } public void update() { int pointIdx = 0; Quaternion segRot = new Quaternion(); Quaternion rot = new Quaternion(); Vector3 bendOffset = new Vector3(0.0, radInt + radiusIn, 0.0); orientation.transform(bendOffset); float angleSeg = 0.0; for (int s=0; s<rings+1; s++) { segRot.setFromAxisRad(zAxis, angleSeg); segRot.mulLeft(orientation); float tubeRadius = map(s, 0, rings, radiusIn, radiusOut); Vector3 segOffset = new Vector3(0.0, radInt + tubeRadius, 0.0); segRot.transform(segOffset); angleSeg += bendAngle / rings; float angle = s * twist/rings; for (int i=0; i<faces; i++) { tmpVec.set(0.0, tubeRadius, 0.0); rot.setFromAxisRad(xAxis, angle); rot.mulLeft(segRot); rot.transform(tmpVec); points[pointIdx++] = tmpVec.cpy().sub(segOffset).add(bendOffset).add(pos); angle += TWO_PI / faces; } } outPos.set(0, -radiusOut - radInt, 0); outOrientation.set(orientation); outOrientation.mul(new Quaternion().setFromAxisRad(zAxis, bendAngle)); outOrientation.transform(outPos); outOrientation.mul(new Quaternion().setFromAxisRad(xAxis, twist)); tmpVec.set(0.0, radInt + radiusIn, 0.0); orientation.transform(tmpVec); outPos.add(tmpVec).add(pos); } public void draw() { for (Vector3 p : points) { point(p.x, p.y, p.z); } for (int s=0; s<rings; s++) { beginShape(QUAD_STRIP); for (int i=0; i<faces+1; i++) { tmpVec.set(points[s * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); tmpVec.set(points[(s+1) * faces + i%faces]); vertex(tmpVec.x, tmpVec.y, tmpVec.z); } endShape(); } } }