Detect rectangle intersection in 3D

Hi there,

I am writing a program to draw some white squares in 3D space and I would like that, where they intersect, a black line is shown. I can’t find an easy way to do that.

However, if I use different colors for the squares, the lines I want to draw appear between the colors like in the image below.

Can anyone help me find an easy solution?

Thank you very much!

float x,y,z,a,b ;
int num = 50 ; 
int side = 500;
int imax = 0; 
float y0 = 2./3;
int radius;

int previousDisplayTime;  // Keep track of the last time a frame of the animation was displayed
int deltaTime; 

boolean sense = true;
boolean pause = false;

void setup() {
  size(1000,1000,P3D);
  lights();
  //ortho();
  y0 = height;///2+side/2;
  println(y0);
  x = width/2;
  y = y0;
  z = 0;
  a = 0;
  b = 0;
  
  directionalLight(255, 255, 255, 0, -1, -1);
  
  deltaTime = 100;
  previousDisplayTime = 0;  
  
  //float fov = PI/2;
  //float cameraZ = (4000) / tan(fov/2.0);
  //perspective(fov, float(width)/float(height), 
  //          cameraZ/10.0, cameraZ*10.0);
}

void draw() {
  radius = side + mouseX;
  //lights();
  //stroke(220);
  stroke(255*1/4);
  //noStroke();
  background(220, 220, 220);  
  //background(255);
  //fill(255, 0, 0, 10);
  camera(//width, 0, width/3, // eyeX, eyeY, eyeZ
         width/2-10*width/4, height/2-10*width/4, radius-10*width/4,
         0, 0, 0,
         //3*width/4, width/4, width/8, // centerX, centerY, centerZ
         0.0, 1.0, 0.0);


  perspective(PI/3.0, float(width)/float(height), mouseX, 10*radius-100*width/4);//1, 25*radius);
  
  
  if ((millis() > previousDisplayTime + deltaTime) & sense & !pause) {
    imax++;
    if (imax > num) { 
      sense = false;
    }
    previousDisplayTime = millis();
  }

  if ((millis() > previousDisplayTime + deltaTime) & !sense & !pause) {
    imax--;
    if (imax < 0) { 
      sense = true;
    }
    previousDisplayTime = millis();
  }


  for (int i=0; i < imax; i++) {
      x = radius * cos(i*2*PI/num);
      y = radius * sin(i*2*PI/num); 
      drawSheet(i);
      //y-=side/(num-1); // The rectangle moves forward as z increments.
      a+=0.02*mouseX/width*20;
      b+=0.02*mouseY/width*20;
  }
  
  a = 0;
  b = 0;
  y = y0;
  x=width/2;

}

void mousePressed() {
 pause = !pause; 
}

void drawSheet(int i) {
  pushMatrix();
  translate(x,y,0);
  rectMode(CENTER);
  fill(255, random(255), random(255));
  //rotateX(-PI/6);
  //rotateY(PI/3);
  rotateX(PI/2);
  rotateY(2*PI/num*i);
  rotateZ(a);
  rotateY(b);
  rect(0,0,side,side);
  popMatrix();
}

There isn’t a great easy solution, but this kinda works. At the bottom of your draw() add:

  loadPixels();
  for( int jj=0; jj<height-1; jj++ ) {
    for( int ii=0; ii<width-1; ii++ ) {
      int idx = jj*width+ii;
      if( pixels[idx] == 0 || pixels[idx+1] == 0 || pixels[idx+width] == 0 )
        continue;
      if( pixels[idx] != pixels[idx+1] || pixels[idx] != pixels[idx+width] )
        pixels[idx] = 0;
    }
  }
  updatePixels();

It does a crude edge detection that checks if each pixel is a different color from its neighbor to the right or below, then set to black. It can’t handle anti-aliasing, though, so your edges lose any smoothness. And if two squares are the same color, it won’t put an edge between them.

Thanks a lot for your reply. It would work indeed if I wanted to have the sheets in color. Since I need them all in white, it doesn’t work. Could you think of another solution for this problem, considering the color constraint? Also, the thickness of the lines change, it doesn’t really render well with this solution. Ideas?

when speed is not an issue you can modify the approach:

  • Make a PGraphics pg, same size as your window.

  • Draw the same rects on pg but with color.

  • no anti-aliasing for the pg (because the approach can’t handle anti-aliasing) and no change of thickness of the lines (or use noStroke in the pg)

  • (Don’t display the pg)

NOW use scudly’s code on the pg instead.

From your original code, draw the squares with noStroke() and let my code create the boundaries which will then be a more consistent, though not perfect, thickness.

Do a second pass that turns everything that is NOT black to white.

Hey @Chrisir @scudly, thanks for your help. It works! Here is the integrated code, the rendering is almost perfect.

PGraphics pg;

float x,y,z,a,b ;
int num = 500 ; 
int side = 500;
int imax = 0; 
float y0 = 2./3;
int radius;

int previousDisplayTime;  // Keep track of the last time a frame of the animation was displayed
int deltaTime; 

boolean sense = true;
boolean pause = false;

void setup() {
  size(1000,1000,P3D);
  pg = createGraphics(1000, 1000, P3D);
  pg.noSmooth();
  pg.noStroke();
  lights();
  y0 = height;
  println(y0);
  x = width/2;
  y = y0;
  z = 0;
  a = 0;
  b = 0;
  
  directionalLight(255, 255, 255, 0, -1, -1);
  
  deltaTime = 100;
  previousDisplayTime = 0;  

}

void draw() {
  radius = side + mouseX;
  stroke(255*1/4);
  background(220, 220, 220);  
  

  pg.loadPixels();
  for (int i = 0; i < (width*height); i++) {
    pg.pixels[i] = color(220,220,220);
  }
  pg.updatePixels();  
  
  camera(
         width/2-10*width/4, height/2-10*width/4, radius-10*width/4,
         0, 0, 0,
         0.0, 1.0, 0.0);
  pg.camera(
         width/2-10*width/4, height/2-10*width/4, radius-10*width/4,
         0, 0, 0,
         0.0, 1.0, 0.0);

  perspective(PI/3.0, float(width)/float(height), mouseX, 10*radius-100*width/4);//1, 25*radius);
  pg.perspective(PI/3.0, float(width)/float(height), mouseX, 10*radius-100*width/4);//1, 25*radius);
  
  if ((millis() > previousDisplayTime + deltaTime) & sense & !pause) {
    imax++;
    if (imax > num) { 
      sense = false;
    }
    previousDisplayTime = millis();
  }

  if ((millis() > previousDisplayTime + deltaTime) & !sense & !pause) {
    imax--;
    if (imax < 0) { 
      sense = true;
    }
    previousDisplayTime = millis();
  }

  pg.beginDraw();
  for (int i=0; i < imax; i++) {
      x = radius * cos(i*2*PI/num);
      y = radius * sin(i*2*PI/num); 
      drawSheet(i);
      a+=0.02*mouseX/width*20;
      b+=0.02*mouseY/width*20;
  }
  pg.endDraw();
  
  a = 0;
  b = 0;
  y = y0;
  x=width/2;

  pg.loadPixels();
  loadPixels();
  for( int jj=0; jj<height-1; jj++ ) {
    for( int ii=0; ii<width-1; ii++ ) {
      int idx = jj*width+ii;
      if( pg.pixels[idx] == 0 || pg.pixels[idx+1] == 0 || pg.pixels[idx+width] == 0 )
        continue;
      if( pg.pixels[idx] != pg.pixels[idx+1] || pg.pixels[idx] != pg.pixels[idx+width] )
        pixels[idx] = color(100,100,100);
    }
  }
  updatePixels();

}

void mousePressed() {
 pause = !pause; 
}

void drawSheet(int i) {
  pushMatrix();
  translate(x,y,0);
  rectMode(CENTER);
  noStroke();
  fill(255);
  rotateX(PI/2);
  rotateY(2*PI/num*i);
  rotateZ(a);
  rotateY(b);
  rect(0,0,side,side);
  popMatrix();
  
  pg.pushMatrix();
  pg.translate(x,y,0);
  pg.rectMode(CENTER);
  pg.noStroke();
  pg.fill(255, random(255), random(255));//, random(255), random(255));
  pg.rotateX(PI/2);
  pg.rotateY(2*PI/num*i);
  pg.rotateZ(a);
  pg.rotateY(b);
  pg.rect(0,0,side,side);
  pg.popMatrix();
}

3 Likes