Hi vjjv,
Yeah, making that change is a good idea, lol! After doing that, I count 7 tube sections and 8 rings when the value of the int segments
is 9.
The mash-up of the Bezier code with the earlier code is below. My hack is to make the points which describe the spine of the tube 1 longer than the points which draw the tube. Functions which set
(update) the tube’s vertices are split from functions which draw
them. This leads to more for-loops than would be good for performance. While testing out the code, though, it’s hopefully easier to read, understand and change.
import peasy.*;
import peasy.org.apache.commons.math.*;
import peasy.org.apache.commons.math.geometry.*;
// Fence posting - 9 linear detail = 8 segments
int linearDetail = 9;
int radialDetail = 50;
// Add more numbers to this array for tube radius.
// Sharp changes in tube radius result from this
// array being eased linearly. See smoothStep, easeIn
// functions for better options.
float[] taper = { 100, 25 };
// Turn on/off with arrow keys.
boolean showFill = false;
boolean lights = false;
boolean showStroke = true;
// Changed in setup when width, height are known.
PVector startPoint = new PVector();
PVector endPoint = new PVector();
color spineColor = 0xffff7f00;
color tubeColor = 0xff007fff;
color tubeStroke = 0xffffffff;
PeasyCam cam;
float lnDtlf = 1.0 / float(linearDetail - 1);
float rdDtlf = 1.0 / float(radialDetail);
PVector[] spine = new PVector[linearDetail + 1];
PMatrix3D[] matrices = new PMatrix3D[linearDetail];
PVector[][] vs = new PVector[linearDetail][radialDetail];
// For lookAt function.
PVector refUp = new PVector(0.0, 1.0, 0.0);
PVector right = new PVector();
PVector up = new PVector();
PVector forward = new PVector();
void setup() {
size(512, 512, P3D);
pixelDensity(displayDensity());
smooth(8);
cam = new PeasyCam(this, height);
// Starting and ending points of cylinder spine.
startPoint.set(-width * .35, -height * .35, height * 0.5);
endPoint.set(width * .35, height * .35, -height * 0.5);
for (int i = 0; i < linearDetail; ++i) {
matrices[i] = new PMatrix3D();
spine[i] = new PVector();
for (int j = 0; j < radialDetail; ++j) {
vs[i][j] = new PVector();
}
}
spine[linearDetail] = new PVector();
setSpine(spine, startPoint, endPoint, 0.5);
//setMatrices(spine, matrices, refUp, right, up, forward);
setVertices(matrices, vs);
// Make sure that 100% of the line has been traversed.
// spine[n - 2] should equal end point, while spine[n - 1]
// should exceed end point due to unclamped lerp.
if (spine.length < 15) {
println(startPoint, endPoint);
printArray(spine);
}
}
void draw() {
background(0.0);
if (lights) {
directionalLight(255.0, 255.0, 255.0,
0.0, 0.8, -0.6);
ambientLight(32.0, 32.0, 32.0);
} else {
noLights();
}
setSpine(spine, startPoint, endPoint, frameCount * 0.01);
//setMatrices(spine, matrices, refUp, right, up, forward);
setVertices(matrices, vs);
drawRefMarks(12, width, 10.0);
drawOrigin(25.0);
drawSpine(spine, 3.0, spineColor);
drawTube(vs, tubeStroke, tubeColor);
surface.setTitle(String.format("%.1f", frameRate));
}
void keyReleased() {
if (keyCode == UP) {
showFill = !showFill;
}
if (keyCode == DOWN) {
showStroke = !showStroke;
}
if (keyCode == RIGHT) {
lights = !lights;
}
}
static PVector cylinder(PVector a, PVector b, float step, float anim,
PVector out) {
// Anim variable unused so far.
out.set(a).lerp(b, step);
return out;
}
static PVector helix(PVector a, PVector b, float step, float anim,
PVector out) {
float radius = lerp(a.y, b.y, step);
float t = (step * anim);
out.set(a).lerp(b, step);
out.x += radius * cos(t);
out.y += radius * sin(t);
out.z += lerp(a.x, b.x, step) * 2.0;
return out;
}
PVector[] setSpine(PVector[] spine, PVector startPoint,
PVector endPoint, float anim) {
for (int i = 0; i < linearDetail + 1; ++i) {
float step = i * lnDtlf;
cylinder(startPoint, endPoint, step, anim, spine[i]);
//helix(startPoint, endPoint, step, anim, spine[i]);
}
return spine;
}
PVector setCrossSection(float theta, float radius, PVector out) {
return out.set(cos(theta) * radius, sin(theta) * radius);
}
// Combined with setVertices.
//PMatrix3D[] setMatrices(PVector[] spine, PMatrix3D[] ms, PVector refUp,
// PVector right, PVector up, PVector forward) {
// for (int i = 0; i < linearDetail; ++i) {
// lookAt(spine[i], spine[i + 1],
// refUp, right, up, forward, ms[i]);
// }
// return ms;
//}
PVector[][] setVertices(PMatrix3D[] ms, PVector[][] vs) {
PVector v = new PVector();
PMatrix3D m;
for (int i = 0, j; i < linearDetail; ++i) {
float istp = 1.0 - i * lnDtlf;
// Radius of cylinder set here.
float sclr = lerp(taper, istp);
// Brought in from setMatrices.
m = lookAt(spine[i], spine[i + 1],
refUp, right, up, forward, ms[i]);
for (j = 0; j < radialDetail + 1; ++j) {
float jstp;
if (j < radialDetail) {
jstp = j * rdDtlf;
float t = TWO_PI * jstp;
setCrossSection(t, sclr, v);
m.mult(v, vs[i][j]);
}
jstp = j * rdDtlf;
}
}
return vs;
}
void drawOrigin(float len) {
pushStyle();
strokeWeight(1.5);
stroke(255.0, 0.0, 0.0);
line(0.0, 0.0, 0.0, len, 0.0, 0.0);
stroke(0.0, 255.0, 0.0);
line(0.0, 0.0, 0.0, 0.0, len, 0.0);
stroke(0.0, 0.0, 255.0);
line(0.0, 0.0, 0.0, 0.0, 0.0, len);
popStyle();
}
void drawRefMarks(int count, float refSpan, float markerSize) {
pushStyle();
strokeWeight(1.0);
float toPercent = 1.0 / float(count - 1);
for (int i = 0; i < count; ++i) {
float iPrc = i * toPercent;
float mz = lerp(-refSpan, refSpan, iPrc);
for (int j = 0; j < count; ++j) {
float jPrc = j * toPercent;
float my = lerp(-refSpan, refSpan, jPrc);
for (int k = 0; k < count; ++k) {
float kPrc = k * toPercent;
float mx = lerp(-refSpan, refSpan, kPrc);
float avgPrc = (iPrc + jPrc + kPrc) * 0.33333333;
color strokeColor = lerpColor(
0xafff0000, 0xaf0000ff,
avgPrc, HSB);
stroke(strokeColor);
line(mx - markerSize, my, mz,
mx + markerSize, my, mz);
line(mx, my - markerSize, mz,
mx, my + markerSize, mz);
line(mx, my, mz - markerSize,
mx, my, mz + markerSize);
}
}
}
popStyle();
}
static PMatrix3D lookAt(PVector origin, PVector target,
PVector ref, PVector i, PVector j, PVector k, PMatrix3D out) {
PVector.sub(target, origin, k);
float m = k.magSq();
if (m < EPSILON) {
return out;
}
k.mult(1.0 / sqrt(m));
PVector.cross(k, ref, i);
i.normalize();
PVector.cross(k, i, j);
j.normalize();
out.set(
i.x, j.x, k.x, origin.x,
i.y, j.y, k.y, origin.y,
i.z, j.z, k.z, origin.z,
0.0, 0.0, 0.0, 1.0);
return out;
}
// Last point on spine is not drawn.
void drawSpine(PVector[] spine, float strokeWeight, color stroke) {
pushStyle();
noFill();
strokeWeight(strokeWeight);
stroke(stroke);
beginShape(LINE_STRIP);
for (int i = 0; i < linearDetail; ++i) {
vertex(spine[i].x, spine[i].y, spine[i].z);
}
endShape();
popStyle();
}
void drawCaps(PVector[] vs, color stroke, color fill) {
pushStyle();
if (showStroke) {
strokeWeight(1.0);
stroke(stroke);
} else {
noStroke();
}
if (showFill) {
fill(fill);
} else {
noFill();
}
beginShape(TRIANGLE_FAN);
for (int j = 0; j < radialDetail; ++j) {
vertex(vs[j].x, vs[j].y, vs[j].z);
}
endShape(CLOSE);
popStyle();
}
void drawTube(PVector[][] vs, color stroke, color fill) {
// Draw end caps.
drawCaps(vs[0], stroke, fill);
drawCaps(vs[linearDetail - 1], stroke, fill);
pushStyle();
if (showStroke) {
strokeWeight(1.0);
stroke(stroke);
} else {
noStroke();
}
if (showFill) {
fill(fill);
} else {
noFill();
}
for (int i = 1, j; i < linearDetail; ++i) {
int l = i - 1;
for (j = 0; j < radialDetail; ++j) {
int m = j + 1;
int n = m % radialDetail;
//beginShape(QUADS);
//vertex(vs[l][j].x, vs[l][j].y, vs[l][j].z);
//vertex(vs[i][j].x, vs[i][j].y, vs[i][j].z);
//vertex(vs[i][n].x, vs[i][n].y, vs[i][n].z);
//vertex(vs[l][n].x, vs[l][n].y, vs[l][n].z);
//endShape(CLOSE);
beginShape(TRIANGLES);
vertex(vs[l][j].x, vs[l][j].y, vs[l][j].z);
vertex(vs[i][j].x, vs[i][j].y, vs[i][j].z);
vertex(vs[i][n].x, vs[i][n].y, vs[i][n].z);
vertex(vs[l][j].x, vs[l][j].y, vs[l][j].z);
vertex(vs[i][n].x, vs[i][n].y, vs[i][n].z);
vertex(vs[l][n].x, vs[l][n].y, vs[l][n].z);
endShape(CLOSE);
}
}
popStyle();
}
// Taper float array is fed to this function to control the
// tube's radius.
static float lerp(float[] array, float t) {
int sz = array.length;
if (sz == 1 || t <= 0.0) {
return array[0];
} else if (t >= 1.0) {
return array[sz - 1];
}
float sclt = t * float(sz - 1);
int i = int(sclt);
return lerp(array[i], array[i + 1], sclt - i);
}
Best,