perfect-arrows
Advanced tools
Comparing version 0.2.0 to 0.2.1
@@ -0,1 +1,5 @@ | ||
# 0.2.1 | ||
- Adds box to box algorithm. | ||
# 0.2.0 | ||
@@ -2,0 +6,0 @@ |
@@ -1,2 +0,2 @@ | ||
import { getArrow, ArrowOptions } from "./lib"; | ||
export { getArrow, ArrowOptions }; | ||
import { getArrow, getBoxToBoxArrow, ArrowOptions } from "./lib"; | ||
export { getArrow, getBoxToBoxArrow, ArrowOptions }; |
@@ -13,2 +13,3 @@ export declare type ArrowOptions = { | ||
* getArrow | ||
* Get the points for a linking line between two points. | ||
* @description Draw an arrow between two points. | ||
@@ -15,0 +16,0 @@ * @param x0 The x position of the "from" point. |
import getArrow, { ArrowOptions } from "./getArrow"; | ||
export { getArrow, ArrowOptions }; | ||
import getBoxToBoxArrow from "./getBoxToBoxArrow"; | ||
export { getArrow, ArrowOptions, getBoxToBoxArrow }; |
/** | ||
* Modulate a value between two ranges | ||
* Modulate a value between two ranges. | ||
* @param value | ||
@@ -11,7 +11,7 @@ * @param a from [low, high] | ||
* Rotate a point around a center. | ||
* @param x | ||
* @param y | ||
* @param cx | ||
* @param cy | ||
* @param angle | ||
* @param x The x-axis coordinate of the point. | ||
* @param y The y-axis coordinate of the point. | ||
* @param cx The x-axis coordinate of the point to rotate round. | ||
* @param cy The y-axis coordinate of the point to rotate round. | ||
* @param angle The distance (in radians) to rotate. | ||
*/ | ||
@@ -21,6 +21,6 @@ export declare function rotatePoint(x: number, y: number, cx: number, cy: number, angle: number): number[]; | ||
* Get the distance between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -30,6 +30,6 @@ export declare function getDistance(x0: number, y0: number, x1: number, y1: number): number; | ||
* Get an angle (radians) between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -47,6 +47,6 @@ export declare function getAngle(x0: number, y0: number, x1: number, y1: number): number; | ||
* Get a point between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
* @param d Normalized | ||
@@ -57,4 +57,4 @@ */ | ||
* Get the sector of an angle (e.g. quadrant, octant) | ||
* @param a angle | ||
* @param s number of sectors | ||
* @param a The angle to check. | ||
* @param s The number of sectors to check. | ||
*/ | ||
@@ -64,2 +64,106 @@ export declare function getSector(a: number, s?: number): number; | ||
* Get a normal value representing how close two points are from being at a 45 degree angle. | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
export declare function getAngliness(x0: number, y0: number, x1: number, y1: number): number; | ||
/** | ||
* Get the points at which an ellipse intersects a rectangle. | ||
* @param x | ||
* @param y | ||
* @param w | ||
* @param h | ||
* @param cx | ||
* @param cy | ||
* @param rx | ||
* @param ry | ||
* @param angle | ||
*/ | ||
export declare function getEllipseRectangleIntersectionPoints(x: number, y: number, w: number, h: number, cx: number, cy: number, rx: number, ry: number, angle: number): number[][]; | ||
/** | ||
* Find the point(s) where a line segment intersects an ellipse. | ||
* @param x0 The x-axis coordinate of the line's start point. | ||
* @param y0 The y-axis coordinate of the line's start point. | ||
* @param x1 The x-axis coordinate of the line's end point. | ||
* @param y1 The y-axis coordinate of the line's end point. | ||
* @param cx The x-axis (horizontal) coordinate of the ellipse's center. | ||
* @param cy The y-axis (vertical) coordinate of the ellipse's center. | ||
* @param rx The ellipse's major-axis radius. Must be non-negative. | ||
* @param ry The ellipse's minor-axis radius. Must be non-negative. | ||
* @param rotation The rotation of the ellipse, expressed in radians. | ||
* @param segment_only When true, will test the segment as a line (of infinite length). | ||
*/ | ||
export declare function getEllipseSegmentIntersections(x0: number, y0: number, x1: number, y1: number, cx: number, cy: number, rx: number, ry: number, rotation?: number, segment_only?: boolean): number[][]; | ||
/** | ||
* Check whether two rectangles will collide (overlap). | ||
* @param x0 The x-axis coordinate of the first rectangle. | ||
* @param y0 The y-axis coordinate of the first rectangle. | ||
* @param w0 The width of the first rectangle. | ||
* @param h0 The height of the first rectangle. | ||
* @param x1 The x-axis coordinate of the second rectangle. | ||
* @param y1 The y-axis coordinate of the second rectangle. | ||
* @param w1 The width of the second rectangle. | ||
* @param h1 The height of the second rectangle. | ||
*/ | ||
export declare function doRectanglesCollide(x0: number, y0: number, w0: number, h0: number, x1: number, y1: number, w1: number, h1: number): boolean; | ||
/** | ||
* Find the point(s) where a segment intersects a rectangle. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of the segment's starting point. | ||
* @param x1 The x-axis coordinate of the segment's ending point. | ||
* @param y1 The y-axis coordinate of the segment's ending point. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
*/ | ||
export declare function getSegmentRectangleIntersectionPoints(x0: number, y0: number, x1: number, y1: number, x: number, y: number, w: number, h: number): number[][]; | ||
/** | ||
* Find the point, if any, where two segments intersect. | ||
* @param x0 The x-axis coordinate of the first segment's starting point. | ||
* @param y0 The y-axis coordinate of the first segment's starting point. | ||
* @param x1 The x-axis coordinate of the first segment's ending point. | ||
* @param y1 The y-axis coordinate of the first segment's ending point. | ||
* @param x2 The x-axis coordinate of the second segment's starting point. | ||
* @param y2 The y-axis coordinate of the second segment's starting point. | ||
* @param x3 The x-axis coordinate of the second segment's ending point. | ||
* @param y3 The y-axis coordinate of the second segment's ending point. | ||
*/ | ||
export declare function getSegmentSegmentIntersection(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number): number[] | undefined; | ||
/** | ||
* Get the intersection points between a line segment and a rectangle with rounded corners. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
* @param r The corner radius of the rectangle. | ||
*/ | ||
export declare function getSegmentRoundedRectangleIntersectionPoints(x0: number, y0: number, x1: number, y1: number, x: number, y: number, w: number, h: number, r: number): number[][]; | ||
/** | ||
* Get the point(s) where a line segment intersects a circle. | ||
* @param cx The x-axis coordinate of the circle's center. | ||
* @param cy The y-axis coordinate of the circle's center. | ||
* @param r The circle's radius. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
*/ | ||
export declare function getSegmentCircleIntersections(cx: number, cy: number, r: number, x0: number, y0: number, x1: number, y1: number): number[][]; | ||
/** | ||
* Normalize an angle (in radians) | ||
* @param radians The radians quantity to normalize. | ||
*/ | ||
export declare function normalizeAngle(radians: number): number; | ||
/** | ||
* | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param w | ||
* @param h | ||
* @param x0 | ||
@@ -70,2 +174,19 @@ * @param y0 | ||
*/ | ||
export declare function getAngliness(x0: number, y0: number, x1: number, y1: number): number; | ||
export declare function getRayRectangleIntersectionPoints(ox: number, oy: number, dx: number, dy: number, x: number, y: number, w: number, h: number): number[][]; | ||
/** | ||
* Get the point at which a ray intersects a segment. | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param dx The x-axis delta of the angle. | ||
* @param dy The y-axis delta of the angle. | ||
* @param x0 The x-axis coordinate of the segment's start point. | ||
* @param y0 The y-axis coordinate of the segment's start point. | ||
* @param x1 The x-axis coordinate of the segment's end point. | ||
* @param y1 The y-axis coordinate of the segment's end point. | ||
*/ | ||
export declare function getRaySegmentIntersection(x: number, y: number, dx: number, dy: number, x0: number, y0: number, x1: number, y1: number): number[] | undefined; | ||
/** | ||
* Get the normalized delta (x and y) for an angle. | ||
* @param angle The angle in radians | ||
*/ | ||
export declare function getDelta(angle: number): number[]; |
@@ -6,3 +6,3 @@ 'use strict'; | ||
/** | ||
* Modulate a value between two ranges | ||
* Modulate a value between two ranges. | ||
* @param value | ||
@@ -33,7 +33,7 @@ * @param a from [low, high] | ||
* Rotate a point around a center. | ||
* @param x | ||
* @param y | ||
* @param cx | ||
* @param cy | ||
* @param angle | ||
* @param x The x-axis coordinate of the point. | ||
* @param y The y-axis coordinate of the point. | ||
* @param cx The x-axis coordinate of the point to rotate round. | ||
* @param cy The y-axis coordinate of the point to rotate round. | ||
* @param angle The distance (in radians) to rotate. | ||
*/ | ||
@@ -52,6 +52,6 @@ | ||
* Get the distance between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -64,6 +64,6 @@ | ||
* Get an angle (radians) between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -87,6 +87,6 @@ | ||
* Get a point between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
* @param d Normalized | ||
@@ -104,4 +104,4 @@ */ | ||
* Get the sector of an angle (e.g. quadrant, octant) | ||
* @param a angle | ||
* @param s number of sectors | ||
* @param a The angle to check. | ||
* @param s The number of sectors to check. | ||
*/ | ||
@@ -118,2 +118,207 @@ | ||
* Get a normal value representing how close two points are from being at a 45 degree angle. | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
function getAngliness(x0, y0, x1, y1) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)); | ||
} | ||
/** | ||
* Check whether two rectangles will collide (overlap). | ||
* @param x0 The x-axis coordinate of the first rectangle. | ||
* @param y0 The y-axis coordinate of the first rectangle. | ||
* @param w0 The width of the first rectangle. | ||
* @param h0 The height of the first rectangle. | ||
* @param x1 The x-axis coordinate of the second rectangle. | ||
* @param y1 The y-axis coordinate of the second rectangle. | ||
* @param w1 The width of the second rectangle. | ||
* @param h1 The height of the second rectangle. | ||
*/ | ||
function doRectanglesCollide(x0, y0, w0, h0, x1, y1, w1, h1) { | ||
return !(x0 >= x1 + w1 || x1 >= x0 + w0 || y0 >= y1 + h1 || y1 >= y0 + h0); | ||
} | ||
/** | ||
* Find the point(s) where a segment intersects a rectangle. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of the segment's starting point. | ||
* @param x1 The x-axis coordinate of the segment's ending point. | ||
* @param y1 The y-axis coordinate of the segment's ending point. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
*/ | ||
function getSegmentRectangleIntersectionPoints(x0, y0, x1, y1, x, y, w, h) { | ||
var points = []; | ||
for (var _i2 = 0, _arr2 = [[x, y, x + w, y], [x + w, y, x + w, y + h], [x + w, y + h, x, y + h], [x, y + h, x, y]]; _i2 < _arr2.length; _i2++) { | ||
var _arr2$_i = _arr2[_i2], | ||
px0 = _arr2$_i[0], | ||
py0 = _arr2$_i[1], | ||
px1 = _arr2$_i[2], | ||
py1 = _arr2$_i[3]; | ||
var ints = getSegmentSegmentIntersection(px0, py0, px1, py1, x0, y0, x1, y1); | ||
if (ints) { | ||
points.push(ints); | ||
} | ||
} | ||
return points; | ||
} | ||
/** | ||
* Find the point, if any, where two segments intersect. | ||
* @param x0 The x-axis coordinate of the first segment's starting point. | ||
* @param y0 The y-axis coordinate of the first segment's starting point. | ||
* @param x1 The x-axis coordinate of the first segment's ending point. | ||
* @param y1 The y-axis coordinate of the first segment's ending point. | ||
* @param x2 The x-axis coordinate of the second segment's starting point. | ||
* @param y2 The y-axis coordinate of the second segment's starting point. | ||
* @param x3 The x-axis coordinate of the second segment's ending point. | ||
* @param y3 The y-axis coordinate of the second segment's ending point. | ||
*/ | ||
function getSegmentSegmentIntersection(x0, y0, x1, y1, x2, y2, x3, y3) { | ||
var denom = (y3 - y2) * (x1 - x0) - (x3 - x2) * (y1 - y0); | ||
var numeA = (x3 - x2) * (y0 - y2) - (y3 - y2) * (x0 - x2); | ||
var numeB = (x1 - x0) * (y0 - y2) - (y1 - y0) * (x0 - x2); | ||
if (denom === 0) { | ||
if (numeA === 0 && numeB === 0) { | ||
return undefined; // Colinear | ||
} | ||
return undefined; // Parallel | ||
} | ||
var uA = numeA / denom; | ||
var uB = numeB / denom; | ||
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { | ||
return [x0 + uA * (x1 - x0), y0 + uA * (y1 - y0)]; | ||
} | ||
return undefined; // No intersection | ||
} | ||
/** | ||
* Get the intersection points between a line segment and a rectangle with rounded corners. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
* @param r The corner radius of the rectangle. | ||
*/ | ||
function getSegmentRoundedRectangleIntersectionPoints(x0, y0, x1, y1, x, y, w, h, r) { | ||
var mx = x + w, | ||
my = y + h, | ||
rx = x + r, | ||
ry = y + r, | ||
mrx = x + w - r, | ||
mry = y + h - r; | ||
var segments = [[x, mry, x, ry, x, y], [rx, y, mrx, y, mx, y], [mx, ry, mx, mry, mx, my], [mrx, my, rx, my, x, my]]; | ||
var corners = [[rx, ry, Math.PI, Math.PI * 1.5], [mrx, ry, Math.PI * 1.5, Math.PI * 2], [mrx, mry, 0, Math.PI * 0.5], [rx, mry, Math.PI * 0.5, Math.PI]]; | ||
var points = []; | ||
segments.forEach(function (segment, i) { | ||
var px0 = segment[0], | ||
py0 = segment[1], | ||
px1 = segment[2], | ||
py1 = segment[3]; | ||
var _corners$i = corners[i], | ||
cx = _corners$i[0], | ||
cy = _corners$i[1], | ||
as = _corners$i[2], | ||
ae = _corners$i[3]; | ||
getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1).filter(function (pt) { | ||
var pointAngle = normalizeAngle(getAngle(cx, cy, pt[0], pt[1])); | ||
return pointAngle > as && pointAngle < ae; | ||
}).forEach(function (pt) { | ||
return points.push(pt); | ||
}); | ||
var segmentInt = getSegmentSegmentIntersection(x0, y0, x1, y1, px0, py0, px1, py1); | ||
if (!!segmentInt) { | ||
points.push(segmentInt); | ||
} | ||
}); | ||
return points; | ||
} | ||
/** | ||
* Get the point(s) where a line segment intersects a circle. | ||
* @param cx The x-axis coordinate of the circle's center. | ||
* @param cy The y-axis coordinate of the circle's center. | ||
* @param r The circle's radius. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
*/ | ||
function getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1) { | ||
var b, | ||
c, | ||
d, | ||
u1, | ||
u2, | ||
ret, | ||
retP1, | ||
retP2, | ||
v1 = [x1 - x0, y1 - y0], | ||
v2 = [x0 - cx, y0 - cy]; | ||
b = v1[0] * v2[0] + v1[1] * v2[1]; | ||
c = 2 * (v1[0] * v1[0] + v1[1] * v1[1]); | ||
b *= -2; | ||
d = Math.sqrt(b * b - 2 * c * (v2[0] * v2[0] + v2[1] * v2[1] - r * r)); | ||
if (isNaN(d)) { | ||
// no intercept | ||
return []; | ||
} | ||
u1 = (b - d) / c; // these represent the unit distance of point one and two on the line | ||
u2 = (b + d) / c; | ||
retP1 = []; // return points | ||
retP2 = []; | ||
ret = []; // return array | ||
if (u1 <= 1 && u1 >= 0) { | ||
// add point if on the line segment | ||
retP1[0] = x0 + v1[0] * u1; | ||
retP1[1] = y0 + v1[1] * u1; | ||
ret[0] = retP1; | ||
} | ||
if (u2 <= 1 && u2 >= 0) { | ||
// second add point if on the line segment | ||
retP2[0] = x0 + v1[0] * u2; | ||
retP2[1] = y0 + v1[1] * u2; | ||
ret[ret.length] = retP2; | ||
} | ||
return ret; | ||
} | ||
/** | ||
* Normalize an angle (in radians) | ||
* @param radians The radians quantity to normalize. | ||
*/ | ||
function normalizeAngle(radians) { | ||
return radians - Math.PI * 2 * Math.floor(radians / (Math.PI * 2)); | ||
} | ||
/** | ||
* | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param w | ||
* @param h | ||
* @param x0 | ||
@@ -125,8 +330,62 @@ * @param y0 | ||
function getAngliness(x0, y0, x1, y1) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)); | ||
function getRayRectangleIntersectionPoints(ox, oy, dx, dy, x, y, w, h) { | ||
var points = []; | ||
for (var _i3 = 0, _arr3 = [[x, y, x + w, y], [x + w, y, x + w, y + h], [x + w, y + h, x, y + h], [x, y + h, x, y]]; _i3 < _arr3.length; _i3++) { | ||
var _arr3$_i = _arr3[_i3], | ||
px0 = _arr3$_i[0], | ||
py0 = _arr3$_i[1], | ||
px1 = _arr3$_i[2], | ||
py1 = _arr3$_i[3]; | ||
var ints = getRaySegmentIntersection(ox, oy, dx, dy, px0, py0, px1, py1); | ||
if (ints) { | ||
points.push(ints); | ||
} | ||
} | ||
return points; | ||
} | ||
/** | ||
* Get the point at which a ray intersects a segment. | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param dx The x-axis delta of the angle. | ||
* @param dy The y-axis delta of the angle. | ||
* @param x0 The x-axis coordinate of the segment's start point. | ||
* @param y0 The y-axis coordinate of the segment's start point. | ||
* @param x1 The x-axis coordinate of the segment's end point. | ||
* @param y1 The y-axis coordinate of the segment's end point. | ||
*/ | ||
function getRaySegmentIntersection(x, y, dx, dy, x0, y0, x1, y1) { | ||
var r, s, d; | ||
if (dy * (x1 - x0) !== dx * (y1 - y0)) { | ||
d = dx * (y1 - y0) - dy * (x1 - x0); | ||
if (d !== 0) { | ||
r = ((y - y0) * (x1 - x0) - (x - x0) * (y1 - y0)) / d; | ||
s = ((y - y0) * dx - (x - x0) * dy) / d; | ||
if (r >= 0 && s >= 0 && s <= 1) { | ||
return [x + r * dx, y + r * dy]; | ||
} | ||
} | ||
} | ||
return undefined; | ||
} | ||
/** | ||
* Get the normalized delta (x and y) for an angle. | ||
* @param angle The angle in radians | ||
*/ | ||
function getDelta(angle) { | ||
return [Math.cos(angle), Math.sin(angle)]; | ||
} | ||
/** | ||
* getArrow | ||
* Get the points for a linking line between two points. | ||
* @description Draw an arrow between two points. | ||
@@ -276,3 +535,161 @@ * @param x0 The x position of the "from" point. | ||
/** | ||
* getArrowBetweenBoxes | ||
* Get the points for a linking line between two boxes. | ||
* @param x0 The x-axis coordinate of the first box. | ||
* @param y0 The y-axis coordinate of the first box. | ||
* @param w0 The width of the first box. | ||
* @param h0 The height of the first box. | ||
* @param x1 The x-axis coordinate of the second box. | ||
* @param y1 The y-axis coordinate of the second box. | ||
* @param w1 The width of the second box. | ||
* @param h1 The height of the second box. | ||
* @param options | ||
*/ | ||
function getBoxToBoxArrow(x0, y0, w0, h0, x1, y1, w1, h1, options) { | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var i0x, i0y, i1x, i1y; | ||
var _options = options, | ||
_options$bow = _options.bow, | ||
bow = _options$bow === void 0 ? 0 : _options$bow, | ||
_options$stretch = _options.stretch, | ||
stretch = _options$stretch === void 0 ? 0.5 : _options$stretch, | ||
_options$stretchMin = _options.stretchMin, | ||
stretchMin = _options$stretchMin === void 0 ? 0 : _options$stretchMin, | ||
_options$stretchMax = _options.stretchMax, | ||
stretchMax = _options$stretchMax === void 0 ? 420 : _options$stretchMax, | ||
_options$padStart = _options.padStart, | ||
padStart = _options$padStart === void 0 ? 20 : _options$padStart, | ||
_options$padEnd = _options.padEnd, | ||
padEnd = _options$padEnd === void 0 ? 20 : _options$padEnd, | ||
_options$flip = _options.flip, | ||
flip = _options$flip === void 0 ? false : _options$flip, | ||
_options$straights = _options.straights, | ||
straights = _options$straights === void 0 ? true : _options$straights; | ||
var cx0 = x0 + w0 / 2, | ||
cy0 = y0 + h0 / 2, | ||
cx1 = x1 + w1 / 2, | ||
cy1 = y1 + h1 / 2, | ||
px0 = x0 - padStart, | ||
py0 = y0 - padStart, | ||
pw0 = w0 + padStart * 2, | ||
ph0 = h0 + padStart * 2, | ||
px1 = x1 - padEnd, | ||
py1 = y1 - padEnd, | ||
pw1 = w1 + padEnd * 2, | ||
ph1 = h1 + padEnd * 2; | ||
var _getPointBetween = getPointBetween(cx0, cy0, cx1, cy1, 0.5), | ||
mx = _getPointBetween[0], | ||
my = _getPointBetween[1]; | ||
var angle = getAngle(cx0, cy0, cx1, cy1); | ||
var angliness = getAngliness(cx0, cy0, cx1, cy1); | ||
var minLength = Math.min(pw0, ph0, pw1, ph1) * 2; | ||
var direct = getDistance(x0, y0, x1, y1); | ||
var overflow = minLength - direct; | ||
var isColliding = doRectanglesCollide(px0, py0, pw0, ph0, px1, py1, pw1, ph1); | ||
if (straights && ([0, 1, Infinity].includes(angliness) || cx0 === cx1 || cy0 === cy1) && !isColliding) { | ||
var _getSegmentRectangleI = getSegmentRectangleIntersectionPoints(cx0, cy0, cx1, cy1, px0, py0, pw0, ph0); | ||
var _getSegmentRectangleI2 = _getSegmentRectangleI[0]; | ||
i0x = _getSegmentRectangleI2[0]; | ||
i0y = _getSegmentRectangleI2[1]; | ||
var _getSegmentRectangleI3 = getSegmentRectangleIntersectionPoints(cx0, cy0, cx1, cy1, px1, py1, pw1, ph1); | ||
var _getSegmentRectangleI4 = _getSegmentRectangleI3[0]; | ||
i1x = _getSegmentRectangleI4[0]; | ||
i1y = _getSegmentRectangleI4[1]; | ||
return [i0x, i0y, mx, my, i1x, i1y, angle, angle + Math.PI, angle]; | ||
} // ⤜⤏ Arrow is an arc! | ||
// Is the arc clockwise or counterclockwise? | ||
var rot = (getSector(angle) % 2 === 0 ? 1 : -1) * (flip ? -1 : 1); // Calculate how much the line should "bow" away from center | ||
var stretchEffect = mod(direct, [stretchMin, stretchMax], [1, 0], true); | ||
var arc = bow + stretchEffect * stretch / 2; // Step 1 ⤜⤏ Find padded points. | ||
// Get control point. | ||
var _getPointBetween2 = getPointBetween(cx0, cy0, cx1, cy1, 0.5 - arc), | ||
cx = _getPointBetween2[0], | ||
cy = _getPointBetween2[1]; | ||
var _rotatePoint = rotatePoint(cx, cy, mx, my, Math.PI / 2 * rot); | ||
cx = _rotatePoint[0]; | ||
cy = _rotatePoint[1]; | ||
if (overflow > 0) { | ||
var _projectPoint = projectPoint(cx, cy, angle + Math.PI / 2 * -rot, overflow * arc); | ||
cx = _projectPoint[0]; | ||
cy = _projectPoint[1]; | ||
} // Get padded start point. | ||
var i0 = getSegmentRoundedRectangleIntersectionPoints(cx0, cy0, cx, cy, px0, py0, pw0, ph0, padStart); // Get padded end point. | ||
var i1 = getSegmentRoundedRectangleIntersectionPoints(cx1, cy1, cx, cy, px1, py1, pw1, ph1, padEnd); // If we don't have intersections, or if the distance between the | ||
// intersections is very small, use the backup point-finding stategy | ||
if (!(i0[0] && i1[0]) || getDistance(i0[0][0], i0[0][1], i1[0][0], i1[0][1]) < padStart + padEnd) { | ||
// Basically, shoot a ray based on the opposite angle between | ||
// the two centers, and see where it intersects the rectangles. | ||
// This part can definitely be improved! | ||
var _getDelta = getDelta(angle + Math.PI), | ||
dx = _getDelta[0], | ||
dy = _getDelta[1]; | ||
var _getRayRectangleInter = getRayRectangleIntersectionPoints(cx0, cy0, dx, dy, px0, py0, pw0, ph0); | ||
var _getRayRectangleInter2 = _getRayRectangleInter[0]; | ||
i0x = _getRayRectangleInter2[0]; | ||
i0y = _getRayRectangleInter2[1]; | ||
var _getRayRectangleInter3 = getRayRectangleIntersectionPoints(cx1, cy1, dx, dy, px1, py1, pw1, ph1); | ||
var _getRayRectangleInter4 = _getRayRectangleInter3[0]; | ||
i1x = _getRayRectangleInter4[0]; | ||
i1y = _getRayRectangleInter4[1]; | ||
arc = (bow + stretchEffect * stretch) * -1; | ||
} else { | ||
var _i0$ = i0[0]; | ||
i0x = _i0$[0]; | ||
i0y = _i0$[1]; | ||
var _i1$ = i1[0]; | ||
i1x = _i1$[0]; | ||
i1y = _i1$[1]; | ||
arc = bow + stretchEffect * stretch; | ||
} // Step 3 ⤜⤏ Calculate arrow points using same algorithm as getArrow | ||
var _getPointBetween3 = getPointBetween(i0x, i0y, i1x, i1y, 0.5), | ||
mx1 = _getPointBetween3[0], | ||
my1 = _getPointBetween3[1]; | ||
var _getPointBetween4 = getPointBetween(i0x, i0y, i1x, i1y, 0.5 - arc); | ||
cx = _getPointBetween4[0]; | ||
cy = _getPointBetween4[1]; | ||
var _rotatePoint2 = rotatePoint(cx, cy, mx1, my1, Math.PI / (2 * rot)); | ||
cx = _rotatePoint2[0]; | ||
cy = _rotatePoint2[1]; | ||
var as = getAngle(cx, cy, i0x, i0y); | ||
var ae = getAngle(cx, cy, i1x, i1y); | ||
return [i0x, i0y, cx, cy, i1x, i1y, ae, as, angle]; | ||
} | ||
exports.getArrow = getArrow; | ||
exports.getBoxToBoxArrow = getBoxToBoxArrow; | ||
//# sourceMappingURL=perfect-arrows.cjs.development.js.map |
@@ -1,2 +0,2 @@ | ||
"use strict";function t(t,r,n,i,a){var o=Math.sin(a),e=Math.cos(a),u=t-n,h=r-i;return[u*e-h*o+n,u*o+h*e+i]}function r(t,r,n,i){return Math.atan2(i-r,n-t)}function n(t,r,n,i){return[Math.cos(n)*i+t,Math.sin(n)*i+r]}function i(t,r,n,i,a){return void 0===a&&(a=.5),[t+(n-t)*a,r+(i-r)*a]}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getArrow=function(a,o,e,u,h){void 0===h&&(h={});var M=h.bow,d=void 0===M?0:M,v=h.stretch,c=void 0===v?.5:v,f=h.stretchMin,s=void 0===f?0:f,p=h.stretchMax,l=void 0===p?420:p,x=h.padStart,I=void 0===x?0:x,P=h.padEnd,m=void 0===P?0:P,b=h.flip,y=void 0!==b&&b,g=h.straights,w=void 0===g||g,_=r(a,o,e,u),j=function(t,r,n,i){return Math.hypot(i-r,n-t)}(a,o,e,u),A=function(t,r,n,i){return Math.abs((n-t)/2/((i-r)/2))}(a,o,e,u);if(j<2*(I+m)||0===d&&0===c||w&&[0,1,Infinity].includes(A)){var E=Math.max(0,Math.min(j-I,I)),O=Math.max(0,Math.min(j-E,m)),S=n(a,o,_,E),k=S[0],q=S[1],z=n(e,u,_+Math.PI,O),B=z[0],C=z[1],D=i(k,q,B,C,.5);return[k,q,D[0],D[1],B,C,_,_,_]}var F,G=(0==(void 0===F&&(F=8),Math.floor(F*(.5+_/(2*Math.PI)%F))%2)?1:-1)*(y?-1:1),H=d+function(t,r,n,i){void 0===i&&(i=!1);var a=n[0]<n[1]?[n[0],n[1]]:[n[1],n[0]],o=a[0],e=a[1],u=n[0]+(t-r[0])/(r[1]-n[0])*(n[1]-n[0]);if(i){if(u<o)return o;if(u>e)return e}return u}(j,[s,l],[1,0],!0)*c,J=i(a,o,e,u,.5),K=J[0],L=J[1],N=i(a,o,e,u,.5-H),Q=N[0],R=N[1],T=t(Q,R,K,L,Math.PI/2*G),U=n(a,o,r(a,o,Q=T[0],R=T[1]),I),V=U[0],W=U[1],X=n(e,u,r(e,u,Q,R),m),Y=X[0],Z=X[1],$=r(Q,R,a,o),tt=r(Q,R,e,u),rt=i(V,W,Y,Z,.5),nt=rt[0],it=rt[1],at=i(V,W,Y,Z,.5-H),ot=at[0],et=at[1],ut=t(ot,et,nt,it,Math.PI/2*G),ht=i(Q,R,ot=ut[0],et=ut[1],.5);return[V,W,ht[0],ht[1],Y,Z,tt,$,_]}; | ||
"use strict";function t(t,r,n,a){void 0===a&&(a=!1);var i=n[0]<n[1]?[n[0],n[1]]:[n[1],n[0]],o=i[0],u=i[1],h=n[0]+(t-r[0])/(r[1]-n[0])*(n[1]-n[0]);if(a){if(h<o)return o;if(h>u)return u}return h}function r(t,r,n,a,i){var o=Math.sin(i),u=Math.cos(i),h=t-n,v=r-a;return[h*u-v*o+n,h*o+v*u+a]}function n(t,r,n,a){return Math.hypot(a-r,n-t)}function a(t,r,n,a){return Math.atan2(a-r,n-t)}function i(t,r,n,a){return[Math.cos(n)*a+t,Math.sin(n)*a+r]}function o(t,r,n,a,i){return void 0===i&&(i=.5),[t+(n-t)*i,r+(a-r)*i]}function u(t,r){return void 0===r&&(r=8),Math.floor(r*(.5+t/(2*Math.PI)%r))}function h(t,r,n,a){return Math.abs((n-t)/2/((a-r)/2))}function v(t,r,n,a,i,o,u,h){for(var v=[],f=0,M=[[i,o,i+u,o],[i+u,o,i+u,o+h],[i+u,o+h,i,o+h],[i,o+h,i,o]];f<M.length;f++){var c=M[f],d=e(c[0],c[1],c[2],c[3],t,r,n,a);d&&v.push(d)}return v}function e(t,r,n,a,i,o,u,h){var v=(h-o)*(n-t)-(u-i)*(a-r);if(0!==v){var e=((u-i)*(r-o)-(h-o)*(t-i))/v,f=((n-t)*(r-o)-(a-r)*(t-i))/v;return e>=0&&e<=1&&f>=0&&f<=1?[t+e*(n-t),r+e*(a-r)]:void 0}}function f(t,r,n,i,o,u,h,v,f){var M=o+h,c=u+v,d=o+f,s=u+f,I=o+h-f,P=u+v-f,p=[[d,s,Math.PI,1.5*Math.PI],[I,s,1.5*Math.PI,2*Math.PI],[I,P,0,.5*Math.PI],[d,P,.5*Math.PI,Math.PI]],l=[];return[[o,P,o,s,o,u],[d,u,I,u,M,u],[M,s,M,P,M,c],[I,c,d,c,o,c]].forEach((function(o,u){var h=o[0],v=o[1],M=o[2],c=o[3],d=p[u],s=d[0],I=d[1],P=d[2],x=d[3];(function(t,r,n,a,i,o,u){var h,v,e,f,M,c,d,s,I=[o-a,u-i],P=[a-t,i-r];return h=I[0]*P[0]+I[1]*P[1],v=2*(I[0]*I[0]+I[1]*I[1]),h*=-2,e=Math.sqrt(h*h-2*v*(P[0]*P[0]+P[1]*P[1]-n*n)),isNaN(e)?[]:(M=(h+e)/v,d=[],s=[],c=[],(f=(h-e)/v)<=1&&f>=0&&(d[0]=a+I[0]*f,d[1]=i+I[1]*f,c[0]=d),M<=1&&M>=0&&(s[0]=a+I[0]*M,s[1]=i+I[1]*M,c[c.length]=s),c)})(s,I,f,t,r,n,i).filter((function(t){var r,n=(r=a(s,I,t[0],t[1]))-2*Math.PI*Math.floor(r/(2*Math.PI));return n>P&&n<x})).forEach((function(t){return l.push(t)}));var g=e(t,r,n,i,h,v,M,c);g&&l.push(g)})),l}function M(t,r,n,a,i,o,u,h){for(var v=[],e=0,f=[[i,o,i+u,o],[i+u,o,i+u,o+h],[i+u,o+h,i,o+h],[i,o+h,i,o]];e<f.length;e++){var M=f[e],d=c(t,r,n,a,M[0],M[1],M[2],M[3]);d&&v.push(d)}return v}function c(t,r,n,a,i,o,u,h){var v,e,f;if(a*(u-i)!=n*(h-o)&&0!=(f=n*(h-o)-a*(u-i))&&(e=((r-o)*n-(t-i)*a)/f,(v=((r-o)*(u-i)-(t-i)*(h-o))/f)>=0&&e>=0&&e<=1))return[t+v*n,r+v*a]}Object.defineProperty(exports,"__esModule",{value:!0}),exports.getArrow=function(v,e,f,M,c){void 0===c&&(c={});var d=c.bow,s=void 0===d?0:d,I=c.stretch,P=void 0===I?.5:I,p=c.stretchMin,l=void 0===p?0:p,x=c.stretchMax,g=void 0===x?420:x,m=c.padStart,b=void 0===m?0:m,w=c.padEnd,y=void 0===w?0:w,E=c.flip,A=void 0!==E&&E,B=c.straights,N=void 0===B||B,S=a(v,e,f,M),_=n(v,e,f,M),j=h(v,e,f,M);if(_<2*(b+y)||0===s&&0===P||N&&[0,1,Infinity].includes(j)){var q=Math.max(0,Math.min(_-b,b)),O=Math.max(0,Math.min(_-q,y)),T=i(v,e,S,q),k=T[0],z=T[1],C=i(f,M,S+Math.PI,O),D=C[0],F=C[1],G=o(k,z,D,F,.5);return[k,z,G[0],G[1],D,F,S,S,S]}var H=(u(S)%2==0?1:-1)*(A?-1:1),J=s+t(_,[l,g],[1,0],!0)*P,K=o(v,e,f,M,.5),L=K[0],Q=K[1],R=o(v,e,f,M,.5-J),U=R[0],V=R[1],W=r(U,V,L,Q,Math.PI/2*H),X=i(v,e,a(v,e,U=W[0],V=W[1]),b),Y=X[0],Z=X[1],$=i(f,M,a(f,M,U,V),y),tt=$[0],rt=$[1],nt=a(U,V,v,e),at=a(U,V,f,M),it=o(Y,Z,tt,rt,.5),ot=it[0],ut=it[1],ht=o(Y,Z,tt,rt,.5-J),vt=ht[0],et=ht[1],ft=r(vt,et,ot,ut,Math.PI/2*H),Mt=o(U,V,vt=ft[0],et=ft[1],.5);return[Y,Z,Mt[0],Mt[1],tt,rt,at,nt,S]},exports.getBoxToBoxArrow=function(e,c,d,s,I,P,p,l,x){var g,m,b,w;void 0===x&&(x={});var y=x.bow,E=void 0===y?0:y,A=x.stretch,B=void 0===A?.5:A,N=x.stretchMin,S=void 0===N?0:N,_=x.stretchMax,j=void 0===_?420:_,q=x.padStart,O=void 0===q?20:q,T=x.padEnd,k=void 0===T?20:T,z=x.flip,C=void 0!==z&&z,D=x.straights,F=void 0===D||D,G=e+d/2,H=c+s/2,J=I+p/2,K=P+l/2,L=e-O,Q=c-O,R=d+2*O,U=s+2*O,V=I-k,W=P-k,X=p+2*k,Y=l+2*k,Z=o(G,H,J,K,.5),$=Z[0],tt=Z[1],rt=a(G,H,J,K),nt=h(G,H,J,K),at=2*Math.min(R,U,X,Y),it=n(e,c,I,P),ot=at-it,ut=function(t,r,n,a,i,o,u,h){return!(t>=i+u||i>=t+n||r>=o+h||o>=r+a)}(L,Q,R,U,V,W,X,Y);if(F&&([0,1,Infinity].includes(nt)||G===J||H===K)&&!ut){var ht=v(G,H,J,K,L,Q,R,U)[0];g=ht[0],m=ht[1];var vt=v(G,H,J,K,V,W,X,Y)[0];return[g,m,$,tt,b=vt[0],w=vt[1],rt,rt+Math.PI,rt]}var et=(u(rt)%2==0?1:-1)*(C?-1:1),ft=t(it,[S,j],[1,0],!0),Mt=E+ft*B/2,ct=o(G,H,J,K,.5-Mt),dt=ct[0],st=ct[1],It=r(dt,st,$,tt,Math.PI/2*et);if(dt=It[0],st=It[1],ot>0){var Pt=i(dt,st,rt+Math.PI/2*-et,ot*Mt);dt=Pt[0],st=Pt[1]}var pt=f(G,H,dt,st,L,Q,R,U,O),lt=f(J,K,dt,st,V,W,X,Y,k);if(!pt[0]||!lt[0]||n(pt[0][0],pt[0][1],lt[0][0],lt[0][1])<O+k){var xt=function(t){return[Math.cos(t),Math.sin(t)]}(rt+Math.PI),gt=xt[0],mt=xt[1],bt=M(G,H,gt,mt,L,Q,R,U)[0];g=bt[0],m=bt[1];var wt=M(J,K,gt,mt,V,W,X,Y)[0];b=wt[0],w=wt[1],Mt=-1*(E+ft*B)}else{var yt=pt[0];g=yt[0],m=yt[1];var Et=lt[0];b=Et[0],w=Et[1],Mt=E+ft*B}var At=o(g,m,b,w,.5),Bt=At[0],Nt=At[1],St=o(g,m,b,w,.5-Mt),_t=r(dt=St[0],st=St[1],Bt,Nt,Math.PI/(2*et)),jt=a(dt=_t[0],st=_t[1],g,m);return[g,m,dt,st,b,w,a(dt,st,b,w),jt,rt]}; | ||
//# sourceMappingURL=perfect-arrows.cjs.production.min.js.map |
/** | ||
* Modulate a value between two ranges | ||
* Modulate a value between two ranges. | ||
* @param value | ||
@@ -28,7 +28,7 @@ * @param a from [low, high] | ||
* Rotate a point around a center. | ||
* @param x | ||
* @param y | ||
* @param cx | ||
* @param cy | ||
* @param angle | ||
* @param x The x-axis coordinate of the point. | ||
* @param y The y-axis coordinate of the point. | ||
* @param cx The x-axis coordinate of the point to rotate round. | ||
* @param cy The y-axis coordinate of the point to rotate round. | ||
* @param angle The distance (in radians) to rotate. | ||
*/ | ||
@@ -47,6 +47,6 @@ | ||
* Get the distance between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -59,6 +59,6 @@ | ||
* Get an angle (radians) between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -82,6 +82,6 @@ | ||
* Get a point between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
* @param d Normalized | ||
@@ -99,4 +99,4 @@ */ | ||
* Get the sector of an angle (e.g. quadrant, octant) | ||
* @param a angle | ||
* @param s number of sectors | ||
* @param a The angle to check. | ||
* @param s The number of sectors to check. | ||
*/ | ||
@@ -113,2 +113,207 @@ | ||
* Get a normal value representing how close two points are from being at a 45 degree angle. | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
function getAngliness(x0, y0, x1, y1) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)); | ||
} | ||
/** | ||
* Check whether two rectangles will collide (overlap). | ||
* @param x0 The x-axis coordinate of the first rectangle. | ||
* @param y0 The y-axis coordinate of the first rectangle. | ||
* @param w0 The width of the first rectangle. | ||
* @param h0 The height of the first rectangle. | ||
* @param x1 The x-axis coordinate of the second rectangle. | ||
* @param y1 The y-axis coordinate of the second rectangle. | ||
* @param w1 The width of the second rectangle. | ||
* @param h1 The height of the second rectangle. | ||
*/ | ||
function doRectanglesCollide(x0, y0, w0, h0, x1, y1, w1, h1) { | ||
return !(x0 >= x1 + w1 || x1 >= x0 + w0 || y0 >= y1 + h1 || y1 >= y0 + h0); | ||
} | ||
/** | ||
* Find the point(s) where a segment intersects a rectangle. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of the segment's starting point. | ||
* @param x1 The x-axis coordinate of the segment's ending point. | ||
* @param y1 The y-axis coordinate of the segment's ending point. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
*/ | ||
function getSegmentRectangleIntersectionPoints(x0, y0, x1, y1, x, y, w, h) { | ||
var points = []; | ||
for (var _i2 = 0, _arr2 = [[x, y, x + w, y], [x + w, y, x + w, y + h], [x + w, y + h, x, y + h], [x, y + h, x, y]]; _i2 < _arr2.length; _i2++) { | ||
var _arr2$_i = _arr2[_i2], | ||
px0 = _arr2$_i[0], | ||
py0 = _arr2$_i[1], | ||
px1 = _arr2$_i[2], | ||
py1 = _arr2$_i[3]; | ||
var ints = getSegmentSegmentIntersection(px0, py0, px1, py1, x0, y0, x1, y1); | ||
if (ints) { | ||
points.push(ints); | ||
} | ||
} | ||
return points; | ||
} | ||
/** | ||
* Find the point, if any, where two segments intersect. | ||
* @param x0 The x-axis coordinate of the first segment's starting point. | ||
* @param y0 The y-axis coordinate of the first segment's starting point. | ||
* @param x1 The x-axis coordinate of the first segment's ending point. | ||
* @param y1 The y-axis coordinate of the first segment's ending point. | ||
* @param x2 The x-axis coordinate of the second segment's starting point. | ||
* @param y2 The y-axis coordinate of the second segment's starting point. | ||
* @param x3 The x-axis coordinate of the second segment's ending point. | ||
* @param y3 The y-axis coordinate of the second segment's ending point. | ||
*/ | ||
function getSegmentSegmentIntersection(x0, y0, x1, y1, x2, y2, x3, y3) { | ||
var denom = (y3 - y2) * (x1 - x0) - (x3 - x2) * (y1 - y0); | ||
var numeA = (x3 - x2) * (y0 - y2) - (y3 - y2) * (x0 - x2); | ||
var numeB = (x1 - x0) * (y0 - y2) - (y1 - y0) * (x0 - x2); | ||
if (denom === 0) { | ||
if (numeA === 0 && numeB === 0) { | ||
return undefined; // Colinear | ||
} | ||
return undefined; // Parallel | ||
} | ||
var uA = numeA / denom; | ||
var uB = numeB / denom; | ||
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { | ||
return [x0 + uA * (x1 - x0), y0 + uA * (y1 - y0)]; | ||
} | ||
return undefined; // No intersection | ||
} | ||
/** | ||
* Get the intersection points between a line segment and a rectangle with rounded corners. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
* @param r The corner radius of the rectangle. | ||
*/ | ||
function getSegmentRoundedRectangleIntersectionPoints(x0, y0, x1, y1, x, y, w, h, r) { | ||
var mx = x + w, | ||
my = y + h, | ||
rx = x + r, | ||
ry = y + r, | ||
mrx = x + w - r, | ||
mry = y + h - r; | ||
var segments = [[x, mry, x, ry, x, y], [rx, y, mrx, y, mx, y], [mx, ry, mx, mry, mx, my], [mrx, my, rx, my, x, my]]; | ||
var corners = [[rx, ry, Math.PI, Math.PI * 1.5], [mrx, ry, Math.PI * 1.5, Math.PI * 2], [mrx, mry, 0, Math.PI * 0.5], [rx, mry, Math.PI * 0.5, Math.PI]]; | ||
var points = []; | ||
segments.forEach(function (segment, i) { | ||
var px0 = segment[0], | ||
py0 = segment[1], | ||
px1 = segment[2], | ||
py1 = segment[3]; | ||
var _corners$i = corners[i], | ||
cx = _corners$i[0], | ||
cy = _corners$i[1], | ||
as = _corners$i[2], | ||
ae = _corners$i[3]; | ||
getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1).filter(function (pt) { | ||
var pointAngle = normalizeAngle(getAngle(cx, cy, pt[0], pt[1])); | ||
return pointAngle > as && pointAngle < ae; | ||
}).forEach(function (pt) { | ||
return points.push(pt); | ||
}); | ||
var segmentInt = getSegmentSegmentIntersection(x0, y0, x1, y1, px0, py0, px1, py1); | ||
if (!!segmentInt) { | ||
points.push(segmentInt); | ||
} | ||
}); | ||
return points; | ||
} | ||
/** | ||
* Get the point(s) where a line segment intersects a circle. | ||
* @param cx The x-axis coordinate of the circle's center. | ||
* @param cy The y-axis coordinate of the circle's center. | ||
* @param r The circle's radius. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
*/ | ||
function getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1) { | ||
var b, | ||
c, | ||
d, | ||
u1, | ||
u2, | ||
ret, | ||
retP1, | ||
retP2, | ||
v1 = [x1 - x0, y1 - y0], | ||
v2 = [x0 - cx, y0 - cy]; | ||
b = v1[0] * v2[0] + v1[1] * v2[1]; | ||
c = 2 * (v1[0] * v1[0] + v1[1] * v1[1]); | ||
b *= -2; | ||
d = Math.sqrt(b * b - 2 * c * (v2[0] * v2[0] + v2[1] * v2[1] - r * r)); | ||
if (isNaN(d)) { | ||
// no intercept | ||
return []; | ||
} | ||
u1 = (b - d) / c; // these represent the unit distance of point one and two on the line | ||
u2 = (b + d) / c; | ||
retP1 = []; // return points | ||
retP2 = []; | ||
ret = []; // return array | ||
if (u1 <= 1 && u1 >= 0) { | ||
// add point if on the line segment | ||
retP1[0] = x0 + v1[0] * u1; | ||
retP1[1] = y0 + v1[1] * u1; | ||
ret[0] = retP1; | ||
} | ||
if (u2 <= 1 && u2 >= 0) { | ||
// second add point if on the line segment | ||
retP2[0] = x0 + v1[0] * u2; | ||
retP2[1] = y0 + v1[1] * u2; | ||
ret[ret.length] = retP2; | ||
} | ||
return ret; | ||
} | ||
/** | ||
* Normalize an angle (in radians) | ||
* @param radians The radians quantity to normalize. | ||
*/ | ||
function normalizeAngle(radians) { | ||
return radians - Math.PI * 2 * Math.floor(radians / (Math.PI * 2)); | ||
} | ||
/** | ||
* | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param w | ||
* @param h | ||
* @param x0 | ||
@@ -120,8 +325,62 @@ * @param y0 | ||
function getAngliness(x0, y0, x1, y1) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)); | ||
function getRayRectangleIntersectionPoints(ox, oy, dx, dy, x, y, w, h) { | ||
var points = []; | ||
for (var _i3 = 0, _arr3 = [[x, y, x + w, y], [x + w, y, x + w, y + h], [x + w, y + h, x, y + h], [x, y + h, x, y]]; _i3 < _arr3.length; _i3++) { | ||
var _arr3$_i = _arr3[_i3], | ||
px0 = _arr3$_i[0], | ||
py0 = _arr3$_i[1], | ||
px1 = _arr3$_i[2], | ||
py1 = _arr3$_i[3]; | ||
var ints = getRaySegmentIntersection(ox, oy, dx, dy, px0, py0, px1, py1); | ||
if (ints) { | ||
points.push(ints); | ||
} | ||
} | ||
return points; | ||
} | ||
/** | ||
* Get the point at which a ray intersects a segment. | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param dx The x-axis delta of the angle. | ||
* @param dy The y-axis delta of the angle. | ||
* @param x0 The x-axis coordinate of the segment's start point. | ||
* @param y0 The y-axis coordinate of the segment's start point. | ||
* @param x1 The x-axis coordinate of the segment's end point. | ||
* @param y1 The y-axis coordinate of the segment's end point. | ||
*/ | ||
function getRaySegmentIntersection(x, y, dx, dy, x0, y0, x1, y1) { | ||
var r, s, d; | ||
if (dy * (x1 - x0) !== dx * (y1 - y0)) { | ||
d = dx * (y1 - y0) - dy * (x1 - x0); | ||
if (d !== 0) { | ||
r = ((y - y0) * (x1 - x0) - (x - x0) * (y1 - y0)) / d; | ||
s = ((y - y0) * dx - (x - x0) * dy) / d; | ||
if (r >= 0 && s >= 0 && s <= 1) { | ||
return [x + r * dx, y + r * dy]; | ||
} | ||
} | ||
} | ||
return undefined; | ||
} | ||
/** | ||
* Get the normalized delta (x and y) for an angle. | ||
* @param angle The angle in radians | ||
*/ | ||
function getDelta(angle) { | ||
return [Math.cos(angle), Math.sin(angle)]; | ||
} | ||
/** | ||
* getArrow | ||
* Get the points for a linking line between two points. | ||
* @description Draw an arrow between two points. | ||
@@ -271,3 +530,160 @@ * @param x0 The x position of the "from" point. | ||
export { getArrow }; | ||
/** | ||
* getArrowBetweenBoxes | ||
* Get the points for a linking line between two boxes. | ||
* @param x0 The x-axis coordinate of the first box. | ||
* @param y0 The y-axis coordinate of the first box. | ||
* @param w0 The width of the first box. | ||
* @param h0 The height of the first box. | ||
* @param x1 The x-axis coordinate of the second box. | ||
* @param y1 The y-axis coordinate of the second box. | ||
* @param w1 The width of the second box. | ||
* @param h1 The height of the second box. | ||
* @param options | ||
*/ | ||
function getBoxToBoxArrow(x0, y0, w0, h0, x1, y1, w1, h1, options) { | ||
if (options === void 0) { | ||
options = {}; | ||
} | ||
var i0x, i0y, i1x, i1y; | ||
var _options = options, | ||
_options$bow = _options.bow, | ||
bow = _options$bow === void 0 ? 0 : _options$bow, | ||
_options$stretch = _options.stretch, | ||
stretch = _options$stretch === void 0 ? 0.5 : _options$stretch, | ||
_options$stretchMin = _options.stretchMin, | ||
stretchMin = _options$stretchMin === void 0 ? 0 : _options$stretchMin, | ||
_options$stretchMax = _options.stretchMax, | ||
stretchMax = _options$stretchMax === void 0 ? 420 : _options$stretchMax, | ||
_options$padStart = _options.padStart, | ||
padStart = _options$padStart === void 0 ? 20 : _options$padStart, | ||
_options$padEnd = _options.padEnd, | ||
padEnd = _options$padEnd === void 0 ? 20 : _options$padEnd, | ||
_options$flip = _options.flip, | ||
flip = _options$flip === void 0 ? false : _options$flip, | ||
_options$straights = _options.straights, | ||
straights = _options$straights === void 0 ? true : _options$straights; | ||
var cx0 = x0 + w0 / 2, | ||
cy0 = y0 + h0 / 2, | ||
cx1 = x1 + w1 / 2, | ||
cy1 = y1 + h1 / 2, | ||
px0 = x0 - padStart, | ||
py0 = y0 - padStart, | ||
pw0 = w0 + padStart * 2, | ||
ph0 = h0 + padStart * 2, | ||
px1 = x1 - padEnd, | ||
py1 = y1 - padEnd, | ||
pw1 = w1 + padEnd * 2, | ||
ph1 = h1 + padEnd * 2; | ||
var _getPointBetween = getPointBetween(cx0, cy0, cx1, cy1, 0.5), | ||
mx = _getPointBetween[0], | ||
my = _getPointBetween[1]; | ||
var angle = getAngle(cx0, cy0, cx1, cy1); | ||
var angliness = getAngliness(cx0, cy0, cx1, cy1); | ||
var minLength = Math.min(pw0, ph0, pw1, ph1) * 2; | ||
var direct = getDistance(x0, y0, x1, y1); | ||
var overflow = minLength - direct; | ||
var isColliding = doRectanglesCollide(px0, py0, pw0, ph0, px1, py1, pw1, ph1); | ||
if (straights && ([0, 1, Infinity].includes(angliness) || cx0 === cx1 || cy0 === cy1) && !isColliding) { | ||
var _getSegmentRectangleI = getSegmentRectangleIntersectionPoints(cx0, cy0, cx1, cy1, px0, py0, pw0, ph0); | ||
var _getSegmentRectangleI2 = _getSegmentRectangleI[0]; | ||
i0x = _getSegmentRectangleI2[0]; | ||
i0y = _getSegmentRectangleI2[1]; | ||
var _getSegmentRectangleI3 = getSegmentRectangleIntersectionPoints(cx0, cy0, cx1, cy1, px1, py1, pw1, ph1); | ||
var _getSegmentRectangleI4 = _getSegmentRectangleI3[0]; | ||
i1x = _getSegmentRectangleI4[0]; | ||
i1y = _getSegmentRectangleI4[1]; | ||
return [i0x, i0y, mx, my, i1x, i1y, angle, angle + Math.PI, angle]; | ||
} // ⤜⤏ Arrow is an arc! | ||
// Is the arc clockwise or counterclockwise? | ||
var rot = (getSector(angle) % 2 === 0 ? 1 : -1) * (flip ? -1 : 1); // Calculate how much the line should "bow" away from center | ||
var stretchEffect = mod(direct, [stretchMin, stretchMax], [1, 0], true); | ||
var arc = bow + stretchEffect * stretch / 2; // Step 1 ⤜⤏ Find padded points. | ||
// Get control point. | ||
var _getPointBetween2 = getPointBetween(cx0, cy0, cx1, cy1, 0.5 - arc), | ||
cx = _getPointBetween2[0], | ||
cy = _getPointBetween2[1]; | ||
var _rotatePoint = rotatePoint(cx, cy, mx, my, Math.PI / 2 * rot); | ||
cx = _rotatePoint[0]; | ||
cy = _rotatePoint[1]; | ||
if (overflow > 0) { | ||
var _projectPoint = projectPoint(cx, cy, angle + Math.PI / 2 * -rot, overflow * arc); | ||
cx = _projectPoint[0]; | ||
cy = _projectPoint[1]; | ||
} // Get padded start point. | ||
var i0 = getSegmentRoundedRectangleIntersectionPoints(cx0, cy0, cx, cy, px0, py0, pw0, ph0, padStart); // Get padded end point. | ||
var i1 = getSegmentRoundedRectangleIntersectionPoints(cx1, cy1, cx, cy, px1, py1, pw1, ph1, padEnd); // If we don't have intersections, or if the distance between the | ||
// intersections is very small, use the backup point-finding stategy | ||
if (!(i0[0] && i1[0]) || getDistance(i0[0][0], i0[0][1], i1[0][0], i1[0][1]) < padStart + padEnd) { | ||
// Basically, shoot a ray based on the opposite angle between | ||
// the two centers, and see where it intersects the rectangles. | ||
// This part can definitely be improved! | ||
var _getDelta = getDelta(angle + Math.PI), | ||
dx = _getDelta[0], | ||
dy = _getDelta[1]; | ||
var _getRayRectangleInter = getRayRectangleIntersectionPoints(cx0, cy0, dx, dy, px0, py0, pw0, ph0); | ||
var _getRayRectangleInter2 = _getRayRectangleInter[0]; | ||
i0x = _getRayRectangleInter2[0]; | ||
i0y = _getRayRectangleInter2[1]; | ||
var _getRayRectangleInter3 = getRayRectangleIntersectionPoints(cx1, cy1, dx, dy, px1, py1, pw1, ph1); | ||
var _getRayRectangleInter4 = _getRayRectangleInter3[0]; | ||
i1x = _getRayRectangleInter4[0]; | ||
i1y = _getRayRectangleInter4[1]; | ||
arc = (bow + stretchEffect * stretch) * -1; | ||
} else { | ||
var _i0$ = i0[0]; | ||
i0x = _i0$[0]; | ||
i0y = _i0$[1]; | ||
var _i1$ = i1[0]; | ||
i1x = _i1$[0]; | ||
i1y = _i1$[1]; | ||
arc = bow + stretchEffect * stretch; | ||
} // Step 3 ⤜⤏ Calculate arrow points using same algorithm as getArrow | ||
var _getPointBetween3 = getPointBetween(i0x, i0y, i1x, i1y, 0.5), | ||
mx1 = _getPointBetween3[0], | ||
my1 = _getPointBetween3[1]; | ||
var _getPointBetween4 = getPointBetween(i0x, i0y, i1x, i1y, 0.5 - arc); | ||
cx = _getPointBetween4[0]; | ||
cy = _getPointBetween4[1]; | ||
var _rotatePoint2 = rotatePoint(cx, cy, mx1, my1, Math.PI / (2 * rot)); | ||
cx = _rotatePoint2[0]; | ||
cy = _rotatePoint2[1]; | ||
var as = getAngle(cx, cy, i0x, i0y); | ||
var ae = getAngle(cx, cy, i1x, i1y); | ||
return [i0x, i0y, cx, cy, i1x, i1y, ae, as, angle]; | ||
} | ||
export { getArrow, getBoxToBoxArrow }; | ||
//# sourceMappingURL=perfect-arrows.esm.js.map |
{ | ||
"version": "0.2.0", | ||
"version": "0.2.1", | ||
"license": "MIT", | ||
@@ -4,0 +4,0 @@ "main": "dist/index.js", |
@@ -1,3 +0,3 @@ | ||
import { getArrow, ArrowOptions } from "./lib" | ||
import { getArrow, getBoxToBoxArrow, ArrowOptions } from "./lib" | ||
export { getArrow, ArrowOptions } | ||
export { getArrow, getBoxToBoxArrow, ArrowOptions } |
@@ -25,2 +25,3 @@ import { | ||
* getArrow | ||
* Get the points for a linking line between two points. | ||
* @description Draw an arrow between two points. | ||
@@ -27,0 +28,0 @@ * @param x0 The x position of the "from" point. |
import getArrow, { ArrowOptions } from "./getArrow" | ||
import getBoxToBoxArrow from "./getBoxToBoxArrow" | ||
export { getArrow, ArrowOptions } | ||
export { getArrow, ArrowOptions, getBoxToBoxArrow } |
/** | ||
* Modulate a value between two ranges | ||
* Modulate a value between two ranges. | ||
* @param value | ||
@@ -22,7 +22,7 @@ * @param a from [low, high] | ||
* Rotate a point around a center. | ||
* @param x | ||
* @param y | ||
* @param cx | ||
* @param cy | ||
* @param angle | ||
* @param x The x-axis coordinate of the point. | ||
* @param y The y-axis coordinate of the point. | ||
* @param cx The x-axis coordinate of the point to rotate round. | ||
* @param cy The y-axis coordinate of the point to rotate round. | ||
* @param angle The distance (in radians) to rotate. | ||
*/ | ||
@@ -50,6 +50,6 @@ export function rotatePoint( | ||
* Get the distance between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -62,6 +62,6 @@ export function getDistance(x0: number, y0: number, x1: number, y1: number) { | ||
* Get an angle (radians) between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
@@ -85,6 +85,6 @@ export function getAngle(x0: number, y0: number, x1: number, y1: number) { | ||
* Get a point between two points. | ||
* @param x0 | ||
* @param y0 | ||
* @param x1 | ||
* @param y1 | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
* @param d Normalized | ||
@@ -104,4 +104,4 @@ */ | ||
* Get the sector of an angle (e.g. quadrant, octant) | ||
* @param a angle | ||
* @param s number of sectors | ||
* @param a The angle to check. | ||
* @param s The number of sectors to check. | ||
*/ | ||
@@ -114,2 +114,411 @@ export function getSector(a: number, s = 8) { | ||
* Get a normal value representing how close two points are from being at a 45 degree angle. | ||
* @param x0 The x-axis coordinate of the first point. | ||
* @param y0 The y-axis coordinate of the first point. | ||
* @param x1 The x-axis coordinate of the second point. | ||
* @param y1 The y-axis coordinate of the second point. | ||
*/ | ||
export function getAngliness(x0: number, y0: number, x1: number, y1: number) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)) | ||
} | ||
/** | ||
* Get the points at which an ellipse intersects a rectangle. | ||
* @param x | ||
* @param y | ||
* @param w | ||
* @param h | ||
* @param cx | ||
* @param cy | ||
* @param rx | ||
* @param ry | ||
* @param angle | ||
*/ | ||
export function getEllipseRectangleIntersectionPoints( | ||
x: number, | ||
y: number, | ||
w: number, | ||
h: number, | ||
cx: number, | ||
cy: number, | ||
rx: number, | ||
ry: number, | ||
angle: number | ||
) { | ||
let points: number[][] = [] | ||
for (let [px0, py0, px1, py1] of [ | ||
[x, y, x + w, y], | ||
[x + w, y, x + w, y + h], | ||
[x + w, y + h, x, y + h], | ||
[x, y + h, x, y], | ||
]) { | ||
const ints = getEllipseSegmentIntersections( | ||
px0, | ||
py0, | ||
px1, | ||
py1, | ||
cx, | ||
cy, | ||
rx, | ||
ry, | ||
angle | ||
) | ||
if (ints.length > 0) { | ||
points.push(...ints) | ||
} | ||
} | ||
points = points.sort(([x0, y0], [x1, y1]) => { | ||
return Math.sin(getAngle(cx, cy, x0, y0) - getAngle(cx, cy, x1, y1)) > 0 | ||
? -1 | ||
: 1 | ||
}) | ||
return points | ||
} | ||
/** | ||
* Find the point(s) where a line segment intersects an ellipse. | ||
* @param x0 The x-axis coordinate of the line's start point. | ||
* @param y0 The y-axis coordinate of the line's start point. | ||
* @param x1 The x-axis coordinate of the line's end point. | ||
* @param y1 The y-axis coordinate of the line's end point. | ||
* @param cx The x-axis (horizontal) coordinate of the ellipse's center. | ||
* @param cy The y-axis (vertical) coordinate of the ellipse's center. | ||
* @param rx The ellipse's major-axis radius. Must be non-negative. | ||
* @param ry The ellipse's minor-axis radius. Must be non-negative. | ||
* @param rotation The rotation of the ellipse, expressed in radians. | ||
* @param segment_only When true, will test the segment as a line (of infinite length). | ||
*/ | ||
export function getEllipseSegmentIntersections( | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number, | ||
cx: number, | ||
cy: number, | ||
rx: number, | ||
ry: number, | ||
rotation = 0, | ||
segment_only = true | ||
) { | ||
// If the ellipse or line segment are empty, return no tValues. | ||
if (rx === 0 || ry === 0 || (x0 === x1 && y0 === y1)) { | ||
return [] | ||
} | ||
// Get the semimajor and semiminor axes. | ||
rx = rx < 0 ? rx : -rx | ||
ry = ry < 0 ? ry : -ry | ||
// Rotate points. | ||
if (rotation !== 0) { | ||
;[x0, y0] = rotatePoint(x0, y0, cx, cy, -rotation) | ||
;[x1, y1] = rotatePoint(x1, y1, cx, cy, -rotation) | ||
} | ||
// Translate so the ellipse is centered at the origin. | ||
x0 -= cx | ||
y0 -= cy | ||
x1 -= cx | ||
y1 -= cy | ||
// Calculate the quadratic parameters. | ||
var A = ((x1 - x0) * (x1 - x0)) / rx / rx + ((y1 - y0) * (y1 - y0)) / ry / ry | ||
var B = (2 * x0 * (x1 - x0)) / rx / rx + (2 * y0 * (y1 - y0)) / ry / ry | ||
var C = (x0 * x0) / rx / rx + (y0 * y0) / ry / ry - 1 | ||
// Make a list of t values (normalized points on the line where intersections occur). | ||
var tValues: number[] = [] | ||
// Calculate the discriminant. | ||
var discriminant = B * B - 4 * A * C | ||
if (discriminant === 0) { | ||
// One real solution. | ||
tValues.push(-B / 2 / A) | ||
} else if (discriminant > 0) { | ||
// Two real solutions. | ||
tValues.push((-B + Math.sqrt(discriminant)) / 2 / A) | ||
tValues.push((-B - Math.sqrt(discriminant)) / 2 / A) | ||
} | ||
return ( | ||
tValues | ||
// Filter to only points that are on the segment. | ||
.filter(t => !segment_only || (t >= 0 && t <= 1)) | ||
// Solve for points. | ||
.map(t => [x0 + (x1 - x0) * t + cx, y0 + (y1 - y0) * t + cy]) | ||
// Counter-rotate points | ||
.map(p => | ||
rotation === 0 ? p : rotatePoint(p[0], p[1], cx, cy, rotation) | ||
) | ||
) | ||
} | ||
/** | ||
* Check whether two rectangles will collide (overlap). | ||
* @param x0 The x-axis coordinate of the first rectangle. | ||
* @param y0 The y-axis coordinate of the first rectangle. | ||
* @param w0 The width of the first rectangle. | ||
* @param h0 The height of the first rectangle. | ||
* @param x1 The x-axis coordinate of the second rectangle. | ||
* @param y1 The y-axis coordinate of the second rectangle. | ||
* @param w1 The width of the second rectangle. | ||
* @param h1 The height of the second rectangle. | ||
*/ | ||
export function doRectanglesCollide( | ||
x0: number, | ||
y0: number, | ||
w0: number, | ||
h0: number, | ||
x1: number, | ||
y1: number, | ||
w1: number, | ||
h1: number | ||
) { | ||
return !(x0 >= x1 + w1 || x1 >= x0 + w0 || y0 >= y1 + h1 || y1 >= y0 + h0) | ||
} | ||
/** | ||
* Find the point(s) where a segment intersects a rectangle. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of the segment's starting point. | ||
* @param x1 The x-axis coordinate of the segment's ending point. | ||
* @param y1 The y-axis coordinate of the segment's ending point. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
*/ | ||
export function getSegmentRectangleIntersectionPoints( | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number, | ||
x: number, | ||
y: number, | ||
w: number, | ||
h: number | ||
) { | ||
let points: number[][] = [] | ||
for (let [px0, py0, px1, py1] of [ | ||
[x, y, x + w, y], | ||
[x + w, y, x + w, y + h], | ||
[x + w, y + h, x, y + h], | ||
[x, y + h, x, y], | ||
]) { | ||
const ints = getSegmentSegmentIntersection( | ||
px0, | ||
py0, | ||
px1, | ||
py1, | ||
x0, | ||
y0, | ||
x1, | ||
y1 | ||
) | ||
if (ints) { | ||
points.push(ints) | ||
} | ||
} | ||
return points | ||
} | ||
/** | ||
* Find the point, if any, where two segments intersect. | ||
* @param x0 The x-axis coordinate of the first segment's starting point. | ||
* @param y0 The y-axis coordinate of the first segment's starting point. | ||
* @param x1 The x-axis coordinate of the first segment's ending point. | ||
* @param y1 The y-axis coordinate of the first segment's ending point. | ||
* @param x2 The x-axis coordinate of the second segment's starting point. | ||
* @param y2 The y-axis coordinate of the second segment's starting point. | ||
* @param x3 The x-axis coordinate of the second segment's ending point. | ||
* @param y3 The y-axis coordinate of the second segment's ending point. | ||
*/ | ||
export function getSegmentSegmentIntersection( | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number, | ||
x2: number, | ||
y2: number, | ||
x3: number, | ||
y3: number | ||
) { | ||
const denom = (y3 - y2) * (x1 - x0) - (x3 - x2) * (y1 - y0) | ||
const numeA = (x3 - x2) * (y0 - y2) - (y3 - y2) * (x0 - x2) | ||
const numeB = (x1 - x0) * (y0 - y2) - (y1 - y0) * (x0 - x2) | ||
if (denom === 0) { | ||
if (numeA === 0 && numeB === 0) { | ||
return undefined // Colinear | ||
} | ||
return undefined // Parallel | ||
} | ||
const uA = numeA / denom | ||
const uB = numeB / denom | ||
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) { | ||
return [x0 + uA * (x1 - x0), y0 + uA * (y1 - y0)] | ||
} | ||
return undefined // No intersection | ||
} | ||
/** | ||
* Get the intersection points between a line segment and a rectangle with rounded corners. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
* @param x The x-axis coordinate of the rectangle. | ||
* @param y The y-axis coordinate of the rectangle. | ||
* @param w The width of the rectangle. | ||
* @param h The height of the rectangle. | ||
* @param r The corner radius of the rectangle. | ||
*/ | ||
export function getSegmentRoundedRectangleIntersectionPoints( | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number, | ||
x: number, | ||
y: number, | ||
w: number, | ||
h: number, | ||
r: number | ||
) { | ||
const mx = x + w, | ||
my = y + h, | ||
rx = x + r, | ||
ry = y + r, | ||
mrx = x + w - r, | ||
mry = y + h - r | ||
const segments = [ | ||
[x, mry, x, ry, x, y], | ||
[rx, y, mrx, y, mx, y], | ||
[mx, ry, mx, mry, mx, my], | ||
[mrx, my, rx, my, x, my], | ||
] | ||
const corners = [ | ||
[rx, ry, Math.PI, Math.PI * 1.5], | ||
[mrx, ry, Math.PI * 1.5, Math.PI * 2], | ||
[mrx, mry, 0, Math.PI * 0.5], | ||
[rx, mry, Math.PI * 0.5, Math.PI], | ||
] | ||
let points: number[][] = [] | ||
segments.forEach((segment, i) => { | ||
const [px0, py0, px1, py1] = segment | ||
const [cx, cy, as, ae] = corners[i] | ||
getSegmentCircleIntersections(cx, cy, r, x0, y0, x1, y1) | ||
.filter(pt => { | ||
const pointAngle = normalizeAngle(getAngle(cx, cy, pt[0], pt[1])) | ||
return pointAngle > as && pointAngle < ae | ||
}) | ||
.forEach(pt => points.push(pt)) | ||
const segmentInt = getSegmentSegmentIntersection( | ||
x0, | ||
y0, | ||
x1, | ||
y1, | ||
px0, | ||
py0, | ||
px1, | ||
py1 | ||
) | ||
if (!!segmentInt) { | ||
points.push(segmentInt) | ||
} | ||
}) | ||
return points | ||
} | ||
/** | ||
* Get the point(s) where a line segment intersects a circle. | ||
* @param cx The x-axis coordinate of the circle's center. | ||
* @param cy The y-axis coordinate of the circle's center. | ||
* @param r The circle's radius. | ||
* @param x0 The x-axis coordinate of the segment's starting point. | ||
* @param y0 The y-axis coordinate of ththe segment's ending point. | ||
* @param x1 The delta-x of the ray. | ||
* @param y1 The delta-y of the ray. | ||
*/ | ||
export function getSegmentCircleIntersections( | ||
cx: number, | ||
cy: number, | ||
r: number, | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number | ||
) { | ||
var b: number, | ||
c: number, | ||
d: number, | ||
u1: number, | ||
u2: number, | ||
ret: number[][], | ||
retP1: number[], | ||
retP2: number[], | ||
v1 = [x1 - x0, y1 - y0], | ||
v2 = [x0 - cx, y0 - cy] | ||
b = v1[0] * v2[0] + v1[1] * v2[1] | ||
c = 2 * (v1[0] * v1[0] + v1[1] * v1[1]) | ||
b *= -2 | ||
d = Math.sqrt(b * b - 2 * c * (v2[0] * v2[0] + v2[1] * v2[1] - r * r)) | ||
if (isNaN(d)) { | ||
// no intercept | ||
return [] | ||
} | ||
u1 = (b - d) / c // these represent the unit distance of point one and two on the line | ||
u2 = (b + d) / c | ||
retP1 = [] // return points | ||
retP2 = [] | ||
ret = [] // return array | ||
if (u1 <= 1 && u1 >= 0) { | ||
// add point if on the line segment | ||
retP1[0] = x0 + v1[0] * u1 | ||
retP1[1] = y0 + v1[1] * u1 | ||
ret[0] = retP1 | ||
} | ||
if (u2 <= 1 && u2 >= 0) { | ||
// second add point if on the line segment | ||
retP2[0] = x0 + v1[0] * u2 | ||
retP2[1] = y0 + v1[1] * u2 | ||
ret[ret.length] = retP2 | ||
} | ||
return ret | ||
} | ||
/** | ||
* Normalize an angle (in radians) | ||
* @param radians The radians quantity to normalize. | ||
*/ | ||
export function normalizeAngle(radians: number) { | ||
return radians - Math.PI * 2 * Math.floor(radians / (Math.PI * 2)) | ||
} | ||
/** | ||
* | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param w | ||
* @param h | ||
* @param x0 | ||
@@ -120,4 +529,71 @@ * @param y0 | ||
*/ | ||
export function getAngliness(x0: number, y0: number, x1: number, y1: number) { | ||
return Math.abs((x1 - x0) / 2 / ((y1 - y0) / 2)) | ||
export function getRayRectangleIntersectionPoints( | ||
ox: number, | ||
oy: number, | ||
dx: number, | ||
dy: number, | ||
x: number, | ||
y: number, | ||
w: number, | ||
h: number | ||
) { | ||
let points: number[][] = [] | ||
for (let [px0, py0, px1, py1] of [ | ||
[x, y, x + w, y], | ||
[x + w, y, x + w, y + h], | ||
[x + w, y + h, x, y + h], | ||
[x, y + h, x, y], | ||
]) { | ||
const ints = getRaySegmentIntersection(ox, oy, dx, dy, px0, py0, px1, py1) | ||
if (ints) { | ||
points.push(ints) | ||
} | ||
} | ||
return points | ||
} | ||
/** | ||
* Get the point at which a ray intersects a segment. | ||
* @param x The x-axis coordinate of the ray's origin. | ||
* @param y The y-axis coordinate of the ray's origin. | ||
* @param dx The x-axis delta of the angle. | ||
* @param dy The y-axis delta of the angle. | ||
* @param x0 The x-axis coordinate of the segment's start point. | ||
* @param y0 The y-axis coordinate of the segment's start point. | ||
* @param x1 The x-axis coordinate of the segment's end point. | ||
* @param y1 The y-axis coordinate of the segment's end point. | ||
*/ | ||
export function getRaySegmentIntersection( | ||
x: number, | ||
y: number, | ||
dx: number, | ||
dy: number, | ||
x0: number, | ||
y0: number, | ||
x1: number, | ||
y1: number | ||
) { | ||
let r: number, s: number, d: number | ||
if (dy * (x1 - x0) !== dx * (y1 - y0)) { | ||
d = dx * (y1 - y0) - dy * (x1 - x0) | ||
if (d !== 0) { | ||
r = ((y - y0) * (x1 - x0) - (x - x0) * (y1 - y0)) / d | ||
s = ((y - y0) * dx - (x - x0) * dy) / d | ||
if (r >= 0 && s >= 0 && s <= 1) { | ||
return [x + r * dx, y + r * dy] | ||
} | ||
} | ||
} | ||
return undefined | ||
} | ||
/** | ||
* Get the normalized delta (x and y) for an angle. | ||
* @param angle The angle in radians | ||
*/ | ||
export function getDelta(angle: number) { | ||
return [Math.cos(angle), Math.sin(angle)] | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
224249
22
2293