Join vertices of PShapes after translation / rotation

Hello Forum.

I’m trying to join the top and bottom of two cubes which are being rotated in 3D space.

I can’t seem to reliably update the vertices of these shapes after they are being rotated.

I’ve tried simply joining the vertices before and after using getVertex() and setVertex(), but that didn’t seem to work at all.

Here’s an example

PShape sq1, sq2;
void setup() {
  size(600, 600, P3D);
  //init shapes
  sq1 = cube(new PVector(50, 100, 50));
  sq2 = cube(new PVector(40, 120, 40));
  sq1.setFill(#F05C68);
  sq2.setFill(#F05C68);
}
void draw() {
  background(lerpColor(#B388DE, #69ACF2, (sin(frameCount/200.0)+1)/2.0));
  lights(); 
  //move to center & slowly rotate
  translate(width/2.0, height/2.0, 0);
  rotateY(frameCount/400.0);
  //1st shape
  pushMatrix();
  translate(0,50,0);
  rotateX(mouseX/100.0);
  translate(0, 0, 0);
  shape(sq1);
  //2nd shape
  pushMatrix();
  translate(0,50,0);
  rotateX(PI+mouseY/100.0);
  translate(0,60,0);
  shape(sq2);
  popMatrix();
  popMatrix();
  //sq1.setVertex(4,sq2.getVertex(0));
  //sq1.setVertex(5,sq2.getVertex(1));
}
PShape cube(PVector v){
PShape shape = createShape();
  shape.beginShape(QUADS);
  shape.vertex(-1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 0, 0);
  shape.vertex( 1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 1, 0);
  shape.vertex( 1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 1, 1);
  shape.vertex(-1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 0, 1);
  shape.vertex( 1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 0, 0);
  shape.vertex(-1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 1, 0);
  shape.vertex(-1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 1, 1);
  shape.vertex( 1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 0, 1);
  shape.vertex(-1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 0, 0);
  shape.vertex( 1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 1, 0);
  shape.vertex( 1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 1, 1);
  shape.vertex(-1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 0, 1);
  shape.vertex(-1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 0, 0);
  shape.vertex( 1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 1, 0);
  shape.vertex( 1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 1, 1);
  shape.vertex(-1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 0, 1);
  shape.vertex( 1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 0, 0);
  shape.vertex( 1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 1, 0);
  shape.vertex( 1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 1, 1);
  shape.vertex( 1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 0, 1);
  shape.vertex(-1*v.x*.5, -1*v.y*.5, -1*v.z*.5, 0, 0);
  shape.vertex(-1*v.x*.5, -1*v.y*.5,  1*v.z*.5, 1, 0);
  shape.vertex(-1*v.x*.5,  1*v.y*.5,  1*v.z*.5, 1, 1);
  shape.vertex(-1*v.x*.5,  1*v.y*.5, -1*v.z*.5, 0, 1);
  shape.endShape();
  //shape.scale(v.x*.5,v.y*.5,v.z*.5);
  return shape;
}

And here is a visual of what I’m trying to achieve, which looks like a bendy cube.

example7

Is there any technique that will allow me to join these shapes in such a way?

Edit: I found that using setVertex() won’t work for shapes defined this way
Edit: Changed the way the cubes are defined to their vertices are easier to manipulate

2 Likes

to stretch one point (corner) of a cube need to move 3 vertex,
for one side of a cube / 4 points / need to move 12 vertex

float z = 0;

float w = 10;                    // cube q size
PShape q,qa;
PVector p1,p2,p3,p4;
float pstretch = 3;

void setup() {
  size(300, 300, P3D);
  mycube();                      // creates q size 10
  qa = q;                        // copy 1
  stretch();
}

void stretch() {
  p1 = qa.getVertex(0);   //  0
  p1 = p1.mult(pstretch);
  qa.setVertex(0,p1);     // 0
  qa.setVertex(13,p1);    // also 13
  qa.setVertex(19,p1);    // also 19

  p2 = qa.getVertex(1);   //  1
  p2 = p2.mult(pstretch);
  qa.setVertex(1,p2);     // 1
  qa.setVertex(4,p2);     // also 4
  qa.setVertex(18,p2);    // also 18

  p3 = qa.getVertex(2);   //  2
  p3 = p3.mult(pstretch);
  qa.setVertex(2,p3);     // 2
  qa.setVertex(7,p3);     // also 7
  qa.setVertex(22,p3);    // also 22

  p4 = qa.getVertex(3);   //  3
  p4 = p4.mult(pstretch);
  qa.setVertex(3,p4);     // 3
  qa.setVertex(14,p4);    // also 14
  qa.setVertex(23,p4);    // also 23
}

void draw() {
  background(200, 200, 0);
  viewpoint();
  shape(qa,0,0);
  helplines();
}

void viewpoint() {
  translate(width/2, height/2, 0);
  rotateY(mouseX/100.0);
  rotateX(mouseY/100.0);
  rotateZ(z);
  scale(2, 2, 2);
}

void helplines() {
  strokeWeight(2);
  stroke(0,0,200);
  line(0, 0, 0, p1.x, p1.y, p1.z);
  line(0, 0, 0, p2.x, p2.y, p2.z);
  line(0, 0, 0, p3.x, p3.y, p3.z);
  line(0, 0, 0, p4.x, p4.y, p4.z);  
}

void mycube() {           // creates CUBE named q
  float w =10;
  q = createShape();
  q.beginShape(QUADS);
  q.noFill();
  q.stroke(200,0,0);
  
  q.vertex(-w, w, w);   //0
  q.vertex( w, w, w);   //1
  q.vertex( w, -w, w);  //2
  q.vertex(-w, -w, w);  //3

  q.vertex( w, w, w);  //4
  q.vertex( w, w, -w); //5
  q.vertex( w, -w, -w);//6
  q.vertex( w, -w, w); //7

  q.vertex( w, w, -w); //8
  q.vertex(-w, w, -w); //9
  q.vertex(-w, -w, -w);//10
  q.vertex( w, -w, -w);//11

  q.vertex(-w, w, -w); //12
  q.vertex(-w, w, w);  //13
  q.vertex(-w, -w, w); //14
  q.vertex(-w, -w, -w);//15

  q.vertex(-w, w, -w); //16
  q.vertex( w, w, -w); //17
  q.vertex( w, w, w);  //18
  q.vertex(-w, w, w);  //19

  q.vertex(-w, -w, -w);//20
  q.vertex( w, -w, -w);//21
  q.vertex( w, -w, w); //22
  q.vertex(-w, -w, w); //23

  q.endShape(CLOSE);
}

with

  • 2 cube ( and more general with position and side dimensions )
  • mouse rotate zoom
  • stretch one, fit second by key [+] [-]

– there is no translate/rotate/scale used inside ( between ) the two cubes

float zMag =1.0;

PVector qapos = new PVector(20,30,40);    // center qa cube
float wx = 10,wy=15,wz=20;                // cube q size
PVector qbpos = new PVector(20,30,40+wz); // center qb cube

PShape qa,qb;
PVector po1,p1,po2,p2,po3,p3,po4,p4;
float pstretch = 1.0;

void setup() {
  size(500, 500, P3D);
  qa = mycube(qapos,wx,wy,wz);            // creates q at qpos with dx,dy,dz
  qb = mycube(qbpos,wx,wy,wz);            // creates q at qpos with dx,dy,dz
  stretchqa(qapos,pstretch);
  //stretchqb();                          // fit qb to qa resized.
  println("ues: mouseX mouseY for rotate and mouse wheel for zoom\n key[+] [-] for stretch");
}

void stretchqa(PVector off, float pstretch) {
  po1 = qa.getVertex(0);   //  0
  p1 = po1.copy().sub(off);
  p1 = p1.mult(pstretch);
  p1.add(off);
  qa.setVertex(0,p1);     // 0
  qa.setVertex(13,p1);    // also 13
  qa.setVertex(19,p1);    // also 19

  po2 = qa.getVertex(1);   //  1
  p2 = po2.copy().sub(off);
  p2 = p2.mult(pstretch);
  p2.add(off);
  qa.setVertex(1,p2);     // 1
  qa.setVertex(4,p2);     // also 4
  qa.setVertex(18,p2);    // also 18

  po3 = qa.getVertex(2);   //  2
  p3 = po3.copy().sub(off);
  p3 = p3.mult(pstretch);
  p3.add(off);
  qa.setVertex(2,p3);     // 2
  qa.setVertex(7,p3);     // also 7
  qa.setVertex(22,p3);    // also 22

  po4 = qa.getVertex(3);   //  3
  p4 = po4.copy().sub(off);
  p4 = p4.mult(pstretch);
  p4.add(off);
  qa.setVertex(3,p4);     // 3
  qa.setVertex(14,p4);    // also 14
  qa.setVertex(23,p4);    // also 23
}

void stretchqb() {   // to fit to qa stretched side 0 1 2 3 
  qb.setVertex(5,p2);
  qb.setVertex(8,p2);
  qb.setVertex(17,p2);

  qb.setVertex(6,p3);
  qb.setVertex(11,p3);
  qb.setVertex(21,p3);
  
  qb.setVertex(9,p1);
  qb.setVertex(12,p1);
  qb.setVertex(16,p1);

  qb.setVertex(10,p4);
  qb.setVertex(15,p4);
  qb.setVertex(20,p4);
}

void draw() {
  background(200, 200, 0);
  viewpoint();
  shape(qa,0,0);
  shape(qb,0,0);
  helplines();
}

void viewpoint() {
  translate(width/2, height/2, 0);
  float angx = map(mouseY,0,width,-PI,PI);
  float angy = map(mouseX,0,width,-PI,PI);
  rotateY(angy);
  rotateX(angx);
  scale(zMag);
  scale(2, 2, 2);
}

void helplines() {
  stroke(0,0,200);
  line(0, 0, 0, p1.x, p1.y, p1.z);
  line(0, 0, 0, p2.x, p2.y, p2.z);
  line(0, 0, 0, p3.x, p3.y, p3.z);
  line(0, 0, 0, p4.x, p4.y, p4.z);  
  stroke(0,200,200);
  line(0, 0, 0, po1.x, po1.y, po1.z);
  line(0, 0, 0, po2.x, po2.y, po2.z);
  line(0, 0, 0, po3.x, po3.y, po3.z);
  line(0, 0, 0, po4.x, po4.y, po4.z);
}

PShape mycube(PVector pos,float wx,float wy,float wz) {    // returns a CUBE
  float x = pos.x;                     // cube center point 
  float y = pos.y;
  float z = pos.z;
  wx = wx/2;                           // cube size from diameter to radius
  wy = wy/2;
  wz = wz/2;
  PShape q = createShape();
  q.beginShape(QUADS);
  q.noFill();
  q.stroke(200,0,0);
  q.strokeWeight(2);
  q.vertex(x-wx,y+wy,z+wz);   //0
  q.vertex(x+wx,y+wy,z+wz);   //1
  q.vertex(x+wx,y+-wy,z+wz);  //2
  q.vertex(x-wx,y+-wy,z+wz);  //3

  q.vertex(x+wx,y+wy,z+wz);   //4 = 1
  q.vertex(x+wx,y+wy,z-wz);   //5
  q.vertex(x+wx,y-wy,z-wz);   //6
  q.vertex(x+wx,y-wy,z+wz);   //7 = 2

  q.vertex(x+wx,y+wy,z-wz);   //8 = 5
  q.vertex(x-wx,y+wy,z-wz);   //9
  q.vertex(x-wx,y-wy,z-wz);   //10
  q.vertex(x+wx,y-wy,z-wz);   //11 = 6

  q.vertex(x-wx,y+wy,z-wz);   //12 = 9
  q.vertex(x-wx,y+wy,z+wz);   //13 = 0
  q.vertex(x-wx,y-wy,z+wz);   //14 = 3
  q.vertex(x-wx,y-wy,z-wz);   //15 = 10

  q.vertex(x-wx,y+wy,z-wz);   //16 = 9
  q.vertex(x+wx,y+wy,z-wz);   //17 = 5
  q.vertex(x+wx,y+wy,z+wz);   //18 = 1
  q.vertex(x-wx,y+wy,z+wz);   //19 = 0

  q.vertex(x-wx,y+-wy,z-wz);  //20 =10
  q.vertex(x+wx,y+-wy,z-wz);  //21 = 6
  q.vertex(x+wx,y+-wy,z+wz);  //22 = 2
  q.vertex(x-wx,y+-wy,z+wz);  //23 = 3

  q.endShape(CLOSE);
  return q;
}

void mouseWheel(MouseEvent event) {
  int e = event.getCount();                                     // windows +- 1
  zMag += e*0.05;
}

void keyPressed() {
  if ( key == '+' ) pstretch +=0.05; 
  if ( key == '-' ) pstretch -=0.05;
  println("stretch "+nf(pstretch,0,2));
  qa = mycube(qapos,wx,wy,wz);         // creates q at qpos with dx,dy,dz
  qb = mycube(qbpos,wx,wy,wz);         // creates q at qpos with dx,dy,dz
  stretchqa(qapos,pstretch);
  stretchqb();                         // fit qb to qa resized.
}


also now can rotate the second cube using Arrow Keys

float zMag =1.0;

float wx = 10, wy = 15, wz = 30;            // cube size
PVector qapos = new PVector(20, 30, 50);    // center qa cube
PVector qbpos = new PVector(20, 30, 50+wz); // center qb cube

PShape qa, qb;
PVector po1, p1, po2, p2, po3, p3, po4, p4;
float pstretch = 1.0;
PVector qbtouch=qbpos.copy(), qbposrot=qbpos.copy(), qbtrans=qbpos.copy();
float qbr=0, qbrotate = 0.0;
boolean help = true;

void setup() {
  size(500, 500, P3D);
  qa = mycube(qapos, wx, wy, wz);            // creates q at qpos with dx,dy,dz
  qb = mycube(qbpos, wx, wy, wz);            // creates q at qpos with dx,dy,dz
  stretchqa(qapos, pstretch);
  //stretchqb();                          // fit qb to qa resized.
  println("ues: mouseX mouseY for rotate and mouse wheel for zoom\nkey[+] [-] for stretch qa side,\nkey Arrow Up Down for qb rotate angle, key Arrow LEFT RIGHT for qb rotate radius\nkey [h] toggle helplines");
}

void stretchqa(PVector off, float pstretch) {
  po1 = qa.getVertex(0);   //  0
  p1 = po1.copy().sub(off);
  p1 = p1.mult(pstretch);
  p1.add(off);
  qa.setVertex(0, p1);     // 0
  qa.setVertex(13, p1);    // also 13
  qa.setVertex(19, p1);    // also 19

  po2 = qa.getVertex(1);   //  1
  p2 = po2.copy().sub(off);
  p2 = p2.mult(pstretch);
  p2.add(off);
  qa.setVertex(1, p2);     // 1
  qa.setVertex(4, p2);     // also 4
  qa.setVertex(18, p2);    // also 18

  po3 = qa.getVertex(2);   //  2
  p3 = po3.copy().sub(off);
  p3 = p3.mult(pstretch);
  p3.add(off);
  qa.setVertex(2, p3);     // 2
  qa.setVertex(7, p3);     // also 7
  qa.setVertex(22, p3);    // also 22

  po4 = qa.getVertex(3);   //  3
  p4 = po4.copy().sub(off);
  p4 = p4.mult(pstretch);
  p4.add(off);
  qa.setVertex(3, p4);     // 3
  qa.setVertex(14, p4);    // also 14
  qa.setVertex(23, p4);    // also 23
}

void stretchqb() {        // to fit to qb side 5 6 9 10 to qa ( stretched ) side 0 1 2 3 
  qb.setVertex(5, p2);
  qb.setVertex(8, p2);
  qb.setVertex(17, p2);

  qb.setVertex(6, p3);
  qb.setVertex(11, p3);
  qb.setVertex(21, p3);

  qb.setVertex(9, p1);
  qb.setVertex(12, p1);
  qb.setVertex(16, p1);

  qb.setVertex(10, p4);
  qb.setVertex(15, p4);
  qb.setVertex(20, p4);
}

void draw() {
  background(200, 200, 0);
  viewpoint();
  shape(qa, 0, 0);
  shape(qb, 0, 0);
  helplines();
}

void viewpoint() {
  translate(width/2, height/2, 0);
  float angx = -map(mouseY, 0, width, -PI, PI);
  float angy = map(mouseX, 0, width, -PI, PI);
  rotateY(angy);
  rotateX(angx);
  scale(zMag);
  scale(2, 2, 2);
}

void helplines() {
  if ( help ) {
    stroke(0, 0, 200);
    line(0, 0, 0, p1.x, p1.y, p1.z);
    line(0, 0, 0, p2.x, p2.y, p2.z);
    line(0, 0, 0, p3.x, p3.y, p3.z);
    line(0, 0, 0, p4.x, p4.y, p4.z);  
    stroke(0, 200, 200);
    line(0, 0, 0, po1.x, po1.y, po1.z);
    line(0, 0, 0, po2.x, po2.y, po2.z);
    line(0, 0, 0, po3.x, po3.y, po3.z);
    line(0, 0, 0, po4.x, po4.y, po4.z);
    stroke(200, 200, 200);
    line(0, 0, 0, qbtouch.x, qbtouch.y, qbtouch.z);
    line(0, 0, 0, qbposrot.x, qbposrot.y, qbposrot.z);
    line(0, 0, 0, qbpos.x, qbpos.y, qbpos.z);
    line(qbposrot.x, qbposrot.y, qbposrot.z, qbpos.x, qbpos.y, qbpos.z);
  }
}

PShape mycube(PVector pos, float wx, float wy, float wz) {    // returns a CUBE at pos: cube center point 
  wx = wx/2;                                               // cube size from diameter to radius
  wy = wy/2;
  wz = wz/2;
  PVector p0 = new PVector(pos.x-wx, pos.y+wy, pos.z+wz);    // cube 8 corner calc as PVector
  PVector p1 = new PVector(pos.x+wx, pos.y+wy, pos.z+wz);
  PVector p2 = new PVector(pos.x+wx, pos.y+-wy, pos.z+wz);
  PVector p3 = new PVector(pos.x-wx, pos.y+-wy, pos.z+wz);

  PVector p5 = new PVector(pos.x+wx, pos.y+wy, pos.z-wz);
  PVector p6 = new PVector(pos.x+wx, pos.y-wy, pos.z-wz);
  PVector p9 = new PVector(pos.x-wx, pos.y+wy, pos.z-wz);
  PVector p10 =new PVector(pos.x-wx, pos.y-wy, pos.z-wz);

  PShape q = createShape();
  q.beginShape(QUADS);
  q.noFill();
  q.stroke(200, 0, 0);
  q.strokeWeight(2);
  q.vertex(p0.x, p0.y, p0.z);   //0
  q.vertex(p1.x, p1.y, p1.z);   //1
  q.vertex(p2.x, p2.y, p2.z);   //2
  q.vertex(p3.x, p3.y, p3.z);   //3

  q.vertex(p1.x, p1.y, p1.z);   //4 = 1
  q.vertex(p5.x, p5.y, p5.z);   //5
  q.vertex(p6.x, p6.y, p6.z);   //6
  q.vertex(p2.x, p2.y, p2.z);   //7 = 2

  q.vertex(p5.x, p5.y, p5.z);   //8 = 5
  q.vertex(p9.x, p9.y, p9.z);   //9
  q.vertex(p10.x, p10.y, p10.z);//10
  q.vertex(p6.x, p6.y, p6.z);   //11 = 6

  q.vertex(p9.x, p9.y, p9.z);   //12 = 9
  q.vertex(p0.x, p0.y, p0.z);   //13 = 0
  q.vertex(p3.x, p3.y, p3.z);   //14 = 3
  q.vertex(p10.x, p10.y, p10.z);//15 =10

  q.vertex(p9.x, p9.y, p9.z);   //16 = 9
  q.vertex(p5.x, p5.y, p5.z);   //17 = 5
  q.vertex(p1.x, p1.y, p1.z);   //18 = 1
  q.vertex(p0.x, p0.y, p0.z);   //19 = 0

  q.vertex(p10.x, p10.y, p10.z);//20 =10
  q.vertex(p6.x, p6.y, p6.z);   //21 = 6
  q.vertex(p2.x, p2.y, p2.z);   //22 = 2
  q.vertex(p3.x, p3.y, p3.z);   //23 = 3

  q.endShape(CLOSE);
  return q;
}

void mouseWheel(MouseEvent event) {
  int e = event.getCount();                                     // windows +- 1
  zMag += e*0.05;
}

void keyPressed() {
  if ( key == '+' )     pstretch += 0.05;     // [+]
  if ( key == '-' )     pstretch -= 0.05;     // [-]
  if ( keyCode == UP )    qbrotate += 0.08;   // Arrow UP rotation angle
  if ( keyCode == DOWN )  qbrotate -= 0.08;   // Arrow DOWN
  if ( keyCode == LEFT )  qbr -= 5;           // Arrow LEFT rotation radius
  if ( keyCode == RIGHT ) qbr += 5;           // Arrow LEFT
  if ( key == 'h' ) help = ! help;
  //println("key: "+key+" keyCode: "+keyCode);
  println("qa stretch "+nf(pstretch, 0, 2)+" qb rotate deg "+nf(degrees(qbrotate), 0, 1)+" radius "+qbr);
  // rotation part
  qbtouch = qbpos.copy().sub(0, 0, wz/2);            // rotation center
  qbtrans = qbpos.copy().sub(qbtouch);
  qbtrans.set(qbtrans.x+qbr*sin(qbrotate), qbtrans.y+qbr*cos(qbrotate), qbtrans.z);
  qbposrot = qbtouch.copy().add(qbtrans);
  // creation part
  qa = mycube(qapos, wx, wy, wz);         // creates q at qpos with dx,dy,dz
  qb = mycube(qbposrot, wx, wy, wz);      // creates q at qpos ( with rotation ) with dx,dy,dz
  stretchqa(qapos, pstretch);
  stretchqb();                         // fit qb to qa resized.
}

BUT not use translation & rotation for it.
what might be possible with using modelXYZ.

3 Likes

Wow, this looks pretty good!

I’m going to try to use modelXYZ and see if that helps. Perhaps adding angles and trigonometry to the stretch functions will do the trick.

Thanks for the insight and hard work :smiley:

I’ll mark as solution because it proves what I’m trying to do

1 Like