Reflect vector in 3D space - algorithm

A vector should be reflected when intersecting a mesh. When applying the following formula to reflect a vector, the result is set off. I am using toxiclibs in Processing.
// Get the normal of the face that is intersected.
ReadonlyVec3D n = isect.normal;
// calculate the reflected vector b
// a is the green point in the screenshot
b = a.sub(n.scale(2 * a.dot(n)));
b = b.add(b.getNormalized());
EDIT: When taking into account to create a directional vector by subtracting the last point before the intersection with the intersection, still the reflection is off.
Vec3D id = b.sub(isect.pos);
id.normalize();
b = n.scale(2 * id.dot(n)).sub(id);

I had the question a while back and found a few helpful resources:
Paul Bourke's solution for Line-Plane intersection
Vector reflection on 3D Kingdoms
Here's the snippet I used then:
import toxi.geom.Vec3D;
Vec3D[] face = new Vec3D[3];
float ai = TWO_PI/3;//angle increment
float r = 300;//overall radius
float ro = 150;//random offset
Vec3D n;//normal
Ray r1;
void setup() {
size(500, 500, P3D);
for (int i = 0 ; i < 3; i++) face[i] = new Vec3D(cos(ai * i) * r + random(ro), random(-50, 50), sin(ai * i) * r + random(ro));
r1 = new Ray(new Vec3D(-100, -200, -300), new Vec3D(100, 200, 300));
}
void draw() {
background(255);
lights();
translate(width/2, height/2, -500);
rotateX(map(mouseY, 0, height, -PI, PI));
rotateY(map(mouseX, 0, width, -PI, PI));
//draw plane
beginShape(TRIANGLES);
for (Vec3D p : face) vertex(p.x, p.y, p.z);
endShape();
//normals
Vec3D c = new Vec3D();//centroid
for (Vec3D p : face) c.addSelf(p);
c.scaleSelf(1.0/3.0);
Vec3D cb = face[2].sub(face[1]);
Vec3D ab = face[0].sub(face[1]);
n = cb.cross(ab);//compute normal
n.normalize();
line(c.x, c.y, c.z, n.x, n.y, n.z);//draw normal
pushStyle();
//http://paulbourke.net/geometry/planeline/
//line to plane intersection u = N dot ( P3 - P1 ) / N dot (P2 - P1), P = P1 + u (P2-P1), where P1,P2 are on the line and P3 is a point on the plane
Vec3D P2SubP1 = r1.end.sub(r1.start);
Vec3D P3SubP1 = face[0].sub(r1.start);
float u = n.dot(P3SubP1) / n.dot(P2SubP1);
Vec3D P = r1.start.add(P2SubP1.scaleSelf(u));
strokeWeight(5);
point(P.x, P.y, P.z);//point of ray-plane intersection
//vector reflecting http://www.3dkingdoms.com/weekly/weekly.php?a=2
//R = 2*(V dot N)*N - V
//Vnew = -2*(V dot N)*N + V
//PVector V = PVector.sub(r1.start,r1.end);
Vec3D V = r1.start.sub(P);
Vec3D R = n.scaleSelf(2 * (V.dot(n))).sub(V);
strokeWeight(1);
stroke(0, 192, 0);
line(P.x, P.y, P.z, R.x, R.y, R.z);
stroke(192, 0, 0);
line(r1.start.x, r1.start.y, r1.start.z, P.x, P.y, P.z);
stroke(0, 0, 192);
line(P.x, P.y, P.z, r1.end.x, r1.end.y, r1.end.z);
popStyle();
}
void keyPressed() {
setup();
}//reset
class Ray {
Vec3D start = new Vec3D(), end = new Vec3D();
Ray(Vec3D s, Vec3D e) {
start = s ;
end = e;
}
}
Note that this is a basic proof of concept.
Toxiclibs may already provide Ray/Face classes.

Assuming you have the incident direction id and the normal at the point of intersection n, then the reflection rd is
rd = 2 * dot(n,id) * n - id
where all the vectors are normalized.
In your case, if b is the green point, and isect is the point of intersection then id = b - isect normalized.
So the reflection ray r (assuming it has an origin and a direction) is
r.direction = rd
r.origin = isect
You can also look at this Wikipedia article. https://en.wikipedia.org/wiki/Specular_reflection.

Related

How do I get this array of circles to appear within the boundary of a parallelogram?

I understand how to get an array of circles to appear within a given square space, but how do I get them to appear within irregular shaped spaces. I have a parallelogram outlined that defines the boundaries of where I am trying to get the circles to appear, but no idea how to achieve this. Thanks!
Code from p5.js:
let circles = [];
function setup() {
createCanvas(1200, 1200);
colorMode(HSB,360,100,100,1);
for (let i = 0; i < 500; i++) {
x = random(400, 800);
y = random(400, 800);
d = 10;
circles[i] = new circleClass(x, y, d);
}
}
function draw() {
background(35,13,90,1);
noLoop();
for (let i = 0; i < circles.length; i++) {
circles[i].show();
}
noFill();
stroke(0);
quad(400,600,800,200,800,600,400,1000);
}
class circleClass {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
noStroke();
fill(27, 71, 73, 1);
ellipse(this.x, this.y, this.d);
}
}
Seems you want to generate small circles in random positions inside parallelogram.
Take two vectors of parallelogram sides, and generate random points using linear combination of these vectors:
ax = v[1].x - v[0].x (800-400 for your quad)
ay = v[1].y - v[0].y (200-600 for your quad)
bx = v[2].x - v[0].x (and so on)
by = v[2].y - v[0].y
t = random(0..1)
u = random(0..1)
x = v[0].x + ax * t + bx * u
y = v[0].y + ay * t + by * u
where v[] is array of parallelogram vertices
let v = [400,600,800,200,800,600,400,1000]
let ax = v[2] - v[0]
let ay = v[3] - v[1]
let bx = v[4] - v[0]
let by = v[5] - v[0]
for loop {
let t = Math.random()
let u = Math.random()
let x = v[0] + ax * t + bx * u
let y = v[1] + ay * t + by * u
set circle center in x, y
Here's a p5.js version:
function setup() {
createCanvas(600, 600);
background(255);
let v = [200, 300, // top left array indices: [x=0, y=1]
400, 100, // top right array indices: [x=2, y=3]
400, 300, // bottom right array indices: [x=4, y=5]
200, 500]; // bottom left array indices: [x=6, y=7]
// top right - top left
let ax = v[2] - v[0];
let ay = v[3] - v[1];
// bottom left - top left
let bx = v[6] - v[0];
let by = v[7] - v[1];
for(let i = 0; i < 500; i++) {
let t = random(); // interpolation amount on 1 axis
let u = random(); // interpolation amount on the other
// offset by top left point (v[0], v[1])
// interpolate along top left to top right (imagine X axis aligned with parallelogram sides)
// and top left to bottom left (imagine Y axis aligned with parallelogram sides)
let x = v[0] + (ax * t + bx * u);
let y = v[1] + (ay * t + by * u);
// render point
circle(x, y, 10);
}
noFill();
// pass the v array as arguments using the spread operator
quad(...v);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
This is an addition to MBo's answer above (+1).
The gist of is this section:
let x = v[0] + ax * t + bx * u
let y = v[1] + ay * t + by * u
This might look more familiar as the equation of a line.
In p5.js there's already a very useful lerp() function which takes two numbers and third parameter t between 0.0 and 1.0 and returns a number between the first two (e.g. if t is 0.5 the result will be 1/2 between the numbers, 0.25 would the 1/4 there and .75 would be 3/4, etc.).
Here's a basic example:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
function draw(){
background(255);
let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
// interpolate each component, x,y between v1 and v2 using horizontal mouse movement
let x = lerp(v1.x, v2.x, t);
let y = lerp(v1.y, v2.y, t);
// preview interpolated position
circle(x, y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
We could use this directly, but it would get tedious to do 8 times (2 coordinates (x,y) for 4 points (quad)).
There's also very useful p5.Vector class in p5 with a p5.Vector.lerp() which can help easily interpolate between two points:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
function draw(){
background(255);
let t = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
// interpolate each component, x,y between v1 and v2 using horizontal mouse movement
let l = p5.Vector.lerp(v1, v2, t);
// preview interpolated position
circle(l.x, l.y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
If we group interpolation between pairs of points using two different interpolation amounts into a reusable function we essentially implement a form of bilinear interpolation:
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
Demo:
let v1, v2, v3, v4;
function setup() {
createCanvas(600, 600);
v1 = createVector(200, 300);
v2 = createVector(400, 100);
v3 = createVector(400, 300);
v4 = createVector(200, 500);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
// text() is simply for debugging/visualisation purposed: not actually required
text("v1to2", v1to2.x, v1to2.y);
text("v3to4", v3to4.x, v3to4.y);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
function draw(){
background(192, 255, 192);
let u = map(constrain(mouseX, 0, width), 0, width, 0.0, 1.0);
let v = map(constrain(mouseY, 0, height), 0, height, 0.0, 1.0);
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
// preview interpolated position
circle(l.x, l.y, 10);
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Notice as the mouse moves across the whole sketch canvas the circle always stays within the bounds of the quad.
That's pretty much the solution: simply swap the mapped mouse coordinates for random numbers between 0.0 and 1.0.
function setup() {
createCanvas(1200, 1200);
background(192, 255, 192);
let v1 = createVector(400, 600);
let v2 = createVector(800, 200);
let v3 = createVector(800, 600);
let v4 = createVector(400, 1000);
for (let i = 0 ; i < 500; i++) {
let u = random();
let v = random();
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
circle(l.x, l.y, 10);
}
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
Here's a variant closer to your code:
let circles = [];
function setup() {
createCanvas(1200, 1200);
colorMode(HSB,360,100,100,1);
background(35,13,90,1);
let v1 = createVector(400, 600);
let v2 = createVector(800, 200);
let v3 = createVector(800, 600);
let v4 = createVector(400, 1000);
for (let i = 0 ; i < 500; i++) {
let u = random();
let v = random();
// vertex order (winding) is important
let l = quadLerp(v1, v2, v4, v3, u, v);
circles[i] = new Circle(l.x, l.y, 10);
circles[i].show();
}
noFill();
stroke(0);
quad(v1.x, v1.y, v2.x, v2.y, v3.x, v3.y, v4.x, v4.y);
}
/**
* Bilinear interpolation: interpolates a point on a between two lines (defined by 4 points)
* #param xt: traversal on first axis (0.0 -> 1.0)
* #param yt: traversal on second axis (0.0 -> 1.0)
**/
function quadLerp(v1, v2, v3, v4, xt, yt){
let v1to2 = p5.Vector.lerp(v1, v2, yt);
let v3to4 = p5.Vector.lerp(v3, v4, yt);
return p5.Vector.lerp(v1to2, v3to4, xt);
}
class Circle {
constructor(x, y, d) {
this.x = x;
this.y = y;
this.d = d;
}
show() {
noStroke();
fill(27, 71, 73, 1);
ellipse(this.x, this.y, this.d);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
I've made a couple of tweaks:
renamed circleClass to simply Circle keeping the naming convention of a title case class name. I've removed the Class part to avoid potential confusion when instantiating circles (e.g. new circleClass())
I've removed draw(), hence removing the need for noLoop();
the circles array is sort of redundant too in this basic example, but could useful in a more detailed, animated version of this sketch.
Take you time to read/tweak/break/fix the code above.
Understanding the basics will save you so much time on the long run.
It's may appear counter intuitive, but sometimes slowing down helps you speed up. Have fun!

Translating horizontally inverted quads

A couple of days ago I asked a question about translations and rotations in Processing.
I wanted to:
translate, invert and rotate a single quadrilateral (PShape object) multiple times
then change the height of one of its 2 top vertices
so as the whole thing act as an articulated arm that can be bent either to the right or the left.
Thanks to the help of #Rabbid76 I was able to achieve this effect but I am now facing another issue when translating the last 5 top horizontally inverted quads.
When bending the object, the first 3 quads get separated from the last 5 and. And the more the bending leg is curved, the farther they get apart.
I would really appreciate if someone could help me fix the translation part (from line 65 to 68) so as the quads stay attached to each other to matter how strong the bending is.
Any suggestion regarding that matter would be also greatly appreciated.
SCRIPT
int W = 40;
int H = 40;
int nQuads = 8;
int xOffset = 27;
float[] p0 = {-W/2 + xOffset, -H/2};
float[] p1 = {-W/2, H/2};
float[] p2 = {W/2, H/2};
float[] p3 = {W/2, -H/2};
PShape object;
void setup(){
size(600, 600, P2D);
smooth(8);
}
void draw(){
background(255);
// Bending to the left
float bending = sin(frameCount*.05) * .1;
p0[1] -= bending;
pushMatrix();
translate(width/2, height/2);
float minX = min( min(p0[0], p3[0]), min(p2[0], p1[0]) );
float maxX = max( max(p0[0], p3[0]), max(p2[0], p1[0]) );
float cptX = (minX+maxX)/2;
//Rotation Angle
float angle = atan2(p3[1]-p0[1], p3[0]-p0[0]);
//Pivot Height
float PH = p0[1] + (p3[1]-p0[1]) * (cptX-p0[0])/(p3[0]-p0[0]);
for (int i = 0; i < nQuads; i++){
float PivotHeight = (i % 2 == 1) ? PH : H/2;
//Height translation
if (i > 0){
translate(0, PivotHeight);
}
//Rotate once every 2 quads
if (i%2 == 1){
rotate(angle*2);
}
//Height translation
//Flip all quads except 1st one
if (i > 0){
translate(0, PivotHeight);
scale(1, -1);
}
//NOT working --> Flipping horizontally the last 5 top QUADS
if (i == 3){
scale(-1, 1);
translate(- xOffset, 0); //trying to align the quads on the X axis. Y translation is missing
rotate(-angle*2);
}
object();
}
popMatrix();
}
void object() {
beginShape(QUADS);
vertex(p0[0], p0[1]);
vertex(p1[0], p1[1]);
vertex(p2[0], p2[1]);
vertex(p3[0], p3[1]);
endShape();
}
Just providing a workaround to my own question but won't accept it as a valid answer as I don't really understand what I'm doing and it's probably not the most efficient solution.
int W = 40;
int H = 40;
int nQuads = 8;
int xOffset = 27;
float[] p0 = {-W/2 + xOffset, -H/2};
float[] p1 = {-W/2, H/2};
float[] p2 = {W/2, H/2};
float[] p3 = {W/2, -H/2};
PShape object;
void setup(){
size(600, 600, P2D);
smooth(8);
}
void draw(){
background(255);
// Bending to the left
float bending = sin(frameCount*.05) * .3;
p0[1] -= bending;
pushMatrix();
translate(width/2, height/2);
float minX = min( min(p0[0], p3[0]), min(p2[0], p1[0]) );
float maxX = max( max(p0[0], p3[0]), max(p2[0], p1[0]) );
float cptX = (minX+maxX)/2;
//Rotation Angle
float angle = atan2(p3[1]-p0[1], p3[0]-p0[0]);
//Pivot Height
float PH = p0[1] + (p3[1]-p0[1]) * (cptX-p0[0])/(p3[0]-p0[0]);
for (int i = 0; i < nQuads; i++){
float PivotHeight = (i % 2 == 1) ? PH : H/2;
//Height translation
if (i > 0){
translate(0, PivotHeight);
}
//Rotate once every 2 quads
if (i%2 == 1){
rotate(angle*2);
}
//Height translation
//Flip all quads except 1st one
if (i > 0){
translate(0, PivotHeight);
scale(1, -1);
}
//Flipping horizontally the last 5 top QUADS
if (i == 3){
scale(-1, 1);
translate(0, PivotHeight);
rotate(-angle*2);
translate(0, PivotHeight);
translate(-xOffset , H/2 - p0[1]);
}
object();
}
popMatrix();
}
void object() {
beginShape(QUADS);
vertex(p0[0], p0[1]);
vertex(p1[0], p1[1]);
vertex(p2[0], p2[1]);
vertex(p3[0], p3[1]);
endShape();
}

"Mirroring" a PShape object (rotation / translation issue) with Processing

I would like to "mirror" a PShape object like in the picture below:
I know how to display multiple shapes and how to invert them (screenshot below) but things get complicated when I have to rotate them (and probably translating them) so as they "stick" to the preceding shapes (first picture).
I've been trying to compute an angle with the first 2 vertices of the original shape (irregular quadrilateral) and the atan2() function but to no avail.
I would really appreciate if someone could help figuring how to solve this problem.
int W = 20;
int H = 20;
int D = 20;
PShape object;
void setup(){
size(600, 600, P2D);
smooth();
}
void draw(){
background(255);
pushMatrix();
translate(width/2, height/1.3);
int td = -1;
for (int i = 0; i < 6; i++){
translate(0, td*H*2);
scale(-1, 1);
rotate(PI);
object();
td *= -1;
}
popMatrix();
}
void object() {
beginShape(QUADS);
vertex(-20, 20);
vertex(20, 0);
vertex(20, -20);
vertex(-20, -20);
endShape();
}
To do what you want you have to create a shape by 2 given angles for the top and the bottom of the shape angleT and `angleBĀ“. The origin (0,0) is in the center of the shape. This causes that the pivots for the rotations are in the middle of the slopes of the shape :
int W = 40;
int H = 40;
float angleT = -PI/18;
float angleB = PI/15;
PShape object;
void object() {
float H1 = -H/2 + W*tan(angleB);
float H2 = H/2 + W*tan(angleT);
beginShape(QUADS);
vertex(-W/2, -H/2);
vertex(W/2, H1);
vertex(W/2, H2);
vertex(-W/2, H/2);
endShape();
}
When you draw the parts, then you should distinguish between even and odd parts. The parts have to be flipped horizontal by inverting the y axis (scale(1, -1)). The even parts have to be rotated by the double of angleB and the odd parts have to be rotated by the doubled of angleT. For the rotation, the center of the slopes (pivots) have to be translated to the origin:
void setup(){
size(600, 600, P2D);
smooth();
}
void draw(){
background(255);
translate(width/2, height/2);
float HC1 = -H/2 + W*tan(angleB)/2;
float HC2 = H/2 + W*tan(angleT)/2;
for (int i = 0; i < 15; i++){
float angle = (i % 2 == 0) ? -angleB : -angleT;
float HC = (i % 2 == 0) ? HC1 : HC2;
translate(0, -HC);
rotate(angle*2);
translate(0, -HC);
object();
scale(1, -1);
}
}
The algorithm works for any angle, positive and negative including 0.
This algorithm can be further improved. Let's assume you have a quad, defined by 4 points (p0, p1, p2, p3):
float[] p0 = {10, 0};
float[] p1 = {40, 10};
float[] p2 = {60, 45};
float[] p3 = {0, 60};
PShape object;
void object() {
beginShape(QUADS);
vertex(p0[0], p0[1]);
vertex(p1[0], p1[1]);
vertex(p2[0], p2[1]);
vertex(p3[0], p3[1]);
endShape();
}
Calculate the the minimum, maximum, centerpoint, pivots and angles:
float minX = min( min(p0[0], p1[0]), min(p2[0], p3[0]) );
float maxX = max( max(p0[0], p1[0]), max(p2[0], p3[0]) );
float minY = min( min(p0[1], p1[1]), min(p2[1], p3[1]) );
float maxY = max( max(p0[1], p1[1]), max(p2[1], p3[1]) );
float cptX = (minX+maxX)/2;
float cptY = (minY+maxY)/2;
float angleB = atan2(p1[1]-p0[1], p1[0]-p0[0]);
float angleT = atan2(p2[1]-p3[1], p2[0]-p3[0]);
float HC1 = p0[1] + (p1[1]-p0[1])*(cptX-p0[0])/(p1[0]-p0[0]);
float HC2 = p3[1] + (p2[1]-p3[1])*(cptX-p3[0])/(p2[0]-p3[0]);
Draw the shape like before:
for (int i = 0; i < 6; i++){
float angle = (i % 2 == 0) ? -angleB : -angleT;
float HC = (i % 2 == 0) ? HC1 : HC2;
translate(cptX, -HC);
rotate(angle*2);
translate(-cptX, -HC);
object();
scale(1, -1);
}
Another approach would be to stack the shape on both sides:
For this you have to know the heights of the pivots (HC1, HC2) and the angles (angleB, angleT). So this can be implemented based on both of the above approaches.
Define the pivot points and the directions of the top and bottom edge:
PVector dir1 = new PVector(cos(angleB), sin(angleB));
PVector dir2 = new PVector(cos(angleT), sin(angleT));
PVector pv1 = new PVector(0, HC1); // or PVector(cptX, HC1)
PVector pv2 = new PVector(0, HC2); // or PVector(cptX, HC2)
Calculate the intersection point (X) of the both edges. Of course this will work only if the
edges are not parallel:
PVector v12 = pv2.copy().sub(pv1);
PVector nDir = new PVector(dir2.y, -dir2.x);
float d = v12.dot(nDir) / dir1.dot(nDir);
PVector X = pv1.copy().add( dir1.copy().mult(d) );
The stack algorithm works as follows:
for (int i = 0; i < 8; i++){
float fullAngle = angleT-angleB;
float angle = fullAngle * floor(i/2);
if ((i/2) % 2 != 0)
angle += fullAngle;
if (i % 2 != 0)
angle = -angle;
float flip = 1.0;
if (i % 2 != 0)
flip *= -1.0;
if ((i/2) % 2 != 0)
flip *= -1.0;
pushMatrix();
translate(X.x, X.y);
rotate(angle);
scale(1, flip);
rotate(-angleB);
translate(-X.x, -X.y);
object();
popMatrix();
}

Problems rendering a Polar Zonohedron in Processing

I've recently been looking into Zonohedrons and Rob Bell made beautiful ones. I had a play with the free Polar Zonohedron Sketchup Plugin and thought about playing with the geometry using Processing. So far I've open up the plugin/Ruby script and tried to port it directly, but I am not experienced with Ruby and have been using the Sketchup Ruby API reference.
The geometry part of the code is mostly in the polar_zonohedron function:
def polar_zonohedron #frequency, pitch = atan(sqrt(2)/2), len = 1.0 # frequency,pitch,length
mo = Sketchup.active_model
mo.start_operation "polar_zonohedron"
prompts = ["Frequency", "Pitch in radians", "Length"]
values = [8, "atan( sqrt(2)/2 )", 12.inch]
results = inputbox prompts, values, "Polar Zonohedron"
return if not results # This means that the user canceld the operation
ents = mo.active_entities
grp = ents.add_group
ents = grp.entities
grp.frequency = results[0]
grp.pitch = eval( results[1] )
grp.length = results[2]
pts=[]
#we begin by setting pts[0] to the origin
pts[0] = Geom::Point3d.new(0,0,0)
vector = Geom::Vector3d.new(cos(grp.pitch),0,sin(grp.pitch) ) #tilt pitch vector up the xz plane
vector.length = grp.length
#Using the origin as the initial generator we iterate thru each zone of the zonohedron
#our first task is to define the four points of the base rhomb for this zone
#at the end the pts[3] becomes our new origin for the rhomb of the next zone
1.upto(grp.frequency-1){ |i|
p_rotate = Geom::Transformation.rotation( pts[0] , Geom::Vector3d.new(0,0,1), i*2*PI/grp.frequency )
#obtain the other three points of the rhomb face
pts[1] = pts[0].transform vector
pts[3] = pts[1].transform( p_rotate )
pts[2] = pts[3].transform( vector )
#we now have the 4 points which make this zone's base rhomb
#so we rotate around the origin frequency times making a star pattern of faces
0.upto(grp.frequency-1){ |j|
f_rotate = Geom::Transformation.rotation( Geom::Point3d.new(0,0,0) , Geom::Vector3d.new(0,0,1), j*2*PI/grp.frequency )
ents.add_face( pts.collect{|p| p.transform(f_rotate)} )
}
#set the origin for the rhomb of the next zone
pts[0] = pts[3]
}
mo.commit_operation
end
I've understood the loops but am slightly confused by transforms:
pts[1] = pts[0].transform vector
pts[3] = pts[1].transform( p_rotate )
pts[2] = pts[3].transform( vector )
As far as I can tell pts[1] is the vector addiction of pts[0] and vector,
and pts[3] is pts[1] multiplied by the p_rotate rotation matrix. Would pts[2] also be an addition (between pts[3] and vector )?
Here's what my attempt looks like so far:
//a port attempt of Rob Bell's polar_zonohedron.rb script - http://zomebuilder.com/
int frequency = 3;
float pitch = atan(sqrt(2)/2);
float length = 24;
ArrayList<Face> faces = new ArrayList<Face>();
void setup(){
size(400,400,P3D);
strokeWeight(3);
setupZome();
}
void setupZome(){
faces.clear();
PVector[] pts = new PVector[4];
pts[0] = new PVector();
PVector vector = new PVector(cos(pitch),0,sin(pitch));
vector.mult(length);
for(int i = 1 ; i < frequency; i++){
PMatrix3D p_rotate = new PMatrix3D();
p_rotate.rotate(i * TWO_PI / frequency, 0,0,1);
//PVector v = new PVector();
//p_rotate.mult(pts[0],v);
//pts[0] = v;
pts[1] = PVector.add(pts[0],vector);
pts[3] = new PVector();
p_rotate.mult(pts[1],pts[3]);
pts[2] = PVector.add(pts[3],vector);
for(int j = 0; j < frequency; j++){
PMatrix3D f_rotate = new PMatrix3D();
f_rotate.rotate(j*2*PI/frequency , 0,0,1);
Face f = new Face();
for(PVector pt : pts){
PVector p = new PVector();
f_rotate.mult(pt,p);
f.add(p.get());
}
faces.add(f);
}
pts[0] = pts[3];
}
}
void draw(){
background(255);
lights();
translate(width * .5, height * .5,0);
rotateY(map(mouseX,0,width,-PI,PI));
rotateX(map(mouseY,0,height,-PI,PI));
drawAxes(100);
pushMatrix();
translate(0,0,-frequency * length * .25);
for(Face f : faces){
beginShape(mousePressed ? QUADS : POINTS);
for(PVector p : f.pts) vertex(p.x,p.y,p.z);
endShape();
}
popMatrix();
}
void keyPressed(){
if(keyCode == UP && frequency < 32) frequency++;
if(keyCode == DOWN && frequency > 2) frequency--;
setupZome();
}
void drawAxes(int size){
stroke(192,0,0);
line(0,0,0,size,0,0);
stroke(0,192,0);
line(0,0,0,0,size,0);
stroke(0,0,192);
line(0,0,0,0,0,size);
}
class Face{
ArrayList<PVector> pts = new ArrayList<PVector>();
Face(){}
void add(PVector p){
if(pts.size() <= 4) pts.add(p);
}
}
I feel I'm close, but I'm getting the loop conditionals and vertex indices wrong.
Any tips on how to fix this?
I was very close, but not paying attention to all the details.
Turns out I get the correct mesh if I don't increment the rotation on p_rotate:
p_rotate.rotate(TWO_PI / frequency, 0,0,1);
instead of
p_rotate.rotate(i * TWO_PI / frequency, 0,0,1);
Here is the full code listing:
//a port attempt of Rob Bell's polar_zonohedron.rb script - http://zomebuilder.com/
int frequency = 3;
float pitch = atan(sqrt(2)/2);
float length = 24;
ArrayList<Face> faces = new ArrayList<Face>();
void setup(){
size(400,400,P3D);
strokeWeight(3);
setupZome();
}
void setupZome(){
faces.clear();
PVector[] pts = new PVector[4];
pts[0] = new PVector();
PVector vector = new PVector(cos(pitch),0,sin(pitch));
vector.mult(length);
for(int i = 1 ; i < frequency-1; i++){
PMatrix3D p_rotate = new PMatrix3D();
p_rotate.rotate(TWO_PI / frequency, 0,0,1);
pts[1] = PVector.add(pts[0],vector);
pts[3] = new PVector();
p_rotate.mult(pts[1],pts[3]);
pts[2] = PVector.add(pts[3],vector);
for(int j = 0; j < frequency; j++){
PMatrix3D f_rotate = new PMatrix3D();
f_rotate.rotate(j*2*PI/frequency , 0,0,1);
Face f = new Face();
for(PVector pt : pts){
PVector p = new PVector();
f_rotate.mult(pt,p);
f.add(p.get());
}
faces.add(f);
}
pts[0] = pts[3];
}
}
void draw(){
background(255);
lights();
translate(width * .5, height * .5,0);
rotateY(map(mouseX,0,width,-PI,PI));
rotateX(map(mouseY,0,height,-PI,PI));
drawAxes(100);
pushMatrix();
translate(0,0,-frequency * length * .25);
for(Face f : faces){
beginShape(mousePressed ? QUADS : POINTS);
for(PVector p : f.pts) vertex(p.x,p.y,p.z);
endShape();
}
popMatrix();
}
void keyPressed(){
if(keyCode == UP && frequency < 32) frequency++;
if(keyCode == DOWN && frequency > 3) frequency--;
setupZome();
}
void drawAxes(int size){
stroke(192,0,0);
line(0,0,0,size,0,0);
stroke(0,192,0);
line(0,0,0,0,size,0);
stroke(0,0,192);
line(0,0,0,0,0,size);
}
class Face{
ArrayList<PVector> pts = new ArrayList<PVector>();
Face(){}
void add(PVector p){
if(pts.size() <= 4) pts.add(p);
}
}
And here a couple of screenshots:

Rotate some elements in an ellipse path

I am trying to make some objects, say 12, to rotate in an ellipse path continuously in Processing. I got a sketch which does rotation in a circle and I want to make it to rotate in a ellipse. I have some pointer from processing forum but the code from the pointer is different from the code that I posted and I couldn't understand yet (weak in trigonometry).
I googled a bit and found a post trying to achieve this with this algorithm:
You need to define your ellipse with a few parameters:
x, y: center of the ellipse
a, b: semimajor and semiminor axes
If you want to move on the elipses this means that you change the
angle between the major axes and your position on the ellipse. Lets
call this angle alpha.
Your position (X,Y) is:
X = x + (a * Math.cos(alpha));
Y = y + (b * Math.sin(alpha));
In order to move left or right you need to increase/decrease alpha and
then recalculate your position. Source:
http://answers.unity3d.com/questions/27620/move-object-allong-an-ellipsoid-path.html
How do I integrate it into my sketch? Thank you.
Here's my sketch:
void setup()
{
size(1024, 768);
textFont(createFont("Arial", 30));
}
void draw()
{
background(0);
stroke(255);
int cx = 500;
int cy = 350;
int r = 300; //radius of the circle
float t = millis()/4000.0f; //increase to slow down the movement
ellipse(cx, cy, 5, 5);
for (int i = 1 ; i <= 12; i++) {
t = t + 100;
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
line(cx, cy, x, y);
textSize(30);
text(i, x, y);
if (i == 10) {
textSize(15);
text("x: " + x + " y: " + y, x - 50, y - 20);
}
}
}
Replace
int r = 300; //radius of the circle
with
int a = 350; // major axis of ellipse
int b = 250; // minor axis of ellipse
and replace
int x = (int)(cx + r * cos(t));
int y = (int)(cy + r * sin(t));
with
int x = (int)(cx + a * cos(t));
int y = (int)(cy + b * sin(t));

Resources