I am working on a browser game which includes cubic interpolation in many dimensions. I need to take a set of values and a set of coordinates and output the estimated value that point in nD space would have. I have found some great tutorials for 1d cubic interpolation, and some examples of nD cubic interpolation in c++ (Both here). The problem is that this c++ code uses many pointers and bit shifters, both of which I don't think JS has.
1D cubic interpolation is long, but it looks like this for whoever is interested:
f(a, b, c, d, x) = b + 0.5 * x*(c - a + x*(2*a - 5*b + 4*c - d + x*(3*(b - c) + d - a))), Where a-d are known values, and x is a value between 0 and 1 that determines the points relative placement between b and c
Js:
function interpolate(p, x){
return p[1] + 0.5 * x*(p[2] - p[0] + x*(2*p[0] - 5*p[1] + 4*p[2] - p[3] + x*(3*(p[1] - p[2]) + p[3] - p[0])));
}
2d interpolation can be achieved simply like this:
g(a1, b1, c1, d1, a2, b2, c2, d2, a3, b3, c3, d3, a4, b4, c4, d4, x, y) =
f(f(a1, b1, c1, d1, y), f(a2, b2, c2, d2, y), f(a3, b3, c3, d3, y), f(a4, b4, c4, d4, y), x)
Js:
function interpolate2d(arr, x, y){
let a = [
interpolate(arr[0], y),
interpolate(arr[1], y),
interpolate(arr[2], y),
interpolate(arr[3], y),
];
return interpolate(a, x);
}
3d is the same way:
function interpolate3d(arr, x, y, z){
let a = [
interpolate2d(arr[0], y, z),
interpolate2d(arr[1], y, z),
interpolate2d(arr[2], y, z),
interpolate2d(arr[3], y, z)
];
return interpolate(a, x);
}
I'm new to Js, (just came from c++ and java) and don't know many of the extra features. I haven't found anything online, except for Array.reduce() (don't know how that would work with nested arrays) and the recursive c++ code from the link above:
double nCubicInterpolate (int n, double* p, double coordinates[]) {
assert(n > 0);
if (n == 1) {
return cubicInterpolate(p, *coordinates);
}
else {
double arr[4];
int skip = 1 << (n - 1) * 2;
arr[0] = nCubicInterpolate(n - 1, p, coordinates + 1);
arr[1] = nCubicInterpolate(n - 1, p + skip, coordinates + 1);
arr[2] = nCubicInterpolate(n - 1, p + 2*skip, coordinates + 1);
arr[3] = nCubicInterpolate(n - 1, p + 3*skip, coordinates + 1);
return cubicInterpolate(arr, *coordinates);
}
}
What would the Js function to replicate this be?
The translation to JavaScript can be:
function nCubicInterpolate(p, coordinates) {
if (coordinates.length == 1) {
return cubicInterpolate(p, coordinates[0]);
} else {
let fewerCoordinates = coordinates.slice(1);
let arr = p.map(hyperplane => nCubicInterpolate(hyperplane, fewerCoordinates));
return cubicInterpolate(arr, coordinates[0]);
}
}
Note that the parameter n is not needed in JavaScript, as the length of the coordinates array argument corresponds to it. Also the nesting level of p should be that same n. JavaScript has no compile-time type checking, so that liberty comes to our advantage here: no pointers; p is an array, but it can be an array of numbers, or an array of array of numbers, or ...etc.
To be complete, the cubicInterpolate function is as you specified in the question:
function cubicInterpolate(p, x) {
return p[1] + 0.5 * x*(p[2] - p[0] + x*(2*p[0] - 5*p[1] + 4*p[2] - p[3] + x*(3*(p[1] - p[2]) + p[3] - p[0])));
}
Related
I wrote a simple 3d flood-filling algorithm to draw a diamond shape into an array (that array is pseudo multi dimensional as described in this post). The actual method looks like this:
/**
* Draws a 3d diamond shape into an array using a flood fill algorithm.
*
* #param data The array to "draw" the diamond shape in. Must be of length 100 * 100 * 100 and all values must be 0.
* #param pos Tupel of x, y, z position to be the center of the diamond.
*/
export function flood3dDiamondShape(data: Uint8Array, pos: [number, number, number], distance = 0): void {
if (distance > 10) {
return;
}
const [x, y, z] = pos;
const index = (x) + (z * 100) + (y * 100 * 100);
if (data[index] > 0) {
return;
}
data[index] = 1;
flood3dDiamondShape(data, [x + 1, y, z], distance + 1);
flood3dDiamondShape(data, [x - 1, y, z], distance + 1);
flood3dDiamondShape(data, [x, y + 1, z], distance + 1);
flood3dDiamondShape(data, [x, y - 1, z], distance + 1);
flood3dDiamondShape(data, [x, y, z + 1], distance + 1);
flood3dDiamondShape(data, [x, y, z - 1], distance + 1);
}
However, this doesn't result in the expected result but draws a strangely shaped "blob" into the array (refer to image 2). I tried to debug that behavior by wrapping the six calls to flood3dDiamondShape(...) into a setTimeout callback. It looks like this:
setTimeout(() => {
flood3dDiamondShape(data, [x + 1, y, z], distance + 1);
flood3dDiamondShape(data, [x - 1, y, z], distance + 1);
flood3dDiamondShape(data, [x, y + 1, z], distance + 1);
flood3dDiamondShape(data, [x, y - 1, z], distance + 1);
flood3dDiamondShape(data, [x, y, z + 1], distance + 1);
flood3dDiamondShape(data, [x, y, z - 1], distance + 1);
}, 1);
This still works but now the strange "blob" isn't there anymore. It now is the expected diamond shape as you may see in image 3.
Note that using the second method I of course waited for the algorithm to finish before actually drawing the shape.
The question now actually is: Why am I experiencing this behavior? I would like to get the diamond shape 3 but without using setTimeout.
Minimum working example: I created a 2d version in html to show the algorithm. Please refer to: https://pastebin.com/raw/MvyJzR5x. How it looks: 4
Minimum working example in 2d
How it is supposed to look (with setTimeout)
How it actually looks (without setTimeout)
I somewhat solved it. The problem was with the distance field. The algorithm will walk through the array like a snake and will eventually come to a stop because the distance got too big. As the already visited fields will now check for painted fields to the top/bottom, left/right their may already be paint by the first "snake" and no more is painted. This results in an unwanted shape.
Now this isn't really a solution but it explains the problem. I thought about replacing the base case distance > 10 with something like manhattenDistance(x, y, originalX, originalY). I am not able to come up with another solution.
By the way: The setTimeout solved the issue because the order in which the pixels are worked off is essentially reversed. This prevents the algorithm from becoming stuck. This could also be implemented with a list that keeps an order of when to work off which pixel.
EDIT:
I now found a way to keep the original distance field. I initially introduced it to ensure that the shape "bleeds" around already existing shapes in the data array. Using the manhattenDistance the shape would ignore boundaries. The result now looks like this:. The updated algorithm (in 2d like the provided example) now looks like this:
function floodFill(data, x, y, distance) {
if (distance <= 0) {
return;
}
const index = (x) + (y * 100);
if (data[index] >= distance) {
return;
}
data[index] = distance;
floodFill(data, x, y + 1, distance - 1);
floodFill(data, x, y - 1, distance - 1);
floodFill(data, x + 1, y, distance - 1);
floodFill(data, x - 1, y, distance - 1);
}
As you may see it was just a matter of flipping the base case of comparing the already painted color to the color that is to be painted.
Forgive me for the long code example, but I couldn't figure out how to properly explain my question with any less code:
let c = document.querySelector("canvas");
let ctx = c.getContext("2d");
class BezierCurve {
constructor(x1, y1, cpX, cpY, x2, y2) {
this.f = 0;
this.x1 = x1;
this.y1 = y1;
this.cpX = cpX;
this.cpY = cpY;
this.x2 = x2;
this.y2 = y2;
this.pointCache = this.calcPoints();
}
calcX(t) { return (1 - t) * (1 - t) * this.x1 + 2 * (1 - t) * t * this.cpX + t * t * this.x2; }
calcY(t) { return (1 - t) * (1 - t) * this.y1 + 2 * (1 - t) * t * this.cpY + t * t * this.y2; }
calcPoints() {
const step = 0.001, segments = [];
for (let i = 0; i <= 1 - step; i += step) {
let dx = this.calcX(i) - this.calcX(i + step);
let dy = this.calcY(i) - this.calcY(i + step);
segments.push(Math.sqrt(dx * dx + dy * dy));
}
const len = segments.reduce((a, c) => a + c, 0);
let result = [], l = 0, co = 0;
for (let i = 0; i < segments.length; i++) {
l += segments[i];
co += step;
result.push({ t: l / len, co });
}
return result;
}
draw() {
ctx.beginPath();
ctx.moveTo(this.x1, this.y1);
ctx.quadraticCurveTo(this.cpX, this.cpY, this.x2, this.y2);
ctx.stroke();
}
tick(amount = 0.001) {
this.f = this.f < 1 ? this.f + amount : 0;
}
}
function drawCircle(x, y, r) {
ctx.beginPath();
ctx.arc(x, y, r, 0, 2 * Math.PI);
ctx.fill();
}
let a = new BezierCurve(25, 25, 80, 250, 100, 50);
let b = new BezierCurve(225, 25, 280, 250, 300, 50);
function draw(curve, fraction) {
let x = curve.calcX(fraction);
let y = curve.calcY(fraction);
curve.draw();
drawCircle(x, y, 5);
curve.tick();
}
// Inefficient but using this instead of binary search just to save space in code example
function findClosestNumInArray(arr, goal) {
return arr.reduce((prev, cur) => Math.abs(cur.t - goal) < Math.abs(prev.t - goal) ? cur : prev);
}
function drawLoop(elapsed) {
c.width = 600;
c.height = 600;
draw(a, a.f);
let closest = findClosestNumInArray(b.pointCache, b.f).co;
draw(b, closest);
requestAnimationFrame(drawLoop);
}
drawLoop(0);
<canvas></canvas>
Okay, so, to explain what's going on: if you hit Run code snippet you'll see that there are two curves, which I'll refer to as a (left one) and b (right one).
You may notice that the dot moving along a's curve starts off fast, then slows down around the curve, and then speeds up again. This is despite the fractional part being incremented by a constant 0.001 each frame.
The dot for b on the other hand moves at a constant velocity throughout the entire iteration. This is because for b I use the pointCache mapping that I precompute for the curve. This function calcPoints generates a mapping such that the input fractional component t is associated with the "proper" actual percentage along the curve co.
Anyways, this all works, but my issue is that the precomputation calcPoints is expensive, and referencing a lookup table to find the actual fractional part along the line for a percentage is inexact and requires significant memory usage. I was wondering if there was a better way.
What I'm looking for is a way to do something like curve.calcX(0.5) and actually get the 50% mark along the curve. Because currently the existing equation does not do this, and I instead have to do this costly workaround.
We can try to modify your method to be a bit more efficient. It is still not the exact solution you hope for but it might do the trick.
Instead of repeatedly evaluating the Bézier curve at parameter values differing by 0.001 (where you do not reuse the computation from the previous step) we could use the idea of subdivision. Do you know De Casteljau's algorithm? It not only evaluates the Bézier curve at a given parameter t, it also provides you with means to subdivide the curve in two: one Bézier curve that equals the original curve on the interval [0, t] and another one that equals the original curve on [t, 1]. Their control polygons are a much better approximation of the curves than the original control polygon.
So, you would proceed as follows:
Use De Casteljau's algorithm to subdivide the curve at t=0.5.
Use De Casteljau's algorithm to subdivide the first segment at t=0.25.
Use De Casteljau's algorithm to subdivide the second segment at t=0.75.
Proceed recursively in the same manner until prescribed depth. This depends on the precision you would like to achieve.
The control polygons of these segments will be your (piecewise linear) approximation of the original Bézier curve. Either use them to precompute the parameters as you have done so far; or plot this approximation directly instead of using quadraticCurveTo with the original curve. Generating this approximation should be much faster than your procedure.
You can read more about this idea in Sections 3.3, 3.4 and 3.5 of Prautzsch, Boehm and Paluszny: Bézier and B-spline techniques. They also provide an estimate how quickly does this procedure converge to the original curve.
Not totally sure this will work, but are you aware of Horner's Scheme for plotting Bezier points?
/***************************************************************************
//
// This routine plots out a bezier curve, with multiple calls to hornbez()
//
//***************************************************************************
function bezierCalculate(context, NumberOfDots, color, dotSize) {
// This routine uses Horner's Scheme to draw entire Bezier Line...
for (var t = 0.0; t < 1.0001; t = t + 1.0 / NumberOfDots) {
xTemp = hornbez(numberOfControlPoints - 1, "x", t);
yTemp = hornbez(numberOfControlPoints - 1, "y", t);
drawDot(context, xTemp, yTemp, dotSize, color);
}
}
//***************************************************************************
//
// This routine uses Horner's scheme to compute one coordinate
// value of a Bezier curve. Has to be called
// for each coordinate (x,y, and/or z) of a control polygon.
// See Farin, pg 59,60. Note: This technique is also called
// "nested multiplication".
// Input: degree: degree of curve.
// coeff: array with coefficients of curve.
// t: parameter value.
// Output: coordinate value.
//
//***************************************************************************
function hornbez(degree, xORy, t) {
var i;
var n_choose_i; /* shouldn't be too large! */
var fact, t1, aux;
t1 = 1 - t;
fact = 1;
n_choose_i = 1;
var aux = FrameControlPt[0][xORy] * t1;
/* starting the evaluation loop */
for (i = 1; i < degree; i++) {
fact = fact * t;
n_choose_i = n_choose_i * (degree - i + 1) / i; /* always int! */
aux = (aux + fact * n_choose_i * FrameControlPt[i][xORy]) * t1;
}
aux = aux + fact * t * FrameControlPt[degree][xORy];
return aux;
}
Not sure exactly where you are going here, but here's a reference of something I wrote a while ago... And for the contents of just the Bezier iframe, see this... My implied question? Is Bezier the right curve for you?
Note: Your solution can be in browser js or python. angleX will be in radians
I want a method that takes three parameters :-
aCord - (an object/dictionary with the co-ordinates of point A)
bCord - (an object/dictionary with the co-ordinates of point B)
angleX - (angle of rotation)
Lets say that I have a point A and a point B (any where on the screen. Not necessarily parallel as shown in the figure)
Point B is rotated angleX degrees (with point B as the center of the circle) to form point C
But instead, i want to increment b.x and b.y so it is same as c.x and c.y
Your function should return an object/dictionary with two values. An 'x-increment' and a 'y-increment' (which is how much I should increment the x and y of point B)
here is my existing code (in js)
function getIncrement(aCord, bCord, angleX) {
let r = Math.sqrt(Math.pow(aCord.x - bCord.x, 2) + Math.pow(aCord.x - bCord.x, 2));
let angleY = Math.atan(Math.abs(aCord.y - bCord.y) / Math.abs(aCord.x - bCord.x));
let cCord = {
x: Math.cos(angleY + angleX) * r,
y: Math.sin(angleY + angleX) * r
};
return {
xIncrement: cCord.x - aCord.x,
yIncrement: cCord.y - aCord.y
};
}
Sorry if my explaination is not good enough. I could explain in the comments if you do not understand something
Here is a demo of the function to use in a JavaScript snippet. Move the mouse in the box to move the B-point (A is fixed), and the line A-C will be added dynamically. Change the angle in the input box:
// The function to calculate C:
function getC(a, b, angle) {
let sign = -1; // change to 1 if Y axis goes upward
let radius = Math.sqrt((a.x-b.x)**2 + (a.y-b.y)**2);
angle += Math.atan2(sign*(b.y-a.y), b.x-a.x);
return {
x: a.x + Math.cos(angle)*radius,
y: a.y + sign*Math.sin(angle)*radius
};
}
// I/O handling
function drawLine(ctx, a, b, color="black") {
ctx.beginPath();
ctx.moveTo(a.x, a.y);
ctx.lineTo(b.x, b.y);
ctx.strokeStyle = color;
ctx.stroke();
}
function text(ctx, a, txt, color) {
ctx.fillStyle = color;
ctx.fillText(txt, a.x+2, a.y-2);
}
function refresh(ctx, a, b, c) {
outputB.textContent = Math.round(b.x) + "," + Math.round(b.y);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawLine(ctx, a, b, "black");
drawLine(ctx, a, c, "red");
text(ctx, a, "A", "black");
text(ctx, b, "B", "black");
text(ctx, c, "C", "red");
}
let ctx = document.querySelector("canvas").getContext("2d");
let inputDegrees = document.querySelector("#angle");
let outputB = document.querySelector("#b");
let a = { x: 200, y: 75 };
ctx.canvas.onmousemove = function(e) {
let b = {
x: e.clientX-e.target.offsetLeft,
y: e.clientY-e.target.offsetTop,
};
let c = getC(a, b, inputDegrees.value*Math.PI/180);
refresh(ctx, a, b, c);
}
canvas { border: 1px solid }
Angle(degrees): <input id="angle" type="number" value="15"><br>
A=(200,75) B=(<span id="b"></span>)<br>
<canvas width="400" height="150"></canvas>
In the general case, say when you have a circle with center A(x,y) and radius r. The position of the initial point is θ radians along the circle from (a+r,b).
The parametric equation for the circle is (x,y)=(a + r cosθ,b + r sinθ).
In your case, you want to increase by ϕ radians. Then the new point is
(a+ r cos(θ+ϕ),b+ r sin(θ+ϕ))
Perhaps, if you could provide more explanation to your question. it will aid more into solution. Meanwhile i will dig for answer without radius i.e distance between A & B
Consider the given image of the soccer field
As you can see in the image the various ball movements, some of them are curved(i.e. in case of (1), (2), (3) in the image)) and some may not(i.e a line(4)),
so I need to find the intersection points of ball path with goalline and sideline. Sometimes the input may not be a curve(i.e a line) like in case of (4) given image
I have written a program, I have no clue what is wrong - is this right way to solve this kind of program.
if yes then, how to convert bezier curve into an equation for better solving
considering the given as
beizer curve eqaution -> a(x*x) + b*x + c
and line segment equation -> y = y1 + m(x-x1)
//maxCurvedPoint is the topmost of the curve
var getIntersectionPoint = function (room, ballFromPosition, ballToPosition, maxCurvePoint)
{
var linepoints = [[m1,n1], [m2, n2], [m3, n3], [m4, n4]];
//converting three points(ballFromPosition, maxCurvePoint, ballToPosition) into the quadratic equation (Bezier curve) --(1)
//getting the equation of the line segment using the linepoints --(2)
//equating (1) and (2) and getting a quadratic equation and solving and finding intersection points
return intersectionPoint;
}
// solves //(-b(+-)sqrt(b*b - 4ac)/2ac)
function solve(a, b, c)
{
//check curve intersects line or not
if((Math.pow(b, 2) - (4 * a * c)) >= 0)
{
result1 = (-1 * b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
result2 = (-1 * b - Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
return [result1, result2];
}
return [];
}
Can anyone help me with this? Also is the most curve point can be called vertex of the curve?
I find it easier to work with vector equations since the algebra will be rotation-invariant (hence you don't have to re-write the code to deal with e.g. a "horizontal" parabola).
1. Curve representation + Intersection test
Consider a quadratic Bezier curve with endpoints A, C, control point B and parameter t:
And an infinite line with source O, direction D and parameter s:
Equating P and R give a pair of quadratic simultaneous equations, which can be re-arranged to eliminate s and find the parabolic parameter t:
Solve this quadratic equation for t, and only accept real roots in the range [0, 1]. This ensures that any intersection point is always on the segment itself.
2. Dealing with line segments
You can also restrict the intersection point to a line segment, by computing s from t using the equations above, and limiting its value - which equals the distance along the line from O if D is normalized.
3. Computing the control point B
Note that a general value of the control point B will not give a symmetrical parabola. To compute B for a general symmetric curve:
Defining the variables:
M: midpoint of AB
n: clockwise normal to the direction AC
q: signed bulge distance - absolute value is the distance from M to the midpoint of the curve
k: signed distance from M to B
A surprisingly simple result.
4. Sample C# (-style) code
public static Vector2[] computeIntersection
(
Vector2 A, Vector2 C, double q, // parabola segment
Vector2 O, Vector2 P // line segment
)
{
// quadratic solve
double[] solve(double a, double b, double c)
{
double d = b * b - 4.0 * a * c;
if (d < 0.0) // imaginary roots - no intersection at all
return null;
if (d > 0.0) // two distinct real roots
{
double sd = Math.Sqrt(d);
return new double[2] { (-b + sd) / (2.0 * a),
(-b - sd) / (2.0 * a) };
}
else // only one (line segment is tangent to curve)
{
return new double[1] { -b / (2.0 * a) };
}
}
// cross product (in-case undefined)
double cross(Vector2 i, Vector2 j)
{
return i.x * j.y - i.y * j.x;
}
// validity check for t and s
bool valid(double v)
{
return (v >= 0.0) && (v <= 1.0);
}
// compute control point B
Vector2 E = C - A;
Vector2 M = 0.5 * (A + C);
Vector2 N = (new Vector2(E.y, -E.x)).normalize();
Vector2 B = M + (2.0 * q) * N;
// solving for t
Vector2 D = P - O;
bool useX = Math.Abs(D.X) > Math.Abs(D.Y);
double[] T = solve(cross(A + C - 2.0 * B, D),
cross(B - A, D) * 2.0,
cross(A - O, D));
if (T == null) return null;
Vector2[] I = new Vector2[2];
int c = 0;
for (int i = 0; i < T.Length; i++)
{
// check if t is within curve range
double t = T[i];
if (!valid(t)) continue;
// solve for s and check if is within line range
double u = (1 - t) * (1 - t);
double v = 2 * t * (1 - t);
double w = t * t;
double s = useX ? ((u * A.X + v * B.X + w * C.X - O.X) / D.X)
: ((u * A.Y + v * B.Y + w * C.Y - O.Y) / D.Y);
if (!valid(s)) continue;
// compute the corresponding intersection point
I[c++] = O + s * D;
}
// only return valid solutions
if (c == 0) return null;
Array.Resize(ref I, c);
return I;
}
If you translate and rotate all the endpoints in such a way that the line segment becomes (0, 0)-(d, 0), the problem simplifies.
Let the control points be (Xk, Yk), k= 0, 1, 2. The intersections with the axis X are obtained by solving for t the quadratic equation
Y0 (1-t)² + 2 Y1 t(1-t) + Y2 t² = 0.
The corresponding abscissas are given by
X0 (1-t)² + 2 X1 t(1-t) + X2 t² = 0.
You can check if these belong to the interval [0, d]. Then apply the reverse rotation and translation.
Addendum: intersection of two quadratic Beziers
The vector equation of one of the curves can be written
P = P0 (1 - t)² + 2 P1 t (1 - t) + P2 t²
= P0 + 2 (P1 - P0) t + (P0 - 2 P1 + P2) t².
If you apply the affine change of coordinates such that the reference frame becomes (P0, P1 - P0, P0 - 2 P1 + P2), the equation simplifies to
X = 2t
Y = t²
which is the implicit equation
X² = 4Y.
Now by applying the same transform to the control points of the second curve and plugging the parametric equations in the above, you get a quartic equation in t (square of a quadratic polynomial on one side, quadratic polynomial on the other).
There are closed-form formulas for the roots of a quartic equation, and there can be four of them. After selecting the real roots in [0, 1], you evaluate the t parameter of the first curve and also check membership in [0, 1].
Don't forget to restore the original coordinates.
Given a point on a line and that line's slope how would one determine if the line, extending in each direction infinitely, intersects with a line segment (x1,y1), (x2,y2) and, if so, the point at which the intersection occurs?
I found this, but I'm unsure if it's helpful here.
If someone wants to help me understand "rays", that's alright with me.
http://www.realtimerendering.com/intersections.html
I'm sorry that I'm an idiot.
Arbitrary point on the first line has parametric equation
dx = Cos(slope)
dy = Sin(Slope)
x = x0 + t * dx (1)
y = y0 + t * dy
Line containing the second segment
dxx = x2 - x1
dyy = y2 - y1
x = x1 + u * dxx (2)
y = y1 + u * dyy
Intersection exists if linear system
x0 + t * dx = x1 + u * dxx (3)
y0 + t * dy = y1 + u * dyy
has solution for unknowns t and u
and u lies in range [0..1]
Intersection point could be calculated with substitution of u found in the equation pair (2)
Please don't ask me to explain how exactly this is working, I've just extrapolated/rewritten it from some ancient code I've had laying around. (Actionscript 1)
some functions to build the objects for this example:
function point(x, y){
return {x, y}
}
function line(x0, y0, x1, y1){
return {
start: point(x0, y0),
end: point(x1, y1)
}
}
function ray(x, y, vx, vy){
return {
start: point(x, y),
vector: point(vx, vy)
}
}
function ray2(x, y, angle){
var rad = angle * Math.PI / 180;
return ray(x, y, Math.cos(rad), Math.sin(rad));
}
the intersection-code:
//returns the difference vector between two points (pointB - pointA)
function delta(a, b){ return point( b.x - a.x, b.y - a.y ) }
//kind of a 2D-version of the cross-product
function cp(a, b){ return a.y * b.x - a.x * b.y }
function intersection(a, b){
var d21 = a.vector || delta(a.start, a.end),
d43 = b.vector || delta(b.start, b.end),
d13 = delta(b.start, a.start),
d = cp(d43, d21);
//rays are paralell, no intersection possible
if(!d) return null;
//if(!d) return { a, b, position: null, hitsA: false, hitsB: false };
var u = cp(d13, d21) / d,
v = cp(d13, d43) / d;
return {
a, b,
//position of the intersection
position: point(
a.start.x + d21.x * v,
a.start.y + d21.y * v
),
//is position on lineA?
hitsA: v >= 0 && v <= 1,
//is position on lineB?
hitsB: u >= 0 && u <= 1,
timeTillIntersection: v,
};
}
and an example:
var a = line(0, 0, 50, 50);
var b = line(0, 50, 50, 0); //lines are crossing
console.log(intersection(a, b));
var c = line(100, 50, 150, 0); //lines are not crossing
console.log(intersection(a, c));
var d = line(100, -1000, 100, 1000); //intersection is only on d, not on a
console.log(intersection(a, d));
var e = ray(100, 50, -1, -1); //paralell to a
console.log(intersection(a, e));
returns information about the intersection point, and wether it is on the passed lines/rays. Doesn't care wether you pass lines or rays.
about timeTillIntersection: if the first argument/ray represents a ball/bullet/whatever with current position and motion-vector, and the second argument represents a wall or so, then v, aka timeTillIntersection determines how much time it takes till this ball intersects/hits the wall (at the current conditions) in the same unit as used for the velocity of the ball. So you basically get some information for free.
What you search is the dot product. A line can be represented as a vector.
When you have 2 lines they will intersect at some point. Except in the case when they are parallel.
Parallel vectors a,b (both normalized) have a dot product of 1 (dot(a,b) = 1).
If you have the starting and end point of line i, then you can also construct the vector i easily.