allenhwkim
allenhwkim

Reputation: 27748

Reflection end point of intersecting two lines

How do I calculate the bounced end point of intersecting two lines?

When given the following two lines, I was able to calculate intersection point of two lines with the following function.

export function intersection(line1, line2) {
  line1.y1 *= -1; line1.y2 *= -1; // for Webpage coordinates
  line2.y3 *= -1; line2.y4 *= -1; // for Webpage coordinates

  const [x1, y1, x2, y2] = line1;
  const [x3, y3, x4, y4] = line2;

  const  [a1, b1, c1] = [y2 - y1, x1 - x2, x2 * y1 - x1 * y2];
  if ( a1 === 0 && b1 === 0 ) return 'line1 does not have length';

  const [a2, b2, c2] = [y4 - y3, x3 - x4, x4 * y3 - x3 * y4];
  if ( a2 === 0 && b2 === 0 ) return 'line2 does not have length';
  
  const denom = a1 * b2 - a2 * b1;
  if (denom === 0) return 'lines are parallel';

  const x = (b1 * c2 - b2 * c1) / denom;   // (x,y) is the intersection
  const y = (a2 * c1 - a1 * c2) / denom;

  // check if two lines are actually crossing w/o extending it
  function getDist(x1, y1, x2, y2) {
    return Math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2);
  }
  const distLine1 = getDist(x1, y1, x2, y2);
  const distLine2 = getDist(x3, y3, x4, y4);
  const distToXY1  = Math.max(getDist(x1, y1, x, y), getDist(x2, y2, x, y)) ;
  const distToXY2  = Math.max(getDist(x3, y3, x, y), getDist(x4, y4, x, y)) ;
  if (distToXY1 > distLine1 || distToXY2 > distLine2) 
    return 'lines does not meet';

  return {x, y};
}

enter image description here DEMO: https://stackblitz.com/edit/intersection-of-two-lines?file=index.js

However, I am struggling to find a bounced-off position(or reflection point) of two lines using two lines.

What's the formula of getting x/y position from line1(x1, x2, y1, y2) and line2(x1, x2, y1, y2)?

Upvotes: 1

Views: 291

Answers (2)

allenhwkim
allenhwkim

Reputation: 27748

I think I am close to an answer.

This is my approach

  1. get a perpendicular meeting point from the intersection and end of line2.
  2. Make a line(start: end of line2, end: perpendicular meeting point), then extend the line double amount to get the endpoint.

enter image description here https://stackblitz.com/edit/intersection-of-two-lines-nes7zy?view=preview

In the demo, this approach looks fine, If a math expert can confirm this approach, it would be appreciated.

// https://stackoverflow.com/a/1811636/454252
function getPerpendicularPoint(iPoint, line1, line2) {
  const [x1, y1] = [iPoint.x, iPoint.y]; // start point
  const [x2, y2] = [line1[2], line1[3]]; // baseline end point
  const [x3, y3] = [line2[2], line2[3]]; // extended line end point

  const k = ((y2-y1) * (x3-x1) - (x2-x1) * (y3-y1)) / ((y2-y1)**2 + (x2-x1)**2);
  const x4 = x3 - k * (y2-y1);
  const y4 = y3 + k * (x2-x1);
  return {x: x4, y: y4};
}

//http://www.java2s.com/Tutorials/Javascript/Canvas_How_to/Shape/Extend_a_line_before_and_after_original_endpoints.htm
function getExtendedPoint(startPt, endPt, extent) {
  var dx = endPt.x - startPt.x;
  var dy = endPt.y - startPt.y;
  var x = startPt.x + dx * extent;
  var y = startPt.y + dy * extent;
  return {x, y};
}

let line1 = [100, 100, 400, 300];
let line2 = [100, 300, 400, 100];

const iPoint = {x: 250, y: 200}; // intersection(line1, line2);
const pPoint = getPerpendicularPoint(iPoint, line1, line2);
const ePoint = getExtendedPoint({x:line2[2], y:line2[3]}, pPoint, 2);

console.log(pPoint, ePoint);

Upvotes: 0

user5734311
user5734311

Reputation:

You have to split the part of the red vector that's "inside the wall" into two components, one parallel to the wall, the other perpendicular to it. Then you negate the perpendicular component and add them back together.

class Line {
  constructor(x1, y1, x2, y2) {
    this.x1 = x1;
    this.y1 = y1;
    this.x2 = x2;
    this.y2 = y2;
    this.dx = x2 - x1;
    this.dy = y2 - y1;
  }
  intersect(o) {
    const d = this.dy * o.dx - this.dx * o.dy;
    if (d === 0) return null; // parallel
    const k = ((o.y1 - this.y1) * o.dx + (this.x1 - o.x1) * o.dy) / d;
    return {
      x: this.x1 + k * this.dx,
      y: this.y1 + k * this.dy
    };
  }
  bounce(o) {
    const i = this.intersect(o);
    if (!i) return null;
    // vector from intersection to end
    const v = {
      x: this.x2 - i.x,
      y: this.y2 - i.y
    };
    // split v into two perpendicular vectors, one parallel to o
    // the perpendicular one is o's direction rotated by 90°
    const p = { x: o.dy, y: -o.dx };
    // v.x = a * o.dx + b * p.x
    // v.y = a * o.dy + b * p.y
    // division by zero is impossible since o and p are perpendicular
    const a = (v.y * p.x - v.x * p.y) / (o.dy * p.x - o.dx * p.y);
    const b = (v.y * o.dx - v.x * o.dy) / (p.y * o.dx - p.x * o.dy);
    // negate b to mirror end of line on o
    return {
        x: i.x + a * o.dx - b * p.x,
      y: i.y + a * o.dy - b * p.y
    }
  }
}

const l1 = new Line(100, 300, 400, 100);
const l2 = new Line(100, 100, 400, 300);
console.log(l1.bounce(l2));

Upvotes: 2

Related Questions