morphicons 1.1.0 → 1.1.1
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 +12 -4
- package/dist/dom.js +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +1 -1
- package/dist/react.js +1 -1
- package/dist/{spring-BR0BCAfU.js → spring-BTcHiHR5.js} +26 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -208,7 +208,7 @@ Let `aᶜᵢ = aᵢ − c_A` and `b̃ᵢ = R(−θ*)·(bᵢ − c_B)/σ*` (B bro
|
|
|
208
208
|
```
|
|
209
209
|
P(t) = c(t) + σ*ᵗ · R(t·θ*) · [(1−t)·aᶜᵢ + t·b̃ᵢ]
|
|
210
210
|
|
|
211
|
-
c(t) = lerp(c_A, c_B, t) linear translation
|
|
211
|
+
c(t) = lerp(c_A, c_B, t) linear translation (block transport under the global hybrid — below)
|
|
212
212
|
t·θ* = linear angle (θ* is already in (−π, π], short path)
|
|
213
213
|
σ*ᵗ = exp(t·ln σ*) log-linear scale (geodesic in ℝ⁺)
|
|
214
214
|
```
|
|
@@ -220,6 +220,14 @@ Exact at both endpoints (max error ~3.6e-15 in tests). Properties:
|
|
|
220
220
|
- Mixed cases do both simultaneously and continuously.
|
|
221
221
|
- With spring overshoot (t > 1) the formula **extrapolates** naturally: rotation and scale overshoot slightly and come back — free juice.
|
|
222
222
|
|
|
223
|
+
**Block transport (global hybrid).** When the whole icon is congruent (§4), lerping each subpath centroid reintroduces the chord disease one level up: every part spins with the shared θ*, but an off-center part travels the chord — inside the arc — and drifts toward the center mid-flight. An arrow's head sagged ~1 px toward its shaft at t = 0.5 (`r·(1 − cos(θ/2))` with r = 3.5, θ = 90°) while both parts rotated perfectly. Under the hybrid, each centroid rides the shared similarity around the global centroid `g_A` instead:
|
|
224
|
+
|
|
225
|
+
```
|
|
226
|
+
c_k(t) = c_{A,k} + t·d_k + (σ*ᵗ·R(t·θ*) − I)·(c_{A,k} − g_A)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`d_k` is fixed once so that `c_k(1) = c_{B,k}` exactly (it absorbs the tiny global residual). Every point of every subpath then moves under ONE similarity per frame: congruent icons are rigid through the whole flight, not only at the endpoints. With θ* = 0, or centroids sitting on `g_A`, or no hybrid at all, the formula degrades continuously to the plain lerp — no thresholds, no special cases.
|
|
230
|
+
|
|
223
231
|
### 6. Serialization and canonical snap
|
|
224
232
|
|
|
225
233
|
In flight, each subpath is emitted as `M x y L x y …` (+ `Z` if the pair is closed↔closed; closed→open flies open, the loop opening at the cut chosen by the circular correspondence) with 2 decimals — invisible at 24px, and the frame's only allocation. On settle, the driver **snaps to the canonical `d`** of the target icon: exact fidelity at rest (real curves, not polylines), subpath count reset after duplications, and the DOM ends up identical to a static icon's. The jump is < 0.02px — imperceptible.
|
|
@@ -255,9 +263,9 @@ CI budget (size-limit, gzip) as an anti-regression tripwire — gates carry ~10%
|
|
|
255
263
|
|
|
256
264
|
| entry | measured | gate |
|
|
257
265
|
|---|---|---|
|
|
258
|
-
| `morphicons` (core) | 6.
|
|
259
|
-
| `morphicons/dom` (core + driver) |
|
|
260
|
-
| `morphicons/react` (all, react external) | 7.
|
|
266
|
+
| `morphicons` (core) | 6.60 KB | 7 KB |
|
|
267
|
+
| `morphicons/dom` (core + driver) | 7.04 KB | 7.5 KB |
|
|
268
|
+
| `morphicons/react` (all, react external) | 7.77 KB | 8.5 KB |
|
|
261
269
|
|
|
262
270
|
## Playground
|
|
263
271
|
|
package/dist/dom.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-BTcHiHR5.js";
|
|
2
2
|
//#region src/dom/index.ts
|
|
3
3
|
const tickers = /* @__PURE__ */ new Set();
|
|
4
4
|
let rafId = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ interface PlanItem {
|
|
|
18
18
|
/** true if both endpoints are closed loops: the subpath flies with Z.
|
|
19
19
|
* Closed → open flies open: the loop opens at the chosen cut. */
|
|
20
20
|
closed: boolean;
|
|
21
|
+
/** Block transport (set by the global hybrid, else null): mid-flight the
|
|
22
|
+
* centroid rides the shared similarity around the global centroid instead
|
|
23
|
+
* of lerping — off = c_A − g_A; drift closes c(1) = c_B exactly. */
|
|
24
|
+
block: {
|
|
25
|
+
off: readonly [number, number];
|
|
26
|
+
drift: readonly [number, number];
|
|
27
|
+
} | null;
|
|
21
28
|
}
|
|
22
29
|
interface MorphPlan {
|
|
23
30
|
items: PlanItem[];
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-BTcHiHR5.js";
|
|
2
2
|
export { SPRING_PRESETS, Spring, allocOutputs, buildPlan, cubicsToPathD, fitIcon, iconToCubics, interpLinear, interpPolar, resampleIcon, resamplePath, serialize };
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-BTcHiHR5.js";
|
|
2
2
|
import { canonicalD, createMorph } from "./dom.js";
|
|
3
3
|
import { forwardRef, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -8,12 +8,21 @@ function interpPolar(plan, t, out) {
|
|
|
8
8
|
const it = plan.items[k];
|
|
9
9
|
const o = out[k];
|
|
10
10
|
const n = plan.n;
|
|
11
|
-
const cx = it.ca[0] + (it.cb[0] - it.ca[0]) * t;
|
|
12
|
-
const cy = it.ca[1] + (it.cb[1] - it.ca[1]) * t;
|
|
13
11
|
const s = Math.exp(it.lnSigma * t);
|
|
14
12
|
const ang = it.theta * t;
|
|
15
13
|
const cos = Math.cos(ang) * s;
|
|
16
14
|
const sin = Math.sin(ang) * s;
|
|
15
|
+
let cx;
|
|
16
|
+
let cy;
|
|
17
|
+
if (it.block) {
|
|
18
|
+
const [ox, oy] = it.block.off;
|
|
19
|
+
const [dx, dy] = it.block.drift;
|
|
20
|
+
cx = it.ca[0] + dx * t + (ox * cos - oy * sin - ox);
|
|
21
|
+
cy = it.ca[1] + dy * t + (ox * sin + oy * cos - oy);
|
|
22
|
+
} else {
|
|
23
|
+
cx = it.ca[0] + (it.cb[0] - it.ca[0]) * t;
|
|
24
|
+
cy = it.ca[1] + (it.cb[1] - it.ca[1]) * t;
|
|
25
|
+
}
|
|
17
26
|
for (let i = 0; i < n; i++) {
|
|
18
27
|
const px = it.aC[2 * i] + (it.bT[2 * i] - it.aC[2 * i]) * t;
|
|
19
28
|
const py = it.aC[2 * i + 1] + (it.bT[2 * i + 1] - it.aC[2 * i + 1]) * t;
|
|
@@ -806,7 +815,8 @@ function applyGlobal(items, n) {
|
|
|
806
815
|
ga.set(it.a, 2 * n * k);
|
|
807
816
|
gb.set(it.bO, 2 * n * k);
|
|
808
817
|
});
|
|
809
|
-
const
|
|
818
|
+
const gca = centroid(ga);
|
|
819
|
+
const g = procrustes(ga, gb, gca, centroid(gb));
|
|
810
820
|
if (g.res >= GLOBAL_EPS) return;
|
|
811
821
|
const cos = Math.cos(-g.theta);
|
|
812
822
|
const sin = Math.sin(-g.theta);
|
|
@@ -828,6 +838,17 @@ function applyGlobal(items, n) {
|
|
|
828
838
|
it.theta = g.theta;
|
|
829
839
|
it.lnSigma = Math.log(g.sigma);
|
|
830
840
|
it.res = nb > 1e-12 ? Math.sqrt(e2 / nb) : 0;
|
|
841
|
+
const s1 = Math.exp(it.lnSigma);
|
|
842
|
+
const c1 = Math.cos(it.theta) * s1;
|
|
843
|
+
const n1 = Math.sin(it.theta) * s1;
|
|
844
|
+
const ox = it.ca[0] - gca[0];
|
|
845
|
+
const oy = it.ca[1] - gca[1];
|
|
846
|
+
const rx = ox * c1 - oy * n1 - ox;
|
|
847
|
+
const ry = ox * n1 + oy * c1 - oy;
|
|
848
|
+
it.block = {
|
|
849
|
+
off: [ox, oy],
|
|
850
|
+
drift: [it.cb[0] - it.ca[0] - rx, it.cb[1] - it.ca[1] - ry]
|
|
851
|
+
};
|
|
831
852
|
}
|
|
832
853
|
}
|
|
833
854
|
/** Builds the morph plan between two lists of sampled subpaths. The plan is
|
|
@@ -879,7 +900,8 @@ function buildPlan(srcSubs, dstSubs) {
|
|
|
879
900
|
theta: al.theta,
|
|
880
901
|
lnSigma: Math.log(al.sigma),
|
|
881
902
|
res: al.res,
|
|
882
|
-
closed: srcSubs[si].closed && dstSubs[di].closed
|
|
903
|
+
closed: srcSubs[si].closed && dstSubs[di].closed,
|
|
904
|
+
block: null
|
|
883
905
|
};
|
|
884
906
|
});
|
|
885
907
|
if (items.length > 1) applyGlobal(items, n);
|
package/package.json
CHANGED