Parallel lines in 3D space

I have a line in 3D space. The line is not parallel to any of the three axes (X, Y, Z). I know the 3D coordinates of point 0 and point 1.
How would I use Processing to find the coordinates of a parallel line? By parallel, I mean drawing ‘correct.png’ and not drawing ‘wrong.png’. Both images below.
I do not mind about the distance between the two parallel lines.
I realise there are an uncountable number of parallel lines so I am happy to include an angle somewhere (presumably expressed as a rotation around the line), but I do not want to restrict the answers.


1 Like

In both your Correct and Wrong images, you have two parallel lines. You can tell that they are parallel because - if you were to extend the lines forever, they will not collide. In short, they have the same slopes.

What I’m trying to say is… There’s nothing wrong with your Wrong example image! Its lines are also parallel!

Can you explain why you think your Wrong image is not right? Do you have code that draws two lines? If so, please post the code. Otherwise, to even start helping you, we have to start from scratch, which is a lot more work on our part than editing what you already have.

1 Like

This is part of something much bigger. Later, I want to use the two lines to make a quadrilateral with right angles. The correct example is useful for that. The wrong example is sheared.

Understood. I tried to code up something that works, came up with this. It’s not perfect, but it might help you and is a good starting point for anyone else that might want to jump in:

float ax=20, ay=250, bx=130, by=50; // First two points.
float z = 100; // X offset for second line.
  
void setup(){
  size(600,400);
}

void draw(){
  background(0);
  fill(0);
  
  // Find slope of first line.
  if(bx == ax){ return; } // Give up. (Actually a simple case.)  
  float m = (by-ay)/(bx-ax);
  if(m==0){ return; } // Give up, again.
  m = -1/m; // Convert to slope of perpendicular line.

  // Find adjusted points of second line.
  float cx = ax + z;
  float cy = ay + m*z;
  float dx = bx + z;
  float dy = by + m*z;
    
  
  stroke(0,200,0);
  line(ax,ay,bx,by); // Green line AB
  stroke(200,0,0);
  line(ax, ay, cx, cy); // Red line AC
  line(bx, by, dx, dy); // Red line BD
  stroke(0,0,200);
  line(cx, cy, dx, dy); // Blue line CD
  
  stroke(0,200,0);
  ellipse(ax,ay,10,10); // Point A
  ellipse(bx,by,10,10); // Point B
  stroke(0,0,200);
  ellipse(cx,cy,10,10); // Point C
  ellipse(dx,dy,10,10); // Point D  
  
}
  
  
void mousePressed(){
  if(mouseButton == LEFT){
    ax = mouseX;
    ay = mouseY;
  } 
  if(mouseButton == RIGHT){
    bx = mouseX;
    by = mouseY;
  }
  if(mouseButton == CENTER){
    z = random(20,200);
  }
}
1 Like

That is excellent. Thanks @TfGuy44
I want to do this in three dimensional space. I have been trying to do this with PVector, normalising the vectors, adding a quad, and then transforming back. Your approach is much better, but could it work for X, Y, and Z?

If I understand you correctly, …

Let’s say your first line goes from 3D points P0 to P1. Let’s make a vector N = P1 - P0. Vector N is normal to (perpendicular to) a plane passing through P0. You want to define another line with a base point P2 that also lies within this plane. Adding N to P2 gives P3 to make your parallel line.

Let’s define another vector V = P2 - P0 which since both P0 and P2 line in the plane, this vector also lies in the plane and so it is also perpendicular to N. When two vectors are perpendicular, their dot product is equal to 0. So how do you want to generate P2? If you pick two of its coordinates, you can solve the algebra to derive the 3rd so that it lies in the plane.

1 Like

@scudly That sounds very much like what I want!
You ask: > So how do you want to generate P2?

If I have go it right, is it your vector V we need to find? Is there some algebra that will generate vector V from vector N, with dot product equal to 0, with some magnitude, and some angle of rotation around PO?

Write out the algebra for the dot product equation and solve it for one of the P2 components.

Nx * (P2x-P0x) + Ny * (P2y - P0y) + Nz * (P2z - P0z) = 0

solve for, say P2z

Nz * (P2z - P0z) = -Nx * (P2x-P0x) - Ny * (P2y - P0y)

so

P2z = (-Nx * (P2x-P0x) - Ny * (P2y - P0y)) / Nz + P0z

Pick your P2x and P2y (randomly?) and then solve for P2z.

Just be sure that Nz is not 0.

Hi

There is more in this channel

1 Like

So, gratefully taking all suggestions into account, I have made two sketches. Please see P1 and P2 below.
I have some questions:

Q1
Are there any errors? Please be very picky because I plan to use this as the foundations of something bigger.

Q2
Are both p1 and p2 correct? Or is one method preferable?

Q3
How can I print the direction as an angle?

Q4
What is the difference between these two? Which is correct?
PVector x = PVector.sub(a, b);
PVector x = a.sub(b);

Thanks!

P1

PVector a, b;
float m;
  
void setup() {
  size(400, 400, P3D);

  a = new PVector(0, 0, 0); // initial point
  b = new PVector(69.2820323, 40, 20); // point
  
  m = PVector.dist(a, b);
  println(m);
  
}

void draw() {
  background(255);
  noLoop();

  // calculate the direction from a to b
  PVector t = b.copy();
  t.sub(a);
  t.normalize();
  
  // calculate the two perpendicular directions
  PVector x = PVector.sub(a, b);
  PVector p1 = new PVector(-x.y, x.x);
  PVector p2 = x.cross(p1);
  p1.normalize();
  p2.normalize();
  
  translate(width/2,height/2);  // move (0, 0, 0) to centre of window
  
  // Draw a line that goes from initial point a
  // in direction of b
  // with a length of m
  stroke(0, 0, 0);
  line(a.x, a.y, a.z, a.x+t.x*m, a.y+t.y*m, a.z+t.z*m);
  
  // Draw a perpendicular line at point a
  stroke(255, 0, 0);
  line(a.x, a.y, a.z, a.x+p1.x*m, a.y+p1.y*m, a.z+p1.z*m);
  // Draw a perpendicular line at point b
  line(b.x, b.y, b.z, b.x+p1.x*m, b.y+p1.y*m, b.z+p1.z*m);
  
  // complete the square
  stroke(0, 255, 0);
  line(a.x+p1.x*m, a.y+p1.y*m, a.z+p1.z*m, b.x+p1.x*m, b.y+p1.y*m, b.z+p1.z*m);

}

P2

PVector a, b;
float m;
  
void setup() {
  size(400, 400, P3D);

  a = new PVector(0, 0, 0); // initial point
  b = new PVector(69.2820323, 40, 20); // point
  
  m = PVector.dist(a, b);
  println(m);
  
  float u = b.mag();
  float v = a.mag();
  print(u - v);  // to check dist()
  
}

void draw() {
  background(255);
  noLoop();

  // calculate the direction from a to b
  PVector t = b.copy();
  t.sub(a);
  t.normalize();
  
  // calculate the two perpendicular directions
  PVector x = PVector.sub(a, b);
  PVector p1 = new PVector(-x.y, x.x);
  PVector p2 = x.cross(p1);
  p1.normalize();
  p2.normalize();
  
  translate(width/2,height/2);  // move (0, 0, 0) to centre of window
  
  // Draw a line that goes from initial point a
  // in direction of b
  // with a length of m
  stroke(0, 0, 0);
  line(a.x, a.y, a.z, a.x+t.x*m, a.y+t.y*m, a.z+t.z*m);
  
  // Draw the other perpendicular line at point a
  stroke(0, 0, 255);
  line(a.x, a.y, a.z, a.x+p2.x*m, a.y+p2.y*m, a.z+p2.z*m);
  // Draw the other perpendicular line at point b
  stroke(0, 0, 255);
  line(b.x, b.y, b.z, b.x+p2.x*m, b.y+p2.y*m, b.z+p2.z*m);
  
  // complete the other square
  stroke(0, 255, 0);
  line(a.x+p2.x*m, a.y+p2.y*m, a.z+p2.z*m, b.x+p2.x*m, b.y+p2.y*m, b.z+p2.z*m);
  
}
2 Likes

In terms of readability and reusability it would be great if you could make a function makeParallelLine modifying its parameters (2 PVectors).

Then draw() is shorter and nicer

  • PVector.sub(a, b); creates a new PVector and returns it, w/o mutating neither passed objects.
  • a.sub(b); mutates the caller PVector object w/ the result; which is a in this case.
  • BtW, a.sub(b); is equivalent to PVector.sub(a, b, a);.
  • A good strategy is to have some “result” PVector global variable to be used as 3rd argument.
  • This way we can avoid creating unnecessary PVector objects.
4 Likes

@GoToLoop Thanks! I get it now. The word ‘mutates’ did it.

Like this?

PVector a, b;
float m;
  
void setup() {
  size(400, 400, P3D);

  a = new PVector(0, 0, 0); // initial point
  b = new PVector(60, -30, 0); // point
  
  m = PVector.dist(a, b);
  
}

void draw() {
  background(255);
  noLoop();

  translate(width/2,height/2);  // move (0, 0, 0) to centre of window
  makeParallelLines(a, b);
}

void makeParallelLines(PVector a, PVector b) {
  // calculate the direction from a to b
  PVector t = b.copy();
  t.sub(a);
  t.normalize();
  
  // calculate the perpendicular
  PVector x = PVector.sub(a, b);
  PVector p = new PVector(-x.y, x.x);
  p.normalize();
  line(a.x, a.y, a.z, a.x+t.x*m, a.y+t.y*m, a.z+t.z*m);  // first line
  line(a.x+p.x*m, a.y+p.y*m, a.z+p.z*m, b.x+p.x*m, b.y+p.y*m, b.z+p.z*m);
 
}
1 Like

Not quite.

For example the variable m is only needed in the
function so you could move it there entirely.

Also, the line commands do not really belong into the function.

The line would be in draw, using a and b.

(The initial a and b should also be parameters.)

Example:

PVector a, b;

void setup() {
  size(400, 400, P3D);

  a = new PVector(0, 0, 0); // 1st initial point
  b = new PVector(60, -30, 0); // 2nd point
}

void draw() {
  background(255);
  noLoop();

  stroke(0);
  translate(width/2, height/2);  // move (0, 0, 0) to centre of window

  // draw intial line
  linePVector(a, b);

  // calc 2 new points and store them in a and b (destoying the former values
  modifyTwoPVectorsToGetParallelLines(a, b);

  // draw new line
  linePVector(a, b);
}

// ----------------------------------------------------------------------------------------------

void modifyTwoPVectorsToGetParallelLines(PVector a, PVector b) {
  // calc dist
  float m = PVector.dist(a, b);

  // calculate the perpendicular
  PVector diff = PVector.sub(a, b);
  PVector perpendicular = new PVector(-diff.y, diff.x);
  perpendicular.normalize();

  perpendicular.mult(m);

  a.add(perpendicular);
  b.add(perpendicular);
}

// ----------------------------------------------------------------------------------------------

void linePVector( PVector pv1, PVector pv2) {
  line (pv1.x, pv1.y, pv1.z,
    pv2.x, pv2.y, pv2.z);
}
//

This is excellent, thank you @Chrisir . I read the text but somehow missed your example. So I have been working on my inferior version. I will share where I am now, and I have tried to add the second perpendicular parallel line.

Now we are ready for the real challenge!

We have these two squares delimited by our parallel lines. The two squares are perpendicular. There are 90° (PI/2) between the two squares. The rotation is around the initial line (ab).

Now, I want to create a quadrilateral that is some angle (more than 0° and less than 90°) that lies between these two limits; as if one of the two squares had rotated around the axis (ab).

I have three approaches in mind.

A Using the scaffolding we have, it would be easy enough to add a shape to the end of the initial line and then the rotated quadrilateral can hopefully be found with some simple trigonometry.

B If instead of using the cross method we used the cross product formula (magnitude of a, magnitude of b, sine of an angle A, unit vector) then perhaps we could vary the angle A.

C …

Can this be done?

PVector a, b, c, d;

void setup() {
  size(400, 400, P3D);

  a = new PVector(-100.0, -100.0, -100.0); // 1st initial point
  b = new PVector(-100.0, 100.0, -100.0); // 2nd point
}

void draw() {
  background(255);
  noLoop();

  stroke(0);
  translate(width/2, height/2);  // move (0, 0, 0) to centre of window
  
  c = a.copy(); // fork off for second perpendicular parallel line
  d = b.copy();

  // draw initial line
  stroke(0, 0, 0);  // black
  linePVector(a, b);

  // calc 2 new points and store them in a, b and c,d (destroying the former values)
  modifyTwoPVectorsToGetParallelLine1(a, b);
  modifyTwoPVectorsToGetParallelLine2(c, d);

  // draw new line
  stroke(0, 255, 0);  // green
  linePVector(a, b);
  stroke(0, 0, 255);  // blue
  linePVector(c, d);
}

// ----------------------------------------------------------------------------------------------

void modifyTwoPVectorsToGetParallelLine1(PVector a, PVector b) {
  // calc dist
  float m = PVector.dist(a, b);

  // calculate the perpendicular
  PVector diff = PVector.sub(a, b);
  PVector perpendicular1 = new PVector(-diff.y, diff.x);
  perpendicular1.normalize();

  perpendicular1.mult(m);

  a.add(perpendicular1);
  b.add(perpendicular1);
}

// ----------------------------------------------------------------------------------------------

void modifyTwoPVectorsToGetParallelLine2(PVector c, PVector d) {
  // calc dist
  float m = PVector.dist(c, d);

  // calculate the perpendicular
  PVector diff = PVector.sub(c, d);
  PVector perpendicular = new PVector(-diff.y, diff.x);
  PVector perpendicular2 = diff.cross(perpendicular);
  perpendicular2.normalize();

  perpendicular2.mult(m);

  c.add(perpendicular2);
  d.add(perpendicular2);
}

// ----------------------------------------------------------------------------------------------

void linePVector( PVector pv1, PVector pv2) {
  line (pv1.x, pv1.y, pv1.z,
    pv2.x, pv2.y, pv2.z);
}
//

Hello @paulstgeorge,

Thanks for this topic! Refreshing my math and Processing helps visualize concepts… especially cross and dot products.

I gleaned some concepts to create local planes from:

And was able to create infinite parallel lines:

Other resources I found useful:

Can you draw a quick 2D sketch (paper or Processing) to illustrate what you are trying to achieve?

:)

Well @glv hello, here is a quick sketch in Processing. I was thinking it would be cooler to use the cross product formula and vary the angle and so the Sine.

PVector a, b, c;
PVector middle;
float angle = 0.5;
float m;

void setup() {
  size(400, 400, P3D);

  a = new PVector(0, 0, 0);
  b = new PVector(100, 100, 0);
  c = new PVector(100, -100, 0);

  m = PVector.dist(a, b);
}

void draw() {
  background(255);
  strokeWeight(1);
  //noLoop();
  //println(mouseX);
  angle = 1-(mouseY/float(height));

  translate(width/2, height/2);  // move (0, 0, 0) to centre of window


  stroke(0, 0, 0);

  line(a.x, a.y, a.z, b.x, b.y, b.z);
  line(a.x, a.y, a.z, c.x, c.y, c.z);


  middle = PVector.lerp(b, c, angle);
  middle.normalize();
  middle.mult(m);
  stroke(0, 0, 127);
  line(a.x, a.y, a.z, middle.x, middle.y, middle.z);

  pushMatrix(); // move (0, 0, 0) back to centre of window
  translate(a.x, a.y, a.z);
  noFill();
  stroke(0);
  sphere(4);
  popMatrix();

  pushMatrix(); 
  translate(b.x, b.y, b.z);
  noFill();
  stroke(0);
  sphere(4);
  popMatrix();

  pushMatrix(); 
  translate(c.x, c.y, c.z);
  noFill();
  stroke(0);
  sphere(4);
  popMatrix();

  pushMatrix(); 
  translate(middle.x, middle.y, middle.z);
  noFill();
  stroke(127);
  sphere(4);
  popMatrix();
}

I will start a new topic for using vectors to find the line between two lines.

Looking at Points on the surface of a Sphere using beginShape(POINTS or QUADS) - #6 by glv
I think @companje, @Chrisir , and @glv will have a better way to this.