@voila.dev/motion 0.0.0-stage → 5.0.113
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -2
- package/dist/accessibility/reduced-motion.d.ts +13 -0
- package/dist/accessibility/reduced-motion.d.ts.map +1 -0
- package/dist/accessibility/reduced-motion.js +56 -0
- package/dist/accessibility/reduced-motion.js.map +7 -0
- package/dist/animate/animate.d.ts +21 -0
- package/dist/animate/animate.d.ts.map +1 -0
- package/dist/animate/animate.js +76 -0
- package/dist/animate/animate.js.map +7 -0
- package/dist/animate/controls.d.ts +16 -0
- package/dist/animate/controls.d.ts.map +1 -0
- package/dist/animate/controls.js +62 -0
- package/dist/animate/controls.js.map +7 -0
- package/dist/animate/dom.d.ts +11 -0
- package/dist/animate/dom.d.ts.map +1 -0
- package/dist/animate/dom.js +112 -0
- package/dist/animate/dom.js.map +7 -0
- package/dist/animate/js-animation.d.ts +25 -0
- package/dist/animate/js-animation.d.ts.map +1 -0
- package/dist/animate/js-animation.js +117 -0
- package/dist/animate/js-animation.js.map +7 -0
- package/dist/animate/plan.d.ts +17 -0
- package/dist/animate/plan.d.ts.map +1 -0
- package/dist/animate/plan.js +48 -0
- package/dist/animate/plan.js.map +7 -0
- package/dist/animate/style.d.ts +27 -0
- package/dist/animate/style.d.ts.map +1 -0
- package/dist/animate/style.js +96 -0
- package/dist/animate/style.js.map +7 -0
- package/dist/animate/types.d.ts +62 -0
- package/dist/animate/types.d.ts.map +1 -0
- package/dist/animate/types.js +1 -0
- package/dist/animate/types.js.map +7 -0
- package/dist/animate/value.d.ts +13 -0
- package/dist/animate/value.d.ts.map +1 -0
- package/dist/animate/value.js +71 -0
- package/dist/animate/value.js.map +7 -0
- package/dist/animate/waapi.d.ts +20 -0
- package/dist/animate/waapi.d.ts.map +1 -0
- package/dist/animate/waapi.js +152 -0
- package/dist/animate/waapi.js.map +7 -0
- package/dist/dom.d.ts +12 -0
- package/dist/dom.d.ts.map +1 -0
- package/dist/dom.js +17 -0
- package/dist/dom.js.map +7 -0
- package/dist/easing/cubic-bezier.d.ts +4 -0
- package/dist/easing/cubic-bezier.d.ts.map +1 -0
- package/dist/easing/cubic-bezier.js +54 -0
- package/dist/easing/cubic-bezier.js.map +7 -0
- package/dist/easing/linear-css.d.ts +9 -0
- package/dist/easing/linear-css.d.ts.map +1 -0
- package/dist/easing/linear-css.js +30 -0
- package/dist/easing/linear-css.js.map +7 -0
- package/dist/easing/named.d.ts +16 -0
- package/dist/easing/named.d.ts.map +1 -0
- package/dist/easing/named.js +82 -0
- package/dist/easing/named.js.map +7 -0
- package/dist/easing/spring.d.ts +11 -0
- package/dist/easing/spring.d.ts.map +1 -0
- package/dist/easing/spring.js +18 -0
- package/dist/easing/spring.js.map +7 -0
- package/dist/easing/steps.d.ts +4 -0
- package/dist/easing/steps.d.ts.map +1 -0
- package/dist/easing/steps.js +14 -0
- package/dist/easing/steps.js.map +7 -0
- package/dist/easing/types.d.ts +5 -0
- package/dist/easing/types.d.ts.map +1 -0
- package/dist/easing/types.js +1 -0
- package/dist/easing/types.js.map +7 -0
- package/dist/frame/driver.d.ts +13 -0
- package/dist/frame/driver.d.ts.map +1 -0
- package/dist/frame/driver.js +42 -0
- package/dist/frame/driver.js.map +7 -0
- package/dist/frame/frame.d.ts +20 -0
- package/dist/frame/frame.d.ts.map +1 -0
- package/dist/frame/frame.js +77 -0
- package/dist/frame/frame.js.map +7 -0
- package/dist/generators/inertia.d.ts +20 -0
- package/dist/generators/inertia.d.ts.map +1 -0
- package/dist/generators/inertia.js +59 -0
- package/dist/generators/inertia.js.map +7 -0
- package/dist/generators/spring.d.ts +17 -0
- package/dist/generators/spring.d.ts.map +1 -0
- package/dist/generators/spring.js +87 -0
- package/dist/generators/spring.js.map +7 -0
- package/dist/generators/tween.d.ts +15 -0
- package/dist/generators/tween.d.ts.map +1 -0
- package/dist/generators/tween.js +51 -0
- package/dist/generators/tween.js.map +7 -0
- package/dist/generators/types.d.ts +30 -0
- package/dist/generators/types.d.ts.map +1 -0
- package/dist/generators/types.js +1 -0
- package/dist/generators/types.js.map +7 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +97 -0
- package/dist/index.js.map +7 -0
- package/dist/interpolate/array.d.ts +3 -0
- package/dist/interpolate/array.d.ts.map +1 -0
- package/dist/interpolate/array.js +12 -0
- package/dist/interpolate/array.js.map +7 -0
- package/dist/interpolate/color-parse.d.ts +14 -0
- package/dist/interpolate/color-parse.d.ts.map +1 -0
- package/dist/interpolate/color-parse.js +111 -0
- package/dist/interpolate/color-parse.js.map +7 -0
- package/dist/interpolate/color.d.ts +11 -0
- package/dist/interpolate/color.d.ts.map +1 -0
- package/dist/interpolate/color.js +74 -0
- package/dist/interpolate/color.js.map +7 -0
- package/dist/interpolate/complex.d.ts +7 -0
- package/dist/interpolate/complex.d.ts.map +1 -0
- package/dist/interpolate/complex.js +56 -0
- package/dist/interpolate/complex.js.map +7 -0
- package/dist/interpolate/interpolate.d.ts +16 -0
- package/dist/interpolate/interpolate.d.ts.map +1 -0
- package/dist/interpolate/interpolate.js +39 -0
- package/dist/interpolate/interpolate.js.map +7 -0
- package/dist/interpolate/mix.d.ts +8 -0
- package/dist/interpolate/mix.d.ts.map +1 -0
- package/dist/interpolate/mix.js +48 -0
- package/dist/interpolate/mix.js.map +7 -0
- package/dist/interpolate/number.d.ts +2 -0
- package/dist/interpolate/number.d.ts.map +1 -0
- package/dist/interpolate/number.js +7 -0
- package/dist/interpolate/number.js.map +7 -0
- package/dist/interpolate/object.d.ts +5 -0
- package/dist/interpolate/object.d.ts.map +1 -0
- package/dist/interpolate/object.js +17 -0
- package/dist/interpolate/object.js.map +7 -0
- package/dist/interpolate/path/flatten.d.ts +10 -0
- package/dist/interpolate/path/flatten.d.ts.map +1 -0
- package/dist/interpolate/path/flatten.js +135 -0
- package/dist/interpolate/path/flatten.js.map +7 -0
- package/dist/interpolate/path/morph.d.ts +9 -0
- package/dist/interpolate/path/morph.d.ts.map +1 -0
- package/dist/interpolate/path/morph.js +72 -0
- package/dist/interpolate/path/morph.js.map +7 -0
- package/dist/interpolate/path/parse.d.ts +24 -0
- package/dist/interpolate/path/parse.d.ts.map +1 -0
- package/dist/interpolate/path/parse.js +125 -0
- package/dist/interpolate/path/parse.js.map +7 -0
- package/dist/interpolate/path/rings.d.ts +13 -0
- package/dist/interpolate/path/rings.d.ts.map +1 -0
- package/dist/interpolate/path/rings.js +117 -0
- package/dist/interpolate/path/rings.js.map +7 -0
- package/dist/react/index.d.ts +7 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +19 -0
- package/dist/react/index.js.map +7 -0
- package/dist/react/presence.d.ts +27 -0
- package/dist/react/presence.d.ts.map +1 -0
- package/dist/react/presence.js +135 -0
- package/dist/react/presence.js.map +7 -0
- package/dist/react/use-animate.d.ts +17 -0
- package/dist/react/use-animate.d.ts.map +1 -0
- package/dist/react/use-animate.js +34 -0
- package/dist/react/use-animate.js.map +7 -0
- package/dist/react/use-motion-value.d.ts +4 -0
- package/dist/react/use-motion-value.d.ts.map +1 -0
- package/dist/react/use-motion-value.js +11 -0
- package/dist/react/use-motion-value.js.map +7 -0
- package/dist/react/use-reduced-motion.d.ts +7 -0
- package/dist/react/use-reduced-motion.d.ts.map +1 -0
- package/dist/react/use-reduced-motion.js +19 -0
- package/dist/react/use-reduced-motion.js.map +7 -0
- package/dist/react/use-spring.d.ts +9 -0
- package/dist/react/use-spring.d.ts.map +1 -0
- package/dist/react/use-spring.js +27 -0
- package/dist/react/use-spring.js.map +7 -0
- package/dist/react/use-transform.d.ts +13 -0
- package/dist/react/use-transform.d.ts.map +1 -0
- package/dist/react/use-transform.js +44 -0
- package/dist/react/use-transform.js.map +7 -0
- package/dist/sequence/sequence.d.ts +25 -0
- package/dist/sequence/sequence.d.ts.map +1 -0
- package/dist/sequence/sequence.js +68 -0
- package/dist/sequence/sequence.js.map +7 -0
- package/dist/stagger/stagger.d.ts +12 -0
- package/dist/stagger/stagger.d.ts.map +1 -0
- package/dist/stagger/stagger.js +21 -0
- package/dist/stagger/stagger.js.map +7 -0
- package/dist/testing/manual-frames.d.ts +11 -0
- package/dist/testing/manual-frames.d.ts.map +1 -0
- package/dist/testing/manual-frames.js +40 -0
- package/dist/testing/manual-frames.js.map +7 -0
- package/dist/value/motion-value.d.ts +27 -0
- package/dist/value/motion-value.d.ts.map +1 -0
- package/dist/value/motion-value.js +88 -0
- package/dist/value/motion-value.js.map +7 -0
- package/dist/value/transform.d.ts +16 -0
- package/dist/value/transform.d.ts.map +1 -0
- package/dist/value/transform.js +28 -0
- package/dist/value/transform.js.map +7 -0
- package/dist/view/in-view.d.ts +19 -0
- package/dist/view/in-view.d.ts.map +1 -0
- package/dist/view/in-view.js +46 -0
- package/dist/view/in-view.js.map +7 -0
- package/dist/view/resize.d.ts +13 -0
- package/dist/view/resize.d.ts.map +1 -0
- package/dist/view/resize.js +72 -0
- package/dist/view/resize.js.map +7 -0
- package/dist/view/resolve-elements.d.ts +3 -0
- package/dist/view/resolve-elements.d.ts.map +1 -0
- package/dist/view/resolve-elements.js +10 -0
- package/dist/view/resolve-elements.js.map +7 -0
- package/dist/view/scroll-measure.d.ts +16 -0
- package/dist/view/scroll-measure.d.ts.map +1 -0
- package/dist/view/scroll-measure.js +51 -0
- package/dist/view/scroll-measure.js.map +7 -0
- package/dist/view/scroll-offset.d.ts +28 -0
- package/dist/view/scroll-offset.d.ts.map +1 -0
- package/dist/view/scroll-offset.js +55 -0
- package/dist/view/scroll-offset.js.map +7 -0
- package/dist/view/scroll.d.ts +34 -0
- package/dist/view/scroll.d.ts.map +1 -0
- package/dist/view/scroll.js +83 -0
- package/dist/view/scroll.js.map +7 -0
- package/package.json +57 -5
- package/src/accessibility/reduced-motion.ts +75 -0
- package/src/animate/animate.ts +155 -0
- package/src/animate/controls.ts +76 -0
- package/src/animate/dom.ts +166 -0
- package/src/animate/js-animation.ts +151 -0
- package/src/animate/plan.ts +74 -0
- package/src/animate/style.ts +131 -0
- package/src/animate/types.ts +69 -0
- package/src/animate/value.ts +93 -0
- package/src/animate/waapi.ts +212 -0
- package/src/dom.ts +35 -0
- package/src/easing/cubic-bezier.ts +64 -0
- package/src/easing/linear-css.ts +41 -0
- package/src/easing/named.ts +88 -0
- package/src/easing/spring.ts +25 -0
- package/src/easing/steps.ts +16 -0
- package/src/easing/types.ts +18 -0
- package/src/frame/driver.ts +56 -0
- package/src/frame/frame.ts +102 -0
- package/src/generators/inertia.ts +88 -0
- package/src/generators/spring.ts +117 -0
- package/src/generators/tween.ts +74 -0
- package/src/generators/types.ts +31 -0
- package/src/index.ts +91 -0
- package/src/interpolate/array.ts +13 -0
- package/src/interpolate/color-parse.ts +136 -0
- package/src/interpolate/color.ts +97 -0
- package/src/interpolate/complex.ts +80 -0
- package/src/interpolate/interpolate.ts +60 -0
- package/src/interpolate/mix.ts +74 -0
- package/src/interpolate/number.ts +3 -0
- package/src/interpolate/object.ts +20 -0
- package/src/interpolate/path/flatten.ts +185 -0
- package/src/interpolate/path/morph.ts +100 -0
- package/src/interpolate/path/parse.ts +162 -0
- package/src/interpolate/path/rings.ts +151 -0
- package/src/react/index.ts +11 -0
- package/src/react/presence.tsx +194 -0
- package/src/react/use-animate.ts +46 -0
- package/src/react/use-motion-value.ts +9 -0
- package/src/react/use-reduced-motion.ts +22 -0
- package/src/react/use-spring.ts +35 -0
- package/src/react/use-transform.ts +73 -0
- package/src/sequence/sequence.ts +129 -0
- package/src/stagger/stagger.ts +36 -0
- package/src/testing/browser-commands.d.ts +11 -0
- package/src/testing/manual-frames.ts +49 -0
- package/src/value/motion-value.ts +131 -0
- package/src/value/transform.ts +61 -0
- package/src/view/in-view.ts +74 -0
- package/src/view/resize.ts +101 -0
- package/src/view/resolve-elements.ts +11 -0
- package/src/view/scroll-measure.ts +83 -0
- package/src/view/scroll-offset.ts +100 -0
- package/src/view/scroll.ts +143 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"number.d.ts","sourceRoot":"","sources":["../../src/interpolate/number.ts"],"names":[],"mappings":"AAAA,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5E"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/interpolate/number.ts"],
|
|
4
|
+
"sourcesContent": ["export function mixNumber(from: number, to: number, progress: number): number {\n\treturn from + (to - from) * progress;\n}\n"],
|
|
5
|
+
"mappings": "AAAO,SAAS,UAAU,MAAc,IAAY,UAA0B;AAC7E,SAAO,QAAQ,KAAK,QAAQ;AAC7B;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type Record_ = Readonly<Record<string, unknown>>;
|
|
2
|
+
/** Key by key over the target's keys; a key the start lacks starts at its target. */
|
|
3
|
+
export declare function mixObject<T extends Record_>(from: T, to: T): (progress: number) => T;
|
|
4
|
+
export {};
|
|
5
|
+
//# sourceMappingURL=object.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"object.d.ts","sourceRoot":"","sources":["../../src/interpolate/object.ts"],"names":[],"mappings":"AAEA,KAAK,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjD,qFAAqF;AACrF,wBAAgB,SAAS,CAAC,CAAC,SAAS,OAAO,EAC1C,IAAI,EAAE,CAAC,EACP,EAAE,EAAE,CAAC,GACH,CAAC,QAAQ,EAAE,MAAM,KAAK,CAAC,CAWzB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { mix } from "./mix.js";
|
|
2
|
+
function mixObject(from, to) {
|
|
3
|
+
const mixers = Object.keys(to).map(
|
|
4
|
+
(key) => [key, mix(key in from ? from[key] : to[key], to[key])]
|
|
5
|
+
);
|
|
6
|
+
return (progress) => {
|
|
7
|
+
const result = {};
|
|
8
|
+
for (const [key, mixer] of mixers) {
|
|
9
|
+
result[key] = mixer(progress);
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export {
|
|
15
|
+
mixObject
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=object.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/interpolate/object.ts"],
|
|
4
|
+
"sourcesContent": ["import { mix } from \"#/interpolate/mix.ts\";\n\ntype Record_ = Readonly<Record<string, unknown>>;\n\n/** Key by key over the target's keys; a key the start lacks starts at its target. */\nexport function mixObject<T extends Record_>(\n\tfrom: T,\n\tto: T,\n): (progress: number) => T {\n\tconst mixers = Object.keys(to).map(\n\t\t(key) => [key, mix(key in from ? from[key] : to[key], to[key])] as const,\n\t);\n\treturn (progress) => {\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, mixer] of mixers) {\n\t\t\tresult[key] = mixer(progress);\n\t\t}\n\t\treturn result as T;\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,WAAW;AAKb,SAAS,UACf,MACA,IAC0B;AAC1B,QAAM,SAAS,OAAO,KAAK,EAAE,EAAE;AAAA,IAC9B,CAAC,QAAQ,CAAC,KAAK,IAAI,OAAO,OAAO,KAAK,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAAA,EAC/D;AACA,SAAO,CAAC,aAAa;AACpB,UAAM,SAAkC,CAAC;AACzC,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AAClC,aAAO,GAAG,IAAI,MAAM,QAAQ;AAAA,IAC7B;AACA,WAAO;AAAA,EACR;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { PathCommand } from "./parse.js";
|
|
2
|
+
export type Point = readonly [number, number];
|
|
3
|
+
/** One subpath as a polyline. */
|
|
4
|
+
export interface Ring {
|
|
5
|
+
readonly points: readonly Point[];
|
|
6
|
+
readonly closed: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** Every subpath of a parsed path as a polyline; `tolerance` in path units. */
|
|
9
|
+
export declare function flattenPath(commands: readonly PathCommand[], tolerance?: number): Ring[];
|
|
10
|
+
//# sourceMappingURL=flatten.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flatten.d.ts","sourceRoot":"","sources":["../../../src/interpolate/path/flatten.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAE/D,MAAM,MAAM,KAAK,GAAG,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE9C,iCAAiC;AACjC,MAAM,WAAW,IAAI;IACpB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;CACzB;AAwID,+EAA+E;AAC/E,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,SAAS,WAAW,EAAE,EAChC,SAAS,SAAO,GACd,IAAI,EAAE,CAoCR"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const MAX_SEGMENTS = 256;
|
|
2
|
+
function segmentsFor(bend, tolerance) {
|
|
3
|
+
return Math.min(
|
|
4
|
+
MAX_SEGMENTS,
|
|
5
|
+
Math.max(1, Math.ceil(Math.sqrt(0.75 * bend / tolerance)))
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
function cubic(from, values, tolerance) {
|
|
9
|
+
const [x1, y1, x2, y2, x, y] = values;
|
|
10
|
+
const [x0, y0] = from;
|
|
11
|
+
const bend = Math.max(
|
|
12
|
+
Math.hypot(x0 - 2 * x1 + x2, y0 - 2 * y1 + y2),
|
|
13
|
+
Math.hypot(x1 - 2 * x2 + x, y1 - 2 * y2 + y)
|
|
14
|
+
);
|
|
15
|
+
const count = segmentsFor(bend, tolerance);
|
|
16
|
+
return Array.from({ length: count }, (_unused, index) => {
|
|
17
|
+
const t = (index + 1) / count;
|
|
18
|
+
const u = 1 - t;
|
|
19
|
+
const [a, b, c, d] = [u * u * u, 3 * u * u * t, 3 * u * t * t, t * t * t];
|
|
20
|
+
return [a * x0 + b * x1 + c * x2 + d * x, a * y0 + b * y1 + c * y2 + d * y];
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function quadratic(from, values, tolerance) {
|
|
24
|
+
const [x1, y1, x, y] = values;
|
|
25
|
+
const [x0, y0] = from;
|
|
26
|
+
const count = segmentsFor(
|
|
27
|
+
Math.hypot(x0 - 2 * x1 + x, y0 - 2 * y1 + y) / 3,
|
|
28
|
+
tolerance
|
|
29
|
+
);
|
|
30
|
+
return Array.from({ length: count }, (_unused, index) => {
|
|
31
|
+
const t = (index + 1) / count;
|
|
32
|
+
const u = 1 - t;
|
|
33
|
+
return [
|
|
34
|
+
u * u * x0 + 2 * u * t * x1 + t * t * x,
|
|
35
|
+
u * u * y0 + 2 * u * t * y1 + t * t * y
|
|
36
|
+
];
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
function angleBetween(ux, uy, vx, vy) {
|
|
40
|
+
return Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);
|
|
41
|
+
}
|
|
42
|
+
function arc(from, values, tolerance) {
|
|
43
|
+
const [rxIn, ryIn, degrees, large, sweep, x2, y2] = values;
|
|
44
|
+
const [x1, y1] = from;
|
|
45
|
+
if (rxIn === 0 || ryIn === 0) {
|
|
46
|
+
return [[x2, y2]];
|
|
47
|
+
}
|
|
48
|
+
const phi = degrees * Math.PI / 180;
|
|
49
|
+
const [cos, sin] = [Math.cos(phi), Math.sin(phi)];
|
|
50
|
+
const dx = (x1 - x2) / 2;
|
|
51
|
+
const dy = (y1 - y2) / 2;
|
|
52
|
+
const x1p = cos * dx + sin * dy;
|
|
53
|
+
const y1p = -sin * dx + cos * dy;
|
|
54
|
+
const scale = Math.max(
|
|
55
|
+
1,
|
|
56
|
+
Math.sqrt(x1p * x1p / (rxIn * rxIn) + y1p * y1p / (ryIn * ryIn))
|
|
57
|
+
);
|
|
58
|
+
const rx = Math.abs(rxIn) * scale;
|
|
59
|
+
const ry = Math.abs(ryIn) * scale;
|
|
60
|
+
const numerator = rx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;
|
|
61
|
+
const denominator = rx * rx * y1p * y1p + ry * ry * x1p * x1p;
|
|
62
|
+
const coefficient = (large === sweep ? -1 : 1) * Math.sqrt(Math.max(0, numerator / (denominator || 1)));
|
|
63
|
+
const cxp = coefficient * rx * y1p / ry;
|
|
64
|
+
const cyp = -coefficient * ry * x1p / rx;
|
|
65
|
+
const cx = cos * cxp - sin * cyp + (x1 + x2) / 2;
|
|
66
|
+
const cy = sin * cxp + cos * cyp + (y1 + y2) / 2;
|
|
67
|
+
const start = angleBetween(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);
|
|
68
|
+
let delta = angleBetween(
|
|
69
|
+
(x1p - cxp) / rx,
|
|
70
|
+
(y1p - cyp) / ry,
|
|
71
|
+
(-x1p - cxp) / rx,
|
|
72
|
+
(-y1p - cyp) / ry
|
|
73
|
+
);
|
|
74
|
+
if (sweep === 0 && delta > 0) {
|
|
75
|
+
delta -= 2 * Math.PI;
|
|
76
|
+
}
|
|
77
|
+
if (sweep === 1 && delta < 0) {
|
|
78
|
+
delta += 2 * Math.PI;
|
|
79
|
+
}
|
|
80
|
+
const radius = Math.max(rx, ry);
|
|
81
|
+
const step = 2 * Math.acos(Math.max(-1, 1 - tolerance / radius));
|
|
82
|
+
const count = Math.min(
|
|
83
|
+
MAX_SEGMENTS,
|
|
84
|
+
Math.max(1, Math.ceil(Math.abs(delta) / step))
|
|
85
|
+
);
|
|
86
|
+
return Array.from({ length: count }, (_unused, index) => {
|
|
87
|
+
if (index === count - 1) {
|
|
88
|
+
return [x2, y2];
|
|
89
|
+
}
|
|
90
|
+
const theta = start + delta * (index + 1) / count;
|
|
91
|
+
const [c, s] = [Math.cos(theta), Math.sin(theta)];
|
|
92
|
+
return [cx + rx * c * cos - ry * s * sin, cy + rx * c * sin + ry * s * cos];
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
const CURVES = { C: cubic, Q: quadratic, A: arc };
|
|
96
|
+
function flattenPath(commands, tolerance = 0.25) {
|
|
97
|
+
const rings = [];
|
|
98
|
+
let current = {
|
|
99
|
+
points: [],
|
|
100
|
+
closed: false
|
|
101
|
+
};
|
|
102
|
+
let start = [0, 0];
|
|
103
|
+
for (const command of commands) {
|
|
104
|
+
if (command.type === "M" || current.closed) {
|
|
105
|
+
start = command.type === "M" ? command.values : start;
|
|
106
|
+
current = { points: [start], closed: false };
|
|
107
|
+
rings.push(current);
|
|
108
|
+
}
|
|
109
|
+
const last = current.points[current.points.length - 1];
|
|
110
|
+
switch (command.type) {
|
|
111
|
+
case "M":
|
|
112
|
+
break;
|
|
113
|
+
case "L":
|
|
114
|
+
current.points.push(command.values);
|
|
115
|
+
break;
|
|
116
|
+
case "Z":
|
|
117
|
+
current.closed = true;
|
|
118
|
+
break;
|
|
119
|
+
default:
|
|
120
|
+
current.points.push(
|
|
121
|
+
...CURVES[command.type](last, command.values, tolerance)
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return rings.map(({ points, closed }) => {
|
|
126
|
+
const first = points[0];
|
|
127
|
+
const end = points[points.length - 1];
|
|
128
|
+
const repeats = closed && points.length > 1 && first[0] === end[0] && first[1] === end[1];
|
|
129
|
+
return { points: repeats ? points.slice(0, -1) : points, closed };
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
export {
|
|
133
|
+
flattenPath
|
|
134
|
+
};
|
|
135
|
+
//# sourceMappingURL=flatten.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/interpolate/path/flatten.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PathCommand } from \"#/interpolate/path/parse.ts\";\n\nexport type Point = readonly [number, number];\n\n/** One subpath as a polyline. */\nexport interface Ring {\n\treadonly points: readonly Point[];\n\treadonly closed: boolean;\n}\n\nconst MAX_SEGMENTS = 256;\n\n/** Enough segments that no chord strays more than `tolerance` from its curve. */\nfunction segmentsFor(bend: number, tolerance: number): number {\n\treturn Math.min(\n\t\tMAX_SEGMENTS,\n\t\tMath.max(1, Math.ceil(Math.sqrt((0.75 * bend) / tolerance))),\n\t);\n}\n\nfunction cubic(\n\tfrom: Point,\n\tvalues: readonly number[],\n\ttolerance: number,\n): Point[] {\n\tconst [x1, y1, x2, y2, x, y] = values as [\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t];\n\tconst [x0, y0] = from;\n\tconst bend = Math.max(\n\t\tMath.hypot(x0 - 2 * x1 + x2, y0 - 2 * y1 + y2),\n\t\tMath.hypot(x1 - 2 * x2 + x, y1 - 2 * y2 + y),\n\t);\n\tconst count = segmentsFor(bend, tolerance);\n\treturn Array.from({ length: count }, (_unused, index) => {\n\t\tconst t = (index + 1) / count;\n\t\tconst u = 1 - t;\n\t\tconst [a, b, c, d] = [u * u * u, 3 * u * u * t, 3 * u * t * t, t * t * t];\n\t\treturn [a * x0 + b * x1 + c * x2 + d * x, a * y0 + b * y1 + c * y2 + d * y];\n\t});\n}\n\nfunction quadratic(\n\tfrom: Point,\n\tvalues: readonly number[],\n\ttolerance: number,\n): Point[] {\n\tconst [x1, y1, x, y] = values as [number, number, number, number];\n\tconst [x0, y0] = from;\n\tconst count = segmentsFor(\n\t\tMath.hypot(x0 - 2 * x1 + x, y0 - 2 * y1 + y) / 3,\n\t\ttolerance,\n\t);\n\treturn Array.from({ length: count }, (_unused, index) => {\n\t\tconst t = (index + 1) / count;\n\t\tconst u = 1 - t;\n\t\treturn [\n\t\t\tu * u * x0 + 2 * u * t * x1 + t * t * x,\n\t\t\tu * u * y0 + 2 * u * t * y1 + t * t * y,\n\t\t];\n\t});\n}\n\nfunction angleBetween(ux: number, uy: number, vx: number, vy: number): number {\n\treturn Math.atan2(ux * vy - uy * vx, ux * vx + uy * vy);\n}\n\n/** SVG's endpoint arc, converted to its centre form (SVG 1.1, appendix F.6.5). */\nfunction arc(\n\tfrom: Point,\n\tvalues: readonly number[],\n\ttolerance: number,\n): Point[] {\n\tconst [rxIn, ryIn, degrees, large, sweep, x2, y2] = values as [\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t\tnumber,\n\t];\n\tconst [x1, y1] = from;\n\tif (rxIn === 0 || ryIn === 0) {\n\t\treturn [[x2, y2]];\n\t}\n\tconst phi = (degrees * Math.PI) / 180;\n\tconst [cos, sin] = [Math.cos(phi), Math.sin(phi)];\n\tconst dx = (x1 - x2) / 2;\n\tconst dy = (y1 - y2) / 2;\n\tconst x1p = cos * dx + sin * dy;\n\tconst y1p = -sin * dx + cos * dy;\n\tconst scale = Math.max(\n\t\t1,\n\t\tMath.sqrt((x1p * x1p) / (rxIn * rxIn) + (y1p * y1p) / (ryIn * ryIn)),\n\t);\n\tconst rx = Math.abs(rxIn) * scale;\n\tconst ry = Math.abs(ryIn) * scale;\n\tconst numerator =\n\t\trx * rx * ry * ry - rx * rx * y1p * y1p - ry * ry * x1p * x1p;\n\tconst denominator = rx * rx * y1p * y1p + ry * ry * x1p * x1p;\n\tconst coefficient =\n\t\t(large === sweep ? -1 : 1) *\n\t\tMath.sqrt(Math.max(0, numerator / (denominator || 1)));\n\tconst cxp = (coefficient * rx * y1p) / ry;\n\tconst cyp = (-coefficient * ry * x1p) / rx;\n\tconst cx = cos * cxp - sin * cyp + (x1 + x2) / 2;\n\tconst cy = sin * cxp + cos * cyp + (y1 + y2) / 2;\n\tconst start = angleBetween(1, 0, (x1p - cxp) / rx, (y1p - cyp) / ry);\n\tlet delta = angleBetween(\n\t\t(x1p - cxp) / rx,\n\t\t(y1p - cyp) / ry,\n\t\t(-x1p - cxp) / rx,\n\t\t(-y1p - cyp) / ry,\n\t);\n\tif (sweep === 0 && delta > 0) {\n\t\tdelta -= 2 * Math.PI;\n\t}\n\tif (sweep === 1 && delta < 0) {\n\t\tdelta += 2 * Math.PI;\n\t}\n\tconst radius = Math.max(rx, ry);\n\tconst step = 2 * Math.acos(Math.max(-1, 1 - tolerance / radius));\n\tconst count = Math.min(\n\t\tMAX_SEGMENTS,\n\t\tMath.max(1, Math.ceil(Math.abs(delta) / step)),\n\t);\n\treturn Array.from({ length: count }, (_unused, index): Point => {\n\t\tif (index === count - 1) {\n\t\t\treturn [x2, y2];\n\t\t}\n\t\tconst theta = start + (delta * (index + 1)) / count;\n\t\tconst [c, s] = [Math.cos(theta), Math.sin(theta)];\n\t\treturn [cx + rx * c * cos - ry * s * sin, cy + rx * c * sin + ry * s * cos];\n\t});\n}\n\nconst CURVES = { C: cubic, Q: quadratic, A: arc } as const;\n\n/** Every subpath of a parsed path as a polyline; `tolerance` in path units. */\nexport function flattenPath(\n\tcommands: readonly PathCommand[],\n\ttolerance = 0.25,\n): Ring[] {\n\tconst rings: { points: Point[]; closed: boolean }[] = [];\n\tlet current: { points: Point[]; closed: boolean } = {\n\t\tpoints: [],\n\t\tclosed: false,\n\t};\n\tlet start: Point = [0, 0];\n\tfor (const command of commands) {\n\t\tif (command.type === \"M\" || current.closed) {\n\t\t\tstart = command.type === \"M\" ? command.values : start;\n\t\t\tcurrent = { points: [start], closed: false };\n\t\t\trings.push(current);\n\t\t}\n\t\tconst last = current.points[current.points.length - 1] as Point;\n\t\tswitch (command.type) {\n\t\t\tcase \"M\":\n\t\t\t\tbreak;\n\t\t\tcase \"L\":\n\t\t\t\tcurrent.points.push(command.values);\n\t\t\t\tbreak;\n\t\t\tcase \"Z\":\n\t\t\t\tcurrent.closed = true;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tcurrent.points.push(\n\t\t\t\t\t...CURVES[command.type](last, command.values, tolerance),\n\t\t\t\t);\n\t\t}\n\t}\n\treturn rings.map(({ points, closed }) => {\n\t\tconst first = points[0] as Point;\n\t\tconst end = points[points.length - 1] as Point;\n\t\tconst repeats =\n\t\t\tclosed && points.length > 1 && first[0] === end[0] && first[1] === end[1];\n\t\treturn { points: repeats ? points.slice(0, -1) : points, closed };\n\t});\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path between two paths. Same commands: the numbers mix, exactly. Any
|
|
3
|
+
* other pair is flattened to polylines, each subpath resampled to a shared
|
|
4
|
+
* point count by arc length, paired with its nearest counterpart and turned
|
|
5
|
+
* to line up with it, so every frame is a valid path that moves as little as
|
|
6
|
+
* it can. The ends are the input strings themselves.
|
|
7
|
+
*/
|
|
8
|
+
export declare function mixPath(from: string, to: string): (progress: number) => string;
|
|
9
|
+
//# sourceMappingURL=morph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"morph.d.ts","sourceRoot":"","sources":["../../../src/interpolate/path/morph.ts"],"names":[],"mappings":"AAmEA;;;;;;GAMG;AACH,wBAAgB,OAAO,CACtB,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACR,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAsB9B"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { mixComplex } from "../complex.js";
|
|
2
|
+
import {
|
|
3
|
+
flattenPath
|
|
4
|
+
} from "./flatten.js";
|
|
5
|
+
import { parsePath } from "./parse.js";
|
|
6
|
+
import {
|
|
7
|
+
alignRing,
|
|
8
|
+
centroid,
|
|
9
|
+
matchRings,
|
|
10
|
+
resample
|
|
11
|
+
} from "./rings.js";
|
|
12
|
+
const MIN_POINTS = 16;
|
|
13
|
+
const MAX_POINTS = 512;
|
|
14
|
+
function round(value) {
|
|
15
|
+
return Math.round(value * 100) / 100;
|
|
16
|
+
}
|
|
17
|
+
function collapsed(ring, count) {
|
|
18
|
+
return Array.from({ length: count }, () => centroid(ring.points));
|
|
19
|
+
}
|
|
20
|
+
function track(from, to) {
|
|
21
|
+
const reference = from ?? to;
|
|
22
|
+
const count = Math.min(
|
|
23
|
+
MAX_POINTS,
|
|
24
|
+
Math.max(MIN_POINTS, from?.points.length ?? 0, to?.points.length ?? 0)
|
|
25
|
+
);
|
|
26
|
+
const start = from === void 0 ? collapsed(reference, count) : resample(from, count);
|
|
27
|
+
const end = to === void 0 ? collapsed(reference, count) : resample(to, count);
|
|
28
|
+
const closed = (from?.closed ?? true) && (to?.closed ?? true);
|
|
29
|
+
return {
|
|
30
|
+
from: start,
|
|
31
|
+
to: alignRing(start, end, closed),
|
|
32
|
+
closedFrom: from?.closed ?? reference.closed,
|
|
33
|
+
closedTo: to?.closed ?? reference.closed
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function draw(tracks, progress) {
|
|
37
|
+
return tracks.map(({ from, to, closedFrom, closedTo }) => {
|
|
38
|
+
const points = from.map((point, index) => {
|
|
39
|
+
const other = to[index];
|
|
40
|
+
const x = round(point[0] + (other[0] - point[0]) * progress);
|
|
41
|
+
const y = round(point[1] + (other[1] - point[1]) * progress);
|
|
42
|
+
return `${x},${y}`;
|
|
43
|
+
});
|
|
44
|
+
const closed = progress < 0.5 ? closedFrom : closedTo;
|
|
45
|
+
return `M${points.join("L")}${closed ? "Z" : ""}`;
|
|
46
|
+
}).join("");
|
|
47
|
+
}
|
|
48
|
+
function mixPath(from, to) {
|
|
49
|
+
const hasArcs = /a/i.test(from) || /a/i.test(to);
|
|
50
|
+
const same = from === to ? () => from : hasArcs ? void 0 : mixComplex(from, to);
|
|
51
|
+
if (same !== void 0) {
|
|
52
|
+
return same;
|
|
53
|
+
}
|
|
54
|
+
const start = parsePath(from);
|
|
55
|
+
const end = parsePath(to);
|
|
56
|
+
if (start === void 0 || end === void 0) {
|
|
57
|
+
return (progress) => progress < 0.5 ? from : to;
|
|
58
|
+
}
|
|
59
|
+
const tracks = matchRings(flattenPath(start), flattenPath(end)).map(
|
|
60
|
+
([a, b]) => track(a, b)
|
|
61
|
+
);
|
|
62
|
+
return (progress) => {
|
|
63
|
+
if (progress === 0) {
|
|
64
|
+
return from;
|
|
65
|
+
}
|
|
66
|
+
return progress === 1 ? to : draw(tracks, progress);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
mixPath
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=morph.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/interpolate/path/morph.ts"],
|
|
4
|
+
"sourcesContent": ["import { mixComplex } from \"#/interpolate/complex.ts\";\nimport {\n\tflattenPath,\n\ttype Point,\n\ttype Ring,\n} from \"#/interpolate/path/flatten.ts\";\nimport { parsePath } from \"#/interpolate/path/parse.ts\";\nimport {\n\talignRing,\n\tcentroid,\n\tmatchRings,\n\tresample,\n} from \"#/interpolate/path/rings.ts\";\n\nconst MIN_POINTS = 16;\nconst MAX_POINTS = 512;\n\ninterface Track {\n\treadonly from: readonly Point[];\n\treadonly to: readonly Point[];\n\treadonly closedFrom: boolean;\n\treadonly closedTo: boolean;\n}\n\nfunction round(value: number): number {\n\treturn Math.round(value * 100) / 100;\n}\n\n/** A ring that does not exist on one side is that side's centroid, repeated. */\nfunction collapsed(ring: Ring, count: number): Point[] {\n\treturn Array.from({ length: count }, () => centroid(ring.points));\n}\n\nfunction track(from: Ring | undefined, to: Ring | undefined): Track {\n\tconst reference = (from ?? to) as Ring;\n\tconst count = Math.min(\n\t\tMAX_POINTS,\n\t\tMath.max(MIN_POINTS, from?.points.length ?? 0, to?.points.length ?? 0),\n\t);\n\tconst start =\n\t\tfrom === undefined ? collapsed(reference, count) : resample(from, count);\n\tconst end =\n\t\tto === undefined ? collapsed(reference, count) : resample(to, count);\n\tconst closed = (from?.closed ?? true) && (to?.closed ?? true);\n\treturn {\n\t\tfrom: start,\n\t\tto: alignRing(start, end, closed),\n\t\tclosedFrom: from?.closed ?? reference.closed,\n\t\tclosedTo: to?.closed ?? reference.closed,\n\t};\n}\n\nfunction draw(tracks: readonly Track[], progress: number): string {\n\treturn tracks\n\t\t.map(({ from, to, closedFrom, closedTo }) => {\n\t\t\tconst points = from.map((point, index) => {\n\t\t\t\tconst other = to[index] as Point;\n\t\t\t\tconst x = round(point[0] + (other[0] - point[0]) * progress);\n\t\t\t\tconst y = round(point[1] + (other[1] - point[1]) * progress);\n\t\t\t\treturn `${x},${y}`;\n\t\t\t});\n\t\t\tconst closed = progress < 0.5 ? closedFrom : closedTo;\n\t\t\treturn `M${points.join(\"L\")}${closed ? \"Z\" : \"\"}`;\n\t\t})\n\t\t.join(\"\");\n}\n\n/**\n * A path between two paths. Same commands: the numbers mix, exactly. Any\n * other pair is flattened to polylines, each subpath resampled to a shared\n * point count by arc length, paired with its nearest counterpart and turned\n * to line up with it, so every frame is a valid path that moves as little as\n * it can. The ends are the input strings themselves.\n */\nexport function mixPath(\n\tfrom: string,\n\tto: string,\n): (progress: number) => string {\n\t// Arc flags are 0 or 1: mixed as numbers, they would pass through 0.5.\n\tconst hasArcs = /a/i.test(from) || /a/i.test(to);\n\tconst same =\n\t\tfrom === to ? () => from : hasArcs ? undefined : mixComplex(from, to);\n\tif (same !== undefined) {\n\t\treturn same;\n\t}\n\tconst start = parsePath(from);\n\tconst end = parsePath(to);\n\tif (start === undefined || end === undefined) {\n\t\treturn (progress) => (progress < 0.5 ? from : to);\n\t}\n\tconst tracks = matchRings(flattenPath(start), flattenPath(end)).map(\n\t\t([a, b]) => track(a, b),\n\t);\n\treturn (progress) => {\n\t\tif (progress === 0) {\n\t\t\treturn from;\n\t\t}\n\t\treturn progress === 1 ? to : draw(tracks, progress);\n\t};\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,kBAAkB;AAC3B;AAAA,EACC;AAAA,OAGM;AACP,SAAS,iBAAiB;AAC1B;AAAA,EACC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACM;AAEP,MAAM,aAAa;AACnB,MAAM,aAAa;AASnB,SAAS,MAAM,OAAuB;AACrC,SAAO,KAAK,MAAM,QAAQ,GAAG,IAAI;AAClC;AAGA,SAAS,UAAU,MAAY,OAAwB;AACtD,SAAO,MAAM,KAAK,EAAE,QAAQ,MAAM,GAAG,MAAM,SAAS,KAAK,MAAM,CAAC;AACjE;AAEA,SAAS,MAAM,MAAwB,IAA6B;AACnE,QAAM,YAAa,QAAQ;AAC3B,QAAM,QAAQ,KAAK;AAAA,IAClB;AAAA,IACA,KAAK,IAAI,YAAY,MAAM,OAAO,UAAU,GAAG,IAAI,OAAO,UAAU,CAAC;AAAA,EACtE;AACA,QAAM,QACL,SAAS,SAAY,UAAU,WAAW,KAAK,IAAI,SAAS,MAAM,KAAK;AACxE,QAAM,MACL,OAAO,SAAY,UAAU,WAAW,KAAK,IAAI,SAAS,IAAI,KAAK;AACpE,QAAM,UAAU,MAAM,UAAU,UAAU,IAAI,UAAU;AACxD,SAAO;AAAA,IACN,MAAM;AAAA,IACN,IAAI,UAAU,OAAO,KAAK,MAAM;AAAA,IAChC,YAAY,MAAM,UAAU,UAAU;AAAA,IACtC,UAAU,IAAI,UAAU,UAAU;AAAA,EACnC;AACD;AAEA,SAAS,KAAK,QAA0B,UAA0B;AACjE,SAAO,OACL,IAAI,CAAC,EAAE,MAAM,IAAI,YAAY,SAAS,MAAM;AAC5C,UAAM,SAAS,KAAK,IAAI,CAAC,OAAO,UAAU;AACzC,YAAM,QAAQ,GAAG,KAAK;AACtB,YAAM,IAAI,MAAM,MAAM,CAAC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,QAAQ;AAC3D,YAAM,IAAI,MAAM,MAAM,CAAC,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,KAAK,QAAQ;AAC3D,aAAO,GAAG,CAAC,IAAI,CAAC;AAAA,IACjB,CAAC;AACD,UAAM,SAAS,WAAW,MAAM,aAAa;AAC7C,WAAO,IAAI,OAAO,KAAK,GAAG,CAAC,GAAG,SAAS,MAAM,EAAE;AAAA,EAChD,CAAC,EACA,KAAK,EAAE;AACV;AASO,SAAS,QACf,MACA,IAC+B;AAE/B,QAAM,UAAU,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK,EAAE;AAC/C,QAAM,OACL,SAAS,KAAK,MAAM,OAAO,UAAU,SAAY,WAAW,MAAM,EAAE;AACrE,MAAI,SAAS,QAAW;AACvB,WAAO;AAAA,EACR;AACA,QAAM,QAAQ,UAAU,IAAI;AAC5B,QAAM,MAAM,UAAU,EAAE;AACxB,MAAI,UAAU,UAAa,QAAQ,QAAW;AAC7C,WAAO,CAAC,aAAc,WAAW,MAAM,OAAO;AAAA,EAC/C;AACA,QAAM,SAAS,WAAW,YAAY,KAAK,GAAG,YAAY,GAAG,CAAC,EAAE;AAAA,IAC/D,CAAC,CAAC,GAAG,CAAC,MAAM,MAAM,GAAG,CAAC;AAAA,EACvB;AACA,SAAO,CAAC,aAAa;AACpB,QAAI,aAAa,GAAG;AACnB,aAAO;AAAA,IACR;AACA,WAAO,aAAa,IAAI,KAAK,KAAK,QAAQ,QAAQ;AAAA,EACnD;AACD;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A path in four commands, all absolute: H and V become L, S and T their full
|
|
3
|
+
* curves with the reflected control point, so the flattening downstream only
|
|
4
|
+
* ever meets lines, curves, arcs and closes.
|
|
5
|
+
*/
|
|
6
|
+
export type PathCommand = {
|
|
7
|
+
readonly type: "M" | "L";
|
|
8
|
+
readonly values: readonly [number, number];
|
|
9
|
+
} | {
|
|
10
|
+
readonly type: "C";
|
|
11
|
+
readonly values: readonly number[];
|
|
12
|
+
} | {
|
|
13
|
+
readonly type: "Q";
|
|
14
|
+
readonly values: readonly number[];
|
|
15
|
+
} | {
|
|
16
|
+
readonly type: "A";
|
|
17
|
+
readonly values: readonly number[];
|
|
18
|
+
} | {
|
|
19
|
+
readonly type: "Z";
|
|
20
|
+
readonly values: readonly [];
|
|
21
|
+
};
|
|
22
|
+
/** Parses `d` to absolute commands; `undefined` when it is not a valid path. */
|
|
23
|
+
export declare function parsePath(d: string): PathCommand[] | undefined;
|
|
24
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/interpolate/path/parse.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,WAAW,GACpB;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,GACxE;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,CAAA;CAAE,CAAC;AA8GxD,gFAAgF;AAChF,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,WAAW,EAAE,GAAG,SAAS,CAwC9D"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const ARGUMENTS = {
|
|
2
|
+
M: 2,
|
|
3
|
+
L: 2,
|
|
4
|
+
H: 1,
|
|
5
|
+
V: 1,
|
|
6
|
+
C: 6,
|
|
7
|
+
S: 4,
|
|
8
|
+
Q: 4,
|
|
9
|
+
T: 2,
|
|
10
|
+
A: 7,
|
|
11
|
+
Z: 0
|
|
12
|
+
};
|
|
13
|
+
const NUMBER = /\s*,?\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)/iy;
|
|
14
|
+
const FLAG = /\s*,?\s*([01])/y;
|
|
15
|
+
const COMMAND = /\s*,?\s*([MLHVCSQTAZ])/iy;
|
|
16
|
+
function reflect(state, curve) {
|
|
17
|
+
return state.previous === curve ? [2 * state.x - state.controlX, 2 * state.y - state.controlY] : [state.x, state.y];
|
|
18
|
+
}
|
|
19
|
+
function moveTo(state, x, y) {
|
|
20
|
+
state.x = x;
|
|
21
|
+
state.y = y;
|
|
22
|
+
}
|
|
23
|
+
const HANDLERS = {
|
|
24
|
+
M: ([x, y], state) => {
|
|
25
|
+
moveTo(state, x, y);
|
|
26
|
+
state.startX = state.x;
|
|
27
|
+
state.startY = state.y;
|
|
28
|
+
return { type: "M", values: [state.x, state.y] };
|
|
29
|
+
},
|
|
30
|
+
L: ([x, y], state) => {
|
|
31
|
+
moveTo(state, x, y);
|
|
32
|
+
return { type: "L", values: [state.x, state.y] };
|
|
33
|
+
},
|
|
34
|
+
H: ([x], state) => HANDLERS.L?.([x, state.y], state),
|
|
35
|
+
V: ([y], state) => HANDLERS.L?.([state.x, y], state),
|
|
36
|
+
C: (args, state) => {
|
|
37
|
+
[state.controlX, state.controlY] = [args[2], args[3]];
|
|
38
|
+
moveTo(state, args[4], args[5]);
|
|
39
|
+
return { type: "C", values: args };
|
|
40
|
+
},
|
|
41
|
+
S: (args, state) => HANDLERS.C?.([...reflect(state, "C"), ...args], state),
|
|
42
|
+
Q: (args, state) => {
|
|
43
|
+
[state.controlX, state.controlY] = [args[0], args[1]];
|
|
44
|
+
moveTo(state, args[2], args[3]);
|
|
45
|
+
return { type: "Q", values: args };
|
|
46
|
+
},
|
|
47
|
+
T: (args, state) => HANDLERS.Q?.([...reflect(state, "Q"), ...args], state),
|
|
48
|
+
A: (args, state) => {
|
|
49
|
+
moveTo(state, args[5], args[6]);
|
|
50
|
+
return { type: "A", values: args };
|
|
51
|
+
},
|
|
52
|
+
Z: (_args, state) => {
|
|
53
|
+
moveTo(state, state.startX, state.startY);
|
|
54
|
+
return { type: "Z", values: [] };
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const AXES = {
|
|
58
|
+
M: [0, 1],
|
|
59
|
+
L: [0, 1],
|
|
60
|
+
H: [0],
|
|
61
|
+
V: [1],
|
|
62
|
+
C: [0, 1, 0, 1, 0, 1],
|
|
63
|
+
S: [0, 1, 0, 1],
|
|
64
|
+
Q: [0, 1, 0, 1],
|
|
65
|
+
T: [0, 1],
|
|
66
|
+
A: [-1, -1, -1, -1, -1, 0, 1],
|
|
67
|
+
Z: []
|
|
68
|
+
};
|
|
69
|
+
const CURVE_FAMILY = { S: "C", T: "Q" };
|
|
70
|
+
function absolute(type, args, state) {
|
|
71
|
+
const axes = AXES[type];
|
|
72
|
+
return args.map((value, index) => {
|
|
73
|
+
const axis = axes[index];
|
|
74
|
+
return axis === -1 ? value : value + (axis === 0 ? state.x : state.y);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function read(pattern, text, at) {
|
|
78
|
+
pattern.lastIndex = at;
|
|
79
|
+
const match = pattern.exec(text);
|
|
80
|
+
return match === null ? void 0 : { value: match[1], end: pattern.lastIndex };
|
|
81
|
+
}
|
|
82
|
+
function parsePath(d) {
|
|
83
|
+
const state = {
|
|
84
|
+
x: 0,
|
|
85
|
+
y: 0,
|
|
86
|
+
startX: 0,
|
|
87
|
+
startY: 0,
|
|
88
|
+
controlX: 0,
|
|
89
|
+
controlY: 0,
|
|
90
|
+
previous: ""
|
|
91
|
+
};
|
|
92
|
+
const commands = [];
|
|
93
|
+
let at = 0;
|
|
94
|
+
let command = read(COMMAND, d, at);
|
|
95
|
+
if (command === void 0 || command.value.toUpperCase() !== "M") {
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
while (command !== void 0) {
|
|
99
|
+
at = command.end;
|
|
100
|
+
let letter = command.value;
|
|
101
|
+
do {
|
|
102
|
+
const type = letter.toUpperCase();
|
|
103
|
+
const args = [];
|
|
104
|
+
for (let index = 0; index < ARGUMENTS[type]; index += 1) {
|
|
105
|
+
const flag = type === "A" && (index === 3 || index === 4);
|
|
106
|
+
const token = read(flag ? FLAG : NUMBER, d, at);
|
|
107
|
+
if (token === void 0) {
|
|
108
|
+
return void 0;
|
|
109
|
+
}
|
|
110
|
+
args.push(Number(token.value));
|
|
111
|
+
at = token.end;
|
|
112
|
+
}
|
|
113
|
+
const values = letter === type ? args : absolute(type, args, state);
|
|
114
|
+
commands.push(HANDLERS[type](values, state));
|
|
115
|
+
state.previous = CURVE_FAMILY[type] ?? type;
|
|
116
|
+
letter = type === "M" ? letter === "M" ? "L" : "l" : letter;
|
|
117
|
+
} while (letter.toUpperCase() !== "Z" && read(NUMBER, d, at) !== void 0);
|
|
118
|
+
command = read(COMMAND, d, at);
|
|
119
|
+
}
|
|
120
|
+
return /^\s*$/.test(d.slice(at)) ? commands : void 0;
|
|
121
|
+
}
|
|
122
|
+
export {
|
|
123
|
+
parsePath
|
|
124
|
+
};
|
|
125
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/interpolate/path/parse.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * A path in four commands, all absolute: H and V become L, S and T their full\n * curves with the reflected control point, so the flattening downstream only\n * ever meets lines, curves, arcs and closes.\n */\nexport type PathCommand =\n\t| { readonly type: \"M\" | \"L\"; readonly values: readonly [number, number] }\n\t| { readonly type: \"C\"; readonly values: readonly number[] }\n\t| { readonly type: \"Q\"; readonly values: readonly number[] }\n\t| { readonly type: \"A\"; readonly values: readonly number[] }\n\t| { readonly type: \"Z\"; readonly values: readonly [] };\n\ninterface State {\n\tx: number;\n\ty: number;\n\tstartX: number;\n\tstartY: number;\n\t/** The last curve's second control point, for S and T to reflect. */\n\tcontrolX: number;\n\tcontrolY: number;\n\tprevious: string;\n}\n\ntype Handler = (args: number[], state: State) => PathCommand;\n\nconst ARGUMENTS: Readonly<Record<string, number>> = {\n\tM: 2,\n\tL: 2,\n\tH: 1,\n\tV: 1,\n\tC: 6,\n\tS: 4,\n\tQ: 4,\n\tT: 2,\n\tA: 7,\n\tZ: 0,\n};\nconst NUMBER = /\\s*,?\\s*(-?(?:\\d+\\.?\\d*|\\.\\d+)(?:e[-+]?\\d+)?)/iy;\nconst FLAG = /\\s*,?\\s*([01])/y;\nconst COMMAND = /\\s*,?\\s*([MLHVCSQTAZ])/iy;\n\nfunction reflect(state: State, curve: string): [number, number] {\n\treturn state.previous === curve\n\t\t? [2 * state.x - state.controlX, 2 * state.y - state.controlY]\n\t\t: [state.x, state.y];\n}\n\nfunction moveTo(state: State, x: number, y: number) {\n\tstate.x = x;\n\tstate.y = y;\n}\n\nconst HANDLERS: Readonly<Record<string, Handler>> = {\n\tM: ([x, y], state) => {\n\t\tmoveTo(state, x as number, y as number);\n\t\tstate.startX = state.x;\n\t\tstate.startY = state.y;\n\t\treturn { type: \"M\", values: [state.x, state.y] };\n\t},\n\tL: ([x, y], state) => {\n\t\tmoveTo(state, x as number, y as number);\n\t\treturn { type: \"L\", values: [state.x, state.y] };\n\t},\n\tH: ([x], state) => HANDLERS.L?.([x as number, state.y], state) as PathCommand,\n\tV: ([y], state) => HANDLERS.L?.([state.x, y as number], state) as PathCommand,\n\tC: (args, state) => {\n\t\t[state.controlX, state.controlY] = [args[2] as number, args[3] as number];\n\t\tmoveTo(state, args[4] as number, args[5] as number);\n\t\treturn { type: \"C\", values: args };\n\t},\n\tS: (args, state) =>\n\t\tHANDLERS.C?.([...reflect(state, \"C\"), ...args], state) as PathCommand,\n\tQ: (args, state) => {\n\t\t[state.controlX, state.controlY] = [args[0] as number, args[1] as number];\n\t\tmoveTo(state, args[2] as number, args[3] as number);\n\t\treturn { type: \"Q\", values: args };\n\t},\n\tT: (args, state) =>\n\t\tHANDLERS.Q?.([...reflect(state, \"Q\"), ...args], state) as PathCommand,\n\tA: (args, state) => {\n\t\tmoveTo(state, args[5] as number, args[6] as number);\n\t\treturn { type: \"A\", values: args };\n\t},\n\tZ: (_args, state) => {\n\t\tmoveTo(state, state.startX, state.startY);\n\t\treturn { type: \"Z\", values: [] };\n\t},\n};\n\n/** Each argument's axis when relative: 0 adds x, 1 adds y, -1 is not a coordinate. */\nconst AXES: Readonly<Record<string, readonly number[]>> = {\n\tM: [0, 1],\n\tL: [0, 1],\n\tH: [0],\n\tV: [1],\n\tC: [0, 1, 0, 1, 0, 1],\n\tS: [0, 1, 0, 1],\n\tQ: [0, 1, 0, 1],\n\tT: [0, 1],\n\tA: [-1, -1, -1, -1, -1, 0, 1],\n\tZ: [],\n};\nconst CURVE_FAMILY: Readonly<Record<string, string>> = { S: \"C\", T: \"Q\" };\n\nfunction absolute(type: string, args: number[], state: State): number[] {\n\tconst axes = AXES[type] as readonly number[];\n\treturn args.map((value, index) => {\n\t\tconst axis = axes[index];\n\t\treturn axis === -1 ? value : value + (axis === 0 ? state.x : state.y);\n\t});\n}\n\nfunction read(pattern: RegExp, text: string, at: number) {\n\tpattern.lastIndex = at;\n\tconst match = pattern.exec(text);\n\treturn match === null\n\t\t? undefined\n\t\t: { value: match[1] as string, end: pattern.lastIndex };\n}\n\n/** Parses `d` to absolute commands; `undefined` when it is not a valid path. */\nexport function parsePath(d: string): PathCommand[] | undefined {\n\tconst state: State = {\n\t\tx: 0,\n\t\ty: 0,\n\t\tstartX: 0,\n\t\tstartY: 0,\n\t\tcontrolX: 0,\n\t\tcontrolY: 0,\n\t\tprevious: \"\",\n\t};\n\tconst commands: PathCommand[] = [];\n\tlet at = 0;\n\tlet command = read(COMMAND, d, at);\n\tif (command === undefined || command.value.toUpperCase() !== \"M\") {\n\t\treturn undefined;\n\t}\n\twhile (command !== undefined) {\n\t\tat = command.end;\n\t\tlet letter = command.value;\n\t\tdo {\n\t\t\tconst type = letter.toUpperCase();\n\t\t\tconst args: number[] = [];\n\t\t\tfor (let index = 0; index < (ARGUMENTS[type] as number); index += 1) {\n\t\t\t\tconst flag = type === \"A\" && (index === 3 || index === 4);\n\t\t\t\tconst token = read(flag ? FLAG : NUMBER, d, at);\n\t\t\t\tif (token === undefined) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\targs.push(Number(token.value));\n\t\t\t\tat = token.end;\n\t\t\t}\n\t\t\tconst values = letter === type ? args : absolute(type, args, state);\n\t\t\tcommands.push((HANDLERS[type] as Handler)(values, state));\n\t\t\tstate.previous = CURVE_FAMILY[type] ?? type;\n\t\t\t// After a moveto, further pairs are implicit linetos.\n\t\t\tletter = type === \"M\" ? (letter === \"M\" ? \"L\" : \"l\") : letter;\n\t\t} while (letter.toUpperCase() !== \"Z\" && read(NUMBER, d, at) !== undefined);\n\t\tcommand = read(COMMAND, d, at);\n\t}\n\treturn /^\\s*$/.test(d.slice(at)) ? commands : undefined;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Point, Ring } from "./flatten.js";
|
|
2
|
+
/** `count` points evenly spaced by arc length; a closed ring includes its closing edge. */
|
|
3
|
+
export declare function resample(ring: Ring, count: number): Point[];
|
|
4
|
+
export declare function area(points: readonly Point[]): number;
|
|
5
|
+
export declare function centroid(points: readonly Point[]): Point;
|
|
6
|
+
/**
|
|
7
|
+
* `to` rearranged so each point travels as little as it can to its partner
|
|
8
|
+
* in `from`: a closed ring may start anywhere and run either way round, an
|
|
9
|
+
* open one may only run backwards. Both have the same length.
|
|
10
|
+
*/
|
|
11
|
+
export declare function alignRing(from: readonly Point[], to: readonly Point[], closed: boolean): Point[];
|
|
12
|
+
export declare function matchRings(from: readonly Ring[], to: readonly Ring[]): [Ring | undefined, Ring | undefined][];
|
|
13
|
+
//# sourceMappingURL=rings.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rings.d.ts","sourceRoot":"","sources":["../../../src/interpolate/path/rings.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,+BAA+B,CAAC;AAMjE,2FAA2F;AAC3F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,EAAE,CAkC3D;AAED,wBAAgB,IAAI,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,MAAM,CAOrD;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,GAAG,KAAK,CAMxD;AAgBD;;;;GAIG;AACH,wBAAgB,SAAS,CACxB,IAAI,EAAE,SAAS,KAAK,EAAE,EACtB,EAAE,EAAE,SAAS,KAAK,EAAE,EACpB,MAAM,EAAE,OAAO,GACb,KAAK,EAAE,CAoBT;AAWD,wBAAgB,UAAU,CACzB,IAAI,EAAE,SAAS,IAAI,EAAE,EACrB,EAAE,EAAE,SAAS,IAAI,EAAE,GACjB,CAAC,IAAI,GAAG,SAAS,EAAE,IAAI,GAAG,SAAS,CAAC,EAAE,CAiCxC"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
function distance(a, b) {
|
|
2
|
+
return Math.hypot(a[0] - b[0], a[1] - b[1]);
|
|
3
|
+
}
|
|
4
|
+
function resample(ring, count) {
|
|
5
|
+
const points = ring.closed ? [...ring.points, ring.points[0]] : ring.points;
|
|
6
|
+
const lengths = [0];
|
|
7
|
+
for (let index = 1; index < points.length; index += 1) {
|
|
8
|
+
lengths.push(
|
|
9
|
+
lengths[index - 1] + distance(points[index - 1], points[index])
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
const total = lengths[lengths.length - 1];
|
|
13
|
+
if (points.length < 2 || total === 0) {
|
|
14
|
+
return Array.from({ length: count }, () => points[0]);
|
|
15
|
+
}
|
|
16
|
+
const step = total / (ring.closed ? count : Math.max(1, count - 1));
|
|
17
|
+
const result = [];
|
|
18
|
+
let segment = 1;
|
|
19
|
+
for (let index = 0; index < count; index += 1) {
|
|
20
|
+
const along = Math.min(total, index * step);
|
|
21
|
+
while (segment < points.length - 1 && lengths[segment] < along) {
|
|
22
|
+
segment += 1;
|
|
23
|
+
}
|
|
24
|
+
const [a, b] = [points[segment - 1], points[segment]];
|
|
25
|
+
const span = lengths[segment] - lengths[segment - 1];
|
|
26
|
+
const t = span === 0 ? 0 : (along - lengths[segment - 1]) / span;
|
|
27
|
+
result.push([a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t]);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
}
|
|
31
|
+
function area(points) {
|
|
32
|
+
let sum = 0;
|
|
33
|
+
points.forEach((point, index) => {
|
|
34
|
+
const next = points[(index + 1) % points.length];
|
|
35
|
+
sum += point[0] * next[1] - next[0] * point[1];
|
|
36
|
+
});
|
|
37
|
+
return sum / 2;
|
|
38
|
+
}
|
|
39
|
+
function centroid(points) {
|
|
40
|
+
const sum = points.reduce(
|
|
41
|
+
(total, [x, y]) => [total[0] + x, total[1] + y],
|
|
42
|
+
[0, 0]
|
|
43
|
+
);
|
|
44
|
+
return [sum[0] / points.length, sum[1] / points.length];
|
|
45
|
+
}
|
|
46
|
+
function cost(a, b, offset) {
|
|
47
|
+
let sum = 0;
|
|
48
|
+
for (let index = 0; index < a.length; index += 1) {
|
|
49
|
+
const other = b[(index + offset) % b.length];
|
|
50
|
+
const point = a[index];
|
|
51
|
+
sum += (point[0] - other[0]) ** 2 + (point[1] - other[1]) ** 2;
|
|
52
|
+
}
|
|
53
|
+
return sum;
|
|
54
|
+
}
|
|
55
|
+
function alignRing(from, to, closed) {
|
|
56
|
+
const candidates = [to, [...to].reverse()];
|
|
57
|
+
let best = {
|
|
58
|
+
points: to,
|
|
59
|
+
offset: 0,
|
|
60
|
+
cost: Number.POSITIVE_INFINITY
|
|
61
|
+
};
|
|
62
|
+
for (const points of candidates) {
|
|
63
|
+
const offsets = closed ? points.length : 1;
|
|
64
|
+
for (let offset = 0; offset < offsets; offset += 1) {
|
|
65
|
+
const value = cost(from, points, offset);
|
|
66
|
+
if (value < best.cost) {
|
|
67
|
+
best = { points, offset, cost: value };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return best.points.map(
|
|
72
|
+
(_point, index) => best.points[(index + best.offset) % best.points.length]
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
function describe(ring) {
|
|
76
|
+
return { centre: centroid(ring.points), size: Math.abs(area(ring.points)) };
|
|
77
|
+
}
|
|
78
|
+
function matchRings(from, to) {
|
|
79
|
+
const starts = from.map(describe);
|
|
80
|
+
const ends = to.map(describe);
|
|
81
|
+
const pairs = starts.flatMap(
|
|
82
|
+
(start, i) => ends.map((end, j) => {
|
|
83
|
+
const scale = Math.sqrt(Math.max(start.size, end.size, 1));
|
|
84
|
+
return {
|
|
85
|
+
i,
|
|
86
|
+
j,
|
|
87
|
+
score: distance(start.centre, end.centre) / scale + Math.abs(Math.log((start.size + 1) / (end.size + 1)))
|
|
88
|
+
};
|
|
89
|
+
})
|
|
90
|
+
);
|
|
91
|
+
pairs.sort((a, b) => a.score - b.score);
|
|
92
|
+
const usedFrom = /* @__PURE__ */ new Set();
|
|
93
|
+
const usedTo = /* @__PURE__ */ new Set();
|
|
94
|
+
const result = [];
|
|
95
|
+
for (const { i, j } of pairs) {
|
|
96
|
+
if (!usedFrom.has(i) && !usedTo.has(j)) {
|
|
97
|
+
usedFrom.add(i);
|
|
98
|
+
usedTo.add(j);
|
|
99
|
+
result.push([from[i], to[j]]);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const [i, ring] of from.entries()) {
|
|
103
|
+
if (!usedFrom.has(i)) result.push([ring, void 0]);
|
|
104
|
+
}
|
|
105
|
+
for (const [j, ring] of to.entries()) {
|
|
106
|
+
if (!usedTo.has(j)) result.push([void 0, ring]);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
export {
|
|
111
|
+
alignRing,
|
|
112
|
+
area,
|
|
113
|
+
centroid,
|
|
114
|
+
matchRings,
|
|
115
|
+
resample
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=rings.js.map
|