Using vectors to find the line between two lines

I can do this (see sketch) with lerp(), but it cannot be the best way. Using lerp() and starting at the desired angle, one would have to use tan(), then solve two equations before knowing what value to give lerp(). Then I would need to lengthen the line. All doable, but inelegant.

I am guessing the cross formula is the way to go, but I could see further if I stood on the shoulders of giants.

lerp1

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

Hello @paulstgeorge,

This will display a line between two vectors but not return the vector:

//https://github.com/processing/processing4/blob/main/core/src/processing/core/PApplet.java#L12888

PVector a, b, c, ax, v;

float x, y, z;

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

 a = new PVector(0, 0, 0);
 b = new PVector(100, 100, 50);
 c = new PVector(100, -100, -50);
 ax = new PVector(0, 0, 0);
 v = new PVector(0, 0, 0);
 }

void draw() 
 {
 background(255);

 float angleBetween = PVector.angleBetween(b, c);
 float angle = map(mouseX, 0, width, 0, angleBetween);

 translate(width/2, height/2);  // move (0, 0, 0) to centre of window
 rotateY(frameCount%360*(TAU/360)); // This will mess with modelX below

 stroke(0);
 strokeWeight(2);

 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);
 
 ax = b.cross(c).normalize(); //normalize may not be needed

 // Rotates a line about an axis
 stroke(255, 128, 0);
 pushMatrix();
 rotV(angle, ax);
 line(a.x, a.y, a.z, b.x, b.y, b.z);
 popMatrix();
 }
 
void rotV(float angle, PVector axis) 
 {
 rotate(angle, axis.x, axis.y, axis.z);
 }

References:

This gem is not in the on-line references:
https://github.com/processing/processing4/blob/main/core/src/processing/core/PApplet.java#L12888

:)

A gem indeed. Well found @glv
I actually want the vector. This is currently rotating the display of line AB. Isn’t there one or two lines of code we can add to the sketch below to find the new vector?

PVector a, b, c, ax;

float x, y, z, angle;

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

  a = new PVector(0, 0, 0);
  b = new PVector(100, 100, 50);
  c = new PVector(100, -100, -50);
  ax = new PVector(0, 0, 0);
  angle = radians(60);
}

void draw()
{
  background(255);
  translate(width/2, height/2);  // move (0, 0, 0) to centre of window
  stroke(0);
  strokeWeight(2);

  line(a.x, a.y, a.z, b.x, b.y, b.z);  // static line ab
  line(a.x, a.y, a.z, c.x, c.y, c.z);  // static line ac

  //step 1
  ax = b.cross(c);
  
  // Rotates a line about an axis
  stroke(255, 128, 0);
  pushMatrix();
  rotV(angle, ax); //step 2
  line(a.x, a.y, a.z, b.x, b.y, b.z);  // the line ab rotated in display
  popMatrix();

  // a blob for identifying vector points
  pushMatrix();
  translate(a.x, a.y, a.z);
  noFill();
  stroke(127);
  //sphere(4);
  popMatrix();
}

void rotV(float angle, PVector axis)   //step 3
{
  rotate(angle, axis.x, axis.y, axis.z);
}

Hello again!

I do not have a simple solution… and very little time these days to explore.

A search revealed some possible solutions for consideration:

I have used your initial approach in the past for drawing arcs on a sphere.

:)

Hello again!

If you are using transformations you can use get the x, y, z coordinates with the modelX(), modelY(), modelZ() functions.
See references for details.

Example:

PVector a, b, c, ax;
float x, y, z, angle;

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

  a = new PVector(0, 0, 0);
  b = new PVector(100, 100, 60);
  c = new PVector(100, -100, -60);
  ax = new PVector(0, 0, 0);
  angle = radians(60);
  }

void draw()
  {
  background(255);
  translate(width/2, height/2);  // move (0, 0, 0) to center of window
  
  ax = b.cross(c);
 
 // Just for fun! Can uncomment these to animate!
  //float aY = (frameCount%360)*(TAU/360); // One degree per frame
  //rotateY(aY);
  //angle = PVector.angleBetween(b, c)/2*sin(aY) + PVector.angleBetween(b, c)/2;
  
  stroke(0);
  strokeWeight(3);
  line(a.x, a.y, a.z, b.x, b.y, b.z);  // static line ab
  line(a.x, a.y, a.z, c.x, c.y, c.z);  // static line ac
  
  // Rotates a line about an axis and returns PVector
 pushMatrix();
  resetMatrix(); //otherwise this includes the transformations above
  rotV(angle, ax);
  PVector v = new PVector(modelX(b.x, b.y, b.z), 
                          modelY(b.x, b.y, b.z), 
                          modelZ(b.x, b.y, b.z));
  println(v);
 popMatrix();
  
  stroke(255, 0, 0);
  line(a.x, a.y, a.z, v.x, v.y, v.z);  // the line ab rotated in display
  }

void rotV(float angle, PVector axis)
  {
  rotate(angle, axis.x, axis.y, axis.z);
  }

@paulstgeorge Consider refactoring into a single function to rotate PVector and return PVector.

:)

1 Like

Maybe I’m misunderstanding the question, but it sounds like you want to do vector addition.

In your example a is at the origin right, so you’d just be adding b and c

1 Like

Yes @corvanessen , ‘addition’ works very well.

but it bisects the angle

How do I use ‘add’ to choose the ratio of division between the two lines?

so, for example if there were 90° between AC and AB how would I find the line AM that is 30° from AC and 60° from AD.

Adding the two vectors gives me 45° in this example.

1 Like

Solved! I just add the two vectors, but I do not give them equal weight. First multiply each of the two vectors according to the desired ratio. The full sketch is below, here is the pertinent detail:

  b2 = b.copy();
  b2.mult(sin(angle));
  c2 = c.copy();
  c2.mult(cos(angle));

  v3 = PVector.add(b2, c2);
  v3.normalize();
  v3.mult(m);

Sketch:

PVector a, b, c, b2, c2, v3;

float m, dangle, angle;


void setup() {
  size(800, 800, 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);

  textSize(64);

  dangle = 90 * (mouseY/float(height)); // 0.0 at top, 90.0 at bottom
  angle = dangle * PI /180; // radians


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

  b2 = b.copy();
  b2.mult(sin(angle));
  c2 = c.copy();
  c2.mult(cos(angle));

  v3 = PVector.add(b2, c2);
  v3.normalize();
  v3.mult(m);

  line(a.x, a.y, a.z, v3.x, v3.y, v3.z);

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

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

  fill(0, 408, 612, 204);
  text(int(dangle), 40, 360);
}
2 Likes

This topic was engaging!

Another twist on this that was inspired by the discussions:

PVector a, b, c, b2, c2, v3;

float m, dangle, angle;

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

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

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

void draw() 
  {
  background(0);

  float ratio = float(mouseY)/height; 
  println(ratio);
 push();
  translate(width/2, height/2);  // move (0, 0, 0) to centre of window
  rotateY((frameCount%360)*(TAU/360));

  stroke(128);
  strokeWeight(2);
  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);

  //b2 = b.copy();
  //b2.mult(sin(angle));
  //c2 = c.copy();
  //c2.mult(cos(angle));
  
  //Replaces above
  b2 = b.copy();
  b2.mult(ratio);
  c2 = c.copy();
  c2.mult(1-ratio);
  println(b2, c2);
  
  strokeWeight(5);
  stroke(255, 0, 0);
  line(a.x, a.y, a.z, b2.x, b2.y, b2.z);
  stroke(0, 255, 0);
  line(a.x, a.y, a.z, c2.x, c2.y, c2.z);

  v3 = PVector.add(b2, c2);
  //v3.normalize();
  //v3.mult(m);
  
  stroke(255, 255, 0);
  line(a.x, a.y, a.z, v3.x, v3.y, v3.z);
 pop();

  fill(0, 408, 612, 204);
  text( (int) degrees(PVector.angleBetween(b2, v3)), 40, height-40 );
  }

:)

1 Like

@glv I realise that it is not solved. As @corvanessen points out, the solution only works when a is at the origin.

I have been trying and failing to make the solution more general. Can you help?

Everything the same, still 90° between the two lines but the point a can be anywhere.

I know this is a bit late but this might do what you want. :grinning:

PVector vo, vs, ve, vr;
float t = 0, step = 0.01, dt = 1;

void setup() {
  size(512, 512);
  cursor(CROSS);
  textSize(16);
  getRandomPositions();
}

void draw() {
  background(0);
  fill(240);
  text("Hit 'n' key to get new random vectors", 20,20);
  strokeWeight(1.5);
  stroke(100, 255, 1000); // start vector colour
  line(vo.x, vo.y, vs.x, vs.y);
  stroke(255, 100, 100); // end vector colour
  line(vo.x, vo.y, ve.x, ve.y);
  // Now adjust the parametric value t to get wiper blade effect
  t += step * dt;
  if (t > 1) {
    t = 1;
    dt = -1;
  } else if (t < 0) {
    t = 0;
    dt = 1;
  }
  // Get the lerped vector
  vr = lerpBetween2Vectors(vo, vs, ve, t);
  stroke(255, 255, 100); // lerped vector colour
  line(vo.x, vo.y, vr.x, vr.y);
}

// origin is the common origin far all vectors
// t is the parametric value and should be in the range >=0 and <=1
// the function will constrain the passed parameter to this value
PVector lerpBetween2Vectors(PVector origin, PVector start, PVector end, float t) {
  PVector s = PVector.sub(start, origin);
  PVector e = PVector.sub(end, origin);
  t = constrain(t, 0, 1);
  float rx = origin.x + s.x + (e.x - s.x) * t;
  float ry = origin.y + s.y + (e.y - s.y) * t;
  float rz = origin.z + s.z + (e.z - s.z) * t;
  return  new PVector(rx, ry, rz);
}

void getRandomPositions() {
  float minX = 60, maxX = width - 2 * minX;
  float minY = 60, maxY = height - 2 * minY;
  // common origin forstart and end vectors
  vo = new PVector(random(minX, maxX), random(minY, maxY));
  // start and end vector positions
  vs = new PVector(random(minX, maxX), random(minY, maxY));
  ve = new PVector(random(minX, maxX), random(minY, maxY));
}

void keyTyped() {
  if (key == 'n') getRandomPositions();
}
4 Likes

This is impressive! I have been trying to tweak it so the two vectors vs and ve (with common origin vo) and perpendicular. Can your code be reduced so the angle between vo-vs and vo-ve is always 90°?

Yes. :grinning:

Add this method to the sketch and replace the calls to getRandomPositions in setup and keyTyped to getRandomPositions90.

void getRandomPositions90() {
  float minX = 60, maxX = width - 4 * minX;
  float minY = 60, maxY = height - 4 * minY;
  // common origin for start and end vectors
  vo = new PVector(random(minX, maxX), random(minY, maxY));
  // start and end vector positions
  vs = PVector.random2D(); // unit vector
  ve = new PVector(-vs.y, vs.x); // unit perpendiciular
  vs.mult(random(minX, maxX)).add(vo);
  ve.mult(random(minX, maxX)).add(vo);
}
1 Like