flow like the river

This commit is contained in:
root 2025-11-07 00:06:12 +01:00
commit 013fe673f3
42435 changed files with 5764238 additions and 0 deletions

15
VISUALIZACION/node_modules/bezier-js/FUNDING.md generated vendored Executable file
View file

@ -0,0 +1,15 @@
**This package needs your support to stay maintained.** If you work for an organization
whose website is better off using react-onclickoutside than rolling its own code
solution, please consider talking to your manager to help fund this project.
Open Source is free to use, but certainly not free to develop. If you have the
means to reward those whose work you rely on, please consider doing so.
If you wish to help keep this library maintained through a financial contribution,
please visit the [Paypal donation page](https://www.paypal.com/donate/?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW),
and send me an email if you want me to know your contribution was specifically
for this library.
Thank you,
- Pomax

79
VISUALIZACION/node_modules/bezier-js/README.md generated vendored Executable file
View file

@ -0,0 +1,79 @@
# Bezier.js
:warning:
**This package needs your support to stay maintained.** If you work for an organization
whose website is better off using Bezier.js than rolling its own code
solution, please consider talking to your manager to help
[fund this project](https://www.paypal.com/donate/?cmd=_s-xclick&hosted_button_id=QPRDLNGDANJSW).
Open Source is free to use, but certainly not free to develop. If you have the
means to reward those whose work you rely on, please consider doing so.
:warning:
An ES Module based library for Node.js and browsers for doing (quadratic and cubic) Bezier curve work.
For a Demo and the API, hit up either [pomax.github.io/bezierjs](http://pomax.github.io/bezierjs) or read the souce (`./src` for the library code, start at `bezier.js`).
**Note:** if you're looking for the legacy ES5 version of this library, you will have to install v2.6.1 or below. However, be aware that the ES5 version will not have any fixes/updates back-ported.
## Installation
`npm install bezier-js` will add bezier.js to your dependencies, remember to add `--save` or `--save-dev` if you need that to be persistent of course.
### Without using a package manager
There is a rolled-up version of `bezier.js` in the `dist` directory. Just [download that](https://raw.githubusercontent.com/Pomax/bezierjs/master/dist/bezier.js) and drop it in your JS asset dir.
## In Node, as dependency
About as simple as it gets:
```
import { Bezier } from "bezier-js";
const b = new Bezier(...);
```
Or, using the legacy CommonJS syntax:
```
const Bezier = require("bezier-js");
const b = new Bezier(...);
```
### Node support matrix
| Node Version | Require Supported | Import Supported |
| ------------ | ----------------- | ----------------------------------- |
| v12.0.0 | Yes | Yes <sup>Experimental Flag</sup> |
| v12.14.1 | Yes | No <sup>Experimental Flag</sup> |
| v12.17.0 | Yes | Yes <sup>Experimental Warning</sup> |
| v12.22.1 | Yes | Yes |
| v14.0.0 | Yes | Yes |
| v14.16.1 | Yes | Yes |
## In Node or the browser, from file
Copy the contents of the `src` directory to wherever you like (`/js`, `/vendor`, etc), or place the rolled-up version of the library there, and then load the library as an import to whatever script needs to use the `Bezier` constructor using:
```
import { Bezier } from "/js/vendor/bezier.js";
const b = new Bezier(...);
```
## Working on the code
All the code is in the `src` directory, with `bezier.js` as entry point.
To test code (which automatically applies code formatting and rollup), use `npm test`.
There is no explicit build step for the library, `npm test` takes care of everything, except checking for code coverage.
## License
This code is MIT licensed.
## Engagement
For comments and questions, [tweet at me](https://twitter.com/TheRealPomax) or file an issue.

1408
VISUALIZACION/node_modules/bezier-js/dist/bezier.cjs generated vendored Executable file

File diff suppressed because it is too large Load diff

1
VISUALIZACION/node_modules/bezier-js/dist/bezier.js generated vendored Executable file

File diff suppressed because one or more lines are too long

48
VISUALIZACION/node_modules/bezier-js/package.json generated vendored Executable file
View file

@ -0,0 +1,48 @@
{
"name": "bezier-js",
"version": "6.1.4",
"author": "Pomax",
"description": "A javascript library for working with Bezier curves",
"type": "module",
"exports": {
"import": "./src/bezier.js",
"require": "./dist/bezier.cjs"
},
"main": "./dist/bezier.cjs",
"browser": "./src/bezier.js",
"license": "MIT",
"bugs": {
"url": "https://github.com/Pomax/bezierjs/issues"
},
"homepage": "https://github.com/Pomax/bezierjs",
"repository": {
"type": "git",
"url": "https://github.com/Pomax/bezierjs"
},
"funding": {
"type": "individual",
"url": "https://github.com/Pomax/bezierjs/blob/master/FUNDING.md"
},
"keywords": [
"bezier",
"curves"
],
"scripts": {
"test": "npm run prettier jest",
"start": "run-s test build",
"build": "run-s build:*",
"build:browser": "esbuild ./src/bezier.js --bundle --minify --outfile=dist/bezier.js",
"build:node": "esbuild ./src/bezier.js --bundle --platform=node --target=node16 --outfile=dist/bezier.cjs",
"prettier": "run-s prettier:*",
"prettier:src": "prettier ./src/**/*.js --write",
"prettier:text": "prettier ./test/**/*.js --write",
"jest": "cross-env NODE_OPTIONS=--experimental-vm-modules jest ./test/"
},
"devDependencies": {
"cross-env": "^7.0.3",
"esbuild": "^0.14.10",
"jest": "^27.4.7",
"npm-run-all": "^4.1.5",
"prettier": "^2.0.5"
}
}

1011
VISUALIZACION/node_modules/bezier-js/src/bezier.js generated vendored Executable file

File diff suppressed because it is too large Load diff

70
VISUALIZACION/node_modules/bezier-js/src/poly-bezier.js generated vendored Executable file
View file

@ -0,0 +1,70 @@
import { utils } from "./utils.js";
/**
* Poly Bezier
* @param {[type]} curves [description]
*/
class PolyBezier {
constructor(curves) {
this.curves = [];
this._3d = false;
if (!!curves) {
this.curves = curves;
this._3d = this.curves[0]._3d;
}
}
valueOf() {
return this.toString();
}
toString() {
return (
"[" +
this.curves
.map(function (curve) {
return utils.pointsToString(curve.points);
})
.join(", ") +
"]"
);
}
addCurve(curve) {
this.curves.push(curve);
this._3d = this._3d || curve._3d;
}
length() {
return this.curves
.map(function (v) {
return v.length();
})
.reduce(function (a, b) {
return a + b;
});
}
curve(idx) {
return this.curves[idx];
}
bbox() {
const c = this.curves;
var bbox = c[0].bbox();
for (var i = 1; i < c.length; i++) {
utils.expandbox(bbox, c[i].bbox());
}
return bbox;
}
offset(d) {
const offset = [];
this.curves.forEach(function (v) {
offset.push(...v.offset(d));
});
return new PolyBezier(offset);
}
}
export { PolyBezier };

909
VISUALIZACION/node_modules/bezier-js/src/utils.js generated vendored Executable file
View file

@ -0,0 +1,909 @@
import { Bezier } from "./bezier.js";
// math-inlining.
const { abs, cos, sin, acos, atan2, sqrt, pow } = Math;
// cube root function yielding real roots
function crt(v) {
return v < 0 ? -pow(-v, 1 / 3) : pow(v, 1 / 3);
}
// trig constants
const pi = Math.PI,
tau = 2 * pi,
quart = pi / 2,
// float precision significant decimal
epsilon = 0.000001,
// extremas used in bbox calculation and similar algorithms
nMax = Number.MAX_SAFE_INTEGER || 9007199254740991,
nMin = Number.MIN_SAFE_INTEGER || -9007199254740991,
// a zero coordinate, which is surprisingly useful
ZERO = { x: 0, y: 0, z: 0 };
// Bezier utility functions
const utils = {
// Legendre-Gauss abscissae with n=24 (x_i values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x))
Tvalues: [
-0.0640568928626056260850430826247450385909,
0.0640568928626056260850430826247450385909,
-0.1911188674736163091586398207570696318404,
0.1911188674736163091586398207570696318404,
-0.3150426796961633743867932913198102407864,
0.3150426796961633743867932913198102407864,
-0.4337935076260451384870842319133497124524,
0.4337935076260451384870842319133497124524,
-0.5454214713888395356583756172183723700107,
0.5454214713888395356583756172183723700107,
-0.6480936519369755692524957869107476266696,
0.6480936519369755692524957869107476266696,
-0.7401241915785543642438281030999784255232,
0.7401241915785543642438281030999784255232,
-0.8200019859739029219539498726697452080761,
0.8200019859739029219539498726697452080761,
-0.8864155270044010342131543419821967550873,
0.8864155270044010342131543419821967550873,
-0.9382745520027327585236490017087214496548,
0.9382745520027327585236490017087214496548,
-0.9747285559713094981983919930081690617411,
0.9747285559713094981983919930081690617411,
-0.9951872199970213601799974097007368118745,
0.9951872199970213601799974097007368118745,
],
// Legendre-Gauss weights with n=24 (w_i values, defined by a function linked to in the Bezier primer article)
Cvalues: [
0.1279381953467521569740561652246953718517,
0.1279381953467521569740561652246953718517,
0.1258374563468282961213753825111836887264,
0.1258374563468282961213753825111836887264,
0.121670472927803391204463153476262425607,
0.121670472927803391204463153476262425607,
0.1155056680537256013533444839067835598622,
0.1155056680537256013533444839067835598622,
0.1074442701159656347825773424466062227946,
0.1074442701159656347825773424466062227946,
0.0976186521041138882698806644642471544279,
0.0976186521041138882698806644642471544279,
0.086190161531953275917185202983742667185,
0.086190161531953275917185202983742667185,
0.0733464814110803057340336152531165181193,
0.0733464814110803057340336152531165181193,
0.0592985849154367807463677585001085845412,
0.0592985849154367807463677585001085845412,
0.0442774388174198061686027482113382288593,
0.0442774388174198061686027482113382288593,
0.0285313886289336631813078159518782864491,
0.0285313886289336631813078159518782864491,
0.0123412297999871995468056670700372915759,
0.0123412297999871995468056670700372915759,
],
arcfn: function (t, derivativeFn) {
const d = derivativeFn(t);
let l = d.x * d.x + d.y * d.y;
if (typeof d.z !== "undefined") {
l += d.z * d.z;
}
return sqrt(l);
},
compute: function (t, points, _3d) {
// shortcuts
if (t === 0) {
points[0].t = 0;
return points[0];
}
const order = points.length - 1;
if (t === 1) {
points[order].t = 1;
return points[order];
}
const mt = 1 - t;
let p = points;
// constant?
if (order === 0) {
points[0].t = t;
return points[0];
}
// linear?
if (order === 1) {
const ret = {
x: mt * p[0].x + t * p[1].x,
y: mt * p[0].y + t * p[1].y,
t: t,
};
if (_3d) {
ret.z = mt * p[0].z + t * p[1].z;
}
return ret;
}
// quadratic/cubic curve?
if (order < 4) {
let mt2 = mt * mt,
t2 = t * t,
a,
b,
c,
d = 0;
if (order === 2) {
p = [p[0], p[1], p[2], ZERO];
a = mt2;
b = mt * t * 2;
c = t2;
} else if (order === 3) {
a = mt2 * mt;
b = mt2 * t * 3;
c = mt * t2 * 3;
d = t * t2;
}
const ret = {
x: a * p[0].x + b * p[1].x + c * p[2].x + d * p[3].x,
y: a * p[0].y + b * p[1].y + c * p[2].y + d * p[3].y,
t: t,
};
if (_3d) {
ret.z = a * p[0].z + b * p[1].z + c * p[2].z + d * p[3].z;
}
return ret;
}
// higher order curves: use de Casteljau's computation
const dCpts = JSON.parse(JSON.stringify(points));
while (dCpts.length > 1) {
for (let i = 0; i < dCpts.length - 1; i++) {
dCpts[i] = {
x: dCpts[i].x + (dCpts[i + 1].x - dCpts[i].x) * t,
y: dCpts[i].y + (dCpts[i + 1].y - dCpts[i].y) * t,
};
if (typeof dCpts[i].z !== "undefined") {
dCpts[i].z = dCpts[i].z + (dCpts[i + 1].z - dCpts[i].z) * t;
}
}
dCpts.splice(dCpts.length - 1, 1);
}
dCpts[0].t = t;
return dCpts[0];
},
computeWithRatios: function (t, points, ratios, _3d) {
const mt = 1 - t,
r = ratios,
p = points;
let f1 = r[0],
f2 = r[1],
f3 = r[2],
f4 = r[3],
d;
// spec for linear
f1 *= mt;
f2 *= t;
if (p.length === 2) {
d = f1 + f2;
return {
x: (f1 * p[0].x + f2 * p[1].x) / d,
y: (f1 * p[0].y + f2 * p[1].y) / d,
z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z) / d,
t: t,
};
}
// upgrade to quadratic
f1 *= mt;
f2 *= 2 * mt;
f3 *= t * t;
if (p.length === 3) {
d = f1 + f2 + f3;
return {
x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x) / d,
y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y) / d,
z: !_3d ? false : (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z) / d,
t: t,
};
}
// upgrade to cubic
f1 *= mt;
f2 *= 1.5 * mt;
f3 *= 3 * mt;
f4 *= t * t * t;
if (p.length === 4) {
d = f1 + f2 + f3 + f4;
return {
x: (f1 * p[0].x + f2 * p[1].x + f3 * p[2].x + f4 * p[3].x) / d,
y: (f1 * p[0].y + f2 * p[1].y + f3 * p[2].y + f4 * p[3].y) / d,
z: !_3d
? false
: (f1 * p[0].z + f2 * p[1].z + f3 * p[2].z + f4 * p[3].z) / d,
t: t,
};
}
},
derive: function (points, _3d) {
const dpoints = [];
for (let p = points, d = p.length, c = d - 1; d > 1; d--, c--) {
const list = [];
for (let j = 0, dpt; j < c; j++) {
dpt = {
x: c * (p[j + 1].x - p[j].x),
y: c * (p[j + 1].y - p[j].y),
};
if (_3d) {
dpt.z = c * (p[j + 1].z - p[j].z);
}
list.push(dpt);
}
dpoints.push(list);
p = list;
}
return dpoints;
},
between: function (v, m, M) {
return (
(m <= v && v <= M) ||
utils.approximately(v, m) ||
utils.approximately(v, M)
);
},
approximately: function (a, b, precision) {
return abs(a - b) <= (precision || epsilon);
},
length: function (derivativeFn) {
const z = 0.5,
len = utils.Tvalues.length;
let sum = 0;
for (let i = 0, t; i < len; i++) {
t = z * utils.Tvalues[i] + z;
sum += utils.Cvalues[i] * utils.arcfn(t, derivativeFn);
}
return z * sum;
},
map: function (v, ds, de, ts, te) {
const d1 = de - ds,
d2 = te - ts,
v2 = v - ds,
r = v2 / d1;
return ts + d2 * r;
},
lerp: function (r, v1, v2) {
const ret = {
x: v1.x + r * (v2.x - v1.x),
y: v1.y + r * (v2.y - v1.y),
};
if (v1.z !== undefined && v2.z !== undefined) {
ret.z = v1.z + r * (v2.z - v1.z);
}
return ret;
},
pointToString: function (p) {
let s = p.x + "/" + p.y;
if (typeof p.z !== "undefined") {
s += "/" + p.z;
}
return s;
},
pointsToString: function (points) {
return "[" + points.map(utils.pointToString).join(", ") + "]";
},
copy: function (obj) {
return JSON.parse(JSON.stringify(obj));
},
angle: function (o, v1, v2) {
const dx1 = v1.x - o.x,
dy1 = v1.y - o.y,
dx2 = v2.x - o.x,
dy2 = v2.y - o.y,
cross = dx1 * dy2 - dy1 * dx2,
dot = dx1 * dx2 + dy1 * dy2;
return atan2(cross, dot);
},
// round as string, to avoid rounding errors
round: function (v, d) {
const s = "" + v;
const pos = s.indexOf(".");
return parseFloat(s.substring(0, pos + 1 + d));
},
dist: function (p1, p2) {
const dx = p1.x - p2.x,
dy = p1.y - p2.y;
return sqrt(dx * dx + dy * dy);
},
closest: function (LUT, point) {
let mdist = pow(2, 63),
mpos,
d;
LUT.forEach(function (p, idx) {
d = utils.dist(point, p);
if (d < mdist) {
mdist = d;
mpos = idx;
}
});
return { mdist: mdist, mpos: mpos };
},
abcratio: function (t, n) {
// see ratio(t) note on http://pomax.github.io/bezierinfo/#abc
if (n !== 2 && n !== 3) {
return false;
}
if (typeof t === "undefined") {
t = 0.5;
} else if (t === 0 || t === 1) {
return t;
}
const bottom = pow(t, n) + pow(1 - t, n),
top = bottom - 1;
return abs(top / bottom);
},
projectionratio: function (t, n) {
// see u(t) note on http://pomax.github.io/bezierinfo/#abc
if (n !== 2 && n !== 3) {
return false;
}
if (typeof t === "undefined") {
t = 0.5;
} else if (t === 0 || t === 1) {
return t;
}
const top = pow(1 - t, n),
bottom = pow(t, n) + top;
return top / bottom;
},
lli8: function (x1, y1, x2, y2, x3, y3, x4, y4) {
const nx =
(x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4),
ny = (x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4),
d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
if (d == 0) {
return false;
}
return { x: nx / d, y: ny / d };
},
lli4: function (p1, p2, p3, p4) {
const x1 = p1.x,
y1 = p1.y,
x2 = p2.x,
y2 = p2.y,
x3 = p3.x,
y3 = p3.y,
x4 = p4.x,
y4 = p4.y;
return utils.lli8(x1, y1, x2, y2, x3, y3, x4, y4);
},
lli: function (v1, v2) {
return utils.lli4(v1, v1.c, v2, v2.c);
},
makeline: function (p1, p2) {
return new Bezier(
p1.x,
p1.y,
(p1.x + p2.x) / 2,
(p1.y + p2.y) / 2,
p2.x,
p2.y
);
},
findbbox: function (sections) {
let mx = nMax,
my = nMax,
MX = nMin,
MY = nMin;
sections.forEach(function (s) {
const bbox = s.bbox();
if (mx > bbox.x.min) mx = bbox.x.min;
if (my > bbox.y.min) my = bbox.y.min;
if (MX < bbox.x.max) MX = bbox.x.max;
if (MY < bbox.y.max) MY = bbox.y.max;
});
return {
x: { min: mx, mid: (mx + MX) / 2, max: MX, size: MX - mx },
y: { min: my, mid: (my + MY) / 2, max: MY, size: MY - my },
};
},
shapeintersections: function (
s1,
bbox1,
s2,
bbox2,
curveIntersectionThreshold
) {
if (!utils.bboxoverlap(bbox1, bbox2)) return [];
const intersections = [];
const a1 = [s1.startcap, s1.forward, s1.back, s1.endcap];
const a2 = [s2.startcap, s2.forward, s2.back, s2.endcap];
a1.forEach(function (l1) {
if (l1.virtual) return;
a2.forEach(function (l2) {
if (l2.virtual) return;
const iss = l1.intersects(l2, curveIntersectionThreshold);
if (iss.length > 0) {
iss.c1 = l1;
iss.c2 = l2;
iss.s1 = s1;
iss.s2 = s2;
intersections.push(iss);
}
});
});
return intersections;
},
makeshape: function (forward, back, curveIntersectionThreshold) {
const bpl = back.points.length;
const fpl = forward.points.length;
const start = utils.makeline(back.points[bpl - 1], forward.points[0]);
const end = utils.makeline(forward.points[fpl - 1], back.points[0]);
const shape = {
startcap: start,
forward: forward,
back: back,
endcap: end,
bbox: utils.findbbox([start, forward, back, end]),
};
shape.intersections = function (s2) {
return utils.shapeintersections(
shape,
shape.bbox,
s2,
s2.bbox,
curveIntersectionThreshold
);
};
return shape;
},
getminmax: function (curve, d, list) {
if (!list) return { min: 0, max: 0 };
let min = nMax,
max = nMin,
t,
c;
if (list.indexOf(0) === -1) {
list = [0].concat(list);
}
if (list.indexOf(1) === -1) {
list.push(1);
}
for (let i = 0, len = list.length; i < len; i++) {
t = list[i];
c = curve.get(t);
if (c[d] < min) {
min = c[d];
}
if (c[d] > max) {
max = c[d];
}
}
return { min: min, mid: (min + max) / 2, max: max, size: max - min };
},
align: function (points, line) {
const tx = line.p1.x,
ty = line.p1.y,
a = -atan2(line.p2.y - ty, line.p2.x - tx),
d = function (v) {
return {
x: (v.x - tx) * cos(a) - (v.y - ty) * sin(a),
y: (v.x - tx) * sin(a) + (v.y - ty) * cos(a),
};
};
return points.map(d);
},
roots: function (points, line) {
line = line || { p1: { x: 0, y: 0 }, p2: { x: 1, y: 0 } };
const order = points.length - 1;
const aligned = utils.align(points, line);
const reduce = function (t) {
return 0 <= t && t <= 1;
};
if (order === 2) {
const a = aligned[0].y,
b = aligned[1].y,
c = aligned[2].y,
d = a - 2 * b + c;
if (d !== 0) {
const m1 = -sqrt(b * b - a * c),
m2 = -a + b,
v1 = -(m1 + m2) / d,
v2 = -(-m1 + m2) / d;
return [v1, v2].filter(reduce);
} else if (b !== c && d === 0) {
return [(2 * b - c) / (2 * b - 2 * c)].filter(reduce);
}
return [];
}
// see http://www.trans4mind.com/personal_development/mathematics/polynomials/cubicAlgebra.htm
const pa = aligned[0].y,
pb = aligned[1].y,
pc = aligned[2].y,
pd = aligned[3].y;
let d = -pa + 3 * pb - 3 * pc + pd,
a = 3 * pa - 6 * pb + 3 * pc,
b = -3 * pa + 3 * pb,
c = pa;
if (utils.approximately(d, 0)) {
// this is not a cubic curve.
if (utils.approximately(a, 0)) {
// in fact, this is not a quadratic curve either.
if (utils.approximately(b, 0)) {
// in fact in fact, there are no solutions.
return [];
}
// linear solution:
return [-c / b].filter(reduce);
}
// quadratic solution:
const q = sqrt(b * b - 4 * a * c),
a2 = 2 * a;
return [(q - b) / a2, (-b - q) / a2].filter(reduce);
}
// at this point, we know we need a cubic solution:
a /= d;
b /= d;
c /= d;
const p = (3 * b - a * a) / 3,
p3 = p / 3,
q = (2 * a * a * a - 9 * a * b + 27 * c) / 27,
q2 = q / 2,
discriminant = q2 * q2 + p3 * p3 * p3;
let u1, v1, x1, x2, x3;
if (discriminant < 0) {
const mp3 = -p / 3,
mp33 = mp3 * mp3 * mp3,
r = sqrt(mp33),
t = -q / (2 * r),
cosphi = t < -1 ? -1 : t > 1 ? 1 : t,
phi = acos(cosphi),
crtr = crt(r),
t1 = 2 * crtr;
x1 = t1 * cos(phi / 3) - a / 3;
x2 = t1 * cos((phi + tau) / 3) - a / 3;
x3 = t1 * cos((phi + 2 * tau) / 3) - a / 3;
return [x1, x2, x3].filter(reduce);
} else if (discriminant === 0) {
u1 = q2 < 0 ? crt(-q2) : -crt(q2);
x1 = 2 * u1 - a / 3;
x2 = -u1 - a / 3;
return [x1, x2].filter(reduce);
} else {
const sd = sqrt(discriminant);
u1 = crt(-q2 + sd);
v1 = crt(q2 + sd);
return [u1 - v1 - a / 3].filter(reduce);
}
},
droots: function (p) {
// quadratic roots are easy
if (p.length === 3) {
const a = p[0],
b = p[1],
c = p[2],
d = a - 2 * b + c;
if (d !== 0) {
const m1 = -sqrt(b * b - a * c),
m2 = -a + b,
v1 = -(m1 + m2) / d,
v2 = -(-m1 + m2) / d;
return [v1, v2];
} else if (b !== c && d === 0) {
return [(2 * b - c) / (2 * (b - c))];
}
return [];
}
// linear roots are even easier
if (p.length === 2) {
const a = p[0],
b = p[1];
if (a !== b) {
return [a / (a - b)];
}
return [];
}
return [];
},
curvature: function (t, d1, d2, _3d, kOnly) {
let num,
dnm,
adk,
dk,
k = 0,
r = 0;
//
// We're using the following formula for curvature:
//
// x'y" - y'x"
// k(t) = ------------------
// (x'² + y'²)^(3/2)
//
// from https://en.wikipedia.org/wiki/Radius_of_curvature#Definition
//
// With it corresponding 3D counterpart:
//
// sqrt( (y'z" - y"z')² + (z'x" - z"x')² + (x'y" - x"y')²)
// k(t) = -------------------------------------------------------
// (x'² + y'² + z'²)^(3/2)
//
const d = utils.compute(t, d1);
const dd = utils.compute(t, d2);
const qdsum = d.x * d.x + d.y * d.y;
if (_3d) {
num = sqrt(
pow(d.y * dd.z - dd.y * d.z, 2) +
pow(d.z * dd.x - dd.z * d.x, 2) +
pow(d.x * dd.y - dd.x * d.y, 2)
);
dnm = pow(qdsum + d.z * d.z, 3 / 2);
} else {
num = d.x * dd.y - d.y * dd.x;
dnm = pow(qdsum, 3 / 2);
}
if (num === 0 || dnm === 0) {
return { k: 0, r: 0 };
}
k = num / dnm;
r = dnm / num;
// We're also computing the derivative of kappa, because
// there is value in knowing the rate of change for the
// curvature along the curve. And we're just going to
// ballpark it based on an epsilon.
if (!kOnly) {
// compute k'(t) based on the interval before, and after it,
// to at least try to not introduce forward/backward pass bias.
const pk = utils.curvature(t - 0.001, d1, d2, _3d, true).k;
const nk = utils.curvature(t + 0.001, d1, d2, _3d, true).k;
dk = (nk - k + (k - pk)) / 2;
adk = (abs(nk - k) + abs(k - pk)) / 2;
}
return { k: k, r: r, dk: dk, adk: adk };
},
inflections: function (points) {
if (points.length < 4) return [];
// FIXME: TODO: add in inflection abstraction for quartic+ curves?
const p = utils.align(points, { p1: points[0], p2: points.slice(-1)[0] }),
a = p[2].x * p[1].y,
b = p[3].x * p[1].y,
c = p[1].x * p[2].y,
d = p[3].x * p[2].y,
v1 = 18 * (-3 * a + 2 * b + 3 * c - d),
v2 = 18 * (3 * a - b - 3 * c),
v3 = 18 * (c - a);
if (utils.approximately(v1, 0)) {
if (!utils.approximately(v2, 0)) {
let t = -v3 / v2;
if (0 <= t && t <= 1) return [t];
}
return [];
}
const d2 = 2 * v1;
if (utils.approximately(d2, 0)) return [];
const trm = v2 * v2 - 4 * v1 * v3;
if (trm < 0) return [];
const sq = Math.sqrt(trm);
return [(sq - v2) / d2, -(v2 + sq) / d2].filter(function (r) {
return 0 <= r && r <= 1;
});
},
bboxoverlap: function (b1, b2) {
const dims = ["x", "y"],
len = dims.length;
for (let i = 0, dim, l, t, d; i < len; i++) {
dim = dims[i];
l = b1[dim].mid;
t = b2[dim].mid;
d = (b1[dim].size + b2[dim].size) / 2;
if (abs(l - t) >= d) return false;
}
return true;
},
expandbox: function (bbox, _bbox) {
if (_bbox.x.min < bbox.x.min) {
bbox.x.min = _bbox.x.min;
}
if (_bbox.y.min < bbox.y.min) {
bbox.y.min = _bbox.y.min;
}
if (_bbox.z && _bbox.z.min < bbox.z.min) {
bbox.z.min = _bbox.z.min;
}
if (_bbox.x.max > bbox.x.max) {
bbox.x.max = _bbox.x.max;
}
if (_bbox.y.max > bbox.y.max) {
bbox.y.max = _bbox.y.max;
}
if (_bbox.z && _bbox.z.max > bbox.z.max) {
bbox.z.max = _bbox.z.max;
}
bbox.x.mid = (bbox.x.min + bbox.x.max) / 2;
bbox.y.mid = (bbox.y.min + bbox.y.max) / 2;
if (bbox.z) {
bbox.z.mid = (bbox.z.min + bbox.z.max) / 2;
}
bbox.x.size = bbox.x.max - bbox.x.min;
bbox.y.size = bbox.y.max - bbox.y.min;
if (bbox.z) {
bbox.z.size = bbox.z.max - bbox.z.min;
}
},
pairiteration: function (c1, c2, curveIntersectionThreshold) {
const c1b = c1.bbox(),
c2b = c2.bbox(),
r = 100000,
threshold = curveIntersectionThreshold || 0.5;
if (
c1b.x.size + c1b.y.size < threshold &&
c2b.x.size + c2b.y.size < threshold
) {
return [
(((r * (c1._t1 + c1._t2)) / 2) | 0) / r +
"/" +
(((r * (c2._t1 + c2._t2)) / 2) | 0) / r,
];
}
let cc1 = c1.split(0.5),
cc2 = c2.split(0.5),
pairs = [
{ left: cc1.left, right: cc2.left },
{ left: cc1.left, right: cc2.right },
{ left: cc1.right, right: cc2.right },
{ left: cc1.right, right: cc2.left },
];
pairs = pairs.filter(function (pair) {
return utils.bboxoverlap(pair.left.bbox(), pair.right.bbox());
});
let results = [];
if (pairs.length === 0) return results;
pairs.forEach(function (pair) {
results = results.concat(
utils.pairiteration(pair.left, pair.right, threshold)
);
});
results = results.filter(function (v, i) {
return results.indexOf(v) === i;
});
return results;
},
getccenter: function (p1, p2, p3) {
const dx1 = p2.x - p1.x,
dy1 = p2.y - p1.y,
dx2 = p3.x - p2.x,
dy2 = p3.y - p2.y,
dx1p = dx1 * cos(quart) - dy1 * sin(quart),
dy1p = dx1 * sin(quart) + dy1 * cos(quart),
dx2p = dx2 * cos(quart) - dy2 * sin(quart),
dy2p = dx2 * sin(quart) + dy2 * cos(quart),
// chord midpoints
mx1 = (p1.x + p2.x) / 2,
my1 = (p1.y + p2.y) / 2,
mx2 = (p2.x + p3.x) / 2,
my2 = (p2.y + p3.y) / 2,
// midpoint offsets
mx1n = mx1 + dx1p,
my1n = my1 + dy1p,
mx2n = mx2 + dx2p,
my2n = my2 + dy2p,
// intersection of these lines:
arc = utils.lli8(mx1, my1, mx1n, my1n, mx2, my2, mx2n, my2n),
r = utils.dist(arc, p1);
// arc start/end values, over mid point:
let s = atan2(p1.y - arc.y, p1.x - arc.x),
m = atan2(p2.y - arc.y, p2.x - arc.x),
e = atan2(p3.y - arc.y, p3.x - arc.x),
_;
// determine arc direction (cw/ccw correction)
if (s < e) {
// if s<m<e, arc(s, e)
// if m<s<e, arc(e, s + tau)
// if s<e<m, arc(e, s + tau)
if (s > m || m > e) {
s += tau;
}
if (s > e) {
_ = e;
e = s;
s = _;
}
} else {
// if e<m<s, arc(e, s)
// if m<e<s, arc(s, e + tau)
// if e<s<m, arc(s, e + tau)
if (e < m && m < s) {
_ = e;
e = s;
s = _;
} else {
e += tau;
}
}
// assign and done.
arc.s = s;
arc.e = e;
arc.r = r;
return arc;
},
numberSort: function (a, b) {
return a - b;
},
};
export { utils };