@excalidraw/math
Version:
Excalidraw math functions
908 lines (895 loc) • 25.8 kB
JavaScript
// src/utils.ts
var PRECISION = 1e-4;
var clamp = (value, min, max) => {
return Math.min(Math.max(value, min), max);
};
var round = (value, precision, func = "round") => {
const multiplier = Math.pow(10, precision);
return Math[func]((value + Number.EPSILON) * multiplier) / multiplier;
};
var roundToStep = (value, step, func = "round") => {
const factor = 1 / step;
return Math[func](value * factor) / factor;
};
var average = (a, b) => (a + b) / 2;
var isFiniteNumber = (value) => {
return typeof value === "number" && Number.isFinite(value);
};
var isCloseTo = (a, b, precision = PRECISION) => Math.abs(a - b) < precision;
// src/angle.ts
var normalizeRadians = (angle) => angle < 0 ? angle % (2 * Math.PI) + 2 * Math.PI : angle % (2 * Math.PI);
var cartesian2Polar = ([
x,
y
]) => [
Math.hypot(x, y),
normalizeRadians(Math.atan2(y, x))
];
function degreesToRadians(degrees) {
return degrees * Math.PI / 180;
}
function radiansToDegrees(degrees) {
return degrees * 180 / Math.PI;
}
function isRightAngleRads(rads) {
return Math.abs(Math.sin(2 * rads)) < PRECISION;
}
function radiansBetweenAngles(a, min, max) {
a = normalizeRadians(a);
min = normalizeRadians(min);
max = normalizeRadians(max);
if (min < max) {
return a >= min && a <= max;
}
return a >= min || a <= max;
}
function radiansDifference(a, b) {
a = normalizeRadians(a);
b = normalizeRadians(b);
let diff = a - b;
if (diff < -Math.PI) {
diff = diff + 2 * Math.PI;
} else if (diff > Math.PI) {
diff = diff - 2 * Math.PI;
}
return Math.abs(diff);
}
// src/vector.ts
function vector(x, y, originX = 0, originY = 0) {
return [x - originX, y - originY];
}
function vectorFromPoint(p, origin = [0, 0], threshold, defaultValue = [0, 1]) {
const vec = vector(p[0] - origin[0], p[1] - origin[1]);
if (threshold && vectorMagnitudeSq(vec) < threshold * threshold) {
return defaultValue;
}
return vec;
}
function vectorCross(a, b) {
return a[0] * b[1] - b[0] * a[1];
}
function vectorDot(a, b) {
return a[0] * b[0] + a[1] * b[1];
}
function isVector(v) {
return Array.isArray(v) && v.length === 2 && typeof v[0] === "number" && !isNaN(v[0]) && typeof v[1] === "number" && !isNaN(v[1]);
}
function vectorAdd(a, b) {
return [a[0] + b[0], a[1] + b[1]];
}
function vectorSubtract(start, end) {
return [start[0] - end[0], start[1] - end[1]];
}
function vectorScale(v, scalar) {
return vector(v[0] * scalar, v[1] * scalar);
}
function vectorMagnitudeSq(v) {
return v[0] * v[0] + v[1] * v[1];
}
function vectorMagnitude(v) {
return Math.sqrt(vectorMagnitudeSq(v));
}
var vectorNormalize = (v) => {
const m = vectorMagnitude(v);
if (m === 0) {
return vector(0, 0);
}
return vector(v[0] / m, v[1] / m);
};
var vectorNormal = (v) => vector(v[1], -v[0]);
// src/point.ts
function pointFrom(x, y) {
return [x, y];
}
function pointFromArray(numberArray) {
return numberArray.length === 2 ? pointFrom(numberArray[0], numberArray[1]) : void 0;
}
function pointFromPair(pair) {
return pair;
}
function pointFromVector(v, offset = pointFrom(0, 0)) {
return pointFrom(offset[0] + v[0], offset[1] + v[1]);
}
function isPoint(p) {
return Array.isArray(p) && p.length === 2 && typeof p[0] === "number" && !isNaN(p[0]) && typeof p[1] === "number" && !isNaN(p[1]);
}
function pointsEqual(a, b, tolerance = PRECISION) {
const abs = Math.abs;
return abs(a[0] - b[0]) < tolerance && abs(a[1] - b[1]) < tolerance;
}
function pointRotateRads([x, y], [cx, cy], angle) {
return pointFrom(
(x - cx) * Math.cos(angle) - (y - cy) * Math.sin(angle) + cx,
(x - cx) * Math.sin(angle) + (y - cy) * Math.cos(angle) + cy
);
}
function pointRotateDegs(point, center, angle) {
return pointRotateRads(point, center, degreesToRadians(angle));
}
function pointTranslate(p, v = [0, 0]) {
return pointFrom(p[0] + v[0], p[1] + v[1]);
}
function pointCenter(a, b) {
return pointFrom((a[0] + b[0]) / 2, (a[1] + b[1]) / 2);
}
function pointDistance(a, b) {
return Math.hypot(b[0] - a[0], b[1] - a[1]);
}
function pointDistanceSq(a, b) {
const xDiff = b[0] - a[0];
const yDiff = b[1] - a[1];
return xDiff * xDiff + yDiff * yDiff;
}
var pointScaleFromOrigin = (p, mid, multiplier) => pointTranslate(mid, vectorScale(vectorFromPoint(p, mid), multiplier));
var isPointWithinBounds = (p, q, r) => {
return q[0] <= Math.max(p[0], r[0]) && q[0] >= Math.min(p[0], r[0]) && q[1] <= Math.max(p[1], r[1]) && q[1] >= Math.min(p[1], r[1]);
};
// src/constants.ts
var LegendreGaussN24TValues = [
-0.06405689286260563,
0.06405689286260563,
-0.1911188674736163,
0.1911188674736163,
-0.3150426796961634,
0.3150426796961634,
-0.4337935076260451,
0.4337935076260451,
-0.5454214713888396,
0.5454214713888396,
-0.6480936519369755,
0.6480936519369755,
-0.7401241915785544,
0.7401241915785544,
-0.820001985973903,
0.820001985973903,
-0.8864155270044011,
0.8864155270044011,
-0.9382745520027328,
0.9382745520027328,
-0.9747285559713095,
0.9747285559713095,
-0.9951872199970213,
0.9951872199970213
];
var LegendreGaussN24CValues = [
0.12793819534675216,
0.12793819534675216,
0.1258374563468283,
0.1258374563468283,
0.12167047292780339,
0.12167047292780339,
0.1155056680537256,
0.1155056680537256,
0.10744427011596563,
0.10744427011596563,
0.09761865210411388,
0.09761865210411388,
0.08619016153195327,
0.08619016153195327,
0.0733464814110803,
0.0733464814110803,
0.05929858491543678,
0.05929858491543678,
0.04427743881741981,
0.04427743881741981,
0.028531388628933663,
0.028531388628933663,
0.0123412297999872,
0.0123412297999872
];
// src/curve.ts
function curve(a, b, c, d) {
return [a, b, c, d];
}
function gradient(f, t0, s0, delta = 1e-6) {
return [
(f(t0 + delta, s0) - f(t0 - delta, s0)) / (2 * delta),
(f(t0, s0 + delta) - f(t0, s0 - delta)) / (2 * delta)
];
}
function solve(f, t0, s0, tolerance = 1e-3, iterLimit = 10) {
let error = Infinity;
let iter = 0;
while (error >= tolerance) {
if (iter >= iterLimit) {
return null;
}
const y0 = f(t0, s0);
const jacobian = [
gradient((t, s) => f(t, s)[0], t0, s0),
gradient((t, s) => f(t, s)[1], t0, s0)
];
const b = [[-y0[0]], [-y0[1]]];
const det = jacobian[0][0] * jacobian[1][1] - jacobian[0][1] * jacobian[1][0];
if (det === 0) {
return null;
}
const iJ = [
[jacobian[1][1] / det, -jacobian[0][1] / det],
[-jacobian[1][0] / det, jacobian[0][0] / det]
];
const h = [
[iJ[0][0] * b[0][0] + iJ[0][1] * b[1][0]],
[iJ[1][0] * b[0][0] + iJ[1][1] * b[1][0]]
];
t0 = t0 + h[0][0];
s0 = s0 + h[1][0];
const [tErr, sErr] = f(t0, s0);
error = Math.max(Math.abs(tErr), Math.abs(sErr));
iter += 1;
}
return [t0, s0];
}
var bezierEquation = (c, t) => pointFrom(
(1 - t) ** 3 * c[0][0] + 3 * (1 - t) ** 2 * t * c[1][0] + 3 * (1 - t) * t ** 2 * c[2][0] + t ** 3 * c[3][0],
(1 - t) ** 3 * c[0][1] + 3 * (1 - t) ** 2 * t * c[1][1] + 3 * (1 - t) * t ** 2 * c[2][1] + t ** 3 * c[3][1]
);
function curveIntersectLineSegment(c, l) {
const line2 = (s) => pointFrom(
l[0][0] + s * (l[1][0] - l[0][0]),
l[0][1] + s * (l[1][1] - l[0][1])
);
const initial_guesses = [
[0.5, 0],
[0.2, 0],
[0.8, 0]
];
const calculate = ([t0, s0]) => {
const solution2 = solve(
(t2, s2) => {
const bezier_point = bezierEquation(c, t2);
const line_point = line2(s2);
return [
bezier_point[0] - line_point[0],
bezier_point[1] - line_point[1]
];
},
t0,
s0
);
if (!solution2) {
return null;
}
const [t, s] = solution2;
if (t < 0 || t > 1 || s < 0 || s > 1) {
return null;
}
return bezierEquation(c, t);
};
let solution = calculate(initial_guesses[0]);
if (solution) {
return [solution];
}
solution = calculate(initial_guesses[1]);
if (solution) {
return [solution];
}
solution = calculate(initial_guesses[2]);
if (solution) {
return [solution];
}
return [];
}
function curveClosestPoint(c, p, tolerance = 1e-3) {
const localMinimum = (min, max, f, e = tolerance) => {
let m = min;
let n = max;
let k;
while (n - m > e) {
k = (n + m) / 2;
if (f(k - e) < f(k + e)) {
n = k;
} else {
m = k;
}
}
return k;
};
const maxSteps = 30;
let closestStep = 0;
for (let min = Infinity, step = 0; step < maxSteps; step++) {
const d = pointDistance(p, bezierEquation(c, step / maxSteps));
if (d < min) {
min = d;
closestStep = step;
}
}
const t0 = Math.max((closestStep - 1) / maxSteps, 0);
const t1 = Math.min((closestStep + 1) / maxSteps, 1);
const solution = localMinimum(
t0,
t1,
(t) => pointDistance(p, bezierEquation(c, t))
);
if (!solution) {
return null;
}
return bezierEquation(c, solution);
}
function curvePointDistance(c, p) {
const closest = curveClosestPoint(c, p);
if (!closest) {
return 0;
}
return pointDistance(p, closest);
}
function isCurve(v) {
return Array.isArray(v) && v.length === 4 && isPoint(v[0]) && isPoint(v[1]) && isPoint(v[2]) && isPoint(v[3]);
}
function curveTangent([p0, p1, p2, p3], t) {
return vector(
-3 * (1 - t) * (1 - t) * p0[0] + 3 * (1 - t) * (1 - t) * p1[0] - 6 * t * (1 - t) * p1[0] - 3 * t * t * p2[0] + 6 * t * (1 - t) * p2[0] + 3 * t * t * p3[0],
-3 * (1 - t) * (1 - t) * p0[1] + 3 * (1 - t) * (1 - t) * p1[1] - 6 * t * (1 - t) * p1[1] - 3 * t * t * p2[1] + 6 * t * (1 - t) * p2[1] + 3 * t * t * p3[1]
);
}
function curveCatmullRomQuadraticApproxPoints(points, tension = 0.5) {
if (points.length < 2) {
return;
}
const pointSets = [];
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[i - 1 < 0 ? 0 : i - 1];
const p1 = points[i];
const p2 = points[i + 1 >= points.length ? points.length - 1 : i + 1];
const cpX = p1[0] + (p2[0] - p0[0]) * tension / 2;
const cpY = p1[1] + (p2[1] - p0[1]) * tension / 2;
pointSets.push([
pointFrom(cpX, cpY),
pointFrom(p2[0], p2[1])
]);
}
return pointSets;
}
function curveCatmullRomCubicApproxPoints(points, tension = 0.5) {
if (points.length < 2) {
return;
}
const pointSets = [];
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[i - 1 < 0 ? 0 : i - 1];
const p1 = points[i];
const p2 = points[i + 1 >= points.length ? points.length - 1 : i + 1];
const p3 = points[i + 2 >= points.length ? points.length - 1 : i + 2];
const tangent1 = [(p2[0] - p0[0]) * tension, (p2[1] - p0[1]) * tension];
const tangent2 = [(p3[0] - p1[0]) * tension, (p3[1] - p1[1]) * tension];
const cp1x = p1[0] + tangent1[0] / 3;
const cp1y = p1[1] + tangent1[1] / 3;
const cp2x = p2[0] - tangent2[0] / 3;
const cp2y = p2[1] - tangent2[1] / 3;
pointSets.push(
curve(
pointFrom(p1[0], p1[1]),
pointFrom(cp1x, cp1y),
pointFrom(cp2x, cp2y),
pointFrom(p2[0], p2[1])
)
);
}
return pointSets;
}
function curveOffsetPoints([p0, p1, p2, p3], offset, steps = 50) {
const offsetPoints = [];
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const c = curve(p0, p1, p2, p3);
const point = bezierEquation(c, t);
const tangent = vectorNormalize(curveTangent(c, t));
const normal = vectorNormal(tangent);
offsetPoints.push(pointFromVector(vectorScale(normal, offset), point));
}
return offsetPoints;
}
function offsetPointsForQuadraticBezier(p0, p1, p2, offsetDist, steps = 50) {
const offsetPoints = [];
for (let i = 0; i <= steps; i++) {
const t = i / steps;
const t1 = 1 - t;
const point = pointFrom(
t1 * t1 * p0[0] + 2 * t1 * t * p1[0] + t * t * p2[0],
t1 * t1 * p0[1] + 2 * t1 * t * p1[1] + t * t * p2[1]
);
const tangentX = 2 * (1 - t) * (p1[0] - p0[0]) + 2 * t * (p2[0] - p1[0]);
const tangentY = 2 * (1 - t) * (p1[1] - p0[1]) + 2 * t * (p2[1] - p1[1]);
const tangent = vectorNormalize(vector(tangentX, tangentY));
const normal = vectorNormal(tangent);
offsetPoints.push(pointFromVector(vectorScale(normal, offsetDist), point));
}
return offsetPoints;
}
function curveLength(c) {
const z2 = 0.5;
let sum = 0;
for (let i = 0; i < 24; i++) {
const t = z2 * LegendreGaussN24TValues[i] + z2;
const derivativeVector = curveTangent(c, t);
const magnitude = Math.sqrt(
derivativeVector[0] * derivativeVector[0] + derivativeVector[1] * derivativeVector[1]
);
sum += LegendreGaussN24CValues[i] * magnitude;
}
return z2 * sum;
}
function curveLengthAtParameter(c, t) {
if (t <= 0) {
return 0;
}
if (t >= 1) {
return curveLength(c);
}
const z1 = t / 2;
const z2 = t / 2;
let sum = 0;
for (let i = 0; i < 24; i++) {
const parameter = z1 * LegendreGaussN24TValues[i] + z2;
const derivativeVector = curveTangent(c, parameter);
const magnitude = Math.sqrt(
derivativeVector[0] * derivativeVector[0] + derivativeVector[1] * derivativeVector[1]
);
sum += LegendreGaussN24CValues[i] * magnitude;
}
return z1 * sum;
}
function curvePointAtLength(c, percent) {
if (percent <= 0) {
return bezierEquation(c, 0);
}
if (percent >= 1) {
return bezierEquation(c, 1);
}
const totalLength = curveLength(c);
const targetLength = totalLength * percent;
let tMin = 0;
let tMax = 1;
let t = percent;
let currentLength = 0;
const tolerance = totalLength * 1e-4;
const maxIterations = 20;
for (let iteration = 0; iteration < maxIterations; iteration++) {
currentLength = curveLengthAtParameter(c, t);
const error = Math.abs(currentLength - targetLength);
if (error < tolerance) {
break;
}
if (currentLength < targetLength) {
tMin = t;
} else {
tMax = t;
}
t = (tMin + tMax) / 2;
}
return bezierEquation(c, t);
}
// src/ellipse.ts
function ellipse(center, halfWidth, halfHeight) {
return {
center,
halfWidth,
halfHeight
};
}
var ellipseIncludesPoint = (p, ellipse2) => {
const { center, halfWidth, halfHeight } = ellipse2;
const normalizedX = (p[0] - center[0]) / halfWidth;
const normalizedY = (p[1] - center[1]) / halfHeight;
return normalizedX * normalizedX + normalizedY * normalizedY <= 1;
};
var ellipseTouchesPoint = (point, ellipse2, threshold = PRECISION) => {
return ellipseDistanceFromPoint(point, ellipse2) <= threshold;
};
var ellipseDistanceFromPoint = (p, ellipse2) => {
const { halfWidth, halfHeight, center } = ellipse2;
const a = halfWidth;
const b = halfHeight;
const translatedPoint = vectorAdd(
vectorFromPoint(p),
vectorScale(vectorFromPoint(center), -1)
);
const px = Math.abs(translatedPoint[0]);
const py = Math.abs(translatedPoint[1]);
let tx = 0.707;
let ty = 0.707;
for (let i = 0; i < 3; i++) {
const x = a * tx;
const y = b * ty;
const ex = (a * a - b * b) * tx ** 3 / a;
const ey = (b * b - a * a) * ty ** 3 / b;
const rx = x - ex;
const ry = y - ey;
const qx = px - ex;
const qy = py - ey;
const r = Math.hypot(ry, rx);
const q = Math.hypot(qy, qx);
tx = Math.min(1, Math.max(0, (qx * r / q + ex) / a));
ty = Math.min(1, Math.max(0, (qy * r / q + ey) / b));
const t = Math.hypot(ty, tx);
tx /= t;
ty /= t;
}
const [minX, minY] = [
a * tx * Math.sign(translatedPoint[0]),
b * ty * Math.sign(translatedPoint[1])
];
return pointDistance(pointFromVector(translatedPoint), pointFrom(minX, minY));
};
function ellipseSegmentInterceptPoints(e, s) {
const rx = e.halfWidth;
const ry = e.halfHeight;
const dir = vectorFromPoint(s[1], s[0]);
const diff = vector(s[0][0] - e.center[0], s[0][1] - e.center[1]);
const mDir = vector(dir[0] / (rx * rx), dir[1] / (ry * ry));
const mDiff = vector(diff[0] / (rx * rx), diff[1] / (ry * ry));
const a = vectorDot(dir, mDir);
const b = vectorDot(dir, mDiff);
const c = vectorDot(diff, mDiff) - 1;
const d = b * b - a * c;
const intersections = [];
if (d > 0) {
const t_a = (-b - Math.sqrt(d)) / a;
const t_b = (-b + Math.sqrt(d)) / a;
if (0 <= t_a && t_a <= 1) {
intersections.push(
pointFrom(
s[0][0] + (s[1][0] - s[0][0]) * t_a,
s[0][1] + (s[1][1] - s[0][1]) * t_a
)
);
}
if (0 <= t_b && t_b <= 1) {
intersections.push(
pointFrom(
s[0][0] + (s[1][0] - s[0][0]) * t_b,
s[0][1] + (s[1][1] - s[0][1]) * t_b
)
);
}
} else if (d === 0) {
const t = -b / a;
if (0 <= t && t <= 1) {
intersections.push(
pointFrom(
s[0][0] + (s[1][0] - s[0][0]) * t,
s[0][1] + (s[1][1] - s[0][1]) * t
)
);
}
}
return intersections;
}
function ellipseLineIntersectionPoints({ center, halfWidth, halfHeight }, [g, h]) {
const [cx, cy] = center;
const x1 = g[0] - cx;
const y1 = g[1] - cy;
const x2 = h[0] - cx;
const y2 = h[1] - cy;
const a = Math.pow(x2 - x1, 2) / Math.pow(halfWidth, 2) + Math.pow(y2 - y1, 2) / Math.pow(halfHeight, 2);
const b = 2 * (x1 * (x2 - x1) / Math.pow(halfWidth, 2) + y1 * (y2 - y1) / Math.pow(halfHeight, 2));
const c = Math.pow(x1, 2) / Math.pow(halfWidth, 2) + Math.pow(y1, 2) / Math.pow(halfHeight, 2) - 1;
const t1 = (-b + Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
const t2 = (-b - Math.sqrt(Math.pow(b, 2) - 4 * a * c)) / (2 * a);
const candidates = [
pointFrom(x1 + t1 * (x2 - x1) + cx, y1 + t1 * (y2 - y1) + cy),
pointFrom(x1 + t2 * (x2 - x1) + cx, y1 + t2 * (y2 - y1) + cy)
].filter((p) => !isNaN(p[0]) && !isNaN(p[1]));
if (candidates.length === 2 && pointsEqual(candidates[0], candidates[1])) {
return [candidates[0]];
}
return candidates;
}
// src/line.ts
function line(a, b) {
return [a, b];
}
function linesIntersectAt(a, b) {
const A1 = a[1][1] - a[0][1];
const B1 = a[0][0] - a[1][0];
const A2 = b[1][1] - b[0][1];
const B2 = b[0][0] - b[1][0];
const D = A1 * B2 - A2 * B1;
if (D !== 0) {
const C1 = A1 * a[0][0] + B1 * a[0][1];
const C2 = A2 * b[0][0] + B2 * b[0][1];
return pointFrom((C1 * B2 - C2 * B1) / D, (A1 * C2 - A2 * C1) / D);
}
return null;
}
// src/segment.ts
function lineSegment(a, b) {
return [a, b];
}
var isLineSegment = (segment) => Array.isArray(segment) && segment.length === 2 && isPoint(segment[0]) && isPoint(segment[0]);
var lineSegmentRotate = (l, angle, origin) => {
return lineSegment(
pointRotateRads(l[0], origin || pointCenter(l[0], l[1]), angle),
pointRotateRads(l[1], origin || pointCenter(l[0], l[1]), angle)
);
};
var segmentsIntersectAt = (a, b) => {
const a0 = vectorFromPoint(a[0]);
const a1 = vectorFromPoint(a[1]);
const b0 = vectorFromPoint(b[0]);
const b1 = vectorFromPoint(b[1]);
const r = vectorSubtract(a1, a0);
const s = vectorSubtract(b1, b0);
const denominator = vectorCross(r, s);
if (denominator === 0) {
return null;
}
const i = vectorSubtract(vectorFromPoint(b[0]), vectorFromPoint(a[0]));
const u = vectorCross(i, r) / denominator;
const t = vectorCross(i, s) / denominator;
if (u === 0) {
return null;
}
const p = vectorAdd(a0, vectorScale(r, t));
if (t >= 0 && t < 1 && u >= 0 && u < 1) {
return pointFromVector(p);
}
return null;
};
var pointOnLineSegment = (point, line2, threshold = PRECISION) => {
const distance = distanceToLineSegment(point, line2);
if (distance === 0) {
return true;
}
return distance < threshold;
};
var distanceToLineSegment = (point, line2) => {
const [x, y] = point;
const [[x1, y1], [x2, y2]] = line2;
const A = x - x1;
const B = y - y1;
const C = x2 - x1;
const D = y2 - y1;
const dot = A * C + B * D;
const len_sq = C * C + D * D;
let param = -1;
if (len_sq !== 0) {
param = dot / len_sq;
}
let xx;
let yy;
if (param < 0) {
xx = x1;
yy = y1;
} else if (param > 1) {
xx = x2;
yy = y2;
} else {
xx = x1 + param * C;
yy = y1 + param * D;
}
const dx = x - xx;
const dy = y - yy;
return Math.sqrt(dx * dx + dy * dy);
};
function lineSegmentIntersectionPoints(l, s, threshold) {
const candidate = linesIntersectAt(line(l[0], l[1]), line(s[0], s[1]));
if (!candidate || !pointOnLineSegment(candidate, s, threshold) || !pointOnLineSegment(candidate, l, threshold)) {
return null;
}
return candidate;
}
// src/polygon.ts
function polygon(...points) {
return polygonClose(points);
}
function polygonFromPoints(points) {
return polygonClose(points);
}
var polygonIncludesPoint = (point, polygon2) => {
const x = point[0];
const y = point[1];
let inside = false;
for (let i = 0, j = polygon2.length - 1; i < polygon2.length; j = i++) {
const xi = polygon2[i][0];
const yi = polygon2[i][1];
const xj = polygon2[j][0];
const yj = polygon2[j][1];
if ((yi > y && yj <= y || yi <= y && yj > y) && x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
inside = !inside;
}
}
return inside;
};
var polygonIncludesPointNonZero = (point, polygon2) => {
const [x, y] = point;
let windingNumber = 0;
for (let i = 0; i < polygon2.length; i++) {
const j = (i + 1) % polygon2.length;
const [xi, yi] = polygon2[i];
const [xj, yj] = polygon2[j];
if (yi <= y) {
if (yj > y) {
if ((xj - xi) * (y - yi) - (x - xi) * (yj - yi) > 0) {
windingNumber++;
}
}
} else if (yj <= y) {
if ((xj - xi) * (y - yi) - (x - xi) * (yj - yi) < 0) {
windingNumber--;
}
}
}
return windingNumber !== 0;
};
var pointOnPolygon = (p, poly, threshold = PRECISION) => {
let on = false;
for (let i = 0, l = poly.length - 1; i < l; i++) {
if (pointOnLineSegment(p, lineSegment(poly[i], poly[i + 1]), threshold)) {
on = true;
break;
}
}
return on;
};
function polygonClose(polygon2) {
return polygonIsClosed(polygon2) ? polygon2 : [...polygon2, polygon2[0]];
}
function polygonIsClosed(polygon2) {
return pointsEqual(polygon2[0], polygon2[polygon2.length - 1]);
}
// src/range.ts
import { toBrandedType } from "@excalidraw/common";
function rangeInclusive(start, end) {
return toBrandedType([start, end]);
}
function rangeInclusiveFromPair(pair) {
return toBrandedType(pair);
}
var rangesOverlap = ([a0, a1], [b0, b1]) => {
if (a0 <= b0) {
return a1 >= b0;
}
if (a0 >= b0) {
return b1 >= a0;
}
return false;
};
var rangeIntersection = ([a0, a1], [b0, b1]) => {
const rangeStart = Math.max(a0, b0);
const rangeEnd = Math.min(a1, b1);
if (rangeStart <= rangeEnd) {
return toBrandedType([rangeStart, rangeEnd]);
}
return null;
};
var rangeIncludesValue = (value, [min, max]) => {
return value >= min && value <= max;
};
// src/rectangle.ts
function rectangle(topLeft, bottomRight) {
return [topLeft, bottomRight];
}
function rectangleFromNumberSequence(minX, minY, maxX, maxY) {
return rectangle(pointFrom(minX, minY), pointFrom(maxX, maxY));
}
function rectangleIntersectLineSegment(r, l) {
return [
lineSegment(r[0], pointFrom(r[1][0], r[0][1])),
lineSegment(pointFrom(r[1][0], r[0][1]), r[1]),
lineSegment(r[1], pointFrom(r[0][0], r[1][1])),
lineSegment(pointFrom(r[0][0], r[1][1]), r[0])
].map((s) => lineSegmentIntersectionPoints(l, s)).filter((i) => !!i);
}
function rectangleIntersectRectangle(rectangle1, rectangle2) {
const [[minX1, minY1], [maxX1, maxY1]] = rectangle1;
const [[minX2, minY2], [maxX2, maxY2]] = rectangle2;
return minX1 < maxX2 && maxX1 > minX2 && minY1 < maxY2 && maxY1 > minY2;
}
// src/triangle.ts
function triangleIncludesPoint([a, b, c], p) {
const triangleSign = (p1, p2, p3) => (p1[0] - p3[0]) * (p2[1] - p3[1]) - (p2[0] - p3[0]) * (p1[1] - p3[1]);
const d1 = triangleSign(p, a, b);
const d2 = triangleSign(p, b, c);
const d3 = triangleSign(p, c, a);
const has_neg = d1 < 0 || d2 < 0 || d3 < 0;
const has_pos = d1 > 0 || d2 > 0 || d3 > 0;
return !(has_neg && has_pos);
}
export {
PRECISION,
average,
bezierEquation,
cartesian2Polar,
clamp,
curve,
curveCatmullRomCubicApproxPoints,
curveCatmullRomQuadraticApproxPoints,
curveClosestPoint,
curveIntersectLineSegment,
curveLength,
curveLengthAtParameter,
curveOffsetPoints,
curvePointAtLength,
curvePointDistance,
curveTangent,
degreesToRadians,
distanceToLineSegment,
ellipse,
ellipseDistanceFromPoint,
ellipseIncludesPoint,
ellipseLineIntersectionPoints,
ellipseSegmentInterceptPoints,
ellipseTouchesPoint,
isCloseTo,
isCurve,
isFiniteNumber,
isLineSegment,
isPoint,
isPointWithinBounds,
isRightAngleRads,
isVector,
line,
lineSegment,
lineSegmentIntersectionPoints,
lineSegmentRotate,
linesIntersectAt,
normalizeRadians,
offsetPointsForQuadraticBezier,
pointCenter,
pointDistance,
pointDistanceSq,
pointFrom,
pointFromArray,
pointFromPair,
pointFromVector,
pointOnLineSegment,
pointOnPolygon,
pointRotateDegs,
pointRotateRads,
pointScaleFromOrigin,
pointTranslate,
pointsEqual,
polygon,
polygonFromPoints,
polygonIncludesPoint,
polygonIncludesPointNonZero,
radiansBetweenAngles,
radiansDifference,
radiansToDegrees,
rangeIncludesValue,
rangeInclusive,
rangeInclusiveFromPair,
rangeIntersection,
rangesOverlap,
rectangle,
rectangleFromNumberSequence,
rectangleIntersectLineSegment,
rectangleIntersectRectangle,
round,
roundToStep,
segmentsIntersectAt,
triangleIncludesPoint,
vector,
vectorAdd,
vectorCross,
vectorDot,
vectorFromPoint,
vectorMagnitude,
vectorMagnitudeSq,
vectorNormal,
vectorNormalize,
vectorScale,
vectorSubtract
};
//# sourceMappingURL=index.js.map