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
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)
The red circle is at a known angle of 130°, then I want to draw the navy line from the center to 130° using x and y of the red circle but it looks like I missed the calculation.
Currently, the angle of the Navy line is a reflection to the angle of the red line and if I add minus sign ➖ to *diffX * at line13, it'll work as expected but Why do I need to do that by myself, why can't the Calculations at line 10 and 13 figured out if x should be minus ➖ or plus.
I couldn't figure out where I was wrong..any help/suggestions are appreciated!
let ctx, W = innerWidth,
H = innerHeight;
// params for the red circle
let hypothenus = 100;
let knownAngle = (-130 * Math.PI) / 180;
let x = (W / 2) + Math.cos(knownAngle) * hypothenus;
let y = (H / 2) + Math.sin(knownAngle) * hypothenus;
// params for navy line
let diffX = x - (W / 2);
let diffY = (H / 2) - y;
let dist = Math.hypot(diffX, diffY); // pythagoras
let unknownAngle = -Math.atan2(diffY, diffX);
let newX = (W / 2) + Math.cos(unknownAngle) * dist;
let newY = (H / 2) + Math.sin(unknownAngle) * dist;
let angInDegree1 = ~~Math.abs(knownAngle * 180 / Math.PI);
let angInDegree2 = ~~Math.abs(unknownAngle * 180 / Math.PI) | 0;
const msg = document.getElementById("msg")
msg.innerHTML = `Hypothenus1: ${hypothenus}, angle: ${angInDegree1}<br>`;
msg.innerHTML +=`Hypothenus2: ${dist}, angle: ${angInDegree2}`;
// everything to be rendered to the screen
const update = () => {
if (ctx == null) return;
// drawing the red line
draw.line([W / 2, 0], [W / 2, H], 6, "red");
draw.line([0, H / 2], [W, H / 2], 6, "red");
// the red circle
draw.circle([x, y], 10, "red");
// draw line
draw.line([W / 2, H / 2], [newX, newY], 4, "navy");
}
// utility object for drawing
const draw = {
line(from, to, width, color) {
with(ctx) {
beginPath();
lineWidth = width;
strokeStyle = color;
moveTo(...from);
lineTo(...to);
stroke();
closePath();
}
},
circle(pos, radius, color) {
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(...pos, radius, 0, 2 * Math.PI);
ctx.fill();
ctx.closePath();
}
}
// init function
const init = () => {
ctx = document.querySelector("#cvs").getContext("2d");
W = ctx.canvas.width = innerWidth;
H = ctx.canvas.height = innerHeight;
update();
}
window.addEventListener("load", init);
<div id="msg"></div>
<canvas id="cvs"></canvas>
Seems you are using too much minuses.
At first, you define angle -130 degrees, close to -3Pi/4. Cosine and sine values for this angle are about -0.7, using hypothenus = 100, we get x =W/2-70, y = H/2-70
diffX = x - W/2 = -70
diffY = y - H/2 = -70
atan2(-70, -70) gives -2.3561 radians = -3/4*Pi = -135 degrees
When you change sign of diffY (note - diffY formula is wrong, not difX one!), you make reflection against OX axis, and change angle sign - that is why another minus before Math.atan2 is required
Corrected code:
let diffX = x - (W / 2);
let diffY = y - (H / 2);
let dist = Math.hypot(diffX, diffY); // pythagoras
let unknownAngle = Math.atan2(diffY, diffX);
void ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle [, anticlockwise]);
The canvas context 2D API ellipse() method creates an elliptical arc centered at (x, y) with the radii radiusX and radiusY. The path starts at startAngle and ends at endAngle, and travels in the direction given by anticlockwise.
How to get the axis-aligned bounding box of a ellipse with the given parameters:x, y, radiusX, radiusY, rotation, startAngle, endAngle , anticlockwise?
Two solutions
This answer contains two exact solutions it is not an approximation.
The solutions are
boundEllipseAll will find the bounds of the full ellipse. If is a lot less complex than the full solution but you need to ensure that the x radius is greater than the y radius (eg rotate the ellipse 90 deg and swap the x, y radius)
boundEllipse Will find the bounds for a segment of an ellipse. It will work for all ellipses however I have not included the CCW flag. To get the bounds for CCW ellipse swap the start and end angles.
It works by first finding the x, y coords at the start and end points calculating the min and max along each axis. It then calculates the extrema angles, first for the x axis extremes, then the y axis extremes.
If the extrema angle is between the start and end angle, the x,y position of that angle is calculated and the point tested against the min and max extent.
There is a lot of room to optimize as many of the points need only the x, or y parts, and the inner while loop in function extrema can exit early if min and max are change for the axis it is working on.
Example
The example ensures I have not made any mistakes, and uses the second solution, animating an ellipse by moving the start and end angles, rotation, and y axis radius. Drawing the bounding box and the ellipse it bounds.
Update April 2022
Example shows use of both full ellipse boundEllipseAll and ellipse segment boundEllipse.
Note that boundEllipse is only for ellipse segment where endAngle n and startAngle m fit the rule {m <= n <= m + 2Pi}
Fixed bug in boundEllipse that did not show full ellipse when endAngle == startAngle + 2 * Math.PI
const ctx = canvas.getContext("2d");
const W = 200, H= 180;
const TAU = Math.PI * 2;
const ellipse = {
x: W / 2,
y: H / 2,
rx: W / 3,
ry: W / 3,
rotate: 0,
startAng: 0,
endAng: Math.PI * 2,
dir: false,
};
function boundEllipseAll({x, y, rx, ry, rotate}) {
const xAx = Math.cos(rotate);
const xAy = Math.sin(rotate);
const w = ((rx * xAx) ** 2 + (ry * xAy) ** 2) ** 0.5;
const h = ((rx * xAy) ** 2 + (ry * xAx) ** 2) ** 0.5;
return {x: -w + x, y: -h + y, w: w * 2, h: h * 2};
}
function boundEllipse({x, y, rx, ry, rotate, startAng, endAng}) {
const normalizeAng = ang => (ang % TAU + TAU) % TAU;
const getPoint = ang => {
const cA = Math.cos(ang);
const sA = Math.sin(ang);
return [cA * rx * xAx - sA * ry * xAy, cA * rx * xAy + sA * ry * xAx];
}
const extrema = a => { // from angle
var i = 0;
while(i < 4) {
const ang = normalizeAng(a + Math.PI * (i / 2));
if ((ang > startAng && ang < endAng) || (ang + TAU > startAng && ang + TAU < endAng)) {
const [xx, yy] = getPoint(ang);
minX = Math.min(minX, xx);
maxX = Math.max(maxX, xx);
minY = Math.min(minY, yy);
maxY = Math.max(maxY, yy);
}
i ++;
}
}
// UPDATE bug fix (1) for full ellipse
const checkFull = startAng !== endAng; // Update fix (1)
startAng = normalizeAng(startAng);
endAng = normalizeAng(endAng);
(checkFull && startAng === endAng) && (endAng += TAU); // Update fix (1)
const xAx = Math.cos(rotate);
const xAy = Math.sin(rotate);
endAng += endAng < startAng ? TAU : 0;
const [sx, sy] = getPoint(startAng);
const [ex, ey] = getPoint(endAng);
var minX = Math.min(sx, ex);
var maxX = Math.max(sx, ex);
var minY = Math.min(sy, ey);
var maxY = Math.max(sy, ey);
extrema(-Math.atan((ry * xAy) / (rx * xAx))); // Add x Axis extremas
extrema(-Math.atan((rx * xAy) / (ry * xAx))); // Add y Axis extremas
return {x: minX + x, y: minY + y, w: maxX - minX, h: maxY - minY};
}
function drawExtent({x,y,w,h}) {
ctx.moveTo(x,y);
ctx.rect(x, y, w, h);
}
function drawEllipse({x, y, rx, ry, rotate, startAng, endAng, dir}) {
ctx.ellipse(x, y, rx, ry, rotate, startAng, endAng, dir);
}
function drawFullEllipse({x, y, rx, ry, rotate, dir}) {
ctx.ellipse(x, y, rx, ry, rotate, 0, TAU, dir);
}
mainLoop(0);
function mainLoop(time) {
ctx.clearRect(0, 0, W, H);
// Animate ellipse
ellipse.startAng = time / 1000;
ellipse.endAng = time / 2000;
ellipse.rotate = Math.cos(time / 14000) * Math.PI * 2;
ellipse.ry = Math.cos(time / 6000) * (W / 4 - 10) + (W / 4);
// Draw full ellipse and bounding box.
ctx.strokeStyle = "#F008";
ctx.beginPath();
drawFullEllipse(ellipse);
drawExtent(boundEllipseAll(ellipse));
ctx.stroke();
// Draw ellipse segment and bounding box.
ctx.strokeStyle = "#0008";
ctx.beginPath();
drawEllipse(ellipse);
drawExtent(boundEllipse(ellipse));
ctx.stroke();
requestAnimationFrame(mainLoop)
}
canvas { border: 1px solid black }
<canvas id="canvas" width="200" height="180"></canvas>
I've been trying to understand arc svg since it seems I need them in plotly -- my goal is to plot circle intersections.
My original idea was something like this:
for every intersection, to find the start and end coordinates as well as the height - but I am not very sure of where to go from here. It seems I am lacking the rotation and Large Arc Flag / Sweep parameters, and I am not sure how I would go about retrieving them. If anyone could point me into the right direction here, that would be great!
Circles and intercepting points
Don't know much about SVG arcTo. MDN gives "A rx,ry xAxisRotate LargeArcFlag,SweepFlag x,y". as an arc in the path element. What rx and ry are??? I would guess radius for x,y.
I am guessing you would use it as
// x,y start position
// rx,ry radius x and y
// x1,y1 end position
<path d="M x,y A rx, ry, 0 1 1 x1, y1"/>
Below is the problem solved as javascript. I have Commented the part you need for the SVG. The two end points (intercepts)
There is a lot of redundancy but its not clear what you want so the code provides how to find other parts of two intersecting circles.
Law of Cosines
The math to solve the problem is called the law of cosines that is used to solve triangles.
In this case the triangle is created from 3 lengths. One each of the circle radius and one is the distance between circle centers. The image gives more details
With the angle c you can find the lengths GE, DE, and EF. If you want the angle for the other side at point f just swap B and C.
Example
Move mouse to check intercept.
const ctx = canvas.getContext("2d");
const m = {
x: 0,
y: 0
};
document.addEventListener("mousemove", e => {
var b = canvas.getBoundingClientRect();
m.x = e.pageX - b.left - scrollX;
m.y = e.pageY - b.top - scrollY;
});
const PI = Math.PI;
const PI2 = Math.PI * 2;
const circles = [];
function circle(x, y, r, col, f = 0, t = PI2, w = 2) {
var c;
circles.push(c = { x, y,r, col, f, t, w});
return c;
};
function drawCircle(A) {
ctx.strokeStyle = A.col;
ctx.lineWidth = A.w;
ctx.beginPath();
ctx.arc(A.x, A.y, A.r, A.f, A.t);
ctx.stroke();
}
function mark(x, y, r, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(x, y, r, 0, PI2);
ctx.stroke();
}
function line(A, B, c) {
ctx.strokeStyle = c;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.lineTo(A.x, A.y);
ctx.lineTo(B.x, B.y);
ctx.stroke();
}
// note I am sharing calc results between function
function circleIntercept(A, B) {
var vx, vy, dist, c, d, x, y, x1, y1, x2, y2, dir, a1, a2;
// Vec from A to B
vx = B.x - A.x;
vy = B.y - A.y;
// Distance between
dist = Math.sqrt(vx * vx + vy * vy);
// Are the intercepting
if (dist < A.r + B.r && dist > B.r - A.r) {
c = (B.r * B.r - (dist * dist + A.r * A.r)) / (-2 * dist);
// Find mid point on cord
x = A.x + vx * (c / dist);
y = A.y + vy * (c / dist);
mark(x, y, 5, "blue");
// Find circumference intercepts
//#################################################################
//=================================================================
// SVG path
// Use x1,y1 and x2,y2 as the start and end angles of the ArcTo SVG
d = Math.sqrt(A.r * A.r - c * c);
x1 = x - vy * (d / dist);
y1 = y + vx * (d / dist);
x2 = x + vy * (d / dist);
y2 = y - vx * (d / dist);
// SVG path from above coords
// d = `M ${x1}, ${y1} A ${A.r}, ${A,r1} 0, 1, 1, ${x2}, ${y2}`;
//=================================================================
// draw the chord
line({x: x1,y: y1}, {x: x2,y: y2}, "red");
// mark the intercepts
mark(x1, y1, 5, "Green");
mark(x2, y2, 5, "Orange");
// Get direction from A to B
dir = Math.atan2(vy, vx);
// Get half inside sweep
a1 = Math.acos(c / A.r);
// Draw arc for A
A.col = "black";
A.w = 4;
A.f = dir - a1;
A.t = dir + a1;
drawCircle(A);
A.col = "#aaa";
A.w = 2;
A.f = 0;
A.t = PI2;
// inside sweep for B
a2 = Math.asin(d / B.r);
// Draw arc for B
B.col = "black";
B.w = 4;
if (dist < c) {
B.t = dir - a2;
B.f = dir + a2;
} else {
B.f = dir + PI - a2;
B.t = dir + PI + a2;
}
drawCircle(B);
B.col = "#aaa";
B.w = 2;
B.f = 0;
B.t = PI2;
}
}
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var C1 = circle(cw, ch, ch * 0.5, "#aaa");
var C2 = circle(cw, ch, ch * 0.8, "#aaa");
function update(timer) {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.globalAlpha = 1;
if (w !== innerWidth || h !== innerHeight) {
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
C1.x = cw;
C1.y = ch;
C1.r = ch * 0.5;
ctx.lineCap = "round";
}
C2.x = m.x;
C2.y = m.y;
ctx.clearRect(0, 0, w, h);
drawCircle(C1);
drawCircle(C2);
circleIntercept(C1, C2);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
Let's start with some terminology to clear up what is a clockwise direction (remember the y axis of SVG goes down): the first circle has radius r1, the second r2.
If the center of the first circle is lower than that of the second (cy1 > cy2), then name the intersection point with the smaller x coordinate (x1, y1), and the other (x2, y2).
If the center of the first circle is higher than that of the second (cy1 < cy2), then name the intersection point with the greater x coordinate (x1, y1), and the other (x2, y2).
Else, name the intersection point with the smaller y coordinate (x1, y1), and the other (x2, y2).
Now we will draw an arc from the first to the second intersection point with the radius of the first circle. The first two arc parameters are the horizontal and vertical radius. Since we are drawing a circle, both are identical. For the same reason, rotating the radii does not make sense and the third parameter is 0.
The intersection of two circles always uses the small arc (the large arc would be used for the union), therefore the large arc flag is 0. We are drawing the arc clockwise, therefore the sweep flag is 1.
Still unclear? The spec uses this picture for explaining the flags:
The second arc is going from the second to the first intersection point with the radius of the second circle. The flags remain the same.
The result looks like this:
M x1, y1 A r1 r1 0 0 1 x2, y2 A r2 r2 0 0 1 x1, y1 Z
I am trying to extend a line (from to points(X,Y)) to the end of the drawing area.
so far i found a couple of instructions on how to calculate the extension end point.
however i don't really get it done it works in one direction and breaks as soon as you reach over the middle point.
see attached code sample (the real product i am working on is in swift, but as it is not a programming language related issue, i ported it to javascript)
on the right side it seems to work, black line is the one the user selects, red one is the extension to the edge of canvas, going to the left side produces garbage.
var canvas = document.getElementById("myCanvas");
var endPoint = {
x: 200,
y: 200
};
function draw() {
//Demo only in final product user also can select the startpoint
startPoint = {
x: 150,
y: 150
}
screenMax = {
x: canvas.height,
y: canvas.width
}
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.moveTo(startPoint.x, startPoint.y);
ctx.lineTo(endPoint.x, endPoint.y);
ctx.strokeStyle = "#000000";
ctx.stroke();
//Extend line to end of canvas according to slope
var slope = 1.0
var extendedPoint = {
x: 0,
y: 0
}
if (endPoint.x != startPoint.x) {
slope = (endPoint.y - startPoint.y) / (endPoint.x - startPoint.x);
extendedPoint = {
x: screenMax.x,
y: slope * (screenMax.x - endPoint.x) + endPoint.y
}
} else {
slope = 0
extendedPoint.x = endPoint.x;
extendedPoint.y = screenMax.y;
}
console.log(endPoint);
//Draw the Extension
ctx.beginPath();
ctx.moveTo(endPoint.x, endPoint.y);
ctx.lineTo(extendedPoint.x, extendedPoint.y);
ctx.strokeStyle = "#FF0000";
ctx.stroke();
}
//initial draw
draw();
//handle Mouse dOwn
canvas.onmousedown = function(e) {
handleMouseDown(e);
}
// handle the mousedown event
//Set new endpoint
function handleMouseDown(e) {
mouseX = parseInt(e.clientX);
mouseY = parseInt(e.clientY);
endPoint = {
x: mouseX,
y: mouseY
}
draw();
}
<!DOCTYPE html>
<html>
<body>
<canvas id="myCanvas" width="300" height="300" style="border:1px solid #d3d3d3;">
Your browser does not support the HTML5 canvas tag.</canvas>
</body>
</html>
This function may help, takes the line x1,y1 to x2,y2 and extends it to the border defined by left,top,right,bottom returning the intercept point as {x:?,y:?}
function toBorder(x1, y1, x2, y2, left, top, right, bottom){
var dx, dy, py, vx, vy;
vx = x2 - x1;
vy = y2 - y1;
dx = vx < 0 ? left : right;
dy = py = vy < 0 ? top : bottom;
if(vx === 0){
dx = x1;
}else if(vy === 0){
dy = y1;
}else{
dy = y1 + (vy / vx) * (dx - x1);
if(dy < top || dy > bottom){
dx = x1 + (vx / vy) * (py - y1);
dy = py;
}
}
return {x : dx, y : dy}
}
Slope approach is not universal - it cannot work with vertical lines (x0=x1).
I'd use parametric representation of ray (line)
x0 = startPoint.x
x1 = endPoint.x
y0 = startPoint.y
y1 = endPoint.y
dx = x1 - x0
dy = y1 - y0
x = x0 + dx * t
y = y0 + dy * t
Now check what border will be intersected first (with smaller t value)
//prerequisites: potential border positions
if dx > 0 then
bx = width
else
bx = 0
if dy > 0 then
by = height
else
bx = 0
//first check for horizontal/vertical lines
if dx = 0 then
return ix = x0, iy = by
if dy = 0 then
return iy = y0, ix = bx
//in general case find parameters of intersection with horizontal and vertical edge
tx = (bx - x0) / dx
ty = (by - y0) / dy
//and get intersection for smaller parameter value
if tx <= ty then
ix = bx
iy = y0 + tx * dy
else
iy = by
ix = x0 + ty * dx
return ix, iy