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};
}
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)?
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));
I think I am close to an answer.
This is my approach
get a perpendicular meeting point from the intersection and end of line2.
Make a line(start: end of line2, end: perpendicular meeting point), then extend the line double amount to get the endpoint.
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);
Related
I need to find the midpoint of the arc USING JavaScript
.
I want to find M in terms of the following information:
A.X and A.Y, the coordinates of A
B.X and B.Y, the coordinates of B
Radius, the radius of the arc
C.X and C.Y, the center of the arc
How do I compute the coordinates of M?
I have a problem with the x sign
var a = {x:x1,y:y1}
var b = {x:x2,y:y2}
var c = {x:cx,y:cy}
var theta1 = Math.atan(a.y / a.y);
var theta2 = Math.atan(b.y / b.x)
var theta = (theta1 + theta2) / 2;
var mx = r * Math.cos(theta);
var my = r * Math.sin(theta);
var positive
if (cx > 0) {
positive = 1
} else {
positive = -1
}
var midx = positive * (Math.abs(mx) + Math.abs(cx))
var midy = my + cy
writeBlock(cx, cy);
writeBlock(mx, my, x1, y1, x2, y2);
Here's how I would do it, using a unit circle to make things simple:
const A = { x: 0, y: 1 };
const B = { x: 1, y: 0 };
const C = { x: 0, y: 0 };
// get A and B as vectors relative to C
const vA = { x: A.x - C.x, y: A.y - C.y };
const vB = { x: B.x - C.x, y: B.y - C.y };
// angle between A and B
const angle = Math.atan2(vA.y, vA.x) - Math.atan2(vB.y, vB.x);
// half of that
const half = angle / 2;
// rotate point B by half of the angle
const s = Math.sin(half);
const c = Math.cos(half);
const xnew = vB.x * c - vB.y * s;
const ynew = vB.x * s + vB.y * c;
// midpoint is new coords plus C
const midpoint = { x: xnew + C.x, y: ynew + C.y };
console.log(midpoint); // { x: sqrt2 / 2, y: sqrt2 / 2 }
Please note that this assumes that point B is always "after" A (going clockwise) and it always assumes the arc is defined clockwise.
Sum CA and CB vectors, making bisector D
Normalize bisector dividing by its length
Multiply normalized bisector by R
Add result to C to get M
Dx = A.x + B.x - 2*C.x
Dy = A.y + B.y - 2*C.y
Len = sqrt(Dx*Dx + Dy*Dy)
f = R / Len
Mx = C.x + Dx * f
My = C.y + Dy * f
(doesn't work for 180 degrees arc, for that case just rotate Dx by 90)
It's apparently very hard to find the answer to whether a line segment and circle intersect. For example, if you google you'll find this question and even the top two answers seem wrong.
The accepted answer has a comment saying: This seems to compute the intersection of a circle with a line, not a segment Scroll down to the next answer and you'll find another comment saying Isn't this answer in incomplete? It finds whether a circle and line intersect, not a line segment.
I've then tried to search for a function for determining if just a segment intersects a circle, but to no avail. The best I could find is a pseudocode explanation here.
I've tried to adapt his code and while it seems to work, it seems overly verbose and I'm not sure if my implementation is correct. I'm asking whether or not this is correct and if it is, is there indeed no better way of determining this? What is the ideal way of determining if a line segment and circle intersects? Please note, I only need to know if they intersect, not where they intersect.
I've provided a full demo reproduction below so you can also visualize it.
function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
let deltaX = x2 - x1;
let deltaY = y2 - y1;
let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
let unitX = deltaX / mag;
let unitY = deltaY / mag;
let d = (cx - x1) * unitY - (cy - y1) * unitX;
if (d < -r || d > r) { return false; }
let x1CXDelta = x1 - cx;
let y1CYDelta = y1 - cy;
let x2CXDelta = x2 - cx;
let y2CYDelta = y2 - cy;
let pointOneWithinCircle = x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
if (pointOneWithinCircle) { return true; }
let pointTwoWithinCircle = x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
if (pointTwoWithinCircle) { return true; }
let foo = unitX * x1 + unitY * y1;
let bar = unitX * cx + unitY * cy;
let baz = unitX * x2 + unitY * y2;
return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}
let ctx = document.querySelector("canvas").getContext("2d");
function drawCircle(xCenter, yCenter, radius) {
ctx.beginPath();
ctx.arc(xCenter, yCenter, radius, 0, 2 * Math.PI);
ctx.fill();
}
function drawLine(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
let circleX = 340;
let circleY = 250;
let circleR = 60;
let lineX1 = 50;
let lineY1 = 350;
let lineX2 = 285;
let lineY2 = 250;
draw = () => {
ctx.fillStyle = "#b2c7ef";
ctx.fillRect(0, 0, 800, 800);
ctx.fillStyle = "#ffffff";
drawCircle(circleX, circleY, circleR);
drawLine(lineX1, lineY1, lineX2, lineY2);
}
console.log(lineSegmentIntersectsCircle(lineX1, lineY1, lineX2, lineY2, circleX, circleY, circleR))
draw();
canvas { display: flex; margin: 0 auto; }
<canvas width="400" height="400"></canvas>
I think it would be a simpler to (1) compute the line-disk intersection, which is either empty, a point, or a line segment (2) test whether the intersection intersects the line segment.
The points of the line are ((1-t) x1 + t x2, (1-t) y1 + t y2) for all real t. Let x(t) = (1-t) x1 + t x2 - cx and y(t) = (1-t) y1 + t y2 - cy. The line-disk intersection is nonempty if and only if the polynomial x(t)^2 + y(t)^2 - r^2 = 0 has real roots t1 <= t2. In this case, the line-disk intersection is all t in [t1, t2]. The line segment is all t in [0, 1]. The two intersect if and only if t1 <= 1 and t2 >= 0.
Computing t1 and t2 requires a square root, which we can avoid. Let a, b, c be such that x(t)^2 + y(t)^2 - r^2 = a t^2 + b t + c. We have t1 + t2 = -b/a and t1 t2 = c/a.
The roots t1 and t2 are real if and only if b^2 - 4 a c >= 0.
The condition t1 <= 1 is false if and only if t1 - 1 > 0 and t2 - 1 > 0, which in turn is true if and only if (t1 - 1) + (t2 - 1) > 0 and (t1 - 1) (t2 - 1) > 0, which is equivalent to -b/a - 2 > 0 and c/a + b/a + 1 > 0. Since a > 0, this simplifies to -b > 2 a and c + b + a > 0.
The condition t2 >= 0 is false if and only if t1 < 0 and t2 < 0, which in turn is true if and only if t1 + t2 = -b/a < 0 and t1 t2 = c/a > 0. Since a > 0, this simplifies to b > 0 and c > 0.
Implementation in Javascript.
function lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r) {
let x_linear = x2 - x1;
let x_constant = x1 - cx;
let y_linear = y2 - y1;
let y_constant = y1 - cy;
let a = x_linear * x_linear + y_linear * y_linear;
let half_b = x_linear * x_constant + y_linear * y_constant;
let c = x_constant * x_constant + y_constant * y_constant - r * r;
return (
half_b * half_b >= a * c &&
(-half_b <= a || c + half_b + half_b + a <= 0) &&
(half_b <= 0 || c <= 0)
);
}
function lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) {
let deltaX = x2 - x1;
let deltaY = y2 - y1;
let mag = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
let unitX = deltaX / mag;
let unitY = deltaY / mag;
let d = (cx - x1) * unitY - (cy - y1) * unitX;
if (d < -r || d > r) {
return false;
}
let x1CXDelta = x1 - cx;
let y1CYDelta = y1 - cy;
let x2CXDelta = x2 - cx;
let y2CYDelta = y2 - cy;
let pointOneWithinCircle =
x1CXDelta * x1CXDelta + y1CYDelta * y1CYDelta < r * r;
if (pointOneWithinCircle) {
return true;
}
let pointTwoWithinCircle =
x2CXDelta * x2CXDelta + y2CYDelta * y2CYDelta < r * r;
if (pointTwoWithinCircle) {
return true;
}
let foo = unitX * x1 + unitY * y1;
let bar = unitX * cx + unitY * cy;
let baz = unitX * x2 + unitY * y2;
return (foo < bar && bar < baz) || (baz < bar && bar < foo);
}
function test() {
for (let i = 0; i < 10000000; i++) {
let x1 = Math.random();
let y1 = Math.random();
let x2 = Math.random();
let y2 = Math.random();
let cx = Math.random();
let cy = Math.random();
let r = Math.random();
if (
lineSegmentIntersectsCircle(x1, y1, x2, y2, cx, cy, r) !=
lineSegmentIntersectsCircleOptimized(x1, y1, x2, y2, cx, cy, r)
) {
console.log("bad");
break;
}
}
}
test();
for a game I'm building, I need to draw a rectangle on two sides of a line made from two coordinates.
I have an image illustrating this "hard to ask" question.
given coordinates (-4,3) and (3, -4)
given that the width of the rectangle will be 4 (for example)
I need to find all (x1, y1), (x2, y2), (x3, y3), (x4, y4)
** I need to write this in Javascript eventually.
your help is much appreciated.
I've tried to solve this using javascript & canvas. The problem is that the coordinates in canvas are upside down, I suppose you already know this. Also since your rect would be extremely small, I've multiplied your numbers by 10.
const canvas = document.querySelector("canvas");
const ctx = canvas.getContext("2d");
let cw = canvas.width = 300,
cx = cw / 2;
let ch = canvas.height = 300,
cy = ch / 2;
const rad = Math.PI / 180;
ctx.translate(cx,cy)
//axis
ctx.strokeStyle = "#d9d9d9";
ctx.beginPath();
ctx.moveTo(-cx,0);
ctx.lineTo(cx,0);
ctx.moveTo(0,-cy);
ctx.lineTo(0,cy);
ctx.stroke();
// your data
let p1={x:-40,y:30};
let p2={x:30,y:-40};
// the angle of the initial line
let angle = Math.atan2(p2.y-p1.y, p2.x-p1.x);
// the center of the line
let c =
{ x: p1.x + (p2.x - p1.x)/2,
y: p1.y + (p2.y - p1.y)/2
}
let w = dist(p1, p2);//the width of the rect
let h = 60;//the height of the rect
// draw the initial line
line(p1,p2);
// draw the center as a red point
marker(c);
// calculate the opoints of the rect
function rectPoints(w,h){
let p1 = {
x : c.x -w/2,
y : c.y -h/2
}
let p2 = {
x : c.x + w/2,
y : c.y -h/2
}
let p3 = {
x : c.x + w/2,
y : c.y +h/2
}
let p4 = {
x : c.x -w/2,
y : c.y +h/2
}
// this rotate all the points relative to the center c
return [
rotate(p1,c, angle),
rotate(p2,c, angle),
rotate(p3,c, angle),
rotate(p4,c, angle)
]
}
// draw the rect
ctx.strokeStyle = "blue";
drawRect(rectPoints(w,h));
// some helpful functions
function line(p1,p2){
ctx.beginPath();
ctx.moveTo(p1.x,p1.y);
ctx.lineTo(p2.x,p2.y);
ctx.stroke();
}
function dist(p1, p2) {
let dx = p2.x - p1.x;
let dy = p2.y - p1.y;
return Math.sqrt(dx * dx + dy * dy);
}
function marker(p,color){
ctx.beginPath();
ctx.fillStyle = color || "red";
ctx.arc(p.x,p.y,4,0,2*Math.PI);
ctx.fill();
}
function rotate(p,c, angle){
let cos = Math.cos(angle);
let sin = Math.sin(angle);
return {
x: c.x + (p.x - c.x) * cos - (p.y - c.y) * sin,
y: c.y + (p.x - c.x) * sin + (p.y - c.y) * cos
}
}
function drawRect(points){
ctx.beginPath();
ctx.moveTo(points[0].x,points[0].y);
ctx.lineTo(points[1].x,points[1].y);
ctx.lineTo(points[2].x,points[2].y);
ctx.lineTo(points[3].x,points[3].y);
ctx.lineTo(points[0].x,points[0].y);
ctx.closePath();
ctx.stroke();
}
canvas{border:1px solid #d9d9d9}
<canvas></canvas>
Points A, B form vector
M.X = B.X - A.X
M.Y = B.Y - A.Y
Perpendicular vector
P.X = -M.Y
P.Y = M.X
Length of P:
Len = Math.sqrt(P.X*P.X + P.Y*P.Y)
Normalized (unit) perpendicular:
uP.X = P.X / Len
uP.Y = P.Y / Len
Points
X1 = A.X + uP.X * HalfWidth
Y1 = A.Y + uP.Y * HalfWidth
(X4, Y4) = (A.X - uP.X * HalfWidth, A.Y - uP.Y * HalfWidth)
and similar for points 2 and 3 around B
calculate new points(x3,y3),(x4,y4) of the line(x1,y1),(x2,y2) after shifting d distance and it should always parallel
Something like this?
function lineOffset(x1, y1, x2, y2, d){
// delta between points
var dx = x2 - x1;
var dy = y2 - y1;
// gradient of line segment
var dx_dy = dx / dy;
// inverse (perpendiular line)
var dy_dx = 1 / dx_dy;
// point inbetween P1 and P2
var x12 = (x1 + x2) / 2;
var y12 = (y1 + y2) / 2;
// point inbetween P3 and P4
var x34 = x12 + (d * Math.cos(dy_dx));
var y34 = y12 + (d * Math.sin(dy_dx));
// sub 1/2 the line length to get P3
var x3 = x34 - (dx / 2);
var y3 = y34 - (dy / 2);
// add 1/2 the line length to get P4
var x4 = x34 + (dx / 2);
var y4 = y34 + (dy / 2);
return {
x3: x3,
y3: y3,
x4: x4,
y4: y4
}
}
I want to get all the x,y coordinates between 2 given points, on a straight line.
While this seems like such an easy task, I can't seem to get my head around it.
So, for example:
Point 1: (10,5)
Point 2: (15,90)
Edit: The solution below only applies from a geometrical point of view. Drawing on a screen is different than theoretical geometry, you should listen to the people suggesting Bresenham's algorithm.
Given, two points, and knowing that the line's equation is y = m*x + b, where m is the slope and b the intercept, you can calculate m and b and then apply the equation to all the values of the X axis between your A and B points:
var A = [10, 5];
var B = [15, 90];
function slope(a, b) {
if (a[0] == b[0]) {
return null;
}
return (b[1] - a[1]) / (b[0] - a[0]);
}
function intercept(point, slope) {
if (slope === null) {
// vertical line
return point[0];
}
return point[1] - slope * point[0];
}
var m = slope(A, B);
var b = intercept(A, m);
var coordinates = [];
for (var x = A[0]; x <= B[0]; x++) {
var y = m * x + b;
coordinates.push([x, y]);
}
console.log(coordinates); // [[10, 5], [11, 22], [12, 39], [13, 56], [14, 73], [15, 90]]
Given the point A(10, 5) and B(15, 90) and C(x, y) in AB we have:
(x - 10) / (y - 5) = (15 - 10) / (90 - 5)
What you can do is to iterate from x=10 to x=15 and calculate the corresponding y. Since x and y are integers, some times you have to round the result (or skip it).
Based on the wiki article, here's my JS code which handles both high and low lines:
const drawLine = (x0, y0, x1, y1) => {
const lineLow = (x0, y0, x1, y1) => {
const dx = x1 - x0
let dy = y1 - y0
let yi = 1
if (dy < 0) {
yi = -1
dy = -dy
}
let D = 2 * dy - dx
let y = y0
for (let x = x0; x < x1; x++) {
drawPoint(x, y)
if (D > 0) {
y = y + yi
D = D - 2 * dx
}
D = D + 2 * dy
}
}
const lineHigh = (x0, y0, x1, y1) => {
let dx = x1 - x0
const dy = y1 - y0
let xi = 1
if (dx < 0) {
xi = -1
dx = -dx
}
let D = 2 * dx - dy
let x = x0
for (let y = y0; y < y1; y++) {
drawPoint(x, y)
if (D > 0) {
x = x + xi
D = D - 2 * dy
}
D = D + 2 * dx
}
}
const { abs } = Math
if (abs(y1 - y0) < abs(x1 - x0)) {
if (x0 > x1) {
lineLow(x1, y1, x0, y0)
} else {
lineLow(x0, y0, x1, y1)
}
} else {
if (y0 > y1) {
lineHigh(x1, y1, x0, y0)
} else {
lineHigh(x0, y0, x1, y1)
}
}
}