We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Hello everyone,
I wrote a Processing code that draws two spheres which are initially in contact. As we know, when two spheres are in contact, there is (usually) a circle. [When they are tangent, it is a point, but let's focus on the circle case]. To draw the circle, I have a function that draws a cylinder via beginShape and endShape functions. When the height parameter is zero, there is only the top shape of the cylinder.
I have some calculations to determine the center and the radius of this circle. However, I cannot solve how should I do to rotate the circle to follow the PVector between the two spheres. That is to say, if we consider the vector between the spheres' centers, the circle should follow that direction.
If no rotations are done, the circle's shape is oriented towards the XY plane. What I need is a way to calculate properly the rotations around the 3 axis to achieve the expected result.
I leave here my source code:
float x0, y0, z0, r0;
float x1, y1, z1, r1;
float camX, camY, camZ;
float centerX, centerY, centerZ;
float upX, upY, upZ;
float iniX0, iniY0, iniZ0;
float iniX1, iniY1, iniZ1;
float iniCamX, iniCamY, iniCamZ;
boolean x0Left, x0Right, y0Up, y0Down, z0Out, z0In;
boolean x1Left, x1Right, y1Up, y1Down, z1Out, z1In;
boolean camXLeft, camXRight, camYUp, camYDown, camZOut, camZIn;
boolean calc;
float red, green, blue;
float lightPosX, lightPosY, lightPosZ;
int tipInt;
PVector center;
float radius;
PVector orient;
Object[] calcRes;
void setup()
{
size(800, 600, P3D);
x0 = 200;
y0 = 200;
z0 = 50;
r0 = 100;
x1 = 350;
y1 = 210;
z1 = -50;
r1 = 150;
camX = width;
camY = height / 2.0;
camZ = (height/2.0) / tan(PI / 6.0);
iniX0 = x0; iniY0 = y0; iniZ0 = z0;
iniX1 = x1; iniY1 = y1; iniZ1 = z1;
iniCamX = camX; iniCamY = camY; iniCamZ = camZ;
centerX = width / 2.0;
centerY = height / 2.0;
centerZ = 0;
upX = 0;
upY = 1;
upZ = 0;
x0Left = false; x0Right = false; y0Up = false; y0Down = false; z0Out = false; z0In = false;
x1Left = false; x1Right = false; y1Up = false; y1Down = false; z1Out = false; z1In = false;
camXLeft = false; camXRight = false; camYUp = false; camYDown = false; camZOut = false; camZIn = false;
calc = true;
tipInt = -1;
center = null;
radius = 0;
orient = null;
calcRes = new Object[4];
red = 51;
green = 102;
blue = 126;
lightPosX = 300;
lightPosY = 250;
lightPosZ = 300;
}
void draw()
{
if (x0Left) --x0;
if (x0Right) ++x0;
if (y0Up) --y0;
if (y0Down) ++y0;
if (z0Out) --z0;
if (z0In) ++z0;
if (x1Left) --x1;
if (x1Right) ++x1;
if (y1Up) --y1;
if (y1Down) ++y1;
if (z1Out) --z1;
if (z1In) ++z1;
if (camXLeft) camX += 10;
if (camXRight) camX -= 10;
if (camYUp) camY += 10;
if (camYDown) camY -= 10;
if (camZOut) camZ += 10;
if (camZIn) camZ -= 10;
if (calc)
{
calcRes = calcIntersection();
tipInt = (int)(calcRes[0]);
center = (PVector)(calcRes[1]);
radius = (float)(calcRes[2]);
orient = (PVector)(calcRes[3]);
}
camera(camX, camY, camZ, centerX, centerY, centerZ, upX, upY, upZ);
background(0);
drawAxis(0, 0, 0);
stroke(64);
if (!mousePressed)
{
strokeWeight(1);
fill(color(255, 0, 0));
pushMatrix();
translate(x0, y0, z0);
sphere(r0);
popMatrix();
fill(0, 0, 255);
pushMatrix();
translate(x1, y1, z1);
sphere(r1);
popMatrix();
}
if (tipInt == 1 && center != null)
{
drawAxis(center.x, center.y, center.z);
fill(0);
stroke(0);
pushMatrix();
translate(center.x, center.y, center.z);
sphere(10);
popMatrix();
fill(242, 242, 7);
stroke(242, 242, 7);
pushMatrix();
translate(center.x, center.y, center.z);
cylinder(radius*3, 0, 100); // radi, alt, detall
popMatrix();
}
}
void keyPressed(KeyEvent evt)
{
switch (key)
{
case 'a':
case 'A': x0Left = true;
calc = true;
break;
case 'd':
case 'D': x0Right = true;
calc = true;
break;
case 'w':
case 'W': y0Up = true;
calc = true;
break;
case 's':
case 'S': y0Down = true;
calc = true;
break;
case 'e':
case 'E': z0Out = true;
calc = true;
break;
case 'q':
case 'Q': z0In = true;
calc = true;
break;
case 'k':
case 'K': camXLeft = true;
break;
case 'ñ':
case 'Ñ': camXRight = true;
break;
case 'o':
case 'O': camYUp = true;
break;
case 'l':
case 'L': camYDown = true;
break;
case 'p':
case 'P': camZOut = true;
break;
case 'i':
case 'I': camZIn = true;
break;
default: switch (keyCode)
{
case 32: x0 = iniX0; y0 = iniY0; z0 = iniZ0;
x1 = iniX1; y1 = iniY1; z1 = iniZ1;
camX = iniCamX; camY = iniCamY; camZ = iniCamZ;
calc = true;
break;
case 132: x1Left = true;
calc = true;
break;
case 134: x1Right = true;
calc = true;
break;
case 136: y1Up = true;
calc = true;
break;
case 133: y1Down = true;
calc = true;
break;
case 137: z1Out = true;
calc = true;
break;
case 135: z1In = true;
calc = true;
break;
}
break;
}
}
void keyReleased(KeyEvent evt)
{
switch (key)
{
case 'a':
case 'A': x0Left = false;
calc = true;
break;
case 'd':
case 'D': x0Right = false;
calc = true;
break;
case 'w':
case 'W': y0Up = false;
calc = true;
break;
case 's':
case 'S': y0Down = false;
calc = true;
break;
case 'e':
case 'E': z0Out = false;
calc = true;
break;
case 'q':
case 'Q': z0In = false;
calc = true;
break;
case 'k':
case 'K': camXLeft = false;
break;
case 'ñ':
case 'Ñ': camXRight = false;
break;
case 'o':
case 'O': camYUp = false;
break;
case 'l':
case 'L': camYDown = false;
break;
case 'p':
case 'P': camZOut = false;
break;
case 'i':
case 'I': camZIn = false;
break;
default: switch (keyCode)
{
case 32: x0 = iniX0; y0 = iniY0; z0 = iniZ0;
x1 = iniX1; y1 = iniY1; z1 = iniZ1;
camX = iniCamX; camY = iniCamY; camZ = iniCamZ;
calc = true;
break;
case 132: x1Left = false;
calc = true;
break;
case 134: x1Right = false;
calc = true;
break;
case 136: y1Up = false;
calc = true;
break;
case 133: y1Down = false;
calc = true;
break;
case 137: z1Out = false;
calc = true;
break;
case 135: z1In = false;
calc = true;
break;
}
break;
}
}
Object[] calcIntersection()
{
Object[] o = new Object[4];
PVector A = new PVector(x0, y0, z0);
PVector B = new PVector(x1, y1, z1);
float d = PVector.dist(A, B);
int codIntersection = -1; // separate spheres
PVector I = null;
float r = 0;
PVector diff = null;
if (d > (r0 + r1)) codIntersection = -1; // separate spheres
else if (d == (r0 + r1)) codIntersection = 0; // exterior tangency
else if (d < r0 + r1 || d > abs(r0 - r1)) codIntersection = 1; // intersection
else if (d == abs(r0 - r1)) codIntersection = 2; // interior tangency
else if (d < abs(r0 - r1)) codIntersection = 3; // inclusion
if (codIntersection == 1)
{
float k = (sq(r0) - sq(r1) + sq(d)) / (2*sq(d));
diff = PVector.sub(B, A);
I = A.copy().add(diff.mult(k));
r = sqrt(sq(r0) - sq(k*d));
println("circle's center: " + I + ", circle's radii: " + r);
}
calc = false;
o[0] = codIntersection;
o[1] = I;
o[2] = r;
o[3] = PVector.sub(B, A);
return o;
}
void drawAxis(float posX, float posY, float posZ)
{
// X axis
stroke(255, 0, 0);
fill(255, 0, 0);
line(posX, posY, posZ, posX+200, posY, posZ);
text("X-axis", posX+150, posY-20, posZ);
// Y axis
stroke(0, 128, 0);
fill(0, 255, 0);
line(posX, posY, posZ, posX, posY-150, posZ);
text("Y-axis", posX, posY-170, posZ);
// Z axis
stroke(0, 255, 255);
fill(0, 255, 255);
line(posX, posY, posZ, posX, posY, posZ+240);
text("Z-axis", posX, posY, posZ+240);
}
void cylinder(float cylRadius, float cylHeight, int cylDetail)
{
float x, y, theta;
beginShape();
for (int i = 0; i < cylDetail; i++)
{
theta = 360 * i / cylDetail;
x = cylRadius * cos(radians(theta));
y = cylRadius * sin(radians(theta));
vertex(x, y, (cylHeight > 0) ? -cylHeight/2 : -1.0/1000000.0);
}
endShape(CLOSE);
if (cylHeight > 0)
{
beginShape(TRIANGLE_STRIP);
for (int i = 0; i <= cylDetail; i++)
{
theta = 360 * i / cylDetail;
x = cylRadius * cos(radians(theta));
y = cylRadius * sin(radians(theta));
vertex( x, y, cylHeight/2);
vertex( x, y, -cylHeight/2);
}
endShape(CLOSE);
beginShape();
for (int i = 0; i < cylDetail; i++)
{
theta = 360 * i / cylDetail;
x = cylRadius * cos(radians(theta));
y = cylRadius * sin(radians(theta));
vertex(x, y, cylHeight/2);
}
endShape(CLOSE);
}
}
Thanks in advance.
Answers
Adding
rotateY(90)
after line 145 could be a potential partial solution for that case?A circle's surface is defined by its normal vector at its center. Do you want this vector to be aligned to the vector defined between the two spheres's center?
Kf
Hi kfrajer,
The solution goes on the way you suggest. In fact, I realized that rotating 5° in the X axis and 125° in the Y axis is close to what I need. The problem is, how should I calculate this two angles?
Effectively, I would like to have the normal vector aligned to the vector between the two spheres’ center.
Thank you so much!!
Please check the reference under the keyword PVector for some of the functions next.
The angles you are looking for are theta and phi. What you want is express your vector k (define as the difference between center's position) in spherical coordinates. In Processing, most operations are done in the cartesian coordinate system. Have a look at the following code. Adjust the vector
vekBetweenCenters
to see the resultant angles. For example, work with the trivial case where the Z component of your vector is zero, then the problem reduces to 2D calculations..........
Kf
Hi again,
I have tried with your suggested code, but surely I'm doing something wrong, because the result is almost what I need, but it's not exactly...
The portion of code is as follows:
The console shows this information:
What I seen on the screen is this image: img01
However, I'd like to have something similar to this: img02
I don't know if it's a problem with the definition of yAxis and zAxis, the type of rotations (rotateX, rotateY) o its order (I'm aware that it's not the same to do rotateX+rotateY and rotateY+rotateX).
If you see something wrong, please, let me know. Thanks!!!!
Order is important. You apply theta first and then phi. Think about a vector in 3D space. Project the vector to the XY plane. This vector will rotate about the Z axis an angle of theta. In Processing, when you do this rotation, your XY also rotates as you know. The next step is to rotate about Y (I think) an angle of phi and then translate.
I suggest you do a test program using the peasyCam kibrary as it will allow you to interact with the 3D space using the mouse. You can see your results faster, instead of using key inputs.
I have the feeling you are not drawing the yellow circle at the right distance. Unfortunately I can't test your code atm.
Kf
Now I have tried with:
It's close, but something else is lacking... I'll investigate.
Thank you for your patience.