2
votes

I would like to "mirror" a PShape object like in the picture below:

https://i.imgur.com/kwTQ7vA.png

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).

enter image description here

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();
}
1
If it is possible for your project I would suggest a different approach. Create the final shape to be draw without rotations, but with regular calculus.J.D.
@J.D. Please do, any suggestion is welcomed.solub

1 Answers

4
votes

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();
}