lr-core
Version:
Line Rider core library
157 lines (142 loc) • 4.38 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.lineLineIntersection = lineLineIntersection;
exports.lineInBox = lineInBox;
exports.lineInBoxOrdered = lineInBoxOrdered;
exports.pointLineDistance = pointLineDistance;
exports.pointLineDistanceSquared = pointLineDistanceSquared;
/**
* line 1 endpoints: (x0, y0), (x1, y1)
* line 2 endpoints: (x2, y2), (x3, y3)
* inclusive: include edge cases e.g. endpoint touching an edge or on a point (default false)
*
* returns:
* if: there is an intersection
* then: a value between 0 and 1 describing the position of intersection on line 1
* or true if lines are collinear and inclusive is true (undefined point of intersectoin)
* else: null
*/
function lineLineIntersection(x0, y0, x1, y1, x2, y2, x3, y3, inclusive) {
const x01 = x1 - x0;
const y01 = y1 - y0;
const x23 = x3 - x2;
const y23 = y3 - y2;
const _01cross23 = x01 * y23 - x23 * y01;
if (_01cross23 === 0) {
// collinear
return inclusive ? true : null;
}
const orientation = _01cross23 > 0;
const x02 = x2 - x0;
const y02 = y2 - y0;
const _02cross01 = x02 * y01 - y02 * x01;
if (_02cross01 === 0 ? !inclusive : _02cross01 < 0 === orientation) {
return null;
}
const _02cross23 = x02 * y23 - y02 * x23;
if (_02cross23 === 0 ? !inclusive : _02cross23 < 0 === orientation) {
return null;
}
if (_02cross01 === _01cross23 ? !inclusive : _02cross01 > _01cross23 === orientation) {
return null;
}
if (_02cross23 === _01cross23 ? !inclusive : _02cross23 > _01cross23 === orientation) {
return null;
}
return _02cross23 / _01cross23;
}
/**
* line endpoints: (x0, y0), (x1, y1)
* box corners diagonal from each other: (x2, y2), (x3, y3)
* inclusive: include edge cases e.g. endpoint touching the box edge or corner (default false)
*
* returns true if the line is contained within or intersects with the box, false otherwise
*/
function lineInBox(x0, y0, x1, y1, x2, y2, x3, y3, inclusive) {
if (x2 < x3) {
if (y2 < y3) {
return lineInBoxOrdered(x0, y0, x1, y1, x2, y2, x3, y3, inclusive);
} else {
return lineInBoxOrdered(x0, y0, x1, y1, x2, y3, x3, y2, inclusive);
}
} else {
if (y2 < y3) {
return lineInBoxOrdered(x0, y0, x1, y1, x3, y2, x2, y3, inclusive);
} else {
return lineInBoxOrdered(x0, y0, x1, y1, x3, y3, x2, y2, inclusive);
}
}
}
/**
* same as lineInBox except
* (x2, y2) is the top-left corner of the box
* (x3, y3) is the bottom-right corner of the box
*/
function lineInBoxOrdered(x0, y0, x1, y1, x2, y2, x3, y3, inclusive) {
let L0, R0, T0, B0, L1, R1, T1, B1;
if (inclusive) {
L0 = x0 < x2;
R0 = x0 > x3;
T0 = y0 < y2;
B0 = y0 > y3;
L1 = x1 < x2;
R1 = x1 > x3;
T1 = y1 < y2;
B1 = y1 > y3;
} else {
L0 = x0 <= x2;
R0 = x0 >= x3;
T0 = y0 <= y2;
B0 = y0 >= y3;
L1 = x1 <= x2;
R1 = x1 >= x3;
T1 = y1 <= y2;
B1 = y1 >= y3;
}
// both endpoints are totally on one side of the box
if (L0 && L1 || R0 && R1 || T0 && T1 || B0 && B1) {
return false;
}
// both endpoints are not on one side of the box
// but between left/right or top/bottom sides
// or one point inside
if (!L0 && !R0 && (!T0 && !B0 || !L1 && !R1) || !T1 && !B1 && (!L1 && !R1 || !T0 && !B0)) {
return true;
}
// TL - BR
if ((L0 || B0 || R1 || T1) && (R0 || T0 || L1 || B1)) {
return lineLineIntersection(x0, y0, x1, y1, x2, y3, x3, y2, inclusive) !== null;
} else {
// TR - BL
return lineLineIntersection(x0, y0, x1, y1, x2, y2, x3, y3, inclusive) !== null;
}
}
/**
* point: (x0, y0)
* line endpoints: (x1, y1), (x2, y2)
*
* returns: the closest distance between the point and the line segment
*/
function pointLineDistance(x0, y0, x1, y1, x2, y2) {
return Math.sqrt(pointLineDistanceSquared(x0, y0, x1, y1, x2, y2));
}
function pointLineDistanceSquared(x0, y0, x1, y1, x2, y2) {
const x12 = x2 - x1;
const y12 = y2 - y1;
const x10 = x0 - x1;
const y10 = y0 - y1;
const dot = x10 * x12 + y10 * y12;
if (dot <= 0) {
return x10 * x10 + y10 * y10;
}
const lengthSq = x12 * x12 + y12 * y12;
if (dot >= lengthSq) {
const x20 = x0 - x2;
const y20 = y0 - y2;
return x20 * x20 + y20 * y20;
}
const cross = x10 * y12 - y10 * x12;
return cross * cross / lengthSq;
}