morphicons 1.7.1 → 2.0.0
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 +61 -428
- package/dist/MorphIcon.astro +4 -2
- package/dist/MorphIcon.svelte +19 -8
- package/dist/adapters.d.ts +12 -11
- package/dist/adapters.js +223 -23
- package/dist/{controller-CXZuwJ_M.js → controller--7SSBggk.js} +45 -11
- package/dist/{controller-Dkruvf3Y.d.ts → controller-DKm8s2ni.d.ts} +15 -6
- package/dist/dom.d.ts +2 -2
- package/dist/dom.js +292 -31
- package/dist/element.d.ts +7 -6
- package/dist/element.js +18 -5
- package/dist/index-DDNCPQ4Z.d.ts +55 -0
- package/dist/{index-DXp5b6BU.d.ts → index-DpvKR75N.d.ts} +26 -3
- package/dist/index.d.ts +12 -47
- package/dist/index.js +2 -1
- package/dist/plan-DhaDhEVq.d.ts +76 -0
- package/dist/react-native.d.ts +7 -6
- package/dist/react-native.js +57 -12
- package/dist/react.d.ts +7 -6
- package/dist/react.js +32 -7
- package/dist/region-BjECuWZR.js +60 -0
- package/dist/region.d.ts +2 -0
- package/dist/region.js +819 -0
- package/dist/{spring-CFHloqPP.js → resample-CXlaZ5ZQ.js} +56 -142
- package/dist/spring-B_P92nf6.js +95 -0
- package/dist/spring-BgZHrI2W.d.ts +33 -0
- package/dist/svelte-shared.d.ts +6 -5
- package/dist/svelte-shared.js +1 -2
- package/dist/vue.d.ts +6 -5
- package/dist/vue.js +31 -7
- package/package.json +62 -36
- package/dist/types-JAF3s76Y.d.ts +0 -60
package/dist/region.js
ADDED
|
@@ -0,0 +1,819 @@
|
|
|
1
|
+
import { n as iconToCubics } from "./normalize-CYnN3Npw.js";
|
|
2
|
+
import { c as makeItem, d as reversePts, f as rotatePts, i as applyGlobal, l as pairUp, n as resamplePath, o as centroid, s as costMatrix, u as procrustes } from "./resample-CXlaZ5ZQ.js";
|
|
3
|
+
import { a as paintOf, i as isRegion, l as translucent, n as distSeg, r as inside, s as signedArea, t as REGION_ATTR } from "./region-BjECuWZR.js";
|
|
4
|
+
//#region src/region/plan.ts
|
|
5
|
+
const REGION_THRESHOLDS = [
|
|
6
|
+
Math.PI / 4,
|
|
7
|
+
Math.PI / 3,
|
|
8
|
+
Math.PI / 2,
|
|
9
|
+
Math.PI
|
|
10
|
+
];
|
|
11
|
+
const FINE = .3;
|
|
12
|
+
function coarse(path, s) {
|
|
13
|
+
const n = s.length / 2;
|
|
14
|
+
let j = 0;
|
|
15
|
+
for (let v = 0; v < path.length; v += 6) for (let k = 0;; k++) {
|
|
16
|
+
if (k === n) return true;
|
|
17
|
+
const a = (j + k) % n;
|
|
18
|
+
const b = (a + 1) % n;
|
|
19
|
+
if (distSeg(path[v], path[v + 1], s[2 * a], s[2 * a + 1], s[2 * b], s[2 * b + 1]) <= FINE) {
|
|
20
|
+
j = a;
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
/** Samples a normalized region: one Sampled per contour, every one closed,
|
|
27
|
+
* `hole` read from the winding (outers positive, holes negative). N per
|
|
28
|
+
* contour: 64, or 128 for every contour of an icon that 64 can't follow
|
|
29
|
+
* (snowflake tips, spiral turns: the landing would snap). */
|
|
30
|
+
function resampleRegion(icon, N) {
|
|
31
|
+
const paths = iconToCubics(icon);
|
|
32
|
+
const at = (n) => paths.map((path) => {
|
|
33
|
+
let pts;
|
|
34
|
+
for (const th of REGION_THRESHOLDS) try {
|
|
35
|
+
pts = resamplePath({
|
|
36
|
+
pts: path.pts,
|
|
37
|
+
closed: true
|
|
38
|
+
}, n, th);
|
|
39
|
+
break;
|
|
40
|
+
} catch {}
|
|
41
|
+
const s = pts;
|
|
42
|
+
return {
|
|
43
|
+
pts: s,
|
|
44
|
+
closed: true,
|
|
45
|
+
hole: signedArea(s) < 0
|
|
46
|
+
};
|
|
47
|
+
});
|
|
48
|
+
const s = at(N ?? 64);
|
|
49
|
+
return N || !paths.some((p, k) => coarse(p.pts, s[k].pts)) ? s : at(128);
|
|
50
|
+
}
|
|
51
|
+
const REGION_LAMBDA = .3;
|
|
52
|
+
const FOLD_SLACK = .1;
|
|
53
|
+
const FOLD_TRIES = 16;
|
|
54
|
+
function folds(a, b) {
|
|
55
|
+
const L = a.length;
|
|
56
|
+
const p = new Float64Array(L);
|
|
57
|
+
for (let i = 0; i < L; i++) p[i] = (a[i] + b[i]) / 2;
|
|
58
|
+
for (let i = 0; i < L; i += 2) {
|
|
59
|
+
const ax = p[i];
|
|
60
|
+
const ay = p[i + 1];
|
|
61
|
+
const ux = p[(i + 2) % L] - ax;
|
|
62
|
+
const uy = p[(i + 3) % L] - ay;
|
|
63
|
+
for (let j = i + 4; j < L - (i ? 0 : 2); j += 2) {
|
|
64
|
+
const cx = p[j];
|
|
65
|
+
const cy = p[j + 1];
|
|
66
|
+
const vx = p[(j + 2) % L] - cx;
|
|
67
|
+
const vy = p[(j + 3) % L] - cy;
|
|
68
|
+
if ((ux * (cy - ay) - uy * (cx - ax)) * (ux * (cy + vy - ay) - uy * (cx + vx - ax)) >= 0) continue;
|
|
69
|
+
if ((vx * (ay - cy) - vy * (ax - cx)) * (vx * (ay + uy - cy) - vy * (ax + ux - cx)) < 0) return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
function outerItem(a, b) {
|
|
75
|
+
const n = a.length / 2;
|
|
76
|
+
const k = n > 64 ? 2 : 1;
|
|
77
|
+
const half = (p, o) => k > 1 ? p.filter((_, i) => (i >> 1) % 2 === o) : p;
|
|
78
|
+
const as = half(a, 0);
|
|
79
|
+
const ca = centroid(as);
|
|
80
|
+
const bs = [half(b, 0), half(b, 1)];
|
|
81
|
+
const cbs = bs.map(centroid);
|
|
82
|
+
const offs = [];
|
|
83
|
+
for (let off = 0; off < n; off++) {
|
|
84
|
+
const j = off % k;
|
|
85
|
+
const s = procrustes(as, rotatePts(bs[j], (off - j) / k), ca, cbs[j]);
|
|
86
|
+
offs.push([off, s.res + REGION_LAMBDA * Math.abs(s.theta) / Math.PI]);
|
|
87
|
+
}
|
|
88
|
+
offs.sort((x, y) => x[1] - y[1]);
|
|
89
|
+
let best;
|
|
90
|
+
for (const [off, score] of offs.slice(0, FOLD_TRIES)) {
|
|
91
|
+
if (score > offs[0][1] + FOLD_SLACK) break;
|
|
92
|
+
const it = makeItem(a, rotatePts(b, off), false, false, false, REGION_LAMBDA);
|
|
93
|
+
it.closed = true;
|
|
94
|
+
best ??= it;
|
|
95
|
+
if (!folds(half(it.aC, 0), half(it.bT, 0))) return it;
|
|
96
|
+
}
|
|
97
|
+
return best;
|
|
98
|
+
}
|
|
99
|
+
function parentOf(hole, subs) {
|
|
100
|
+
const n = hole.length / 2;
|
|
101
|
+
let best = -1;
|
|
102
|
+
let bestArea = Infinity;
|
|
103
|
+
subs.forEach((s, k) => {
|
|
104
|
+
if (s.hole) return;
|
|
105
|
+
let votes = 0;
|
|
106
|
+
for (const v of [
|
|
107
|
+
0,
|
|
108
|
+
Math.floor(n / 3),
|
|
109
|
+
Math.floor(2 * n / 3)
|
|
110
|
+
]) if (inside(hole[2 * v], hole[2 * v + 1], s.pts)) votes++;
|
|
111
|
+
if (votes < 2) return;
|
|
112
|
+
const ar = Math.abs(signedArea(s.pts));
|
|
113
|
+
if (ar < bestArea) {
|
|
114
|
+
bestArea = ar;
|
|
115
|
+
best = k;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
return best;
|
|
119
|
+
}
|
|
120
|
+
function assign(C, skip, one, cover) {
|
|
121
|
+
const L = C.length;
|
|
122
|
+
const S = one.length;
|
|
123
|
+
const f = new Array(L).fill(-1);
|
|
124
|
+
const mult = new Array(S).fill(0);
|
|
125
|
+
if ((S + 1) ** L > 1e5) {
|
|
126
|
+
const pairs = [];
|
|
127
|
+
for (let i = 0; i < L; i++) for (let j = 0; j < S; j++) if (C[i][j] < Infinity) pairs.push([
|
|
128
|
+
C[i][j],
|
|
129
|
+
i,
|
|
130
|
+
j
|
|
131
|
+
]);
|
|
132
|
+
pairs.sort((x, y) => x[0] - y[0]);
|
|
133
|
+
for (const [, i, j] of pairs) if (f[i] < 0 && !mult[j]) {
|
|
134
|
+
f[i] = j;
|
|
135
|
+
mult[j] = 1;
|
|
136
|
+
}
|
|
137
|
+
for (let i = 0; i < L; i++) {
|
|
138
|
+
if (f[i] >= 0) continue;
|
|
139
|
+
for (let j = 0; j < S; j++) if (!one[j] && (f[i] < 0 || C[i][j] < C[i][f[i]])) f[i] = j;
|
|
140
|
+
}
|
|
141
|
+
return f;
|
|
142
|
+
}
|
|
143
|
+
let best = f.slice();
|
|
144
|
+
let bc = Infinity;
|
|
145
|
+
const rec = (i, acc, covered) => {
|
|
146
|
+
if (acc >= bc || cover && S - covered > L - i) return;
|
|
147
|
+
if (i === L) {
|
|
148
|
+
bc = acc;
|
|
149
|
+
best = f.slice();
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
for (let j = 0; j < S; j++) {
|
|
153
|
+
if (one[j] && mult[j] || !(C[i][j] < Infinity)) continue;
|
|
154
|
+
f[i] = j;
|
|
155
|
+
mult[j]++;
|
|
156
|
+
rec(i + 1, acc + C[i][j], covered + (mult[j] === 1 ? 1 : 0));
|
|
157
|
+
mult[j]--;
|
|
158
|
+
}
|
|
159
|
+
f[i] = -1;
|
|
160
|
+
rec(i + 1, acc + skip[i], covered);
|
|
161
|
+
};
|
|
162
|
+
rec(0, 0, 0);
|
|
163
|
+
return best;
|
|
164
|
+
}
|
|
165
|
+
const SKIP = 1e6;
|
|
166
|
+
function carry(x, y, from, to, theta, s) {
|
|
167
|
+
const c = Math.cos(theta) * s;
|
|
168
|
+
const n = Math.sin(theta) * s;
|
|
169
|
+
const dx = x - from[0];
|
|
170
|
+
const dy = y - from[1];
|
|
171
|
+
return [to[0] + dx * c - dy * n, to[1] + dx * n + dy * c];
|
|
172
|
+
}
|
|
173
|
+
function dots(c, n) {
|
|
174
|
+
const d = new Float64Array(2 * n);
|
|
175
|
+
for (let i = 0; i < 2 * n; i += 2) {
|
|
176
|
+
d[i] = c[0];
|
|
177
|
+
d[i + 1] = c[1];
|
|
178
|
+
}
|
|
179
|
+
return d;
|
|
180
|
+
}
|
|
181
|
+
function inFrame(a, b, f, hole, half) {
|
|
182
|
+
const n = a.length / 2;
|
|
183
|
+
const s = Math.exp(f.lnSigma);
|
|
184
|
+
const aC = new Float64Array(2 * n);
|
|
185
|
+
const bt = new Float64Array(2 * n);
|
|
186
|
+
let nb = 0;
|
|
187
|
+
for (let i = 0; i < 2 * n; i += 2) {
|
|
188
|
+
aC[i] = a[i] - f.ca[0];
|
|
189
|
+
aC[i + 1] = a[i + 1] - f.ca[1];
|
|
190
|
+
[bt[i], bt[i + 1]] = carry(b[i], b[i + 1], f.cb, [0, 0], -f.theta, 1 / s);
|
|
191
|
+
nb += (b[i] - f.cb[0]) ** 2 + (b[i + 1] - f.cb[1]) ** 2;
|
|
192
|
+
}
|
|
193
|
+
let off = 0;
|
|
194
|
+
let be = Infinity;
|
|
195
|
+
for (let o = 0; o < n && be > 0; o++) {
|
|
196
|
+
let e = 0;
|
|
197
|
+
for (let i = 0; i < n && e < be; i++) {
|
|
198
|
+
const j = (i + o) % n;
|
|
199
|
+
e += (aC[2 * i] - bt[2 * j]) ** 2 + (aC[2 * i + 1] - bt[2 * j + 1]) ** 2;
|
|
200
|
+
}
|
|
201
|
+
if (e < be) {
|
|
202
|
+
be = e;
|
|
203
|
+
off = o;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
a,
|
|
208
|
+
aC,
|
|
209
|
+
bT: rotatePts(bt, off),
|
|
210
|
+
bO: rotatePts(b, off),
|
|
211
|
+
ca: f.ca,
|
|
212
|
+
cb: f.cb,
|
|
213
|
+
theta: f.theta,
|
|
214
|
+
lnSigma: f.lnSigma,
|
|
215
|
+
res: nb > 1e-12 ? Math.sqrt(s * s * be / nb) : 0,
|
|
216
|
+
closed: true,
|
|
217
|
+
hole,
|
|
218
|
+
half,
|
|
219
|
+
block: f.block
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function vanishingHole(pts, outer, grow) {
|
|
223
|
+
const s = Math.exp(outer.lnSigma);
|
|
224
|
+
let [x, y] = centroid(pts);
|
|
225
|
+
const { aC, bT } = outer;
|
|
226
|
+
const near = grow ? outer.bO : outer.a;
|
|
227
|
+
let w = 0;
|
|
228
|
+
let dx = 0;
|
|
229
|
+
let dy = 0;
|
|
230
|
+
for (let i = 0; i < aC.length; i += 2) {
|
|
231
|
+
const k = 1 / ((near[i] - x) ** 2 + (near[i + 1] - y) ** 2 + 1e-9);
|
|
232
|
+
w += k;
|
|
233
|
+
dx += k * (bT[i] - aC[i]);
|
|
234
|
+
dy += k * (bT[i + 1] - aC[i + 1]);
|
|
235
|
+
}
|
|
236
|
+
w = (grow ? -.5 : .5) / w;
|
|
237
|
+
if (grow) [x, y] = carry(x, y, outer.cb, outer.ca, -outer.theta, 1 / s);
|
|
238
|
+
x += w * dx;
|
|
239
|
+
y += w * dy;
|
|
240
|
+
const dot = dots(grow ? [x, y] : carry(x, y, outer.ca, outer.cb, outer.theta, s), pts.length / 2);
|
|
241
|
+
return grow ? inFrame(dot, pts, outer, true, 1) : inFrame(pts, dot, outer, true, 0);
|
|
242
|
+
}
|
|
243
|
+
const weight = (it, t) => Math.min(1, Math.max(0, 2 * t - (it.half ?? t)));
|
|
244
|
+
function strays(h, o, holes) {
|
|
245
|
+
let k = 0;
|
|
246
|
+
for (const t of [
|
|
247
|
+
.25,
|
|
248
|
+
.5,
|
|
249
|
+
.75
|
|
250
|
+
]) {
|
|
251
|
+
if (!(weight(h, t) % 1)) continue;
|
|
252
|
+
const [H, O, ...G] = [
|
|
253
|
+
h,
|
|
254
|
+
o,
|
|
255
|
+
...holes.filter((g) => g !== h)
|
|
256
|
+
].map((it) => it.aC.map((v, i) => v + weight(it, t) * (it.bT[i] - v)));
|
|
257
|
+
for (let i = 0; i < H.length; i += 2) if (!inside(H[i], H[i + 1], O) || G.some((p) => inside(H[i], H[i + 1], p))) k++;
|
|
258
|
+
}
|
|
259
|
+
return k;
|
|
260
|
+
}
|
|
261
|
+
function vanishingPiece(outer, holes, grow) {
|
|
262
|
+
const c = centroid(outer);
|
|
263
|
+
const f = {
|
|
264
|
+
ca: c,
|
|
265
|
+
cb: c,
|
|
266
|
+
theta: 0,
|
|
267
|
+
lnSigma: 0,
|
|
268
|
+
block: null
|
|
269
|
+
};
|
|
270
|
+
const dot = dots(c, outer.length / 2);
|
|
271
|
+
return [outer, ...holes].map((p, k) => grow ? inFrame(dot, p, f, k > 0) : inFrame(p, dot, f, k > 0));
|
|
272
|
+
}
|
|
273
|
+
const vanishCost = (pts) => costMatrix([pts], [dots(centroid(pts), pts.length / 2)])[0][0];
|
|
274
|
+
function bbox(p) {
|
|
275
|
+
const b = [
|
|
276
|
+
p[0],
|
|
277
|
+
p[1],
|
|
278
|
+
p[0],
|
|
279
|
+
p[1]
|
|
280
|
+
];
|
|
281
|
+
for (let i = 2; i < p.length; i += 2) {
|
|
282
|
+
b[0] = Math.min(b[0], p[i]);
|
|
283
|
+
b[1] = Math.min(b[1], p[i + 1]);
|
|
284
|
+
b[2] = Math.max(b[2], p[i]);
|
|
285
|
+
b[3] = Math.max(b[3], p[i + 1]);
|
|
286
|
+
}
|
|
287
|
+
return b;
|
|
288
|
+
}
|
|
289
|
+
const HOLE_IOU = .25;
|
|
290
|
+
function holeCosts(HA, HB, outer) {
|
|
291
|
+
const s = Math.exp(outer.lnSigma);
|
|
292
|
+
const C = costMatrix(HA, HB);
|
|
293
|
+
const inA = HB.map((h) => {
|
|
294
|
+
const q = new Float64Array(h.length);
|
|
295
|
+
for (let i = 0; i < h.length; i += 2) [q[i], q[i + 1]] = carry(h[i], h[i + 1], outer.cb, outer.ca, -outer.theta, 1 / s);
|
|
296
|
+
return bbox(q);
|
|
297
|
+
});
|
|
298
|
+
HA.forEach((h, i) => {
|
|
299
|
+
const a = bbox(h);
|
|
300
|
+
inA.forEach((b, j) => {
|
|
301
|
+
const w = Math.min(a[2], b[2]) - Math.max(a[0], b[0]);
|
|
302
|
+
const v = Math.min(a[3], b[3]) - Math.max(a[1], b[1]);
|
|
303
|
+
const inter = w > 0 && v > 0 ? w * v : 0;
|
|
304
|
+
const union = (a[2] - a[0]) * (a[3] - a[1]) + (b[2] - b[0]) * (b[3] - b[1]) - inter;
|
|
305
|
+
if (!(inter >= HOLE_IOU * union)) C[i][j] = Infinity;
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
return C;
|
|
309
|
+
}
|
|
310
|
+
function piecesOf(subs) {
|
|
311
|
+
const idx = [];
|
|
312
|
+
subs.forEach((s, k) => {
|
|
313
|
+
if (!s.hole) idx.push(k);
|
|
314
|
+
});
|
|
315
|
+
const holes = idx.map(() => []);
|
|
316
|
+
subs.forEach((s) => {
|
|
317
|
+
if (!s.hole) return;
|
|
318
|
+
let k = idx.indexOf(parentOf(s.pts, subs));
|
|
319
|
+
if (k < 0) {
|
|
320
|
+
const [hx, hy] = centroid(s.pts);
|
|
321
|
+
let bd = Infinity;
|
|
322
|
+
idx.forEach((o, m) => {
|
|
323
|
+
const [ox, oy] = centroid(subs[o].pts);
|
|
324
|
+
const d = Math.hypot(ox - hx, oy - hy);
|
|
325
|
+
if (d < bd) {
|
|
326
|
+
bd = d;
|
|
327
|
+
k = m;
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
holes[k].push(s.pts);
|
|
332
|
+
});
|
|
333
|
+
return {
|
|
334
|
+
outers: idx.map((k) => subs[k].pts),
|
|
335
|
+
holes
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
function doubled(p) {
|
|
339
|
+
const m = p.length;
|
|
340
|
+
const q = new Float64Array(2 * m);
|
|
341
|
+
for (let i = 0; i < m; i += 2) {
|
|
342
|
+
const j = (i + 2) % m;
|
|
343
|
+
q[2 * i] = p[i];
|
|
344
|
+
q[2 * i + 1] = p[i + 1];
|
|
345
|
+
q[2 * i + 2] = (p[i] + p[j]) / 2;
|
|
346
|
+
q[2 * i + 3] = (p[i + 1] + p[j + 1]) / 2;
|
|
347
|
+
}
|
|
348
|
+
return q;
|
|
349
|
+
}
|
|
350
|
+
/** Region plan: outers ↔ outers (holed contours never duplicated), holes ↔
|
|
351
|
+
* overlapping holes of the paired outer, direction locked; whatever has no
|
|
352
|
+
* partner collapses/grows without ever breaking the nesting (rules above).
|
|
353
|
+
* Every item flies closed. */
|
|
354
|
+
function buildRegionPlan(srcSubs, dstSubs) {
|
|
355
|
+
const n = Math.max(srcSubs[0].pts.length, dstSubs[0].pts.length) / 2;
|
|
356
|
+
const fit = (s) => s[0].pts.length / 2 < n ? s.map((c) => ({
|
|
357
|
+
...c,
|
|
358
|
+
pts: doubled(c.pts)
|
|
359
|
+
})) : s;
|
|
360
|
+
const A = piecesOf(fit(srcSubs));
|
|
361
|
+
const B = piecesOf(fit(dstSubs));
|
|
362
|
+
if (A.outers.length === 0 || B.outers.length === 0) throw new Error("morphicons: region has no outer contour");
|
|
363
|
+
let pairs;
|
|
364
|
+
const p = A.outers.length;
|
|
365
|
+
const q = B.outers.length;
|
|
366
|
+
const [large, small] = p < q ? [B, A] : [A, B];
|
|
367
|
+
if (p === q || small.holes.every((h) => h.length === 0)) pairs = pairUp(A.outers, B.outers);
|
|
368
|
+
else pairs = assign(costMatrix(large.outers, small.outers), large.outers.map((o) => SKIP + vanishCost(o)), small.holes.map((h) => h.length > 0), true).map((s, l) => p < q ? [s, l] : [l, s]);
|
|
369
|
+
const items = [];
|
|
370
|
+
let partial = false;
|
|
371
|
+
for (const [i, j] of pairs) {
|
|
372
|
+
if (i < 0 || j < 0) {
|
|
373
|
+
partial = true;
|
|
374
|
+
items.push(...i < 0 ? vanishingPiece(B.outers[j], B.holes[j], true) : vanishingPiece(A.outers[i], A.holes[i], false));
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
const it = outerItem(A.outers[i], B.outers[j]);
|
|
378
|
+
it.hole = false;
|
|
379
|
+
items.push(it);
|
|
380
|
+
const HA = A.holes[i];
|
|
381
|
+
const HB = B.holes[j];
|
|
382
|
+
const f = assign(holeCosts(HA, HB, it), HA.map(() => SKIP), HB.map(() => true), false);
|
|
383
|
+
const holes = HA.map((h, k) => f[k] < 0 ? vanishingHole(h, it, false) : inFrame(h, HB[f[k]], it, true));
|
|
384
|
+
HB.forEach((h, k) => {
|
|
385
|
+
if (!f.includes(k)) holes.push(vanishingHole(h, it, true));
|
|
386
|
+
});
|
|
387
|
+
for (let k = 0; k < holes.length; k++) {
|
|
388
|
+
const h = holes[k];
|
|
389
|
+
if (h.half !== void 0) {
|
|
390
|
+
partial = true;
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const c = strays(h, it, holes);
|
|
394
|
+
if (!c) continue;
|
|
395
|
+
holes.splice(k, 1, vanishingHole(h.a, it, false), vanishingHole(h.bO, it, true));
|
|
396
|
+
if (strays(holes[k], it, holes) + strays(holes[k + 1], it, holes) >= c) holes.splice(k, 2, h);
|
|
397
|
+
}
|
|
398
|
+
items.push(...holes);
|
|
399
|
+
}
|
|
400
|
+
if (items.length > 1 && !partial) applyGlobal(items, n);
|
|
401
|
+
return {
|
|
402
|
+
items,
|
|
403
|
+
n
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
//#endregion
|
|
407
|
+
//#region src/region/index.ts
|
|
408
|
+
const BIG = 1e9;
|
|
409
|
+
const painted = (v) => v !== void 0 && String(v).trim().toLowerCase() !== "none";
|
|
410
|
+
const num = (v, fallback) => {
|
|
411
|
+
const x = typeof v === "number" ? v : Number.parseFloat(String(v ?? ""));
|
|
412
|
+
return Number.isFinite(x) ? x : fallback;
|
|
413
|
+
};
|
|
414
|
+
function flatten(input, tol) {
|
|
415
|
+
const lines = [];
|
|
416
|
+
const closed = [];
|
|
417
|
+
for (const { pts, closed: c } of iconToCubics(input)) {
|
|
418
|
+
const out = [pts[0], pts[1]];
|
|
419
|
+
for (let i = 0; i + 7 < pts.length; i += 6) {
|
|
420
|
+
const x0 = pts[i];
|
|
421
|
+
const y0 = pts[i + 1];
|
|
422
|
+
const x1 = pts[i + 2];
|
|
423
|
+
const y1 = pts[i + 3];
|
|
424
|
+
const x2 = pts[i + 4];
|
|
425
|
+
const y2 = pts[i + 5];
|
|
426
|
+
const x3 = pts[i + 6];
|
|
427
|
+
const y3 = pts[i + 7];
|
|
428
|
+
const dd = Math.max(Math.hypot(x0 - 2 * x1 + x2, y0 - 2 * y1 + y2), Math.hypot(x1 - 2 * x2 + x3, y1 - 2 * y2 + y3));
|
|
429
|
+
const k = Math.min(128, Math.max(1, Math.ceil(Math.sqrt(6 * dd / (8 * tol)))));
|
|
430
|
+
for (let s = 1; s <= k; s++) {
|
|
431
|
+
const t = s / k;
|
|
432
|
+
const u = 1 - t;
|
|
433
|
+
out.push(u * u * u * x0 + 3 * u * u * t * x1 + 3 * u * t * t * x2 + t * t * t * x3, u * u * u * y0 + 3 * u * u * t * y1 + 3 * u * t * t * y2 + t * t * t * y3);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
lines.push(Float64Array.from(out));
|
|
437
|
+
closed.push(c);
|
|
438
|
+
}
|
|
439
|
+
return {
|
|
440
|
+
lines,
|
|
441
|
+
closed
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function partsOf(input, width, tol) {
|
|
445
|
+
if (typeof input === "string") return [{
|
|
446
|
+
...flatten(input, tol),
|
|
447
|
+
half: width / 2,
|
|
448
|
+
fill: 0
|
|
449
|
+
}];
|
|
450
|
+
const fillIcon = paintOf(input) === "fill";
|
|
451
|
+
const parts = [];
|
|
452
|
+
for (const el of input) {
|
|
453
|
+
const a = el[1];
|
|
454
|
+
if (translucent(a)) continue;
|
|
455
|
+
const strokeOn = fillIcon ? painted(a.stroke) : a.stroke === void 0 || painted(a.stroke);
|
|
456
|
+
const fillOn = fillIcon && (painted(a.fill) || a.fill === void 0 && !strokeOn);
|
|
457
|
+
if (!strokeOn && !fillOn) continue;
|
|
458
|
+
parts.push({
|
|
459
|
+
...flatten([el], tol),
|
|
460
|
+
half: strokeOn ? num(a["stroke-width"], width) / 2 : 0,
|
|
461
|
+
fill: fillOn ? String(a["fill-rule"] ?? "").trim() === "evenodd" ? 2 : 1 : 0
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
return parts;
|
|
465
|
+
}
|
|
466
|
+
function eachNear(g, ax, ay, bx, by, r, fn) {
|
|
467
|
+
const i0 = Math.max(0, Math.floor((Math.min(ax, bx) - r - g.x0) / g.h));
|
|
468
|
+
const i1 = Math.min(g.nx - 1, Math.ceil((Math.max(ax, bx) + r - g.x0) / g.h));
|
|
469
|
+
const j0 = Math.max(0, Math.floor((Math.min(ay, by) - r - g.y0) / g.h));
|
|
470
|
+
const j1 = Math.min(g.ny - 1, Math.ceil((Math.max(ay, by) + r - g.y0) / g.h));
|
|
471
|
+
for (let j = j0; j <= j1; j++) {
|
|
472
|
+
const y = g.y0 + j * g.h;
|
|
473
|
+
for (let i = i0; i <= i1; i++) fn(j * g.nx + i, g.x0 + i * g.h, y);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function eachSeg(p, close, fn) {
|
|
477
|
+
const n = p.length / 2;
|
|
478
|
+
if (n === 1) fn(p[0], p[1], p[0], p[1]);
|
|
479
|
+
for (let i = 0; i + 1 < n; i++) fn(p[2 * i], p[2 * i + 1], p[2 * i + 2], p[2 * i + 3]);
|
|
480
|
+
if (close && n > 2) fn(p[2 * n - 2], p[2 * n - 1], p[0], p[1]);
|
|
481
|
+
}
|
|
482
|
+
function field(parts, g) {
|
|
483
|
+
const F = new Float32Array(g.nx * g.ny).fill(BIG);
|
|
484
|
+
const band = 2 * g.h;
|
|
485
|
+
let D = null;
|
|
486
|
+
for (const part of parts) {
|
|
487
|
+
if (part.half > 0) {
|
|
488
|
+
const r = part.half + band;
|
|
489
|
+
part.lines.forEach((p, li) => {
|
|
490
|
+
eachSeg(p, part.closed[li], (ax, ay, bx, by) => eachNear(g, ax, ay, bx, by, r, (k, x, y) => {
|
|
491
|
+
const d = distSeg(x, y, ax, ay, bx, by) - part.half;
|
|
492
|
+
if (d < F[k]) F[k] = d;
|
|
493
|
+
}));
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
if (part.fill) {
|
|
497
|
+
D ??= new Float32Array(g.nx * g.ny);
|
|
498
|
+
const Dm = D;
|
|
499
|
+
Dm.fill(BIG);
|
|
500
|
+
for (const p of part.lines) eachSeg(p, true, (ax, ay, bx, by) => eachNear(g, ax, ay, bx, by, band, (k, x, y) => {
|
|
501
|
+
const d = distSeg(x, y, ax, ay, bx, by);
|
|
502
|
+
if (d < Dm[k]) Dm[k] = d;
|
|
503
|
+
}));
|
|
504
|
+
const xs = [];
|
|
505
|
+
const ws = [];
|
|
506
|
+
for (let j = 0; j < g.ny; j++) {
|
|
507
|
+
const y = g.y0 + j * g.h;
|
|
508
|
+
const cross = [];
|
|
509
|
+
for (const p of part.lines) eachSeg(p, true, (ax, ay, bx, by) => {
|
|
510
|
+
if (ay <= y !== by <= y) cross.push([ax + (y - ay) * (bx - ax) / (by - ay), by > ay ? 1 : -1]);
|
|
511
|
+
});
|
|
512
|
+
cross.sort((u, v) => u[0] - v[0]);
|
|
513
|
+
xs.length = 0;
|
|
514
|
+
ws.length = 0;
|
|
515
|
+
for (const [x, w] of cross) {
|
|
516
|
+
xs.push(x);
|
|
517
|
+
ws.push(w);
|
|
518
|
+
}
|
|
519
|
+
let c = 0;
|
|
520
|
+
let wind = 0;
|
|
521
|
+
for (let i = 0; i < g.nx; i++) {
|
|
522
|
+
const x = g.x0 + i * g.h;
|
|
523
|
+
while (c < xs.length && xs[c] < x) wind += ws[c++];
|
|
524
|
+
const isIn = part.fill === 1 ? wind !== 0 : (wind & 1) === 1;
|
|
525
|
+
const k = j * g.nx + i;
|
|
526
|
+
const v = isIn ? -Dm[k] : Dm[k];
|
|
527
|
+
if (v < F[k]) F[k] = v;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
return F;
|
|
533
|
+
}
|
|
534
|
+
function trace(F, g) {
|
|
535
|
+
const { nx, ny, h, x0, y0 } = g;
|
|
536
|
+
const pt = /* @__PURE__ */ new Map();
|
|
537
|
+
const crossing = (key) => {
|
|
538
|
+
if (pt.has(key)) return key;
|
|
539
|
+
const node = key >> 1;
|
|
540
|
+
const i = node % nx;
|
|
541
|
+
const j = (node - i) / nx;
|
|
542
|
+
const other = key & 1 ? node + nx : node + 1;
|
|
543
|
+
const fa = F[node];
|
|
544
|
+
const t = Math.min(Math.max(fa / (fa - F[other]), 1e-5), .99999);
|
|
545
|
+
pt.set(key, key & 1 ? [x0 + i * h, y0 + (j + t) * h] : [x0 + (i + t) * h, y0 + j * h]);
|
|
546
|
+
return key;
|
|
547
|
+
};
|
|
548
|
+
const adj = /* @__PURE__ */ new Map();
|
|
549
|
+
const link = (a, b) => {
|
|
550
|
+
const ka = crossing(a);
|
|
551
|
+
const kb = crossing(b);
|
|
552
|
+
const la = adj.get(ka);
|
|
553
|
+
if (la) la.push(kb);
|
|
554
|
+
else adj.set(ka, [kb]);
|
|
555
|
+
const lb = adj.get(kb);
|
|
556
|
+
if (lb) lb.push(ka);
|
|
557
|
+
else adj.set(kb, [ka]);
|
|
558
|
+
};
|
|
559
|
+
for (let j = 0; j + 1 < ny; j++) for (let i = 0; i + 1 < nx; i++) {
|
|
560
|
+
const a = j * nx + i;
|
|
561
|
+
const b = a + 1;
|
|
562
|
+
const c = a + nx + 1;
|
|
563
|
+
const d = a + nx;
|
|
564
|
+
const ia = F[a] < 0;
|
|
565
|
+
const ib = F[b] < 0;
|
|
566
|
+
const ic = F[c] < 0;
|
|
567
|
+
const id = F[d] < 0;
|
|
568
|
+
const top = 2 * a;
|
|
569
|
+
const right = 2 * b + 1;
|
|
570
|
+
const bottom = 2 * d;
|
|
571
|
+
const left = 2 * a + 1;
|
|
572
|
+
const edges = [];
|
|
573
|
+
if (ia !== ib) edges.push(top);
|
|
574
|
+
if (ib !== ic) edges.push(right);
|
|
575
|
+
if (id !== ic) edges.push(bottom);
|
|
576
|
+
if (ia !== id) edges.push(left);
|
|
577
|
+
if (edges.length === 2) link(edges[0], edges[1]);
|
|
578
|
+
else if (edges.length === 4) {
|
|
579
|
+
if ((F[a] + F[b] + F[c] + F[d]) / 4 < 0 === ia) {
|
|
580
|
+
link(top, right);
|
|
581
|
+
link(bottom, left);
|
|
582
|
+
} else {
|
|
583
|
+
link(left, top);
|
|
584
|
+
link(right, bottom);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const loops = [];
|
|
589
|
+
const seen = /* @__PURE__ */ new Set();
|
|
590
|
+
for (const start of adj.keys()) {
|
|
591
|
+
if (seen.has(start)) continue;
|
|
592
|
+
const ring = [];
|
|
593
|
+
let prev = -1;
|
|
594
|
+
let cur = start;
|
|
595
|
+
while (!seen.has(cur)) {
|
|
596
|
+
seen.add(cur);
|
|
597
|
+
const p = pt.get(cur);
|
|
598
|
+
ring.push(p[0], p[1]);
|
|
599
|
+
const nb = adj.get(cur);
|
|
600
|
+
const next = nb[0] !== prev ? nb[0] : nb[1];
|
|
601
|
+
prev = cur;
|
|
602
|
+
cur = next;
|
|
603
|
+
}
|
|
604
|
+
if (ring.length >= 6) loops.push(Float64Array.from(ring));
|
|
605
|
+
}
|
|
606
|
+
return loops;
|
|
607
|
+
}
|
|
608
|
+
function rdp(p, i0, i1, tol, keep) {
|
|
609
|
+
const n = p.length / 2;
|
|
610
|
+
const ax = p[2 * (i0 % n)];
|
|
611
|
+
const ay = p[2 * (i0 % n) + 1];
|
|
612
|
+
const bx = p[2 * (i1 % n)];
|
|
613
|
+
const by = p[2 * (i1 % n) + 1];
|
|
614
|
+
let worst = -1;
|
|
615
|
+
let wd = tol;
|
|
616
|
+
for (let i = i0 + 1; i < i1; i++) {
|
|
617
|
+
const d = distSeg(p[2 * (i % n)], p[2 * (i % n) + 1], ax, ay, bx, by);
|
|
618
|
+
if (d > wd) {
|
|
619
|
+
wd = d;
|
|
620
|
+
worst = i;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (worst < 0) return;
|
|
624
|
+
keep[worst % n] = 1;
|
|
625
|
+
rdp(p, i0, worst, tol, keep);
|
|
626
|
+
rdp(p, worst, i1, tol, keep);
|
|
627
|
+
}
|
|
628
|
+
function simplify(p, tol) {
|
|
629
|
+
const n = p.length / 2;
|
|
630
|
+
if (n < 4) return p;
|
|
631
|
+
let far = 0;
|
|
632
|
+
let fd = -1;
|
|
633
|
+
for (let i = 1; i < n; i++) {
|
|
634
|
+
const d = Math.hypot(p[2 * i] - p[0], p[2 * i + 1] - p[1]);
|
|
635
|
+
if (d > fd) {
|
|
636
|
+
fd = d;
|
|
637
|
+
far = i;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
const keep = new Uint8Array(n);
|
|
641
|
+
keep[0] = 1;
|
|
642
|
+
keep[far] = 1;
|
|
643
|
+
rdp(p, 0, far, tol, keep);
|
|
644
|
+
rdp(p, far, n, tol, keep);
|
|
645
|
+
const out = [];
|
|
646
|
+
for (let i = 0; i < n; i++) if (keep[i]) out.push(p[2 * i], p[2 * i + 1]);
|
|
647
|
+
return Float64Array.from(out);
|
|
648
|
+
}
|
|
649
|
+
function recoverCorners(p, h) {
|
|
650
|
+
const n = p.length / 2;
|
|
651
|
+
if (n < 4) return p;
|
|
652
|
+
const len = (k) => {
|
|
653
|
+
const a = k % n;
|
|
654
|
+
const b = (k + 1) % n;
|
|
655
|
+
return Math.hypot(p[2 * b] - p[2 * a], p[2 * b + 1] - p[2 * a + 1]);
|
|
656
|
+
};
|
|
657
|
+
const long = [];
|
|
658
|
+
for (let k = 0; k < n; k++) if (len(k) >= 2.5 * h) long.push(k);
|
|
659
|
+
if (long.length < 2) return p;
|
|
660
|
+
const drop = new Uint8Array(n);
|
|
661
|
+
const moved = /* @__PURE__ */ new Map();
|
|
662
|
+
for (let q = 0; q < long.length; q++) {
|
|
663
|
+
const k1 = long[q];
|
|
664
|
+
const k2 = long[(q + 1) % long.length];
|
|
665
|
+
const gap = (k2 - k1 - 1 + n) % n;
|
|
666
|
+
if (gap < 1 || gap > 3) continue;
|
|
667
|
+
let gl = 0;
|
|
668
|
+
for (let s = 1; s <= gap; s++) gl += len(k1 + s);
|
|
669
|
+
if (gl > 3 * h) continue;
|
|
670
|
+
const a0 = k1 % n;
|
|
671
|
+
const a1 = (k1 + 1) % n;
|
|
672
|
+
const b0 = k2 % n;
|
|
673
|
+
const b1 = (k2 + 1) % n;
|
|
674
|
+
const d1x = p[2 * a1] - p[2 * a0];
|
|
675
|
+
const d1y = p[2 * a1 + 1] - p[2 * a0 + 1];
|
|
676
|
+
const d2x = p[2 * b1] - p[2 * b0];
|
|
677
|
+
const d2y = p[2 * b1 + 1] - p[2 * b0 + 1];
|
|
678
|
+
const cr = d1x * d2y - d1y * d2x;
|
|
679
|
+
if (Math.abs(Math.atan2(cr, d1x * d2x + d1y * d2y)) < Math.PI / 12) continue;
|
|
680
|
+
const t = ((p[2 * b0] - p[2 * a0]) * d2y - (p[2 * b0 + 1] - p[2 * a0 + 1]) * d2x) / cr;
|
|
681
|
+
const X = [p[2 * a0] + t * d1x, p[2 * a0 + 1] + t * d1y];
|
|
682
|
+
const mx = (p[2 * a1] + p[2 * b0]) / 2;
|
|
683
|
+
const my = (p[2 * a1 + 1] + p[2 * b0 + 1]) / 2;
|
|
684
|
+
if (Math.hypot(X[0] - mx, X[1] - my) > 2 * h) continue;
|
|
685
|
+
moved.set(a1, X);
|
|
686
|
+
for (let s = 2; s <= gap + 1; s++) drop[(k1 + s) % n] = 1;
|
|
687
|
+
}
|
|
688
|
+
if (moved.size === 0) return p;
|
|
689
|
+
const out = [];
|
|
690
|
+
for (let i = 0; i < n; i++) {
|
|
691
|
+
if (drop[i]) continue;
|
|
692
|
+
const m = moved.get(i);
|
|
693
|
+
if (m) out.push(m[0], m[1]);
|
|
694
|
+
else out.push(p[2 * i], p[2 * i + 1]);
|
|
695
|
+
}
|
|
696
|
+
return Float64Array.from(out);
|
|
697
|
+
}
|
|
698
|
+
const fmt = (v) => String(Math.round(v * 1e3) / 1e3);
|
|
699
|
+
/** Contours of the visible surface: closed, non-overlapping, outers
|
|
700
|
+
* positive / holes negative (nesting depth decides which is which). */
|
|
701
|
+
function regionContours(input, options) {
|
|
702
|
+
const h = options?.cell ?? .125;
|
|
703
|
+
const parts = partsOf(input, options?.strokeWidth ?? 2, .004);
|
|
704
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
705
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
706
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
707
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
708
|
+
let pad = 0;
|
|
709
|
+
for (const part of parts) {
|
|
710
|
+
pad = Math.max(pad, part.half);
|
|
711
|
+
for (const p of part.lines) for (let i = 0; i < p.length; i += 2) {
|
|
712
|
+
minX = Math.min(minX, p[i]);
|
|
713
|
+
maxX = Math.max(maxX, p[i]);
|
|
714
|
+
minY = Math.min(minY, p[i + 1]);
|
|
715
|
+
maxY = Math.max(maxY, p[i + 1]);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
if (!(maxX >= minX)) return [];
|
|
719
|
+
pad += 3 * h;
|
|
720
|
+
const g = {
|
|
721
|
+
x0: minX - pad,
|
|
722
|
+
y0: minY - pad,
|
|
723
|
+
h,
|
|
724
|
+
nx: Math.ceil((maxX - minX + 2 * pad) / h) + 1,
|
|
725
|
+
ny: Math.ceil((maxY - minY + 2 * pad) / h) + 1
|
|
726
|
+
};
|
|
727
|
+
const loops = trace(field(parts, g), g).filter((l) => Math.abs(signedArea(l)) > .01);
|
|
728
|
+
return loops.map((l) => {
|
|
729
|
+
let depth = 0;
|
|
730
|
+
for (const o of loops) if (o !== l && inside(l[0], l[1], o)) depth++;
|
|
731
|
+
const hole = depth % 2 === 1;
|
|
732
|
+
return recoverCorners(simplify(signedArea(l) > 0 === !hole ? l : reversePts(l), h * .08), h);
|
|
733
|
+
});
|
|
734
|
+
}
|
|
735
|
+
/** Any icon → its canonical region: one `<path>` (nonzero) whose contours
|
|
736
|
+
* are the visible surface. Idempotent on regions. Stroke icons outline at
|
|
737
|
+
* `strokeWidth` (round caps and joins — Lucide, Tabler, Feather,
|
|
738
|
+
* Heroicons outline); fill icons honor each element's fill rule. */
|
|
739
|
+
function toRegion(input, options) {
|
|
740
|
+
if (isRegion(input)) return input;
|
|
741
|
+
let d = "";
|
|
742
|
+
for (const p of regionContours(input, options)) {
|
|
743
|
+
d += `M${fmt(p[0])} ${fmt(p[1])}`;
|
|
744
|
+
for (let i = 2; i < p.length; i += 2) d += `L${fmt(p[i])} ${fmt(p[i + 1])}`;
|
|
745
|
+
d += "Z";
|
|
746
|
+
}
|
|
747
|
+
if (!d) throw new Error("morphicons: icon has no visible surface");
|
|
748
|
+
return [["path", {
|
|
749
|
+
d,
|
|
750
|
+
fill: "currentColor",
|
|
751
|
+
stroke: "none",
|
|
752
|
+
"fill-rule": "nonzero",
|
|
753
|
+
[REGION_ATTR]: "1"
|
|
754
|
+
}]];
|
|
755
|
+
}
|
|
756
|
+
const forms = /* @__PURE__ */ new WeakMap();
|
|
757
|
+
const byContent = /* @__PURE__ */ new Map();
|
|
758
|
+
const samples = /* @__PURE__ */ new WeakMap();
|
|
759
|
+
const plans = /* @__PURE__ */ new WeakMap();
|
|
760
|
+
/** The region the driver draws for an icon (cached): a stroke icon
|
|
761
|
+
* outlined at the element's stroke width, a fill icon as is. */
|
|
762
|
+
function regionOf(icon, strokeWidth) {
|
|
763
|
+
if (isRegion(icon)) return icon;
|
|
764
|
+
const width = paintOf(icon) === "fill" && !icon.some(([, a]) => painted(a.stroke) && a["stroke-width"] === void 0) ? 0 : strokeWidth;
|
|
765
|
+
let byWidth = typeof icon === "string" ? void 0 : forms.get(icon);
|
|
766
|
+
let r = byWidth?.get(width);
|
|
767
|
+
if (r) return r;
|
|
768
|
+
const key = `${width}|${typeof icon === "string" ? icon : JSON.stringify(icon)}`;
|
|
769
|
+
r = byContent.get(key);
|
|
770
|
+
if (r) byContent.delete(key);
|
|
771
|
+
else r = toRegion(icon, { strokeWidth });
|
|
772
|
+
byContent.set(key, r);
|
|
773
|
+
if (byContent.size > 64) byContent.delete(byContent.keys().next().value);
|
|
774
|
+
if (typeof icon !== "string") {
|
|
775
|
+
if (!byWidth) {
|
|
776
|
+
byWidth = /* @__PURE__ */ new Map();
|
|
777
|
+
forms.set(icon, byWidth);
|
|
778
|
+
}
|
|
779
|
+
byWidth.set(width, r);
|
|
780
|
+
}
|
|
781
|
+
return r;
|
|
782
|
+
}
|
|
783
|
+
function sampledOf(region) {
|
|
784
|
+
let s = samples.get(region);
|
|
785
|
+
if (!s) {
|
|
786
|
+
s = resampleRegion(region);
|
|
787
|
+
samples.set(region, s);
|
|
788
|
+
}
|
|
789
|
+
return s;
|
|
790
|
+
}
|
|
791
|
+
/** Region plan from `src` at rest to `dst` (cached by reference). */
|
|
792
|
+
function regionPlan(src, dst, strokeWidth) {
|
|
793
|
+
const a = regionOf(src, strokeWidth);
|
|
794
|
+
const b = regionOf(dst, strokeWidth);
|
|
795
|
+
let inner = plans.get(a);
|
|
796
|
+
if (!inner) {
|
|
797
|
+
inner = /* @__PURE__ */ new WeakMap();
|
|
798
|
+
plans.set(a, inner);
|
|
799
|
+
}
|
|
800
|
+
let p = inner.get(b);
|
|
801
|
+
if (!p) {
|
|
802
|
+
p = buildRegionPlan(sampledOf(a), sampledOf(b));
|
|
803
|
+
inner.set(b, p);
|
|
804
|
+
}
|
|
805
|
+
return p;
|
|
806
|
+
}
|
|
807
|
+
/** Region plan from the shape in flight to `dst` (never cached): region
|
|
808
|
+
* samples as they are, or a centerline `d` (a stroke morph interrupted
|
|
809
|
+
* toward a fill icon), outlined as it is. */
|
|
810
|
+
function regionPlanFrom(shape, dst, strokeWidth) {
|
|
811
|
+
return buildRegionPlan(typeof shape === "string" ? resampleRegion(regionOf(shape, strokeWidth)) : shape, sampledOf(regionOf(dst, strokeWidth)));
|
|
812
|
+
}
|
|
813
|
+
globalThis[Symbol.for("morphicons.region")] = {
|
|
814
|
+
regionOf,
|
|
815
|
+
regionPlan,
|
|
816
|
+
regionPlanFrom
|
|
817
|
+
};
|
|
818
|
+
//#endregion
|
|
819
|
+
export { buildRegionPlan, isRegion, paintOf, regionContours, regionOf, regionPlan, regionPlanFrom, resampleRegion, toRegion };
|