react-native-nano-icons 0.0.0 → 0.1.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.
Files changed (159) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/app.plugin.js +1 -0
  4. package/lib/commonjs/cli/build.d.ts +28 -0
  5. package/lib/commonjs/cli/build.js +83 -0
  6. package/lib/commonjs/cli/config.d.ts +9 -0
  7. package/lib/commonjs/cli/config.js +25 -0
  8. package/lib/commonjs/cli/index.d.ts +4 -0
  9. package/lib/commonjs/cli/index.js +13 -0
  10. package/lib/commonjs/cli/link.d.ts +11 -0
  11. package/lib/commonjs/cli/link.js +135 -0
  12. package/lib/commonjs/cli/logger.d.ts +27 -0
  13. package/lib/commonjs/cli/logger.js +83 -0
  14. package/lib/commonjs/index.node.js +8 -0
  15. package/lib/commonjs/plugin/src/buildFonts.d.ts +7 -0
  16. package/lib/commonjs/plugin/src/buildFonts.js +23 -0
  17. package/lib/commonjs/plugin/src/index.d.ts +5 -0
  18. package/lib/commonjs/plugin/src/index.js +43 -0
  19. package/lib/commonjs/plugin/src/types.d.ts +31 -0
  20. package/lib/commonjs/plugin/src/types.js +2 -0
  21. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.d.ts +14 -0
  22. package/lib/commonjs/plugin/src/withNanoIconsFontLinking.js +92 -0
  23. package/lib/commonjs/scripts/cli.js +28 -0
  24. package/lib/commonjs/src/const/colors.d.ts +1 -0
  25. package/lib/commonjs/src/const/colors.js +153 -0
  26. package/lib/commonjs/src/core/font/compile.d.ts +9 -0
  27. package/lib/commonjs/src/core/font/compile.js +68 -0
  28. package/lib/commonjs/src/core/font/metrics.d.ts +1 -0
  29. package/lib/commonjs/src/core/font/metrics.js +33 -0
  30. package/lib/commonjs/src/core/pipeline/config.d.ts +14 -0
  31. package/lib/commonjs/src/core/pipeline/config.js +17 -0
  32. package/lib/commonjs/src/core/pipeline/index.d.ts +3 -0
  33. package/lib/commonjs/src/core/pipeline/index.js +7 -0
  34. package/lib/commonjs/src/core/pipeline/managers.d.ts +11 -0
  35. package/lib/commonjs/src/core/pipeline/managers.js +89 -0
  36. package/lib/commonjs/src/core/pipeline/run.d.ts +14 -0
  37. package/lib/commonjs/src/core/pipeline/run.js +99 -0
  38. package/lib/commonjs/src/core/svg/layers.d.ts +24 -0
  39. package/lib/commonjs/src/core/svg/layers.js +42 -0
  40. package/lib/commonjs/src/core/svg/svg_dom.d.ts +22 -0
  41. package/lib/commonjs/src/core/svg/svg_dom.js +84 -0
  42. package/lib/commonjs/src/core/svg/svg_pathops.d.ts +21 -0
  43. package/lib/commonjs/src/core/svg/svg_pathops.js +611 -0
  44. package/lib/commonjs/src/core/types.d.ts +120 -0
  45. package/lib/commonjs/src/core/types.js +2 -0
  46. package/lib/commonjs/src/utils/fingerPrint.d.ts +1 -0
  47. package/lib/commonjs/src/utils/fingerPrint.js +20 -0
  48. package/lib/commonjs/src/utils/parse.d.ts +2 -0
  49. package/lib/commonjs/src/utils/parse.js +64 -0
  50. package/lib/module/const/colors.js +153 -0
  51. package/lib/module/const/colors.js.map +1 -0
  52. package/lib/module/core/font/compile.js +70 -0
  53. package/lib/module/core/font/compile.js.map +1 -0
  54. package/lib/module/core/font/metrics.js +37 -0
  55. package/lib/module/core/font/metrics.js.map +1 -0
  56. package/lib/module/core/pipeline/config.js +20 -0
  57. package/lib/module/core/pipeline/config.js.map +1 -0
  58. package/lib/module/core/pipeline/index.js +5 -0
  59. package/lib/module/core/pipeline/index.js.map +1 -0
  60. package/lib/module/core/pipeline/managers.js +80 -0
  61. package/lib/module/core/pipeline/managers.js.map +1 -0
  62. package/lib/module/core/pipeline/run.js +114 -0
  63. package/lib/module/core/pipeline/run.js.map +1 -0
  64. package/lib/module/core/shims/pathops.py +181 -0
  65. package/lib/module/core/svg/layers.js +51 -0
  66. package/lib/module/core/svg/layers.js.map +1 -0
  67. package/lib/module/core/svg/svg_dom.js +83 -0
  68. package/lib/module/core/svg/svg_dom.js.map +1 -0
  69. package/lib/module/core/svg/svg_pathops.js +566 -0
  70. package/lib/module/core/svg/svg_pathops.js.map +1 -0
  71. package/lib/module/core/tsconfig.json +32 -0
  72. package/lib/module/core/types.js +2 -0
  73. package/lib/module/core/types.js.map +1 -0
  74. package/lib/module/createNanoIconsSet.js +84 -0
  75. package/lib/module/createNanoIconsSet.js.map +1 -0
  76. package/lib/module/index.js +5 -0
  77. package/lib/module/index.js.map +1 -0
  78. package/lib/module/index.node.js +13 -0
  79. package/lib/module/index.node.js.map +1 -0
  80. package/lib/module/package.json +1 -0
  81. package/lib/module/utils/fingerPrint.js +17 -0
  82. package/lib/module/utils/fingerPrint.js.map +1 -0
  83. package/lib/module/utils/parse.js +53 -0
  84. package/lib/module/utils/parse.js.map +1 -0
  85. package/lib/typescript/__tests__/build.unit.test.d.ts +3 -0
  86. package/lib/typescript/__tests__/build.unit.test.d.ts.map +1 -0
  87. package/lib/typescript/__tests__/clippath.e2e.test.d.ts +3 -0
  88. package/lib/typescript/__tests__/clippath.e2e.test.d.ts.map +1 -0
  89. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts +3 -0
  90. package/lib/typescript/__tests__/fingerprint.unit.test.d.ts.map +1 -0
  91. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts +3 -0
  92. package/lib/typescript/__tests__/pipeline.e2e.test.d.ts.map +1 -0
  93. package/lib/typescript/__tests__/placement.unit.test.d.ts +3 -0
  94. package/lib/typescript/__tests__/placement.unit.test.d.ts.map +1 -0
  95. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts +3 -0
  96. package/lib/typescript/__tests__/svg_dom.unit.test.d.ts.map +1 -0
  97. package/lib/typescript/cli/build.d.ts +29 -0
  98. package/lib/typescript/cli/build.d.ts.map +1 -0
  99. package/lib/typescript/cli/logger.d.ts +28 -0
  100. package/lib/typescript/cli/logger.d.ts.map +1 -0
  101. package/lib/typescript/package.json +1 -0
  102. package/lib/typescript/src/const/colors.d.ts +2 -0
  103. package/lib/typescript/src/const/colors.d.ts.map +1 -0
  104. package/lib/typescript/src/core/font/compile.d.ts +10 -0
  105. package/lib/typescript/src/core/font/compile.d.ts.map +1 -0
  106. package/lib/typescript/src/core/font/metrics.d.ts +2 -0
  107. package/lib/typescript/src/core/font/metrics.d.ts.map +1 -0
  108. package/lib/typescript/src/core/pipeline/config.d.ts +15 -0
  109. package/lib/typescript/src/core/pipeline/config.d.ts.map +1 -0
  110. package/lib/typescript/src/core/pipeline/index.d.ts +4 -0
  111. package/lib/typescript/src/core/pipeline/index.d.ts.map +1 -0
  112. package/lib/typescript/src/core/pipeline/managers.d.ts +12 -0
  113. package/lib/typescript/src/core/pipeline/managers.d.ts.map +1 -0
  114. package/lib/typescript/src/core/pipeline/run.d.ts +15 -0
  115. package/lib/typescript/src/core/pipeline/run.d.ts.map +1 -0
  116. package/lib/typescript/src/core/svg/layers.d.ts +25 -0
  117. package/lib/typescript/src/core/svg/layers.d.ts.map +1 -0
  118. package/lib/typescript/src/core/svg/svg_dom.d.ts +23 -0
  119. package/lib/typescript/src/core/svg/svg_dom.d.ts.map +1 -0
  120. package/lib/typescript/src/core/svg/svg_pathops.d.ts +22 -0
  121. package/lib/typescript/src/core/svg/svg_pathops.d.ts.map +1 -0
  122. package/lib/typescript/src/core/types.d.ts +121 -0
  123. package/lib/typescript/src/core/types.d.ts.map +1 -0
  124. package/lib/typescript/src/createNanoIconsSet.d.ts +19 -0
  125. package/lib/typescript/src/createNanoIconsSet.d.ts.map +1 -0
  126. package/lib/typescript/src/index.d.ts +3 -0
  127. package/lib/typescript/src/index.d.ts.map +1 -0
  128. package/lib/typescript/src/index.node.d.ts +2 -0
  129. package/lib/typescript/src/index.node.d.ts.map +1 -0
  130. package/lib/typescript/src/utils/fingerPrint.d.ts +2 -0
  131. package/lib/typescript/src/utils/fingerPrint.d.ts.map +1 -0
  132. package/lib/typescript/src/utils/parse.d.ts +3 -0
  133. package/lib/typescript/src/utils/parse.d.ts.map +1 -0
  134. package/package.json +160 -1
  135. package/plugin/src/buildFonts.ts +29 -0
  136. package/plugin/src/index.ts +68 -0
  137. package/plugin/src/types.ts +33 -0
  138. package/plugin/src/withNanoIconsFontLinking.ts +119 -0
  139. package/plugin/tsconfig.json +9 -0
  140. package/scripts/cli.ts +34 -0
  141. package/scripts/tsconfig.json +25 -0
  142. package/src/const/colors.ts +150 -0
  143. package/src/core/font/compile.ts +96 -0
  144. package/src/core/font/metrics.ts +44 -0
  145. package/src/core/pipeline/config.ts +24 -0
  146. package/src/core/pipeline/index.ts +3 -0
  147. package/src/core/pipeline/managers.ts +114 -0
  148. package/src/core/pipeline/run.ts +151 -0
  149. package/src/core/shims/pathops.py +181 -0
  150. package/src/core/svg/layers.ts +66 -0
  151. package/src/core/svg/svg_dom.ts +99 -0
  152. package/src/core/svg/svg_pathops.ts +796 -0
  153. package/src/core/tsconfig.json +32 -0
  154. package/src/core/types.ts +138 -0
  155. package/src/createNanoIconsSet.tsx +131 -0
  156. package/src/index.node.ts +14 -0
  157. package/src/index.ts +7 -0
  158. package/src/utils/fingerPrint.ts +20 -0
  159. package/src/utils/parse.ts +67 -0
@@ -0,0 +1,611 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildPathopsBackend = buildPathopsBackend;
4
+ //** */
5
+ // ----- numeric helpers -----
6
+ const EPS = 1e-2;
7
+ // ✅ round-half-to-even (banker's rounding) at ndigits
8
+ function roundN(x, ndigits = 3) {
9
+ const m = 10 ** ndigits;
10
+ const s = x * m;
11
+ // Handle very large values safely
12
+ if (!Number.isFinite(s))
13
+ return x;
14
+ const floor = Math.floor(s);
15
+ const frac = s - floor;
16
+ // floating tolerance for "exactly .5"
17
+ const TIE_EPS = 1e-12;
18
+ let roundedInt;
19
+ if (Math.abs(frac - 0.5) < TIE_EPS) {
20
+ // tie -> choose even
21
+ roundedInt = floor % 2 === 0 ? floor : floor + 1;
22
+ }
23
+ else if (Math.abs(frac + 0.5) < TIE_EPS) {
24
+ // negative tie case (rare due to floor behavior, but keep for completeness)
25
+ const ceil = Math.ceil(s);
26
+ roundedInt = ceil % 2 === 0 ? ceil : ceil - 1;
27
+ }
28
+ else {
29
+ roundedInt = Math.round(s);
30
+ }
31
+ return roundedInt / m;
32
+ }
33
+ function normPt(p) {
34
+ return [Math.round(p[0] * 10000) / 10000, Math.round(p[1] * 10000) / 10000];
35
+ }
36
+ function eqPt(a, b) {
37
+ const aa = normPt(a);
38
+ const bb = normPt(b);
39
+ return Math.abs(aa[0] - bb[0]) < EPS && Math.abs(aa[1] - bb[1]) < EPS;
40
+ }
41
+ function signedAreaPolyline(points) {
42
+ if (points.length < 3)
43
+ return 0;
44
+ let a = 0;
45
+ for (let i = 0; i < points.length; i++) {
46
+ const [x1, y1] = points[i];
47
+ const [x2, y2] = points[(i + 1) % points.length];
48
+ a += x1 * y2 - x2 * y1;
49
+ }
50
+ return a / 2;
51
+ }
52
+ function approxSignedAreaFromContourCmds(contourCmds, VERB, steps = 24) {
53
+ let cx = 0, cy = 0;
54
+ let sx = 0, sy = 0;
55
+ const pts = [];
56
+ const add = (x, y) => pts.push([x, y]);
57
+ for (const cmd of contourCmds) {
58
+ const v = cmd[0];
59
+ if (v === VERB.MOVE) {
60
+ cx = cmd[1];
61
+ cy = cmd[2];
62
+ sx = cx;
63
+ sy = cy;
64
+ add(cx, cy);
65
+ }
66
+ else if (v === VERB.LINE) {
67
+ cx = cmd[1];
68
+ cy = cmd[2];
69
+ add(cx, cy);
70
+ }
71
+ else if (v === VERB.QUAD) {
72
+ const x0 = cx, y0 = cy;
73
+ const x1 = cmd[1], y1 = cmd[2];
74
+ const x2 = cmd[3], y2 = cmd[4];
75
+ for (let i = 1; i <= steps; i++) {
76
+ const t = i / steps;
77
+ const mt = 1 - t;
78
+ const x = mt * mt * x0 + 2 * mt * t * x1 + t * t * x2;
79
+ const y = mt * mt * y0 + 2 * mt * t * y1 + t * t * y2;
80
+ add(x, y);
81
+ }
82
+ cx = x2;
83
+ cy = y2;
84
+ }
85
+ else if (v === VERB.CUBIC) {
86
+ const x0 = cx, y0 = cy;
87
+ const x1 = cmd[1], y1 = cmd[2];
88
+ const x2 = cmd[3], y2 = cmd[4];
89
+ const x3 = cmd[5], y3 = cmd[6];
90
+ for (let i = 1; i <= steps; i++) {
91
+ const t = i / steps;
92
+ const mt = 1 - t;
93
+ const x = mt * mt * mt * x0 +
94
+ 3 * mt * mt * t * x1 +
95
+ 3 * mt * t * t * x2 +
96
+ t * t * t * x3;
97
+ const y = mt * mt * mt * y0 +
98
+ 3 * mt * mt * t * y1 +
99
+ 3 * mt * t * t * y2 +
100
+ t * t * t * y3;
101
+ add(x, y);
102
+ }
103
+ cx = x3;
104
+ cy = y3;
105
+ }
106
+ else if (v === VERB.CLOSE) {
107
+ add(sx, sy);
108
+ }
109
+ }
110
+ if (pts.length &&
111
+ (pts[0][0] !== pts[pts.length - 1][0] ||
112
+ pts[0][1] !== pts[pts.length - 1][1])) {
113
+ pts.push([pts[0][0], pts[0][1]]);
114
+ }
115
+ return signedAreaPolyline(pts);
116
+ }
117
+ function splitContours(cmds, VERB) {
118
+ const contours = [];
119
+ let cur = null;
120
+ for (const cmd of cmds) {
121
+ const v = cmd[0];
122
+ if (v === VERB.MOVE) {
123
+ if (cur && cur.length)
124
+ contours.push(cur);
125
+ cur = [cmd];
126
+ }
127
+ else if (cur) {
128
+ cur.push(cmd);
129
+ }
130
+ }
131
+ if (cur && cur.length)
132
+ contours.push(cur);
133
+ return contours;
134
+ }
135
+ function ensureClosed(contourCmds, VERB) {
136
+ return contourCmds.some((c) => c[0] === VERB.CLOSE)
137
+ ? [...contourCmds]
138
+ : [...contourCmds, [VERB.CLOSE]];
139
+ }
140
+ function explicitCloseWantedFromCmds(contourCmds, VERB) {
141
+ if (!contourCmds?.length)
142
+ return false;
143
+ const m = contourCmds[0];
144
+ if (m[0] !== VERB.MOVE)
145
+ return false;
146
+ const start = [m[1], m[2]];
147
+ for (let i = contourCmds.length - 1; i >= 1; i--) {
148
+ const cmd = contourCmds[i];
149
+ const v = cmd[0];
150
+ if (v === VERB.CLOSE)
151
+ continue;
152
+ let end = null;
153
+ if (v === VERB.LINE)
154
+ end = [cmd[1], cmd[2]];
155
+ else if (v === VERB.QUAD)
156
+ end = [cmd[3], cmd[4]];
157
+ else if (v === VERB.CUBIC)
158
+ end = [cmd[5], cmd[6]];
159
+ else
160
+ continue;
161
+ return eqPt(end, start);
162
+ }
163
+ return false;
164
+ }
165
+ function contourToSegments(contourCmds, VERB) {
166
+ const c = ensureClosed(contourCmds, VERB);
167
+ const move = c[0];
168
+ const sx = move[1], sy = move[2];
169
+ let last = [sx, sy];
170
+ const segs = [];
171
+ for (let i = 1; i < c.length; i++) {
172
+ const cmd = c[i];
173
+ const v = cmd[0];
174
+ if (v === VERB.LINE) {
175
+ const end = [cmd[1], cmd[2]];
176
+ segs.push({ type: 'L', start: last, end, synthetic: false });
177
+ last = end;
178
+ }
179
+ else if (v === VERB.QUAD) {
180
+ const ctrl = [cmd[1], cmd[2]];
181
+ const end = [cmd[3], cmd[4]];
182
+ segs.push({ type: 'Q', start: last, ctrl, end, synthetic: false });
183
+ last = end;
184
+ }
185
+ else if (v === VERB.CUBIC) {
186
+ const c1 = [cmd[1], cmd[2]];
187
+ const c2 = [cmd[3], cmd[4]];
188
+ const end = [cmd[5], cmd[6]];
189
+ segs.push({ type: 'C', start: last, c1, c2, end, synthetic: false });
190
+ last = end;
191
+ }
192
+ else if (v === VERB.CLOSE) {
193
+ const end = [sx, sy];
194
+ if (!eqPt(last, end)) {
195
+ segs.push({ type: 'L', start: last, end, synthetic: true });
196
+ }
197
+ last = end;
198
+ }
199
+ }
200
+ return { start: [sx, sy], segs };
201
+ }
202
+ function applyClosePolicy(segs, startPt, explicitCloseWanted) {
203
+ if (!segs.length)
204
+ return segs;
205
+ for (const s of segs)
206
+ s.synthetic = false;
207
+ const last = segs[segs.length - 1];
208
+ const lastEnd = last.end;
209
+ if (!eqPt(lastEnd, startPt)) {
210
+ segs.push({ type: 'L', start: lastEnd, end: startPt, synthetic: false });
211
+ }
212
+ if (!explicitCloseWanted) {
213
+ segs[segs.length - 1].synthetic = true;
214
+ }
215
+ return segs;
216
+ }
217
+ function segmentsToContourCmds(startPt, segs, VERB) {
218
+ const out = [[VERB.MOVE, roundN(startPt[0]), roundN(startPt[1])]];
219
+ for (const s of segs) {
220
+ if (s.synthetic)
221
+ continue;
222
+ if (s.type === 'L') {
223
+ out.push([VERB.LINE, roundN(s.end[0]), roundN(s.end[1])]);
224
+ }
225
+ else if (s.type === 'Q') {
226
+ out.push([
227
+ VERB.QUAD,
228
+ roundN(s.ctrl[0]),
229
+ roundN(s.ctrl[1]),
230
+ roundN(s.end[0]),
231
+ roundN(s.end[1]),
232
+ ]);
233
+ }
234
+ else {
235
+ out.push([
236
+ VERB.CUBIC,
237
+ roundN(s.c1[0]),
238
+ roundN(s.c1[1]),
239
+ roundN(s.c2[0]),
240
+ roundN(s.c2[1]),
241
+ roundN(s.end[0]),
242
+ roundN(s.end[1]),
243
+ ]);
244
+ }
245
+ }
246
+ out.push([VERB.CLOSE]);
247
+ return out;
248
+ }
249
+ function reverseClosedContourKeepStart(contourCmds, explicitCloseWanted, VERB) {
250
+ const { start, segs } = contourToSegments(contourCmds, VERB);
251
+ const reversed = segs
252
+ .slice()
253
+ .reverse()
254
+ .map((s) => {
255
+ if (s.type === 'L') {
256
+ return { type: 'L', start: s.end, end: s.start, synthetic: false };
257
+ }
258
+ if (s.type === 'Q') {
259
+ return {
260
+ type: 'Q',
261
+ start: s.end,
262
+ ctrl: s.ctrl,
263
+ end: s.start,
264
+ synthetic: false,
265
+ };
266
+ }
267
+ return {
268
+ type: 'C',
269
+ start: s.end,
270
+ c1: s.c2,
271
+ c2: s.c1,
272
+ end: s.start,
273
+ synthetic: false,
274
+ };
275
+ });
276
+ applyClosePolicy(reversed, start, explicitCloseWanted);
277
+ return segmentsToContourCmds(start, reversed, VERB);
278
+ }
279
+ function rotateClosedContourToStart(contourCmds, desiredStart, explicitCloseWanted, VERB) {
280
+ const { segs } = contourToSegments(contourCmds, VERB);
281
+ let idx = -1;
282
+ for (let i = 0; i < segs.length; i++) {
283
+ if (eqPt(segs[i].start, desiredStart)) {
284
+ idx = i;
285
+ break;
286
+ }
287
+ }
288
+ if (idx === -1) {
289
+ for (let i = 0; i < segs.length; i++) {
290
+ if (eqPt(segs[i].end, desiredStart)) {
291
+ idx = (i + 1) % segs.length;
292
+ break;
293
+ }
294
+ }
295
+ }
296
+ if (idx === -1)
297
+ return [...contourCmds];
298
+ const rotated = segs.slice(idx).concat(segs.slice(0, idx));
299
+ applyClosePolicy(rotated, desiredStart, explicitCloseWanted);
300
+ return segmentsToContourCmds(desiredStart, rotated, VERB);
301
+ }
302
+ function cmdsToVerbPoints(cmds, VERB) {
303
+ const out = [];
304
+ for (const cmd of cmds) {
305
+ const v = cmd[0];
306
+ if (v === VERB.MOVE)
307
+ out.push([0, [[cmd[1], cmd[2]]]]);
308
+ else if (v === VERB.LINE)
309
+ out.push([1, [[cmd[1], cmd[2]]]]);
310
+ else if (v === VERB.QUAD)
311
+ out.push([
312
+ 2,
313
+ [
314
+ [cmd[1], cmd[2]],
315
+ [cmd[3], cmd[4]],
316
+ ],
317
+ ]);
318
+ else if (v === VERB.CUBIC)
319
+ out.push([
320
+ 3,
321
+ [
322
+ [cmd[1], cmd[2]],
323
+ [cmd[3], cmd[4]],
324
+ [cmd[5], cmd[6]],
325
+ ],
326
+ ]);
327
+ else if (v === VERB.CLOSE)
328
+ out.push([4, []]);
329
+ else
330
+ throw new Error(`Unexpected verb in cmds: ${v}`);
331
+ }
332
+ return out;
333
+ }
334
+ function wrapPath(pathkitPath) {
335
+ return { p: pathkitPath, meta: { moves: [] } };
336
+ }
337
+ function cloneWrap(h, PathKit) {
338
+ const p2 = PathKit.NewPath(h.p);
339
+ const out = wrapPath(p2);
340
+ out.meta.moves = h.meta?.moves ? h.meta.moves.map((m) => [m[0], m[1]]) : [];
341
+ return out;
342
+ }
343
+ function mergeMoves(aMoves, bMoves) {
344
+ const out = [];
345
+ const pushUnique = (pt) => {
346
+ for (const existing of out) {
347
+ if (eqPt(existing, pt))
348
+ return;
349
+ }
350
+ out.push(pt);
351
+ };
352
+ for (const m of aMoves || [])
353
+ pushUnique(normPt(m));
354
+ for (const m of bMoves || [])
355
+ pushUnique(normPt(m));
356
+ return out;
357
+ }
358
+ function bestStartMinYMinX(contourCmds, VERB) {
359
+ let best = null;
360
+ for (const cmd of contourCmds) {
361
+ const v = cmd[0];
362
+ const add = (x, y) => {
363
+ const p = [x, y];
364
+ if (!best) {
365
+ best = p;
366
+ return;
367
+ }
368
+ if (p[1] < best[1] - 1e-9)
369
+ best = p;
370
+ else if (Math.abs(p[1] - best[1]) < 1e-9 && p[0] < best[0] - 1e-9)
371
+ best = p;
372
+ };
373
+ if (v === VERB.MOVE)
374
+ add(cmd[1], cmd[2]);
375
+ else if (v === VERB.LINE)
376
+ add(cmd[1], cmd[2]);
377
+ else if (v === VERB.QUAD)
378
+ add(cmd[3], cmd[4]);
379
+ else if (v === VERB.CUBIC)
380
+ add(cmd[5], cmd[6]);
381
+ }
382
+ return best;
383
+ }
384
+ // proxy to for picosvg to interatc with pathkit
385
+ function buildPathopsBackend(PathKit) {
386
+ const VERB = {
387
+ MOVE: PathKit.MOVE_VERB ?? 0,
388
+ LINE: PathKit.LINE_VERB ?? 1,
389
+ QUAD: PathKit.QUAD_VERB ?? 2,
390
+ CONIC: PathKit.CONIC_VERB ?? 3,
391
+ CUBIC: PathKit.CUBIC_VERB ?? 4,
392
+ CLOSE: PathKit.CLOSE_VERB ?? 5,
393
+ };
394
+ const FILL_EVENODD = PathKit?.FillType?.EVENODD ?? PathKit?.FillType?.EVEN_ODD ?? 1;
395
+ const FILL_WINDING = PathKit?.FillType?.WINDING ?? PathKit?.FillType?.NONZERO ?? 0;
396
+ function cmdsViaSvgRoundtrip(h) {
397
+ const svg = h.p.toSVGString();
398
+ const p2 = PathKit.FromSVGString(svg);
399
+ const cmds = p2 ? p2.toCmds() : [];
400
+ if (p2)
401
+ p2.delete?.();
402
+ return cmds;
403
+ }
404
+ function normalizeSortRotateContours(cmds, h, preferStrokeCanonical = false) {
405
+ const moves = (h.meta?.moves || []).map((m) => normPt([m[0], m[1]]));
406
+ const used = new Array(moves.length).fill(false);
407
+ // Build contour objects, but DO NOT normalize orientation yet.
408
+ const contourObjs = splitContours(cmds, VERB).map((c) => {
409
+ const explicitCloseWanted = explicitCloseWantedFromCmds(c, VERB);
410
+ const cc = ensureClosed(c, VERB);
411
+ const a = approxSignedAreaFromContourCmds(cc, VERB); // signed
412
+ return { cmds: cc, absA: Math.abs(a), explicitCloseWanted };
413
+ });
414
+ // Big-to-small ordering gives deterministic outer→inner ordering.
415
+ contourObjs.sort((x, y) => y.absA - x.absA);
416
+ // Enforce winding convention:
417
+ // - largest contour CCW (positive area)
418
+ // - all subsequent contours CW (negative area)
419
+ const ensureOrient = (obj, wantCCW) => {
420
+ const a = approxSignedAreaFromContourCmds(obj.cmds, VERB);
421
+ const isCCW = a > 0;
422
+ if (wantCCW !== isCCW) {
423
+ obj.cmds = reverseClosedContourKeepStart(obj.cmds, obj.explicitCloseWanted, VERB);
424
+ }
425
+ };
426
+ if (contourObjs.length) {
427
+ ensureOrient(contourObjs[0], true);
428
+ for (let i = 1; i < contourObjs.length; i++) {
429
+ ensureOrient(contourObjs[i], false);
430
+ }
431
+ }
432
+ // Rotate starts deterministically:
433
+ // 1) If we have recorded MOVE points, try to rotate contour to a move point that lies on it.
434
+ // 2) Otherwise, rotate to minY/minX point (helps stroke-ish paths)
435
+ for (const obj of contourObjs) {
436
+ let cc = obj.cmds;
437
+ for (let i = 0; i < moves.length; i++) {
438
+ if (used[i])
439
+ continue;
440
+ const target = moves[i];
441
+ const { segs } = contourToSegments(cc, VERB);
442
+ const found = segs.some((s) => eqPt(s.start, target) || eqPt(s.end, target));
443
+ if (found) {
444
+ used[i] = true;
445
+ cc = rotateClosedContourToStart(cc, target, obj.explicitCloseWanted, VERB);
446
+ break;
447
+ }
448
+ }
449
+ // If no move point was used, apply the deterministic start heuristic
450
+ if (preferStrokeCanonical) {
451
+ const best = bestStartMinYMinX(cc, VERB);
452
+ if (best) {
453
+ cc = rotateClosedContourToStart(cc, best, obj.explicitCloseWanted, VERB);
454
+ }
455
+ }
456
+ obj.cmds = cc;
457
+ }
458
+ return contourObjs.flatMap((x) => x.cmds);
459
+ }
460
+ return {
461
+ create_path(fillTypeInt) {
462
+ const p = PathKit.NewPath();
463
+ p.setFillType(fillTypeInt === 1 ? FILL_EVENODD : FILL_WINDING);
464
+ return wrapPath(p);
465
+ },
466
+ clone_path(h) {
467
+ return cloneWrap(h, PathKit);
468
+ },
469
+ delete_path(h) {
470
+ h?.p?.delete?.();
471
+ },
472
+ get_fill_type(h) {
473
+ const ft = h.p.getFillType?.();
474
+ return ft === FILL_EVENODD ? 1 : 0;
475
+ },
476
+ set_fill_type(h, fillTypeInt) {
477
+ h.p.setFillType(fillTypeInt === 1 ? FILL_EVENODD : FILL_WINDING);
478
+ return true;
479
+ },
480
+ move_to(h, x, y) {
481
+ h.p.moveTo(x, y);
482
+ h.meta.moves.push(normPt([x, y]));
483
+ },
484
+ line_to(h, x, y) {
485
+ h.p.lineTo(x, y);
486
+ },
487
+ quad_to(h, x1, y1, x2, y2) {
488
+ h.p.quadTo(x1, y1, x2, y2);
489
+ },
490
+ cubic_to(h, x1, y1, x2, y2, x3, y3) {
491
+ h.p.cubicTo(x1, y1, x2, y2, x3, y3);
492
+ },
493
+ close(h) {
494
+ h.p.close();
495
+ },
496
+ simplify(h, fixWinding = false) {
497
+ try {
498
+ // 1) simplify in place
499
+ h.p.simplify();
500
+ // 2) normalize representation via SVG roundtrip
501
+ const svg = h.p.toSVGString();
502
+ const p2 = PathKit.FromSVGString(svg);
503
+ if (p2) {
504
+ // replace underlying path handle
505
+ h.p.delete?.();
506
+ h.p = p2;
507
+ }
508
+ if (fixWinding)
509
+ h.p.setFillType(FILL_WINDING);
510
+ return true;
511
+ }
512
+ catch {
513
+ return false;
514
+ }
515
+ },
516
+ stroke(h, width, capInt, joinInt, miterLimit, dashArray, dashOffset) {
517
+ const Caps = PathKit.StrokeCap ?? {};
518
+ const Joins = PathKit.StrokeJoin ?? {};
519
+ const cap = capInt === 1
520
+ ? Caps.ROUND ?? 1
521
+ : capInt === 2
522
+ ? Caps.SQUARE ?? 2
523
+ : Caps.BUTT ?? 0;
524
+ const join = joinInt === 1
525
+ ? Joins.ROUND ?? 1
526
+ : joinInt === 2
527
+ ? Joins.BEVEL ?? 2
528
+ : Joins.MITER ?? 0;
529
+ const work = PathKit.NewPath(h.p);
530
+ try {
531
+ if (Array.isArray(dashArray) &&
532
+ dashArray.length === 2 &&
533
+ typeof work.dash === 'function') {
534
+ work.dash(Number(dashArray[0]), Number(dashArray[1]), dashOffset || 0);
535
+ }
536
+ let stroked = work.stroke({
537
+ width,
538
+ cap,
539
+ join,
540
+ miter_limit: miterLimit,
541
+ });
542
+ if (!stroked || typeof stroked.toCmds !== 'function')
543
+ stroked = work;
544
+ if (stroked !== work)
545
+ work.delete?.();
546
+ const wrapped = wrapPath(stroked);
547
+ wrapped.meta.moves = [];
548
+ return wrapped;
549
+ }
550
+ catch {
551
+ const wrapped = wrapPath(work);
552
+ wrapped.meta.moves = [];
553
+ return wrapped;
554
+ }
555
+ },
556
+ convert_conics_to_quads(_h, _tol) {
557
+ // intentionally no-op
558
+ },
559
+ transform(h, a, b, c, d, e, f) {
560
+ const p = PathKit.NewPath(h.p);
561
+ p.transform(a, c, e, b, d, f, 0, 0, 1);
562
+ const wrapped = wrapPath(p);
563
+ wrapped.meta.moves = (h.meta?.moves || []).map(([x, y]) => normPt([a * x + c * y + e, b * x + d * y + f]));
564
+ return wrapped;
565
+ },
566
+ op(aHandle, bHandle, opInt) {
567
+ try {
568
+ const Ops = PathKit.PathOp ?? {};
569
+ const op = opInt === 1
570
+ ? Ops.INTERSECT ?? 1
571
+ : opInt === 2
572
+ ? Ops.DIFFERENCE ?? 2
573
+ : Ops.UNION ?? 0;
574
+ const out = PathKit.MakeFromOp(aHandle.p, bHandle.p, op);
575
+ if (!out)
576
+ return null;
577
+ const wrapped = wrapPath(out);
578
+ wrapped.meta.moves = mergeMoves(aHandle.meta?.moves, bHandle.meta?.moves);
579
+ return wrapped;
580
+ }
581
+ catch {
582
+ return null;
583
+ }
584
+ },
585
+ bounds(h) {
586
+ const r = h.p.getBounds();
587
+ return [r.fLeft, r.fTop, r.fRight, r.fBottom];
588
+ },
589
+ area(h) {
590
+ try {
591
+ const cmds = cmdsViaSvgRoundtrip(h);
592
+ const contours = splitContours(cmds, VERB);
593
+ let total = 0;
594
+ for (const c of contours) {
595
+ const cc = ensureClosed(c, VERB);
596
+ total += Math.abs(approxSignedAreaFromContourCmds(cc, VERB));
597
+ }
598
+ return total;
599
+ }
600
+ catch {
601
+ return 0.0;
602
+ }
603
+ },
604
+ iter_segments(h) {
605
+ const cmds = cmdsViaSvgRoundtrip(h);
606
+ const preferStrokeCanonical = (h.meta?.moves?.length || 0) === 0;
607
+ const normalized = normalizeSortRotateContours(cmds, h, preferStrokeCanonical);
608
+ return cmdsToVerbPoints(normalized, VERB);
609
+ },
610
+ };
611
+ }