The 3D primitive cylinder function draws a cylinder from its midpoint. I am trying to write a function that will enable me to draw a cylinder between two specified points.
My approach so far has been to find the length and midpoint of the line that connects the two points. So I now have a cylinder at the correct position, and I need to rotate it so the ends meet the target start and end points. I’m not sure how to proceed. I’m guessing I need to find an angle for X rotation and an angle for Y rotation, but how do I go about this? Can someone point me in the right direction?
function setup() {
createCanvas(500, 500, WEBGL);
noStroke();
}
function draw() {
background(220);
//point 1 for reference
push();
fill(0,0,255);
translate(30,30,30);
sphere(3);
pop();
//point 2
push();
fill(255,0,0);
translate(60,60,60);
sphere(3);
pop();
//cylinder
drawCylinder(createVector(30,30,30), createVector(60,60,60));
}
function drawCylinder(startPoint,endPoint) {
push();
//height of cylinder is distance between startPoint and endPoint
var h = sqrt((((startPoint.x - endPoint.x)**2) +
((startPoint.y - endPoint.y)**2) +
((startPoint.z - endPoint.z)**2)))
//translate to midpoint
var c = createVector(((startPoint.x+endPoint.x)/2),
((startPoint.y + endPoint.y)/2),
((startPoint.z+endPoint.z)/2))
//marker
translate(c.x,c.y,c.z);
fill(0);
sphere(4);
//find rotation angles?
//draw the cylinder
fill(0,150,0);
cylinder(3,h);
pop();
}
Any pointers will be greatly appreciated, I’m out of my depth with the maths/ geometry here.
I found a solution to this using trigonometry - as far as I can tell, this works. But if anyone reading it does have suggestions, if it can be improved please let me know.
var point1;
var point2;
function setup() {
createCanvas(500, 500, WEBGL);
background(220);
noStroke();
ambientLight(150);
pointLight(250, 250, 250, 50, 50, 400);
point1 = createVector(-100,-0,30);
point2 = createVector(200,80,50);
//point 1
push();
fill(0,0,255);
translate(point1.x,point1.y,point1.z);
sphere(5);
pop();
//point 2
push();
fill(255,0,0);
translate(point2.x,point2.y,point2.z);
sphere(5);
pop();
//cylinder
drawCylinder(point1,point2);
}
function draw() {
}
function drawCylinder(startPoint,endPoint) {
push();
//height of cylinder is distance between startPoint and endPoint
var h = sqrt((((startPoint.x - endPoint.x)**2) +
((startPoint.y - endPoint.y)**2) +
((startPoint.z - endPoint.z)**2)))
//translate to midpoint
var c = createVector(((startPoint.x+endPoint.x)/2),
((startPoint.y + endPoint.y)/2),
((startPoint.z+endPoint.z)/2))
translate(c.x,c.y,c.z);
// fill(0);
// sphere(4);
//first rotation
p1 = c;
p2 = createVector(c.x,c.y+(h*0.5), c.z);
p3 = endPoint;
//distance 1
var d1 = sqrt( (p2.x - p1.x)**2
+(p2.y-p1.y)**2 );
//print(a);
//distance 2
var d2 = sqrt( (p3.x - p1.x)**2
+(p3.y - p1.y)**2);
//print(b);
//distance 3
var d3 = sqrt( (p3.x - p2.x)**2
+(p3.y - p2.y)**2);
//print(c);
var zrot = acos((d1**2 + d2**2 - d3**2)/(2*d1*d2));
//second rotation
p1 = c;
p2 = createVector(endPoint.x,endPoint.y, c.z);
p3 = endPoint;
//distance 1
var a = sqrt( (p2.x - p1.x)**2
+(p2.y-p1.y)**2 );
print(a);
//distance 2
var b = sqrt( (p3.x - p1.x)**2
+(p3.y - p1.y)**2);
print(b);
//distance 3
var c = sqrt( (p3.x - p2.x)**2
+(p3.y - p2.y)**2
+(p3.z - p2.z)**2);
print(c);
var xrot = acos((a**2 + b**2 - c**2)/(2*a*b));
print(xrot);
//draw
rotateZ(-zrot);
rotateX(xrot);
fill(0,150,0);
cylinder(3,h);
pop();
}
Just try taking it out, draw some short lines, and see what happens.
The cylinder radius lies along the heading – so the two points define the plane of the circular cross section, and then extrudes out of that plane based on the distance between the lines. But you don’t want that. You want the cylinder height axis to lie along the heading. So rotate it by 90 degrees. Now the radius extrudes around the line, and the height runs along the line.
This depends a lot on what you are trying to do. Do the cylinders all lie in a plane? Will you be animating the tumbling of these cylinders, or will they have fixed orientations? Where is your point data coming from – is there an origin point and a heading towards a second derived point, or are they two inputs (probably not with mouse, if you are trying to input in 3D?).
Continuous 3D rotation has a bunch of potential problems – including gimbal lock – and the closest to a one-size-fits-all solution is quaternions, which are complex. Here’s an overview (in Processing, not p5.js, but the relevant API is the same).
The easiest thing might be to rotateZ into the 2D plane where you want your cylinder, then use the 2D solution that you already have within that plane.
Thanks, I will read that link tomorrow with a cup of coffee and try to make sense of it.
The 3D points are joint positions from Kinect data- I am trying to create a (non-animated) visualization of several frames of Kinect data, the cylinders are to join the spheres that represent the joints.
The points share an origin (between the shoulders, if I remember correctly).
I don’t want to animate the skeleton, but I would like to be able to rotate the entire visual around the Y axis. Apologies if I’ve misunderstood your questions - my background is decidedly non mathsy.
Hi AshaSato,
Some of the solutions (e.g. the ones using 'heading() ) above will only work in 2D.
I did something similar with a 3D-Vector ‘v’, with a magnitude representing the desired length of your cylinder. When you only have 2 3D or 2D points, you may calculate the magnitude with dist(x1,x2,y1,y2,z1,z2) and your vector v using vector_to_endpoint.sub(vector_to_startpoint)
I calculate the rotation angles around x,y and z with arctan atan(), and rotate the cylinder accordingly.