Drawing a 3d shape between 2 points in a 3d space

I'm trying to write code that will take 2 points and draw a long thin shape between them.

I have successfully done this in a 2d space. The process involves:

  • drawing a line between the 2 points
  • determining the angle between that line and the x-axis
  • calculating the perpendicular angle to that line
  • calculating the position of points a little way either side of the line (at both the start and end point)
  • joining the resulting 4 points into a quad

Here's the code for that:

float x1 = -200;
float y1 = 0;
float x2 = 200;
float y2 = 200;

float plane_width = 20;

void setup() {
  size(500,500);
}

void draw() {
  translate(width/2, height/2);

  background(255);

  draw_plane_along_line(x1,y1,x2,y2, plane_width);

  line(x1,y1,x2,y2);
}

float get_angle_of_line(float x1, float y1, float x2, float y2) {
  float x_delta = x2 - x1;
  float y_delta = y2 - y1;
  return atan2(y_delta, x_delta);
}

void draw_plane_along_line(float x1, float y1, float x2, float y2, float w) {

  float angle = get_angle_of_line(x1,y1,x2,y2);
  float angle_left = angle - HALF_PI;
  float angle_right = angle + HALF_PI;

  float left_corner_x = w/2 * cos(angle_left) + x1;
  float left_corner_y = w/2 * sin(angle_left) + y1;

  float right_corner_x = w/2 * cos(angle_right) + x1;
  float right_corner_y = w/2 * sin(angle_right) + y1;

  float end_left_corner_x = w/2 * cos(angle_left) + x2;
  float end_left_corner_y = w/2 * sin(angle_left) + y2;

  float end_right_corner_x = w/2 * cos(angle_right) + x2;
  float end_right_corner_y = w/2 * sin(angle_right) + y2;

  beginShape();
  vertex(left_corner_x, left_corner_y);
  vertex(end_left_corner_x, end_left_corner_y);
  vertex(end_right_corner_x, end_right_corner_y);
  vertex(right_corner_x, right_corner_y);
  endShape(CLOSE);

}

Now I'm trying to do the same thing in a 3d space.

It seems that the same theory should apply - except now there should be 2 angles to calculate: the angle from the x-axis, and then the angle from the y-axis. The theory seems simple, but I'm not sure of the maths.

Can anyone help? I'm happy to scrap this code completely if there's a much simpler way! Or indeed if there is a library that will take care of this for me.

Tagged:

Answers

  • the problem is that there are an infinite number of planes between two points in 3d space. which one are you using?

    (this is why camera() takes 9 parameters, a start, an end and an 'up' vector)

    (btw there is something similar to the 2d version in the shaders tutorial, https://processing.org/tutorials/pshader/ - Code listing 10.4)

  • hi @koogs,

    If you're asking which way I want to be "up", I'm happy to assume that it's the positive end of the y-axis... if that makes sense. I'm not sure if that answers your question though.

    When I use the word "plane" in the above code, what I really meant was "2d shape" - I may be using the wrong terminology.

  • Answer ✓

    If you are interested in just using Java mode then you could use the Shapes3D library like this

    import shapes3d.*;
    import shapes3d.animation.*;
    import shapes3d.utils.*;
    
    Tube tube;
    
    void setup(){
      size(600,600,P3D);
      tube = new Tube(this, 1, 8);
      tube.setSize(10,10,10,10);
      tube.fill(color(200,40,40));
      tube.fill(color(200,200,40), Tube.BOTH_CAP);
      tube.drawMode(Shape3D.SOLID);
      tube.drawMode(Shape3D.SOLID, Tube.BOTH_CAP);
      tube.setWorldPos(-20,65,200, 20,-65,-200);
    }
    
    void draw(){
      translate(width/2, height/2);
      background(0);
      tube.draw();
    }
    
  • edited February 2018
  • There are two problems with using line(x0,y0,z0,x1,y1.z1) to draw lines in OpenGL.

    1) you will not get the perspective effect

    2) when combined with surfaces (triangles and quads) depth sorting the lines/surfaces correctly is not very efficient.

    See this discussion

  • @quark - that library is great, thank you!

    Thanks for the suggestion @Chrisir - line() was my original solution, but as @quark points out, there is no perspective effect.

  • ??

    you got me wrong

    there is a function I posted the Link to which essentially is a thin long rect or box

    name of the function is drawLine iirc

  • Sorry my mistake

  • ;-)

    no problem

  • edited March 2016

    just strokeColour

    is in fact fillColor

    ;-)

  • Ah, apologies for the misunderstanding @Chrisir - I might try out that function at some point, thanks for the link!

  • edited March 2016
    // demonstrates lines in 3D
    
    float angle=0.0;
    
    void setup()
    {
      // init
      size(1200, 600, OPENGL);
    } // func 
    
    void draw() 
    { 
      background(0);
      lights();
    
      translate(width/2, height/2);
      rotateY (angle); 
    
      showSomeLines();
    
      // increase angle when no key is pressed
      if (!keyPressed)
        angle+=0.01;
    
      // HUD display for the text 
      camera(); 
      noLights(); 
      fill(255); 
      text("press any key to stop rotation", 16, 16);
    }
    
    // ---------------------------------------------------------
    
    void showSomeLines() {
    
      // some lines 
    
      // yellow
      drawLine(-50, 230, 600, 
        50, 430, -200, 
        13, 
        color (255, 222, 0));
    
      // RED 2
      drawLine(-40, 130, 510, 
        33, 230, -100, 
        15, 
        color (255, 0, 0));
    
      drawLine(-100, -230, 220, 
        90, 130, -200, 
        5, 
        color (255, 22, 255));
    
      drawLine(-200, -230, 520, 
        440, 430, 300, 
        5, 
        color (2, 2, 255));
    
    
      // -------------------------------------------
      // Three lines in gray (color 144)
      // line in y dir 
      drawLine(-20, -200, 400, 
        -20, 230, 400, 
        6, 
        color (144));
    
      // line in x dir 
      drawLine(-20, -20, 400, 
        220, -20, 400, 
        6, 
        color (144));
    
      // line in z dir 
      drawLine(-20, -20, 400, 
        -20, -20, -400, 
        6, 
        color (144));
    }
    
    void drawLine ( float x1, float y1, float z1, 
      float x2, float y2, float z2, 
      float weight, 
      color fillColor)
      // drawLine programmed by James Carruthers
      // see http:// processing.org/discourse/yabb2/num_1262458611.html#4
      // It is a 3D-replacement for the Line from x1,y1,z1 to xy,y2,z2 with
      // weight and fillColor.
    {
      PVector p1 = new PVector(x1, y1, z1);
      PVector p2 = new PVector(x2, y2, z2);
      PVector v1 = new PVector(x2-x1, y2-y1, z2-z1);
      float rho = sqrt(pow(v1.x, 2)+pow(v1.y, 2)+pow(v1.z, 2));
      float phi = acos(v1.z/rho);
      float the = atan2(v1.y, v1.x);
      v1.mult(0.5);
    
      pushMatrix();
      translate(x1, y1, z1);
      translate(v1.x, v1.y, v1.z);
      rotateZ(the);
      rotateY(phi);
      noStroke();
      fill(fillColor);
      box(weight, weight, p1.dist(p2)*1.2);
      popMatrix();
    } // function 
    
  • // demonstrates lines in 3D
    
    float angle=0.0;
    
    
    final color RED =  color (255, 0, 0); 
    final color GREEN =  color (0, 255, 0); 
    final color BLUE =  color (0, 0, 255); 
    
    final color YELLOW =  color (255, 222, 0);
    final color GRAY =  color (139); 
    
    PVector start1;
    PVector end1;
    
    PVector start2;
    PVector end2;
    
    // --------------------------------------------------------
    
    void setup()
    {
      // init
      size(1500, 900, OPENGL);
    
      // from left upper side to right side in RED 
      start1=new PVector(-40, 100, 160); 
      end1=new PVector(400, 240, -160);
    
      // from bottom left to the right in YELLOW
      start2=new PVector(230, 210, 160); 
      end2=new PVector(550, 90, -160);
    } // func 
    
    void draw() {
    
      background(0);
      lights();
    
      translate(width/2, height/2, 110);
      rotateY (angle);
    
      // Show Coordinates 
      ShowCoordinates();
    
      // draw red and yellow line 
      showSomeLines();
    
      // draw lines between them 
      drawConnectingLines(); 
    
      // increase angle when no key is pressed
      if (!keyPressed)
        angle+=0.01;
    
      // HUD display for the text 
      camera(); 
      noLights(); 
      fill(255); 
      text("press any key to stop rotation", 16, 16);
    }
    
    // -----------------------------------------------
    // other functions 
    
    void drawConnectingLines() {
    
      // draw connecting lines 
      for (int i=-1; i < 12; i++) {
    
        // calculate 2 points with lerp()
        float x1=lerp(start1.x, end1.x, i/12.0);
        float y1=lerp(start1.y, end1.y, i/12.0);
        float z1=lerp(start1.z, end1.z, i/12.0);
    
        float x2=lerp(start2.x, end2.x, (i/12.0));
        float y2=lerp(start2.y, end2.y, (i/12.0));
        float z2=lerp(start2.z, end2.z, (i/12.0));
    
        // draw line with those 2 points 
        drawLine(x1, y1, z1, 
          x2, y2, z2, 
          6, 
          BLUE);
      }
    }
    
    void ShowCoordinates () {
      // Show Coordinates x, y and z as lines 
      //
      // X
      stroke (255, 0, 0);
      drawLine (0, 0, 0, 100, 0, 0, 4, RED ) ;
      sphere3D (100, 0, 0, 6, RED );
      text ("X", 120, 20, 0);
    
      // Y
      stroke    (0, 255, 0);
      drawLine (0, 0, 0, 0, 100, 0, 4, GREEN ) ;    
      sphere3D(0, 100, 0, 6, GREEN);    
      text ("Y", 0, 120, 0);
    
      // Z
      stroke (0, 0, 255);
      drawLine (0, 0, 0, 0, 0, -300, 4, BLUE ) ; 
      sphere3D (0, 0, -300, 6, BLUE);    
      text ("-Z", 30, 20, -300);
    } // function 
    
    // ---------------------------------------------------------
    
    void showSomeLines() {
    
      // the 2 lines 
      // 
      drawLinePVector(start1, end1, 
        13, 
        RED);
    
      drawLinePVector(start2, end2, 
        13, 
        YELLOW);
    }
    
    void sphere3D(float x, float y, float z, 
      float sizeSphere, 
      color col) {
      // point as a sphere, fixed size 15, 
      // fixed noStroke, no fill color, you 
      // must set the fill color before using 
      // this method. 
      noStroke();
      fill(col); 
      pushMatrix();
      translate(x, y, z);
      sphere(sizeSphere);
      popMatrix();
    }
    
    void quad22222222222222222222222222(
      float x1, float y1, float z1, 
      float x2, float y2, float z2, 
      float x3, float y3, float z3, 
      float x4, float y4, float z4) {
      beginShape();
      vertex(x1, y1, z1);
      vertex(x2, y2, z2);
      vertex(x3, y3, z3);
      vertex(x4, y4, z4);
      endShape(CLOSE);
    }
    
    void quadArray(PVector[] a1) {
      beginShape();
      for (PVector pv : a1) 
        vertex(pv.x, pv.y, pv.z);
      vertex(a1[0].x, a1[0].y, a1[0].z);
      endShape(CLOSE);
    }
    
    // ----------------------------------------------------
    
    void drawLinePVector(PVector p1, PVector p2, 
      float weight, 
      color fillColor) {
      drawLine ( p1.x, p1.y, p1.z, 
        p2.x, p2.y, p2.z, 
        weight, 
        fillColor);
    }
    
    void drawLine ( float x1, float y1, float z1, 
      float x2, float y2, float z2, 
      float weight, 
      color fillColor)
      // drawLine programmed by James Carruthers
      // see http://processing.org/discourse/yabb2/num_1262458611.html#4
      // It is a 3D-replacement for the Line from x1,y1,z1 to xy,y2,z2 with
      // weight and fillColor.
    {
      PVector p1 = new PVector(x1, y1, z1);
      PVector p2 = new PVector(x2, y2, z2);
      PVector v1 = new PVector(x2-x1, y2-y1, z2-z1);
      float rho = sqrt(pow(v1.x, 2)+pow(v1.y, 2)+pow(v1.z, 2));
      float phi = acos(v1.z/rho);
      float the = atan2(v1.y, v1.x);
      v1.mult(0.5);
    
      pushMatrix();
      translate(x1, y1, z1);
      translate(v1.x, v1.y, v1.z);
      rotateZ(the);
      rotateY(phi);
      noStroke();
      fill(fillColor);
      box(weight, weight, p1.dist(p2)*1.2);
      popMatrix();
    } // function 
    
    //
    
Sign In or Register to comment.