@shopify/klint 0.2.0 → 0.3.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.
@@ -0,0 +1,2518 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/plugins/index.tsx
21
+ var plugins_exports = {};
22
+ __export(plugins_exports, {
23
+ CatmullRom: () => CatmullRom,
24
+ Delaunay: () => Delaunay,
25
+ FontParser: () => FontParser,
26
+ Sprites: () => Sprites,
27
+ Things: () => Things
28
+ });
29
+ module.exports = __toCommonJS(plugins_exports);
30
+
31
+ // src/plugins/FontParser.tsx
32
+ var BinaryReader = class {
33
+ constructor() {
34
+ const ab = new ArrayBuffer(8);
35
+ this.t = {
36
+ buff: ab,
37
+ int8: new Int8Array(ab),
38
+ uint8: new Uint8Array(ab),
39
+ int16: new Int16Array(ab),
40
+ uint16: new Uint16Array(ab),
41
+ int32: new Int32Array(ab),
42
+ uint32: new Uint32Array(ab)
43
+ };
44
+ }
45
+ readFixed(data, o) {
46
+ return (data[o] << 8 | data[o + 1]) + (data[o + 2] << 8 | data[o + 3]) / (256 * 256 + 4);
47
+ }
48
+ readF2dot14(data, o) {
49
+ const num = this.readShort(data, o);
50
+ return num / 16384;
51
+ }
52
+ readInt(buff, p) {
53
+ const a = this.t.uint8;
54
+ a[0] = buff[p + 3];
55
+ a[1] = buff[p + 2];
56
+ a[2] = buff[p + 1];
57
+ a[3] = buff[p];
58
+ return this.t.int32[0];
59
+ }
60
+ readInt8(buff, p) {
61
+ const a = this.t.uint8;
62
+ a[0] = buff[p];
63
+ return this.t.int8[0];
64
+ }
65
+ readShort(buff, p) {
66
+ const a = this.t.uint16;
67
+ a[0] = buff[p] << 8 | buff[p + 1];
68
+ return this.t.int16[0];
69
+ }
70
+ readUshort(buff, p) {
71
+ return buff[p] << 8 | buff[p + 1];
72
+ }
73
+ readUshorts(buff, p, len) {
74
+ const arr = [];
75
+ for (let i = 0; i < len; i++) {
76
+ arr.push(this.readUshort(buff, p + i * 2));
77
+ }
78
+ return arr;
79
+ }
80
+ readUint(buff, p) {
81
+ const a = this.t.uint8;
82
+ a[3] = buff[p];
83
+ a[2] = buff[p + 1];
84
+ a[1] = buff[p + 2];
85
+ a[0] = buff[p + 3];
86
+ return this.t.uint32[0];
87
+ }
88
+ readUint64(buff, p) {
89
+ return this.readUint(buff, p) * (4294967295 + 1) + this.readUint(buff, p + 4);
90
+ }
91
+ readASCII(buff, p, l) {
92
+ let s = "";
93
+ for (let i = 0; i < l; i++) s += String.fromCharCode(buff[p + i]);
94
+ return s;
95
+ }
96
+ readBytes(buff, p, l) {
97
+ const arr = [];
98
+ for (let i = 0; i < l; i++) arr.push(buff[p + i]);
99
+ return arr;
100
+ }
101
+ };
102
+ var bin = new BinaryReader();
103
+ function findTable(data, tab, foff = 0) {
104
+ const numTables = bin.readUshort(data, foff + 4);
105
+ let offset = foff + 12;
106
+ for (let i = 0; i < numTables; i++) {
107
+ const tag = bin.readASCII(data, offset, 4);
108
+ const checkSum = bin.readUint(data, offset + 4);
109
+ const toffset = bin.readUint(data, offset + 8);
110
+ const length = bin.readUint(data, offset + 12);
111
+ if (tag === tab) return [toffset, length];
112
+ offset += 16;
113
+ }
114
+ return null;
115
+ }
116
+ var Tables = {
117
+ head: {
118
+ parseTab(data, offset, length) {
119
+ const obj = {};
120
+ const tableVersion = bin.readFixed(data, offset);
121
+ offset += 4;
122
+ obj.fontRevision = bin.readFixed(data, offset);
123
+ offset += 4;
124
+ const checkSumAdjustment = bin.readUint(data, offset);
125
+ offset += 4;
126
+ const magicNumber = bin.readUint(data, offset);
127
+ offset += 4;
128
+ obj.flags = bin.readUshort(data, offset);
129
+ offset += 2;
130
+ obj.unitsPerEm = bin.readUshort(data, offset);
131
+ offset += 2;
132
+ obj.created = bin.readUint64(data, offset);
133
+ offset += 8;
134
+ obj.modified = bin.readUint64(data, offset);
135
+ offset += 8;
136
+ obj.xMin = bin.readShort(data, offset);
137
+ offset += 2;
138
+ obj.yMin = bin.readShort(data, offset);
139
+ offset += 2;
140
+ obj.xMax = bin.readShort(data, offset);
141
+ offset += 2;
142
+ obj.yMax = bin.readShort(data, offset);
143
+ offset += 2;
144
+ obj.macStyle = bin.readUshort(data, offset);
145
+ offset += 2;
146
+ obj.lowestRecPPEM = bin.readUshort(data, offset);
147
+ offset += 2;
148
+ obj.fontDirectionHint = bin.readShort(data, offset);
149
+ offset += 2;
150
+ obj.indexToLocFormat = bin.readShort(data, offset);
151
+ offset += 2;
152
+ obj.glyphDataFormat = bin.readShort(data, offset);
153
+ offset += 2;
154
+ return obj;
155
+ }
156
+ },
157
+ maxp: {
158
+ parseTab(data, offset, length) {
159
+ const obj = {};
160
+ const ver = bin.readUint(data, offset);
161
+ offset += 4;
162
+ obj.numGlyphs = bin.readUshort(data, offset);
163
+ offset += 2;
164
+ return obj;
165
+ }
166
+ },
167
+ hhea: {
168
+ parseTab(data, offset, length) {
169
+ const obj = {};
170
+ const tableVersion = bin.readFixed(data, offset);
171
+ offset += 4;
172
+ const keys = [
173
+ "ascender",
174
+ "descender",
175
+ "lineGap",
176
+ "advanceWidthMax",
177
+ "minLeftSideBearing",
178
+ "minRightSideBearing",
179
+ "xMaxExtent",
180
+ "caretSlopeRise",
181
+ "caretSlopeRun",
182
+ "caretOffset",
183
+ "res0",
184
+ "res1",
185
+ "res2",
186
+ "res3",
187
+ "metricDataFormat",
188
+ "numberOfHMetrics"
189
+ ];
190
+ for (let i = 0; i < keys.length; i++) {
191
+ const key = keys[i];
192
+ const func = key === "advanceWidthMax" || key === "numberOfHMetrics" ? bin.readUshort : bin.readShort;
193
+ obj[key] = func.call(bin, data, offset + i * 2);
194
+ }
195
+ return obj;
196
+ }
197
+ },
198
+ hmtx: {
199
+ parseTab(data, offset, length, font) {
200
+ const aWidth = [];
201
+ const lsBearing = [];
202
+ const nG = font.maxp.numGlyphs;
203
+ const nH = font.hhea.numberOfHMetrics;
204
+ let aw = 0, lsb = 0, i = 0;
205
+ while (i < nH) {
206
+ aw = bin.readUshort(data, offset + (i << 2));
207
+ lsb = bin.readShort(data, offset + (i << 2) + 2);
208
+ aWidth.push(aw);
209
+ lsBearing.push(lsb);
210
+ i++;
211
+ }
212
+ while (i < nG) {
213
+ aWidth.push(aw);
214
+ lsBearing.push(lsb);
215
+ i++;
216
+ }
217
+ return { aWidth, lsBearing };
218
+ }
219
+ },
220
+ loca: {
221
+ parseTab(data, offset, length, font) {
222
+ const obj = [];
223
+ const ver = font.head.indexToLocFormat;
224
+ const len = font.maxp.numGlyphs + 1;
225
+ if (ver === 0) {
226
+ for (let i = 0; i < len; i++) {
227
+ obj.push(bin.readUshort(data, offset + (i << 1)) << 1);
228
+ }
229
+ }
230
+ if (ver === 1) {
231
+ for (let i = 0; i < len; i++) {
232
+ obj.push(bin.readUint(data, offset + (i << 2)));
233
+ }
234
+ }
235
+ return obj;
236
+ }
237
+ },
238
+ kern: {
239
+ parseTab(data, offset, length, font) {
240
+ const version = bin.readUshort(data, offset);
241
+ if (version === 1) return this.parseV1(data, offset, length, font);
242
+ const nTables = bin.readUshort(data, offset + 2);
243
+ offset += 4;
244
+ const map = { glyph1: [], rval: [] };
245
+ for (let i = 0; i < nTables; i++) {
246
+ offset += 2;
247
+ const length2 = bin.readUshort(data, offset);
248
+ offset += 2;
249
+ const coverage = bin.readUshort(data, offset);
250
+ offset += 2;
251
+ let format = coverage >>> 8;
252
+ format &= 15;
253
+ if (format === 0) offset = this.readFormat0(data, offset, map);
254
+ }
255
+ return map;
256
+ },
257
+ parseV1(data, offset, length, font) {
258
+ const version = bin.readFixed(data, offset);
259
+ const nTables = bin.readUint(data, offset + 4);
260
+ offset += 8;
261
+ const map = { glyph1: [], rval: [] };
262
+ for (let i = 0; i < nTables; i++) {
263
+ const length2 = bin.readUint(data, offset);
264
+ offset += 4;
265
+ const coverage = bin.readUshort(data, offset);
266
+ offset += 2;
267
+ const tupleIndex = bin.readUshort(data, offset);
268
+ offset += 2;
269
+ const format = coverage & 255;
270
+ if (format === 0) offset = this.readFormat0(data, offset, map);
271
+ }
272
+ return map;
273
+ },
274
+ readFormat0(data, offset, map) {
275
+ let pleft = -1;
276
+ const nPairs = bin.readUshort(data, offset);
277
+ const searchRange = bin.readUshort(data, offset + 2);
278
+ const entrySelector = bin.readUshort(data, offset + 4);
279
+ const rangeShift = bin.readUshort(data, offset + 6);
280
+ offset += 8;
281
+ for (let j = 0; j < nPairs; j++) {
282
+ const left = bin.readUshort(data, offset);
283
+ offset += 2;
284
+ const right = bin.readUshort(data, offset);
285
+ offset += 2;
286
+ const value = bin.readShort(data, offset);
287
+ offset += 2;
288
+ if (left !== pleft) {
289
+ map.glyph1.push(left);
290
+ map.rval.push({ glyph2: [], vals: [] });
291
+ }
292
+ const rval = map.rval[map.rval.length - 1];
293
+ rval.glyph2.push(right);
294
+ rval.vals.push(value);
295
+ pleft = left;
296
+ }
297
+ return offset;
298
+ }
299
+ }
300
+ };
301
+ var cmap = {
302
+ parseTab(data, offset, length) {
303
+ const obj = { tables: [], ids: {}, off: offset };
304
+ data = new Uint8Array(data.buffer, offset, length);
305
+ offset = 0;
306
+ const version = bin.readUshort(data, offset);
307
+ offset += 2;
308
+ const numTables = bin.readUshort(data, offset);
309
+ offset += 2;
310
+ const offs = [];
311
+ for (let i = 0; i < numTables; i++) {
312
+ const platformID = bin.readUshort(data, offset);
313
+ offset += 2;
314
+ const encodingID = bin.readUshort(data, offset);
315
+ offset += 2;
316
+ const noffset = bin.readUint(data, offset);
317
+ offset += 4;
318
+ const id = "p" + platformID + "e" + encodingID;
319
+ let tind = offs.indexOf(noffset);
320
+ if (tind === -1) {
321
+ tind = obj.tables.length;
322
+ let subt = {};
323
+ offs.push(noffset);
324
+ const format = subt.format = bin.readUshort(data, noffset);
325
+ if (format === 0) subt = this.parse0(data, noffset, subt);
326
+ else if (format === 4) subt = this.parse4(data, noffset, subt);
327
+ else if (format === 6) subt = this.parse6(data, noffset, subt);
328
+ else if (format === 12) subt = this.parse12(data, noffset, subt);
329
+ obj.tables.push(subt);
330
+ }
331
+ obj.ids[id] = tind;
332
+ }
333
+ return obj;
334
+ },
335
+ parse0(data, offset, obj) {
336
+ const startOffset = offset;
337
+ const format = bin.readUshort(data, offset);
338
+ offset += 2;
339
+ const length = bin.readUshort(data, offset);
340
+ offset += 2;
341
+ const language = bin.readUshort(data, offset);
342
+ offset += 2;
343
+ obj.map = [];
344
+ for (let i = 0; i < 256; i++) obj.map.push(data[offset + i]);
345
+ return obj;
346
+ },
347
+ parse4(data, offset, obj) {
348
+ const startOffset = offset;
349
+ const format = bin.readUshort(data, offset);
350
+ offset += 2;
351
+ const length = bin.readUshort(data, offset);
352
+ offset += 2;
353
+ const language = bin.readUshort(data, offset);
354
+ offset += 2;
355
+ const segCountX2 = bin.readUshort(data, offset);
356
+ offset += 2;
357
+ const segCount = segCountX2 >>> 1;
358
+ obj.searchRange = bin.readUshort(data, offset);
359
+ offset += 2;
360
+ obj.entrySelector = bin.readUshort(data, offset);
361
+ offset += 2;
362
+ obj.rangeShift = bin.readUshort(data, offset);
363
+ offset += 2;
364
+ obj.endCount = bin.readUshorts(data, offset, segCount);
365
+ offset += segCount * 2;
366
+ offset += 2;
367
+ obj.startCount = bin.readUshorts(data, offset, segCount);
368
+ offset += segCount * 2;
369
+ obj.idDelta = [];
370
+ for (let i = 0; i < segCount; i++) {
371
+ obj.idDelta.push(bin.readShort(data, offset));
372
+ offset += 2;
373
+ }
374
+ obj.idRangeOffset = bin.readUshorts(data, offset, segCount);
375
+ offset += segCount * 2;
376
+ obj.glyphIdArray = bin.readUshorts(
377
+ data,
378
+ offset,
379
+ startOffset + length - offset >> 1
380
+ );
381
+ return obj;
382
+ },
383
+ parse6(data, offset, obj) {
384
+ const startOffset = offset;
385
+ const format = bin.readUshort(data, offset);
386
+ offset += 2;
387
+ const length = bin.readUshort(data, offset);
388
+ offset += 2;
389
+ const language = bin.readUshort(data, offset);
390
+ offset += 2;
391
+ obj.firstCode = bin.readUshort(data, offset);
392
+ offset += 2;
393
+ obj.entryCount = bin.readUshort(data, offset);
394
+ offset += 2;
395
+ obj.glyphIdArray = bin.readUshorts(data, offset, obj.entryCount);
396
+ return obj;
397
+ },
398
+ parse12(data, offset, obj) {
399
+ const startOffset = offset;
400
+ offset += 4;
401
+ const length = bin.readUint(data, offset);
402
+ offset += 4;
403
+ const language = bin.readUint(data, offset);
404
+ offset += 4;
405
+ const nGroups = bin.readUint(data, offset) * 3;
406
+ offset += 4;
407
+ obj.groups = new Uint32Array(nGroups);
408
+ for (let i = 0; i < nGroups; i += 3) {
409
+ obj.groups[i] = bin.readUint(data, offset + (i << 2));
410
+ obj.groups[i + 1] = bin.readUint(data, offset + (i << 2) + 4);
411
+ obj.groups[i + 2] = bin.readUint(data, offset + (i << 2) + 8);
412
+ }
413
+ return obj;
414
+ }
415
+ };
416
+ var glyf = {
417
+ parseTab(data, offset, length, font) {
418
+ const obj = [];
419
+ const ng = font.maxp.numGlyphs;
420
+ for (let g = 0; g < ng; g++) obj.push(null);
421
+ return obj;
422
+ },
423
+ parseGlyf(font, g) {
424
+ const data = font._data;
425
+ const loca = font.loca;
426
+ if (loca[g] === loca[g + 1]) return null;
427
+ const offset = findTable(data, "glyf", font._offset)[0] + loca[g];
428
+ const gl = {};
429
+ gl.noc = bin.readShort(data, offset);
430
+ let off = offset + 2;
431
+ gl.xMin = bin.readShort(data, off);
432
+ off += 2;
433
+ gl.yMin = bin.readShort(data, off);
434
+ off += 2;
435
+ gl.xMax = bin.readShort(data, off);
436
+ off += 2;
437
+ gl.yMax = bin.readShort(data, off);
438
+ off += 2;
439
+ if (gl.xMin >= gl.xMax || gl.yMin >= gl.yMax) return null;
440
+ if (gl.noc > 0) {
441
+ gl.endPts = [];
442
+ for (let i = 0; i < gl.noc; i++) {
443
+ gl.endPts.push(bin.readUshort(data, off));
444
+ off += 2;
445
+ }
446
+ const instructionLength = bin.readUshort(data, off);
447
+ off += 2;
448
+ if (data.length - off < instructionLength) return null;
449
+ gl.instructions = bin.readBytes(data, off, instructionLength);
450
+ off += instructionLength;
451
+ const crdnum = gl.endPts[gl.noc - 1] + 1;
452
+ gl.flags = [];
453
+ for (let i = 0; i < crdnum; i++) {
454
+ const flag = data[off];
455
+ off++;
456
+ gl.flags.push(flag);
457
+ if ((flag & 8) !== 0) {
458
+ const rep = data[off];
459
+ off++;
460
+ for (let j = 0; j < rep; j++) {
461
+ gl.flags.push(flag);
462
+ i++;
463
+ }
464
+ }
465
+ }
466
+ gl.xs = [];
467
+ for (let i = 0; i < crdnum; i++) {
468
+ const i8 = (gl.flags[i] & 2) !== 0;
469
+ const same = (gl.flags[i] & 16) !== 0;
470
+ if (i8) {
471
+ gl.xs.push(same ? data[off] : -data[off]);
472
+ off++;
473
+ } else {
474
+ if (same) gl.xs.push(0);
475
+ else {
476
+ gl.xs.push(bin.readShort(data, off));
477
+ off += 2;
478
+ }
479
+ }
480
+ }
481
+ gl.ys = [];
482
+ for (let i = 0; i < crdnum; i++) {
483
+ const i8 = (gl.flags[i] & 4) !== 0;
484
+ const same = (gl.flags[i] & 32) !== 0;
485
+ if (i8) {
486
+ gl.ys.push(same ? data[off] : -data[off]);
487
+ off++;
488
+ } else {
489
+ if (same) gl.ys.push(0);
490
+ else {
491
+ gl.ys.push(bin.readShort(data, off));
492
+ off += 2;
493
+ }
494
+ }
495
+ }
496
+ let x = 0, y = 0;
497
+ for (let i = 0; i < crdnum; i++) {
498
+ x += gl.xs[i];
499
+ y += gl.ys[i];
500
+ gl.xs[i] = x;
501
+ gl.ys[i] = y;
502
+ }
503
+ } else {
504
+ const ARG_1_AND_2_ARE_WORDS = 1;
505
+ const ARGS_ARE_XY_VALUES = 2;
506
+ const WE_HAVE_A_SCALE = 8;
507
+ const MORE_COMPONENTS = 32;
508
+ const WE_HAVE_AN_X_AND_Y_SCALE = 64;
509
+ const WE_HAVE_A_TWO_BY_TWO = 128;
510
+ const WE_HAVE_INSTRUCTIONS = 256;
511
+ gl.parts = [];
512
+ let flags;
513
+ do {
514
+ flags = bin.readUshort(data, off);
515
+ off += 2;
516
+ const part = {
517
+ m: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 },
518
+ p1: -1,
519
+ p2: -1
520
+ };
521
+ gl.parts.push(part);
522
+ part.glyphIndex = bin.readUshort(data, off);
523
+ off += 2;
524
+ let arg1, arg2;
525
+ if (flags & ARG_1_AND_2_ARE_WORDS) {
526
+ arg1 = bin.readShort(data, off);
527
+ off += 2;
528
+ arg2 = bin.readShort(data, off);
529
+ off += 2;
530
+ } else {
531
+ arg1 = bin.readInt8(data, off);
532
+ off++;
533
+ arg2 = bin.readInt8(data, off);
534
+ off++;
535
+ }
536
+ if (flags & ARGS_ARE_XY_VALUES) {
537
+ part.m.tx = arg1;
538
+ part.m.ty = arg2;
539
+ } else {
540
+ part.p1 = arg1;
541
+ part.p2 = arg2;
542
+ }
543
+ if (flags & WE_HAVE_A_SCALE) {
544
+ part.m.a = part.m.d = bin.readF2dot14(data, off);
545
+ off += 2;
546
+ } else if (flags & WE_HAVE_AN_X_AND_Y_SCALE) {
547
+ part.m.a = bin.readF2dot14(data, off);
548
+ off += 2;
549
+ part.m.d = bin.readF2dot14(data, off);
550
+ off += 2;
551
+ } else if (flags & WE_HAVE_A_TWO_BY_TWO) {
552
+ part.m.a = bin.readF2dot14(data, off);
553
+ off += 2;
554
+ part.m.b = bin.readF2dot14(data, off);
555
+ off += 2;
556
+ part.m.c = bin.readF2dot14(data, off);
557
+ off += 2;
558
+ part.m.d = bin.readF2dot14(data, off);
559
+ off += 2;
560
+ }
561
+ } while (flags & MORE_COMPONENTS);
562
+ if (flags & WE_HAVE_INSTRUCTIONS) {
563
+ const numInstr = bin.readUshort(data, off);
564
+ off += 2;
565
+ gl.instr = [];
566
+ for (let i = 0; i < numInstr; i++) {
567
+ gl.instr.push(data[off]);
568
+ off++;
569
+ }
570
+ }
571
+ }
572
+ return gl;
573
+ }
574
+ };
575
+ var VariableTables = {
576
+ fvar: {
577
+ parseTab(data, offset, length, obj) {
578
+ const name = obj.name;
579
+ let off = offset;
580
+ const axes = [];
581
+ const inst = [];
582
+ off += 8;
583
+ const acnt = bin.readUshort(data, off);
584
+ off += 2;
585
+ off += 2;
586
+ const icnt = bin.readUshort(data, off);
587
+ off += 2;
588
+ const isiz = bin.readUshort(data, off);
589
+ off += 2;
590
+ for (let i = 0; i < acnt; i++) {
591
+ const tag = bin.readASCII(data, off, 4);
592
+ const min = bin.readFixed(data, off + 4);
593
+ const def = bin.readFixed(data, off + 8);
594
+ const max = bin.readFixed(data, off + 12);
595
+ const flg = bin.readUshort(data, off + 16);
596
+ const nid = bin.readUshort(data, off + 18);
597
+ axes.push([tag, min, def, max, flg, name ? name["_" + nid] : null]);
598
+ off += 20;
599
+ }
600
+ for (let i = 0; i < icnt; i++) {
601
+ const snid = bin.readUshort(data, off);
602
+ let pnid = null;
603
+ const flg = bin.readUshort(data, off + 2);
604
+ const crd = [];
605
+ for (let j = 0; j < acnt; j++) {
606
+ crd.push(bin.readFixed(data, off + 4 + j * 4));
607
+ }
608
+ off += 4 + acnt * 4;
609
+ if ((isiz & 3) === 2) {
610
+ pnid = bin.readUshort(data, off);
611
+ off += 2;
612
+ }
613
+ inst.push([name ? name["_" + snid] : null, flg, crd, pnid]);
614
+ }
615
+ return [axes, inst];
616
+ }
617
+ },
618
+ avar: {
619
+ parseTab(data, offset, length, obj) {
620
+ let off = offset;
621
+ const out = [];
622
+ off += 6;
623
+ const acnt = bin.readUshort(data, off);
624
+ off += 2;
625
+ for (let ai = 0; ai < acnt; ai++) {
626
+ const cnt = bin.readUshort(data, off);
627
+ off += 2;
628
+ const poly = [];
629
+ out.push(poly);
630
+ for (let i = 0; i < cnt; i++) {
631
+ const x = bin.readF2dot14(data, off);
632
+ const y = bin.readF2dot14(data, off + 2);
633
+ off += 4;
634
+ poly.push(x, y);
635
+ }
636
+ }
637
+ return out;
638
+ }
639
+ },
640
+ gvar: {
641
+ parseTab(data, offset, length, obj) {
642
+ const EMBEDDED_PEAK_TUPLE = 32768;
643
+ const INTERMEDIATE_REGION = 16384;
644
+ const PRIVATE_POINT_NUMBERS = 8192;
645
+ const DELTAS_ARE_ZERO = 128;
646
+ const DELTAS_ARE_WORDS = 64;
647
+ const POINTS_ARE_WORDS = 128;
648
+ const SHARED_POINT_NUMBERS = 32768;
649
+ function readTuple(data2, o, acnt2) {
650
+ const tup = [];
651
+ for (let j = 0; j < acnt2; j++)
652
+ tup.push(bin.readF2dot14(data2, o + j * 2));
653
+ return tup;
654
+ }
655
+ function readTupleVarHeader(data2, off2, vcnt, acnt2, eoff) {
656
+ const out = [];
657
+ for (let j = 0; j < vcnt; j++) {
658
+ const dsiz = bin.readUshort(data2, off2);
659
+ off2 += 2;
660
+ const tind = bin.readUshort(data2, off2);
661
+ const flag = tind & 61440;
662
+ const tupleIndex = tind & 4095;
663
+ off2 += 2;
664
+ let peak = null, start = null, end = null;
665
+ if (flag & EMBEDDED_PEAK_TUPLE) {
666
+ peak = readTuple(data2, off2, acnt2);
667
+ off2 += acnt2 * 2;
668
+ }
669
+ if (flag & INTERMEDIATE_REGION) {
670
+ start = readTuple(data2, off2, acnt2);
671
+ off2 += acnt2 * 2;
672
+ }
673
+ if (flag & INTERMEDIATE_REGION) {
674
+ end = readTuple(data2, off2, acnt2);
675
+ off2 += acnt2 * 2;
676
+ }
677
+ out.push([dsiz, tupleIndex, flag, start, peak, end]);
678
+ }
679
+ return out;
680
+ }
681
+ function readPointNumbers(data2, off2, gid) {
682
+ let cnt = data2[off2];
683
+ off2++;
684
+ if (cnt === 0) return [[], off2];
685
+ if (127 < cnt) {
686
+ cnt = (cnt & 127) << 8 | data2[off2++];
687
+ }
688
+ const pts = [];
689
+ let last = 0;
690
+ while (pts.length < cnt) {
691
+ const v = data2[off2];
692
+ off2++;
693
+ const wds = (v & POINTS_ARE_WORDS) !== 0;
694
+ const runLength = (v & 127) + 1;
695
+ for (let i = 0; i < runLength; i++) {
696
+ let dif = 0;
697
+ if (wds) {
698
+ dif = bin.readUshort(data2, off2);
699
+ off2 += 2;
700
+ } else {
701
+ dif = data2[off2];
702
+ off2++;
703
+ }
704
+ last += dif;
705
+ pts.push(last);
706
+ }
707
+ }
708
+ return [pts, off2];
709
+ }
710
+ let off = offset + 4;
711
+ const acnt = bin.readUshort(data, off);
712
+ off += 2;
713
+ const tcnt = bin.readUshort(data, off);
714
+ off += 2;
715
+ const toff = bin.readUint(data, off);
716
+ off += 4;
717
+ const gcnt = bin.readUshort(data, off);
718
+ off += 2;
719
+ const flgs = bin.readUshort(data, off);
720
+ off += 2;
721
+ const goff = bin.readUint(data, off);
722
+ off += 4;
723
+ const offs = [];
724
+ for (let i = 0; i < gcnt + 1; i++) {
725
+ offs.push(bin.readUint(data, off + i * 4));
726
+ }
727
+ const tups = [], mins = [], maxs = [];
728
+ off = offset + toff;
729
+ for (let i = 0; i < tcnt; i++) {
730
+ const peak = readTuple(data, off + i * acnt * 2, acnt);
731
+ const imin = [], imax = [];
732
+ tups.push(peak);
733
+ mins.push(imin);
734
+ maxs.push(imax);
735
+ for (let k = 0; k < acnt; k++) {
736
+ imin[k] = Math.min(peak[k], 0);
737
+ imax[k] = Math.max(peak[k], 0);
738
+ }
739
+ }
740
+ const i8 = new Int8Array(data.buffer);
741
+ const tabs = [];
742
+ for (let i = 0; i < gcnt; i++) {
743
+ off = offset + goff + offs[i];
744
+ let vcnt = bin.readUshort(data, off);
745
+ off += 2;
746
+ const snum = vcnt & SHARED_POINT_NUMBERS;
747
+ vcnt &= 4095;
748
+ const soff = bin.readUshort(data, off);
749
+ off += 2;
750
+ const hdr = readTupleVarHeader(
751
+ data,
752
+ off,
753
+ vcnt,
754
+ acnt,
755
+ offset + goff + offs[i + 1]
756
+ );
757
+ const tab = [];
758
+ tabs.push(tab);
759
+ off = offset + goff + offs[i] + soff;
760
+ let sind = [];
761
+ if (snum) {
762
+ const oo = readPointNumbers(data, off, i);
763
+ sind = oo[0];
764
+ off = oo[1];
765
+ }
766
+ for (let j = 0; j < vcnt; j++) {
767
+ const vr = hdr[j];
768
+ const end = off + vr[0];
769
+ let ind = sind;
770
+ if (vr[2] & PRIVATE_POINT_NUMBERS) {
771
+ const oo = readPointNumbers(data, off, i);
772
+ ind = oo[0];
773
+ off = oo[1];
774
+ }
775
+ const ds = [];
776
+ while (off < end) {
777
+ const cb = data[off++];
778
+ const cnt = (cb & 63) + 1;
779
+ if (cb & DELTAS_ARE_ZERO) {
780
+ for (let k = 0; k < cnt; k++) ds.push(0);
781
+ } else if (cb & DELTAS_ARE_WORDS) {
782
+ for (let k = 0; k < cnt; k++) {
783
+ ds.push(bin.readShort(data, off + k * 2));
784
+ }
785
+ off += cnt * 2;
786
+ } else {
787
+ for (let k = 0; k < cnt; k++) ds.push(i8[off + k]);
788
+ off += cnt;
789
+ }
790
+ }
791
+ const ti = vr[1];
792
+ tab.push([
793
+ [
794
+ vr[3] ? vr[3] : mins[ti],
795
+ vr[4] ? vr[4] : tups[ti],
796
+ vr[5] ? vr[5] : maxs[ti]
797
+ ],
798
+ ds,
799
+ ind.length == 0 ? null : ind
800
+ ]);
801
+ if (ind.length != 0 && ind.length * 2 != ds.length) throw "e";
802
+ }
803
+ }
804
+ return tabs;
805
+ }
806
+ },
807
+ HVAR: {
808
+ parseTab(data, offset, length, obj) {
809
+ let off = offset;
810
+ const oo = offset;
811
+ off += 4;
812
+ const varO = bin.readUint(data, off);
813
+ off += 4;
814
+ const advO = bin.readUint(data, off);
815
+ off += 4;
816
+ const lsbO = bin.readUint(data, off);
817
+ off += 4;
818
+ const rsbO = bin.readUint(data, off);
819
+ off += 4;
820
+ if (lsbO !== 0 || rsbO !== 0) throw lsbO;
821
+ off = oo + varO;
822
+ const ioff = off;
823
+ const fmt = bin.readUshort(data, off);
824
+ off += 2;
825
+ if (fmt !== 1) throw "e";
826
+ const vregO = bin.readUint(data, off);
827
+ off += 4;
828
+ const vcnt = bin.readUshort(data, off);
829
+ off += 2;
830
+ const offs = [];
831
+ for (let i = 0; i < vcnt; i++) offs.push(bin.readUint(data, off + i * 4));
832
+ off += vcnt * 4;
833
+ off = ioff + vregO;
834
+ const acnt = bin.readUshort(data, off);
835
+ off += 2;
836
+ const rcnt = bin.readUshort(data, off);
837
+ off += 2;
838
+ const regs = [];
839
+ for (let i = 0; i < rcnt; i++) {
840
+ const crd = [[], [], []];
841
+ regs.push(crd);
842
+ for (let j = 0; j < acnt; j++) {
843
+ crd[0].push(bin.readF2dot14(data, off + 0));
844
+ crd[1].push(bin.readF2dot14(data, off + 2));
845
+ crd[2].push(bin.readF2dot14(data, off + 4));
846
+ off += 6;
847
+ }
848
+ }
849
+ const i8 = new Int8Array(data.buffer);
850
+ const varStore = [];
851
+ for (let i = 0; i < offs.length; i++) {
852
+ off = oo + varO + offs[i];
853
+ const vdata = [];
854
+ varStore.push(vdata);
855
+ const icnt = bin.readUshort(data, off);
856
+ off += 2;
857
+ const dcnt = bin.readUshort(data, off);
858
+ off += 2;
859
+ if (dcnt & 32768) throw "e";
860
+ const rcnt2 = bin.readUshort(data, off);
861
+ off += 2;
862
+ const ixs = [];
863
+ for (let j = 0; j < rcnt2; j++) {
864
+ ixs.push(bin.readUshort(data, off + j * 2));
865
+ }
866
+ off += rcnt2 * 2;
867
+ for (let k = 0; k < icnt; k++) {
868
+ const deltaData = [];
869
+ for (let ri = 0; ri < rcnt2; ri++) {
870
+ deltaData.push(ri < dcnt ? bin.readShort(data, off) : i8[off]);
871
+ off += ri < dcnt ? 2 : 1;
872
+ }
873
+ const dd = new Array(regs.length);
874
+ dd.fill(0);
875
+ vdata.push(dd);
876
+ for (let j = 0; j < ixs.length; j++) dd[ixs[j]] = deltaData[j];
877
+ }
878
+ }
879
+ off = oo + advO;
880
+ const fmt2 = data[off++];
881
+ if (fmt2 !== 0) throw "e";
882
+ const entryFormat = data[off++];
883
+ const mapCount = bin.readUshort(data, off);
884
+ off += 2;
885
+ const INNER_INDEX_BIT_COUNT_MASK = 15;
886
+ const MAP_ENTRY_SIZE_MASK = 48;
887
+ const entrySize = ((entryFormat & MAP_ENTRY_SIZE_MASK) >> 4) + 1;
888
+ const dfs = [];
889
+ for (let i = 0; i < mapCount; i++) {
890
+ let entry = 0;
891
+ if (entrySize === 1) entry = data[off++];
892
+ else {
893
+ entry = bin.readUshort(data, off);
894
+ off += 2;
895
+ }
896
+ const outerIndex = entry >> (entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1;
897
+ const innerIndex = entry & (1 << (entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1) - 1;
898
+ dfs.push(varStore[outerIndex][innerIndex]);
899
+ }
900
+ return [regs, dfs];
901
+ }
902
+ }
903
+ };
904
+ var PathBuilder = {
905
+ MoveTo(p, x, y) {
906
+ p.cmds.push("M");
907
+ p.crds.push(x, y);
908
+ },
909
+ LineTo(p, x, y) {
910
+ p.cmds.push("L");
911
+ p.crds.push(x, y);
912
+ },
913
+ qCurveTo(p, a, b, c, d) {
914
+ p.cmds.push("Q");
915
+ p.crds.push(a, b, c, d);
916
+ },
917
+ ClosePath(p) {
918
+ p.cmds.push("Z");
919
+ }
920
+ };
921
+ function codeToGlyph(font, code) {
922
+ if (font._ctab == null) {
923
+ const cmap2 = font.cmap;
924
+ let tind = -1;
925
+ const pps = [
926
+ "p3e10",
927
+ "p0e4",
928
+ "p3e1",
929
+ "p1e0",
930
+ "p0e3",
931
+ "p0e1",
932
+ "p3e0",
933
+ "p3e5"
934
+ ];
935
+ for (let i = 0; i < pps.length; i++) {
936
+ if (cmap2.ids[pps[i]] != null) {
937
+ tind = cmap2.ids[pps[i]];
938
+ break;
939
+ }
940
+ }
941
+ if (tind === -1) throw "no familiar platform and encoding!";
942
+ font._ctab = cmap2.tables[tind];
943
+ }
944
+ const tab = font._ctab;
945
+ const fmt = tab.format;
946
+ let gid = -1;
947
+ if (fmt === 0) {
948
+ if (code >= tab.map.length) gid = 0;
949
+ else gid = tab.map[code];
950
+ } else if (fmt === 4) {
951
+ const ec = tab.endCount;
952
+ gid = 0;
953
+ if (code <= ec[ec.length - 1]) {
954
+ let sind = arrSearch(ec, 1, code);
955
+ if (ec[sind] < code) sind++;
956
+ if (code >= tab.startCount[sind]) {
957
+ let gli = 0;
958
+ if (tab.idRangeOffset[sind] !== 0) {
959
+ gli = tab.glyphIdArray[code - tab.startCount[sind] + (tab.idRangeOffset[sind] >> 1) - (tab.idRangeOffset.length - sind)];
960
+ } else {
961
+ gli = code + tab.idDelta[sind];
962
+ }
963
+ gid = gli & 65535;
964
+ }
965
+ }
966
+ } else if (fmt === 6) {
967
+ const off = code - tab.firstCode;
968
+ const arr = tab.glyphIdArray;
969
+ if (off < 0 || off >= arr.length) gid = 0;
970
+ else gid = arr[off];
971
+ } else if (fmt === 12) {
972
+ const grp = tab.groups;
973
+ gid = 0;
974
+ if (code <= grp[grp.length - 2]) {
975
+ const i = arrSearch(grp, 3, code);
976
+ if (grp[i] <= code && code <= grp[i + 1]) {
977
+ gid = grp[i + 2] + (code - grp[i]);
978
+ }
979
+ }
980
+ } else {
981
+ throw "unknown cmap table format " + tab.format;
982
+ }
983
+ return gid;
984
+ }
985
+ function arrSearch(arr, k, v) {
986
+ let l = 0;
987
+ let r = Math.floor(arr.length / k);
988
+ while (l + 1 !== r) {
989
+ const mid = l + (r - l >>> 1);
990
+ if (arr[mid * k] <= v) l = mid;
991
+ else r = mid;
992
+ }
993
+ return l * k;
994
+ }
995
+ function getGlyphPosition(font, gls, i1) {
996
+ const g1 = gls[i1];
997
+ const g2 = gls[i1 + 1];
998
+ const kern = font.kern;
999
+ if (kern) {
1000
+ const ind1 = kern.glyph1.indexOf(g1);
1001
+ if (ind1 !== -1) {
1002
+ const ind2 = kern.rval[ind1].glyph2.indexOf(g2);
1003
+ if (ind2 !== -1) return [0, 0, kern.rval[ind1].vals[ind2], 0];
1004
+ }
1005
+ }
1006
+ return [0, 0, 0, 0];
1007
+ }
1008
+ function normalizeAxis(font, vv) {
1009
+ const fvar = font.fvar;
1010
+ const avar = font.avar;
1011
+ const fv = fvar ? fvar[0] : null;
1012
+ const nv = [];
1013
+ for (let i = 0; i < fv.length; i++) {
1014
+ const min = fv[i][1];
1015
+ const def = fv[i][2];
1016
+ const max = fv[i][3];
1017
+ const v = Math.max(min, Math.min(max, vv[i]));
1018
+ if (v < def) nv[i] = (def - v) / (min - def);
1019
+ else if (v > def) nv[i] = (v - def) / (max - def);
1020
+ else nv[i] = 0;
1021
+ if (avar && nv[i] !== -1) {
1022
+ const av = avar[i];
1023
+ let j = 0;
1024
+ for (; j < av.length; j += 2) if (av[j] >= nv[i]) break;
1025
+ const f = (nv[i] - av[j - 2]) / (av[j] - av[j - 2]);
1026
+ nv[i] = f * av[j + 1] + (1 - f) * av[j - 1];
1027
+ }
1028
+ }
1029
+ return nv;
1030
+ }
1031
+ function shape(font, str, prm = {}) {
1032
+ let axs = prm.axs;
1033
+ if (font.fvar && axs == null) axs = font.fvar[1][font._index || 0][2];
1034
+ const HVAR = font.HVAR;
1035
+ if (axs && HVAR) {
1036
+ axs = normalizeAxis(font, axs);
1037
+ }
1038
+ const gls = [];
1039
+ for (let i = 0; i < str.length; i++) {
1040
+ const cc = str.codePointAt(i);
1041
+ if (cc > 65535) i++;
1042
+ gls.push(codeToGlyph(font, cc));
1043
+ }
1044
+ const shape2 = [];
1045
+ let x = 0, y = 0;
1046
+ for (let i = 0; i < gls.length; i++) {
1047
+ const padj = getGlyphPosition(font, gls, i);
1048
+ const gid = gls[i];
1049
+ let ax = font.hmtx.aWidth[gid] + padj[2];
1050
+ if (HVAR && HVAR[1][gid]) {
1051
+ const difs = HVAR[1][gid];
1052
+ for (let j = 0; j < HVAR[0].length; j++) {
1053
+ ax += _interpolate(HVAR[0][j], axs) * difs[j];
1054
+ }
1055
+ }
1056
+ shape2.push({ g: gid, cl: i, dx: 0, dy: 0, ax, ay: 0 });
1057
+ x += ax;
1058
+ }
1059
+ return shape2;
1060
+ }
1061
+ function glyphToPath(font, gid, noColor, axs) {
1062
+ const path = { cmds: [], crds: [] };
1063
+ if (font.fvar) {
1064
+ if (axs == null) axs = font.fvar[1][font._index || 0][2];
1065
+ axs = normalizeAxis(font, axs);
1066
+ }
1067
+ if (font.glyf) {
1068
+ drawGlyf(gid, font, path, axs);
1069
+ }
1070
+ return { cmds: path.cmds, crds: path.crds };
1071
+ }
1072
+ function drawGlyf(gid, font, path, axs) {
1073
+ let gl = font.glyf[gid];
1074
+ if (gl == null) {
1075
+ gl = font.glyf[gid] = glyf.parseGlyf(font, gid);
1076
+ }
1077
+ if (gl != null) {
1078
+ if (gl.noc > -1) simpleGlyph(gl, font, gid, path, axs);
1079
+ else compoGlyph(gl, font, gid, path, axs);
1080
+ }
1081
+ }
1082
+ function simpleGlyph(gl, font, gid, p, axs) {
1083
+ let xs = gl.xs;
1084
+ let ys = gl.ys;
1085
+ if (font.fvar && axs) {
1086
+ xs = xs.slice(0);
1087
+ ys = ys.slice(0);
1088
+ const gvar = font.gvar;
1089
+ const gv = gvar ? gvar[gid] : null;
1090
+ if (gv) {
1091
+ for (let vi = 0; vi < gv.length; vi++) {
1092
+ const axv = gv[vi][0];
1093
+ const S = _interpolate(axv, axs);
1094
+ if (S < 1e-9) continue;
1095
+ let dfs = gv[vi][1];
1096
+ const ind = gv[vi][2];
1097
+ if (ind) {
1098
+ dfs = gv[vi][1] = interpolateDeltas(dfs, ind, xs, ys, gl.endPts);
1099
+ gv[vi][2] = null;
1100
+ }
1101
+ if (dfs.length === xs.length * 2 + 8) {
1102
+ for (let i = 0; i < xs.length; i++) {
1103
+ xs[i] += S * dfs[i];
1104
+ ys[i] += S * dfs[i + xs.length + 4];
1105
+ }
1106
+ }
1107
+ }
1108
+ }
1109
+ }
1110
+ for (let c = 0; c < gl.noc; c++) {
1111
+ const i0 = c === 0 ? 0 : gl.endPts[c - 1] + 1;
1112
+ const il = gl.endPts[c];
1113
+ for (let i = i0; i <= il; i++) {
1114
+ const pr = i === i0 ? il : i - 1;
1115
+ const nx = i === il ? i0 : i + 1;
1116
+ const onCurve = gl.flags[i] & 1;
1117
+ const prOnCurve = gl.flags[pr] & 1;
1118
+ const nxOnCurve = gl.flags[nx] & 1;
1119
+ const x = xs[i], y = ys[i];
1120
+ if (i === i0) {
1121
+ if (onCurve) {
1122
+ if (prOnCurve) PathBuilder.MoveTo(p, xs[pr], ys[pr]);
1123
+ else {
1124
+ PathBuilder.MoveTo(p, x, y);
1125
+ continue;
1126
+ }
1127
+ } else {
1128
+ if (prOnCurve) PathBuilder.MoveTo(p, xs[pr], ys[pr]);
1129
+ else
1130
+ PathBuilder.MoveTo(
1131
+ p,
1132
+ Math.floor((xs[pr] + x) * 0.5),
1133
+ Math.floor((ys[pr] + y) * 0.5)
1134
+ );
1135
+ }
1136
+ }
1137
+ if (onCurve) {
1138
+ if (prOnCurve) PathBuilder.LineTo(p, x, y);
1139
+ } else {
1140
+ if (nxOnCurve) PathBuilder.qCurveTo(p, x, y, xs[nx], ys[nx]);
1141
+ else
1142
+ PathBuilder.qCurveTo(
1143
+ p,
1144
+ x,
1145
+ y,
1146
+ Math.floor((x + xs[nx]) * 0.5),
1147
+ Math.floor((y + ys[nx]) * 0.5)
1148
+ );
1149
+ }
1150
+ }
1151
+ PathBuilder.ClosePath(p);
1152
+ }
1153
+ }
1154
+ function compoGlyph(gl, font, gid, p, axs) {
1155
+ const dx = [0, 0, 0, 0, 0, 0];
1156
+ const dy = [0, 0, 0, 0, 0, 0];
1157
+ const ccnt = gl.parts.length;
1158
+ if (font.fvar && axs) {
1159
+ const gvar = font.gvar;
1160
+ const gv = gvar ? gvar[gid] : null;
1161
+ if (gv) {
1162
+ for (let vi = 0; vi < gv.length; vi++) {
1163
+ const axv = gv[vi][0];
1164
+ const S = _interpolate(axv, axs);
1165
+ if (S < 1e-6) continue;
1166
+ const dfs = gv[vi][1];
1167
+ const ind = gv[vi][2];
1168
+ if (ind == null) {
1169
+ for (let i = 0; i < ccnt; i++) {
1170
+ dx[i] += S * dfs[i];
1171
+ dy[i] += S * dfs[i + ccnt + 4];
1172
+ }
1173
+ } else {
1174
+ for (let j = 0; j < ind.length; j++) {
1175
+ const i = ind[j];
1176
+ dx[i] += S * dfs[0];
1177
+ dy[i] += S * dfs[0 + ccnt];
1178
+ }
1179
+ }
1180
+ }
1181
+ }
1182
+ }
1183
+ for (let j = 0; j < ccnt; j++) {
1184
+ const path = { cmds: [], crds: [] };
1185
+ const prt = gl.parts[j];
1186
+ drawGlyf(prt.glyphIndex, font, path, axs);
1187
+ const m = prt.m;
1188
+ const tx = m.tx + dx[j];
1189
+ const ty = m.ty + dy[j];
1190
+ for (let i = 0; i < path.crds.length; i += 2) {
1191
+ const x = path.crds[i];
1192
+ const y = path.crds[i + 1];
1193
+ p.crds.push(x * m.a + y * m.c + tx);
1194
+ p.crds.push(x * m.b + y * m.d + ty);
1195
+ }
1196
+ for (let i = 0; i < path.cmds.length; i++) p.cmds.push(path.cmds[i]);
1197
+ }
1198
+ }
1199
+ function _interpolate(axs, v) {
1200
+ var acnt = v.length, S = 1;
1201
+ var s = axs[0];
1202
+ var p = axs[1];
1203
+ var e = axs[2];
1204
+ for (var i = 0; i < v.length; i++) {
1205
+ var AS = 1;
1206
+ if (s[i] > p[i] || p[i] > e[i]) AS = 1;
1207
+ else if (s[i] < 0 && e[i] > 0 && p[i] != 0) AS = 1;
1208
+ else if (p[i] == 0) AS = 1;
1209
+ else if (v[i] < s[i] || v[i] > e[i]) AS = 0;
1210
+ else {
1211
+ if (v[i] == p[i]) AS = 1;
1212
+ else if (v[i] < p[i]) AS = (v[i] - s[i]) / (p[i] - s[i]);
1213
+ else AS = (e[i] - v[i]) / (e[i] - p[i]);
1214
+ }
1215
+ S = S * AS;
1216
+ }
1217
+ return S;
1218
+ }
1219
+ function interpolateDeltas(dfs, ind, xs, ys, endPts) {
1220
+ const N = xs.length;
1221
+ const ndfs = new Array(N * 2 + 8);
1222
+ ndfs.fill(0);
1223
+ for (let i = 0; i < N; i++) {
1224
+ let dx = 0, dy = 0;
1225
+ const ii = ind.indexOf(i);
1226
+ if (ii !== -1) {
1227
+ dx = dfs[ii];
1228
+ dy = dfs[ind.length + ii];
1229
+ } else {
1230
+ let cmp = 0;
1231
+ while (endPts[cmp] < i) cmp++;
1232
+ const cmp0 = cmp === 0 ? 0 : endPts[cmp - 1] + 1;
1233
+ const cmp1 = endPts[cmp];
1234
+ let i0 = -1, i1 = -1;
1235
+ for (let j = 0; j < ind.length; j++) {
1236
+ const v = ind[j];
1237
+ if (v < cmp0 || v > cmp1 || v >= N) continue;
1238
+ i0 = j;
1239
+ if (i1 === -1) i1 = j;
1240
+ }
1241
+ for (let j = 0; j < ind.length; j++) {
1242
+ const v = ind[j];
1243
+ if (v < cmp0 || v > cmp1 || v >= N) continue;
1244
+ if (v < i) i0 = j;
1245
+ if (i < v) {
1246
+ i1 = j;
1247
+ break;
1248
+ }
1249
+ }
1250
+ for (let ax = 0; ax < 2; ax++) {
1251
+ const crd = ax === 0 ? xs : ys;
1252
+ const ofs = ax * ind.length;
1253
+ let dlt = 0;
1254
+ const c0 = crd[ind[i0]];
1255
+ const c1 = crd[ind[i1]];
1256
+ const cC = crd[i];
1257
+ const d0 = dfs[ofs + i0];
1258
+ const d1 = dfs[ofs + i1];
1259
+ if (c0 === c1) {
1260
+ if (d0 === d1) dlt = d0;
1261
+ else dlt = 0;
1262
+ } else {
1263
+ if (cC <= Math.min(c0, c1)) {
1264
+ if (c0 < c1) dlt = d0;
1265
+ else dlt = d1;
1266
+ } else if (Math.max(c0, c1) <= cC) {
1267
+ if (c0 < c1) dlt = d1;
1268
+ else dlt = d0;
1269
+ } else {
1270
+ const prop = (cC - c0) / (c1 - c0);
1271
+ dlt = prop * d1 + (1 - prop) * d0;
1272
+ }
1273
+ }
1274
+ if (ax === 0) dx = dlt;
1275
+ else dy = dlt;
1276
+ }
1277
+ }
1278
+ ndfs[i] = dx;
1279
+ ndfs[N + 4 + i] = dy;
1280
+ }
1281
+ return ndfs;
1282
+ }
1283
+ function shapeToPath(font, shape2, prm = {}) {
1284
+ const tpath = { cmds: [], crds: [] };
1285
+ let x = 0, y = 0;
1286
+ const axs = prm.axs;
1287
+ for (let i = 0; i < shape2.length; i++) {
1288
+ const it = shape2[i];
1289
+ const path = glyphToPath(font, it.g, false, axs);
1290
+ const crds = path.crds;
1291
+ for (let j = 0; j < crds.length; j += 2) {
1292
+ tpath.crds.push(crds[j] + x + it.dx);
1293
+ tpath.crds.push(crds[j + 1] + y + it.dy);
1294
+ }
1295
+ for (let j = 0; j < path.cmds.length; j++) {
1296
+ tpath.cmds.push(path.cmds[j]);
1297
+ }
1298
+ x += it.ax;
1299
+ y += it.ay;
1300
+ }
1301
+ return { cmds: tpath.cmds, crds: tpath.crds };
1302
+ }
1303
+ var FontUtils = class {
1304
+ static parse(buff) {
1305
+ const data = new Uint8Array(buff);
1306
+ const tmap = {};
1307
+ const font = readFont(data, 0, 0, tmap);
1308
+ const fvar = font.fvar;
1309
+ if (fvar) {
1310
+ const out = [font];
1311
+ for (let i = 0; i < fvar[1].length; i++) {
1312
+ const fv = fvar[1][i];
1313
+ const obj = {};
1314
+ out.push(obj);
1315
+ for (const p in font) obj[p] = font[p];
1316
+ obj._index = i;
1317
+ const name = obj.name = JSON.parse(
1318
+ JSON.stringify(obj.name || {})
1319
+ );
1320
+ name.fontSubfamily = fv[0];
1321
+ if (fv[3] == null) {
1322
+ fv[3] = ((name.fontFamily || "Font") + "-" + (name.fontSubfamily || "Regular")).replace(/ /g, "");
1323
+ }
1324
+ name.postScriptName = fv[3];
1325
+ }
1326
+ return out;
1327
+ }
1328
+ return [font];
1329
+ }
1330
+ };
1331
+ FontUtils.U = {
1332
+ shape,
1333
+ shapeToPath,
1334
+ glyphToPath,
1335
+ codeToGlyph
1336
+ };
1337
+ function readFont(data, idx, offset, tmap) {
1338
+ const parsers = {
1339
+ cmap,
1340
+ head: Tables.head,
1341
+ hhea: Tables.hhea,
1342
+ maxp: Tables.maxp,
1343
+ hmtx: Tables.hmtx,
1344
+ loca: Tables.loca,
1345
+ kern: Tables.kern,
1346
+ glyf,
1347
+ fvar: VariableTables.fvar,
1348
+ gvar: VariableTables.gvar,
1349
+ avar: VariableTables.avar,
1350
+ HVAR: VariableTables.HVAR
1351
+ };
1352
+ const obj = { _data: data, _index: idx, _offset: offset };
1353
+ for (const t in parsers) {
1354
+ const tab = findTable(data, t, offset);
1355
+ if (tab) {
1356
+ const off = tab[0];
1357
+ let tobj = tmap[off];
1358
+ if (tobj == null) {
1359
+ tobj = parsers[t].parseTab(data, off, tab[1], obj);
1360
+ }
1361
+ obj[t] = tmap[off] = tobj;
1362
+ }
1363
+ }
1364
+ if (!obj.name) {
1365
+ obj.name = { fontFamily: "Unknown", postScriptName: "Unknown" };
1366
+ }
1367
+ return obj;
1368
+ }
1369
+ var FontParser = class {
1370
+ constructor() {
1371
+ this.fonts = /* @__PURE__ */ new Map();
1372
+ }
1373
+ async load(url) {
1374
+ const ext = url.split(".").pop()?.toLowerCase();
1375
+ if (ext === "otf") {
1376
+ console.warn("OTF not supported. Convert: http://convertio.co/otf-ttf/");
1377
+ throw new Error("OTF not supported");
1378
+ }
1379
+ if (ext === "woff") {
1380
+ console.warn(
1381
+ "WOFF not supported. Convert: http://convertio.co/woff-ttf/"
1382
+ );
1383
+ throw new Error("WOFF not supported");
1384
+ }
1385
+ if (ext === "woff2") {
1386
+ console.warn(
1387
+ "WOFF2 not supported. Convert: http://convertio.co/woff2-ttf/"
1388
+ );
1389
+ throw new Error("WOFF2 not supported");
1390
+ }
1391
+ const response = await fetch(url);
1392
+ if (!response.ok) throw new Error("Failed to load font");
1393
+ const buffer = await response.arrayBuffer();
1394
+ return this.loadFromBuffer(buffer);
1395
+ }
1396
+ loadFromBuffer(buffer) {
1397
+ const fonts = FontUtils.parse(buffer);
1398
+ return this.createAPI(fonts[0]);
1399
+ }
1400
+ createAPI(font) {
1401
+ return {
1402
+ // For compatibility with the test - expose the font directly
1403
+ ...font,
1404
+ // Real API implementation
1405
+ toPaths(text, size = 100, options = {}) {
1406
+ const layout = this.layoutText(font, text, size, options);
1407
+ const letters = [];
1408
+ const scale = size / font.head.unitsPerEm;
1409
+ const axisValues = options.axisValues || (font.fvar ? font.fvar[1][font._index || 0][2] : null);
1410
+ for (const letter of layout.letters) {
1411
+ const path2D = new Path2D();
1412
+ const glyphPath = FontUtils.U.glyphToPath(
1413
+ font,
1414
+ letter.glyph.g,
1415
+ false,
1416
+ axisValues
1417
+ );
1418
+ let cmdIndex = 0;
1419
+ for (let i = 0; i < glyphPath.cmds.length; i++) {
1420
+ const cmd = glyphPath.cmds[i];
1421
+ if (cmd === "M") {
1422
+ path2D.moveTo(
1423
+ glyphPath.crds[cmdIndex] * scale,
1424
+ -glyphPath.crds[cmdIndex + 1] * scale
1425
+ );
1426
+ cmdIndex += 2;
1427
+ } else if (cmd === "L") {
1428
+ path2D.lineTo(
1429
+ glyphPath.crds[cmdIndex] * scale,
1430
+ -glyphPath.crds[cmdIndex + 1] * scale
1431
+ );
1432
+ cmdIndex += 2;
1433
+ } else if (cmd === "Q") {
1434
+ path2D.quadraticCurveTo(
1435
+ glyphPath.crds[cmdIndex] * scale,
1436
+ -glyphPath.crds[cmdIndex + 1] * scale,
1437
+ glyphPath.crds[cmdIndex + 2] * scale,
1438
+ -glyphPath.crds[cmdIndex + 3] * scale
1439
+ );
1440
+ cmdIndex += 4;
1441
+ } else if (cmd === "Z") {
1442
+ path2D.closePath();
1443
+ }
1444
+ }
1445
+ letters.push({
1446
+ path: path2D,
1447
+ letterIndex: letter.letterIndex,
1448
+ wordIndex: letter.wordIndex,
1449
+ lineIndex: letter.lineIndex,
1450
+ width: letter.width,
1451
+ height: letter.height,
1452
+ center: { x: letter.x, y: letter.y }
1453
+ });
1454
+ }
1455
+ return { letters, block: layout.block };
1456
+ },
1457
+ toPoints(text, size = 100, options = {}) {
1458
+ const layout = this.layoutText(font, text, size, options);
1459
+ const letters = [];
1460
+ const sampling = options.sampling || 0.25;
1461
+ const scale = size / font.head.unitsPerEm;
1462
+ const axisValues = options.axisValues || (font.fvar ? font.fvar[1][font._index || 0][2] : null);
1463
+ for (const letter of layout.letters) {
1464
+ const glyphPath = FontUtils.U.glyphToPath(
1465
+ font,
1466
+ letter.glyph.g,
1467
+ false,
1468
+ axisValues
1469
+ );
1470
+ const rawPoints = this.samplePathPoints(glyphPath, sampling);
1471
+ const points = rawPoints.map((point) => ({
1472
+ x: point.x * scale,
1473
+ y: -point.y * scale,
1474
+ contour: point.contour
1475
+ // Preserve contour information!
1476
+ }));
1477
+ letters.push({
1478
+ shape: points,
1479
+ center: { x: letter.x, y: letter.y },
1480
+ letterIndex: letter.letterIndex,
1481
+ wordIndex: letter.wordIndex,
1482
+ lineIndex: letter.lineIndex,
1483
+ width: letter.width,
1484
+ height: letter.height
1485
+ });
1486
+ }
1487
+ return { letters, block: layout.block };
1488
+ },
1489
+ // Helper methods
1490
+ layoutText(font2, text, size, options = {}) {
1491
+ const lines = text.split(/\r?\n/);
1492
+ const scale = size / font2.head.unitsPerEm;
1493
+ const letters = [];
1494
+ const letterSpacing = options.letterSpacing || 0;
1495
+ const wordSpacing = options.wordSpacing || 0;
1496
+ const lineSpacing = options.lineSpacing || 0;
1497
+ const axisValues = options.axisValues || (font2.fvar ? font2.fvar[1][font2._index || 0][2] : null);
1498
+ const ascender = font2.hhea.ascender * scale;
1499
+ const descender = font2.hhea.descender * scale;
1500
+ const lineGap = (font2.hhea.lineGap || 0) * scale;
1501
+ const lineHeight = size * 1.2 + lineSpacing;
1502
+ let letterIndex = 0;
1503
+ let wordIndex = 0;
1504
+ const shapeOptions = { ltr: true };
1505
+ if (axisValues) {
1506
+ shapeOptions.axs = axisValues;
1507
+ }
1508
+ const lineWidths = lines.map((line) => {
1509
+ const shaped = FontUtils.U.shape(font2, line, shapeOptions);
1510
+ let width = 0;
1511
+ for (let i = 0; i < shaped.length; i++) {
1512
+ const glyph = shaped[i];
1513
+ const char = line.charAt(i);
1514
+ width += (glyph.ax || 0) * scale;
1515
+ if (char === " ") {
1516
+ width += wordSpacing;
1517
+ } else if (i < shaped.length - 1) {
1518
+ width += letterSpacing;
1519
+ }
1520
+ }
1521
+ return width;
1522
+ });
1523
+ const blockWidth = Math.max(...lineWidths, 0);
1524
+ const blockHeight = lineHeight * lines.length;
1525
+ for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
1526
+ const line = lines[lineIndex];
1527
+ const shaped = FontUtils.U.shape(font2, line, shapeOptions);
1528
+ const lineWidth = lineWidths[lineIndex];
1529
+ let x = 0;
1530
+ if (options.align === "center") {
1531
+ x = (blockWidth - lineWidth) / 2;
1532
+ } else if (options.align === "right") {
1533
+ x = blockWidth - lineWidth;
1534
+ }
1535
+ if (options.anchor === "center" && options.align === "center") {
1536
+ x = -lineWidth / 2;
1537
+ }
1538
+ let y;
1539
+ if (options.baseline === "center") {
1540
+ if (lines.length === 1) {
1541
+ y = 0;
1542
+ } else {
1543
+ y = -blockHeight / 2 + lineHeight / 2 + lineIndex * lineHeight;
1544
+ }
1545
+ } else if (options.baseline === "top") {
1546
+ y = -lineHeight / 2 + lineIndex * lineHeight;
1547
+ } else {
1548
+ y = -lineHeight / 2 + lineIndex * lineHeight;
1549
+ }
1550
+ const middleToBaseline = (ascender + descender) / 2;
1551
+ y += middleToBaseline;
1552
+ for (let i = 0; i < shaped.length; i++) {
1553
+ const glyph = shaped[i];
1554
+ const char = line.charAt(i);
1555
+ const advanceWidth = (glyph.ax || 0) * scale;
1556
+ letters.push({
1557
+ glyph,
1558
+ char,
1559
+ x,
1560
+ y,
1561
+ letterIndex: letterIndex++,
1562
+ wordIndex,
1563
+ lineIndex,
1564
+ width: advanceWidth,
1565
+ height: size
1566
+ });
1567
+ x += advanceWidth;
1568
+ if (char === " ") {
1569
+ x += wordSpacing;
1570
+ wordIndex++;
1571
+ } else if (i < shaped.length - 1) {
1572
+ x += letterSpacing;
1573
+ }
1574
+ }
1575
+ if (line.includes(" ")) wordIndex++;
1576
+ }
1577
+ let offsetX = 0, offsetY = 0;
1578
+ if (options.anchor === "center") {
1579
+ if (options.align !== "center") {
1580
+ offsetX = -blockWidth / 2;
1581
+ }
1582
+ if (options.baseline !== "center") {
1583
+ offsetY = -blockHeight / 2;
1584
+ }
1585
+ }
1586
+ letters.forEach((letter) => {
1587
+ letter.x += offsetX;
1588
+ letter.y += offsetY;
1589
+ });
1590
+ return { letters, block: { width: blockWidth, height: blockHeight } };
1591
+ },
1592
+ samplePathPoints(glyphPath, sampling) {
1593
+ const rawContours = this.parseContours(glyphPath);
1594
+ const contoursWithLength = rawContours.map(
1595
+ (contour, index) => ({
1596
+ contour,
1597
+ originalIndex: index,
1598
+ length: this.calculateContourLength(contour)
1599
+ })
1600
+ );
1601
+ contoursWithLength.sort((a, b) => b.length - a.length);
1602
+ const allPoints = [];
1603
+ for (let i = 0; i < contoursWithLength.length; i++) {
1604
+ const { contour } = contoursWithLength[i];
1605
+ const contourPoints = this.sampleContour(contour, sampling, i);
1606
+ allPoints.push(...contourPoints);
1607
+ }
1608
+ return allPoints;
1609
+ },
1610
+ // Calculate total length of a contour
1611
+ calculateContourLength(contour) {
1612
+ let totalLength = 0;
1613
+ let currentX = contour.startX;
1614
+ let currentY = contour.startY;
1615
+ for (const seg of contour.segments) {
1616
+ if (seg.cmd === "L") {
1617
+ const endX = seg.coords[0];
1618
+ const endY = seg.coords[1];
1619
+ totalLength += Math.sqrt(
1620
+ (endX - currentX) ** 2 + (endY - currentY) ** 2
1621
+ );
1622
+ currentX = endX;
1623
+ currentY = endY;
1624
+ } else if (seg.cmd === "Q") {
1625
+ const controlX = seg.coords[0];
1626
+ const controlY = seg.coords[1];
1627
+ const endX = seg.coords[2];
1628
+ const endY = seg.coords[3];
1629
+ totalLength += this.approximateQuadraticLength(
1630
+ currentX,
1631
+ currentY,
1632
+ controlX,
1633
+ controlY,
1634
+ endX,
1635
+ endY
1636
+ );
1637
+ currentX = endX;
1638
+ currentY = endY;
1639
+ }
1640
+ }
1641
+ return totalLength;
1642
+ },
1643
+ // Parse glyph path into separate contours
1644
+ parseContours(glyphPath) {
1645
+ const contours = [];
1646
+ let currentContour = null;
1647
+ let cmdIndex = 0;
1648
+ for (let i = 0; i < glyphPath.cmds.length; i++) {
1649
+ const cmd = glyphPath.cmds[i];
1650
+ if (cmd === "M") {
1651
+ if (currentContour) {
1652
+ contours.push(currentContour);
1653
+ }
1654
+ currentContour = {
1655
+ startX: glyphPath.crds[cmdIndex],
1656
+ startY: glyphPath.crds[cmdIndex + 1],
1657
+ segments: []
1658
+ };
1659
+ cmdIndex += 2;
1660
+ } else if (cmd === "L" || cmd === "Q") {
1661
+ if (currentContour) {
1662
+ currentContour.segments.push({
1663
+ cmd,
1664
+ coords: glyphPath.crds.slice(
1665
+ cmdIndex,
1666
+ cmdIndex + (cmd === "L" ? 2 : 4)
1667
+ )
1668
+ });
1669
+ }
1670
+ cmdIndex += cmd === "L" ? 2 : 4;
1671
+ } else if (cmd === "Z") {
1672
+ if (currentContour) {
1673
+ contours.push(currentContour);
1674
+ currentContour = null;
1675
+ }
1676
+ }
1677
+ }
1678
+ if (currentContour) {
1679
+ contours.push(currentContour);
1680
+ }
1681
+ return contours;
1682
+ },
1683
+ // Sample a single contour uniformly
1684
+ sampleContour(contour, sampling, contourIndex) {
1685
+ const segments = [];
1686
+ let currentX = contour.startX;
1687
+ let currentY = contour.startY;
1688
+ let totalLength = 0;
1689
+ for (const seg of contour.segments) {
1690
+ if (seg.cmd === "L") {
1691
+ const endX = seg.coords[0];
1692
+ const endY = seg.coords[1];
1693
+ const length = Math.sqrt(
1694
+ (endX - currentX) ** 2 + (endY - currentY) ** 2
1695
+ );
1696
+ segments.push({
1697
+ type: "L",
1698
+ startX: currentX,
1699
+ startY: currentY,
1700
+ endX,
1701
+ endY,
1702
+ length,
1703
+ startLength: totalLength
1704
+ });
1705
+ totalLength += length;
1706
+ currentX = endX;
1707
+ currentY = endY;
1708
+ } else if (seg.cmd === "Q") {
1709
+ const controlX = seg.coords[0];
1710
+ const controlY = seg.coords[1];
1711
+ const endX = seg.coords[2];
1712
+ const endY = seg.coords[3];
1713
+ const length = this.approximateQuadraticLength(
1714
+ currentX,
1715
+ currentY,
1716
+ controlX,
1717
+ controlY,
1718
+ endX,
1719
+ endY
1720
+ );
1721
+ segments.push({
1722
+ type: "Q",
1723
+ startX: currentX,
1724
+ startY: currentY,
1725
+ controlX,
1726
+ controlY,
1727
+ endX,
1728
+ endY,
1729
+ length,
1730
+ startLength: totalLength
1731
+ });
1732
+ totalLength += length;
1733
+ currentX = endX;
1734
+ currentY = endY;
1735
+ }
1736
+ }
1737
+ const points = [];
1738
+ const targetPoints = Math.max(
1739
+ 5,
1740
+ Math.floor(totalLength * sampling * 0.1)
1741
+ );
1742
+ for (let i = 0; i <= targetPoints; i++) {
1743
+ const targetLength = i / targetPoints * totalLength;
1744
+ const pointData = this.getPointAtLengthInContour(
1745
+ segments,
1746
+ targetLength
1747
+ );
1748
+ if (pointData) {
1749
+ points.push({
1750
+ x: pointData.x,
1751
+ y: pointData.y,
1752
+ contour: contourIndex
1753
+ });
1754
+ }
1755
+ }
1756
+ return points;
1757
+ },
1758
+ // Get point at length within a single contour
1759
+ getPointAtLengthInContour(segments, targetLength) {
1760
+ for (const segment of segments) {
1761
+ if (targetLength >= segment.startLength && targetLength <= segment.startLength + segment.length) {
1762
+ const localT = (targetLength - segment.startLength) / segment.length;
1763
+ if (segment.type === "L") {
1764
+ return {
1765
+ x: segment.startX + (segment.endX - segment.startX) * localT,
1766
+ y: segment.startY + (segment.endY - segment.startY) * localT
1767
+ };
1768
+ } else if (segment.type === "Q") {
1769
+ const t = localT;
1770
+ return {
1771
+ x: (1 - t) * (1 - t) * segment.startX + 2 * (1 - t) * t * segment.controlX + t * t * segment.endX,
1772
+ y: (1 - t) * (1 - t) * segment.startY + 2 * (1 - t) * t * segment.controlY + t * t * segment.endY
1773
+ };
1774
+ }
1775
+ }
1776
+ }
1777
+ return null;
1778
+ },
1779
+ // Helper function to approximate quadratic curve length
1780
+ approximateQuadraticLength(x0, y0, x1, y1, x2, y2) {
1781
+ const chordLength = Math.sqrt((x2 - x0) ** 2 + (y2 - y0) ** 2);
1782
+ const controlLength = Math.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2) + Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
1783
+ return (chordLength + controlLength) / 2;
1784
+ }
1785
+ };
1786
+ }
1787
+ };
1788
+
1789
+ // src/plugins/Delaunay.tsx
1790
+ var Delaunay = class {
1791
+ /**
1792
+ * Perform Delaunay triangulation on a set of points
1793
+ */
1794
+ static triangulate(points) {
1795
+ if (points.length < 3) {
1796
+ return [];
1797
+ }
1798
+ const triangles = [];
1799
+ for (let i = 0; i < points.length - 2; i++) {
1800
+ triangles.push({
1801
+ p1: points[i],
1802
+ p2: points[i + 1],
1803
+ p3: points[i + 2]
1804
+ });
1805
+ }
1806
+ return triangles;
1807
+ }
1808
+ /**
1809
+ * Draw triangles to the canvas
1810
+ */
1811
+ static drawTriangles(ctx, triangles, options) {
1812
+ const { fill = true, stroke = true } = options || {};
1813
+ triangles.forEach((triangle) => {
1814
+ ctx.beginPath();
1815
+ ctx.moveTo(triangle.p1.x, triangle.p1.y);
1816
+ ctx.lineTo(triangle.p2.x, triangle.p2.y);
1817
+ ctx.lineTo(triangle.p3.x, triangle.p3.y);
1818
+ ctx.closePath();
1819
+ if (fill) {
1820
+ if (options?.fillStyle) {
1821
+ const prevFill = ctx.fillStyle;
1822
+ ctx.fillStyle = options.fillStyle;
1823
+ ctx.fill();
1824
+ ctx.fillStyle = prevFill;
1825
+ } else {
1826
+ ctx.fill();
1827
+ }
1828
+ }
1829
+ if (stroke) {
1830
+ if (options?.strokeStyle) {
1831
+ const prevStroke = ctx.strokeStyle;
1832
+ ctx.strokeStyle = options.strokeStyle;
1833
+ ctx.stroke();
1834
+ ctx.strokeStyle = prevStroke;
1835
+ } else {
1836
+ ctx.stroke();
1837
+ }
1838
+ }
1839
+ });
1840
+ }
1841
+ /**
1842
+ * Calculate circumcenter of a triangle
1843
+ */
1844
+ static circumcenter(triangle) {
1845
+ const { p1, p2, p3 } = triangle;
1846
+ const ax = p1.x, ay = p1.y;
1847
+ const bx = p2.x, by = p2.y;
1848
+ const cx = p3.x, cy = p3.y;
1849
+ const d = 2 * (ax * (by - cy) + bx * (cy - ay) + cx * (ay - by));
1850
+ if (Math.abs(d) < 1e-6) {
1851
+ return { x: (ax + bx + cx) / 3, y: (ay + by + cy) / 3 };
1852
+ }
1853
+ const ux = ((ax * ax + ay * ay) * (by - cy) + (bx * bx + by * by) * (cy - ay) + (cx * cx + cy * cy) * (ay - by)) / d;
1854
+ const uy = ((ax * ax + ay * ay) * (cx - bx) + (bx * bx + by * by) * (ax - cx) + (cx * cx + cy * cy) * (bx - ax)) / d;
1855
+ return { x: ux, y: uy };
1856
+ }
1857
+ /**
1858
+ * Check if a point is inside a triangle's circumcircle
1859
+ */
1860
+ static inCircumcircle(point, triangle) {
1861
+ const center = this.circumcenter(triangle);
1862
+ const radius = Math.sqrt(
1863
+ Math.pow(triangle.p1.x - center.x, 2) + Math.pow(triangle.p1.y - center.y, 2)
1864
+ );
1865
+ const distance = Math.sqrt(
1866
+ Math.pow(point.x - center.x, 2) + Math.pow(point.y - center.y, 2)
1867
+ );
1868
+ return distance < radius;
1869
+ }
1870
+ };
1871
+
1872
+ // src/plugins/Catmull.tsx
1873
+ var CatmullRom = class {
1874
+ /**
1875
+ * Interpolate points using Catmull-Rom spline
1876
+ */
1877
+ static interpolate(points, tension = 0.5, segments = 20) {
1878
+ if (points.length < 2) return points;
1879
+ const result = [];
1880
+ const extendedPoints = [points[0], ...points, points[points.length - 1]];
1881
+ for (let i = 1; i < extendedPoints.length - 2; i++) {
1882
+ const p0 = extendedPoints[i - 1];
1883
+ const p1 = extendedPoints[i];
1884
+ const p2 = extendedPoints[i + 1];
1885
+ const p3 = extendedPoints[i + 2];
1886
+ for (let t = 0; t < 1; t += 1 / segments) {
1887
+ const t2 = t * t;
1888
+ const t3 = t2 * t;
1889
+ const v0 = tension * (p2.x - p0.x);
1890
+ const v1 = tension * (p3.x - p1.x);
1891
+ const x = p1.x + v0 * t + (3 * (p2.x - p1.x) - 2 * v0 - v1) * t2 + (2 * (p1.x - p2.x) + v0 + v1) * t3;
1892
+ const v0y = tension * (p2.y - p0.y);
1893
+ const v1y = tension * (p3.y - p1.y);
1894
+ const y = p1.y + v0y * t + (3 * (p2.y - p1.y) - 2 * v0y - v1y) * t2 + (2 * (p1.y - p2.y) + v0y + v1y) * t3;
1895
+ result.push({ x, y });
1896
+ }
1897
+ }
1898
+ result.push(points[points.length - 1]);
1899
+ return result;
1900
+ }
1901
+ /**
1902
+ * Draw interpolated curve to canvas
1903
+ */
1904
+ static draw(ctx, points, options) {
1905
+ const { tension = 0.5, segments = 20, closed = false } = options || {};
1906
+ const interpolated = this.interpolate(
1907
+ closed ? [...points, points[0]] : points,
1908
+ tension,
1909
+ segments
1910
+ );
1911
+ ctx.save();
1912
+ if (options?.strokeStyle) {
1913
+ ctx.strokeStyle = options.strokeStyle;
1914
+ }
1915
+ if (options?.lineWidth) {
1916
+ ctx.lineWidth = options.lineWidth;
1917
+ }
1918
+ ctx.beginPath();
1919
+ ctx.moveTo(interpolated[0].x, interpolated[0].y);
1920
+ for (let i = 1; i < interpolated.length; i++) {
1921
+ ctx.lineTo(interpolated[i].x, interpolated[i].y);
1922
+ }
1923
+ if (closed) {
1924
+ ctx.closePath();
1925
+ }
1926
+ ctx.stroke();
1927
+ ctx.restore();
1928
+ }
1929
+ /**
1930
+ * Get path as a Path2D object
1931
+ */
1932
+ static toPath2D(points, options) {
1933
+ const { tension = 0.5, segments = 20, closed = false } = options || {};
1934
+ const interpolated = this.interpolate(
1935
+ closed ? [...points, points[0]] : points,
1936
+ tension,
1937
+ segments
1938
+ );
1939
+ const path = new Path2D();
1940
+ path.moveTo(interpolated[0].x, interpolated[0].y);
1941
+ for (let i = 1; i < interpolated.length; i++) {
1942
+ path.lineTo(interpolated[i].x, interpolated[i].y);
1943
+ }
1944
+ if (closed) {
1945
+ path.closePath();
1946
+ }
1947
+ return path;
1948
+ }
1949
+ };
1950
+
1951
+ // src/plugins/Things.tsx
1952
+ var Things = class {
1953
+ /**
1954
+ * Configure the plugin
1955
+ */
1956
+ static configure(config) {
1957
+ this.config = { ...this.config, ...config };
1958
+ }
1959
+ /**
1960
+ * Create a new thing
1961
+ */
1962
+ static create(options = {}) {
1963
+ if (this.things.size >= (this.config.maxThings || 1e3)) {
1964
+ throw new Error(
1965
+ `Maximum number of things (${this.config.maxThings}) reached`
1966
+ );
1967
+ }
1968
+ const thing = {
1969
+ id: options.id || `thing_${this.idCounter++}`,
1970
+ x: options.x || 0,
1971
+ y: options.y || 0,
1972
+ width: options.width || this.config.defaultSize || 50,
1973
+ height: options.height || this.config.defaultSize || 50,
1974
+ rotation: options.rotation || 0,
1975
+ scale: options.scale || 1,
1976
+ color: options.color || this.config.defaultColor || "#ffffff",
1977
+ data: options.data || {}
1978
+ };
1979
+ this.things.set(thing.id, thing);
1980
+ return thing;
1981
+ }
1982
+ /**
1983
+ * Get a thing by ID
1984
+ */
1985
+ static get(id) {
1986
+ return this.things.get(id);
1987
+ }
1988
+ /**
1989
+ * Get all things
1990
+ */
1991
+ static getAll() {
1992
+ return Array.from(this.things.values());
1993
+ }
1994
+ /**
1995
+ * Update a thing's properties
1996
+ */
1997
+ static update(id, updates) {
1998
+ const thing = this.things.get(id);
1999
+ if (thing) {
2000
+ Object.assign(thing, updates);
2001
+ }
2002
+ }
2003
+ /**
2004
+ * Remove a thing
2005
+ */
2006
+ static remove(id) {
2007
+ return this.things.delete(id);
2008
+ }
2009
+ /**
2010
+ * Clear all things
2011
+ */
2012
+ static clear() {
2013
+ this.things.clear();
2014
+ this.idCounter = 0;
2015
+ }
2016
+ /**
2017
+ * Move a thing
2018
+ */
2019
+ static move(id, dx, dy) {
2020
+ const thing = this.things.get(id);
2021
+ if (thing) {
2022
+ thing.x += dx;
2023
+ thing.y += dy;
2024
+ }
2025
+ }
2026
+ /**
2027
+ * Rotate a thing
2028
+ */
2029
+ static rotate(id, angle) {
2030
+ const thing = this.things.get(id);
2031
+ if (thing) {
2032
+ thing.rotation += angle;
2033
+ }
2034
+ }
2035
+ /**
2036
+ * Scale a thing
2037
+ */
2038
+ static scale(id, factor) {
2039
+ const thing = this.things.get(id);
2040
+ if (thing) {
2041
+ thing.scale *= factor;
2042
+ }
2043
+ }
2044
+ /**
2045
+ * Find things within a radius
2046
+ */
2047
+ static findNear(x, y, radius) {
2048
+ const near = [];
2049
+ const radiusSq = radius * radius;
2050
+ this.things.forEach((thing) => {
2051
+ const dx = thing.x - x;
2052
+ const dy = thing.y - y;
2053
+ if (dx * dx + dy * dy <= radiusSq) {
2054
+ near.push(thing);
2055
+ }
2056
+ });
2057
+ return near;
2058
+ }
2059
+ /**
2060
+ * Find things that overlap with a rectangle
2061
+ */
2062
+ static findInRect(x, y, width, height) {
2063
+ const found = [];
2064
+ this.things.forEach((thing) => {
2065
+ const halfW = thing.width * thing.scale / 2;
2066
+ const halfH = thing.height * thing.scale / 2;
2067
+ if (thing.x + halfW >= x && thing.x - halfW <= x + width && thing.y + halfH >= y && thing.y - halfH <= y + height) {
2068
+ found.push(thing);
2069
+ }
2070
+ });
2071
+ return found;
2072
+ }
2073
+ /**
2074
+ * Apply a function to all things
2075
+ */
2076
+ static forEach(fn) {
2077
+ this.things.forEach(fn);
2078
+ }
2079
+ /**
2080
+ * Map things to a new array
2081
+ */
2082
+ static map(fn) {
2083
+ return Array.from(this.things.values()).map(fn);
2084
+ }
2085
+ /**
2086
+ * Filter things
2087
+ */
2088
+ static filter(fn) {
2089
+ return Array.from(this.things.values()).filter(fn);
2090
+ }
2091
+ /**
2092
+ * Sort things by a property or function
2093
+ */
2094
+ static sort(fn) {
2095
+ return Array.from(this.things.values()).sort(fn);
2096
+ }
2097
+ /**
2098
+ * Draw all things
2099
+ */
2100
+ static draw(ctx, options) {
2101
+ const thingsToDraw = options?.filter ? this.filter(options.filter) : this.getAll();
2102
+ thingsToDraw.forEach((thing) => {
2103
+ if (options?.customDraw) {
2104
+ options.customDraw(ctx, thing);
2105
+ } else {
2106
+ this.drawThing(ctx, thing);
2107
+ }
2108
+ });
2109
+ }
2110
+ /**
2111
+ * Default drawing method for a thing
2112
+ */
2113
+ static drawThing(ctx, thing) {
2114
+ ctx.save();
2115
+ ctx.translate(thing.x, thing.y);
2116
+ ctx.rotate(thing.rotation);
2117
+ ctx.scale(thing.scale, thing.scale);
2118
+ ctx.fillStyle = thing.color;
2119
+ ctx.fillRect(
2120
+ -thing.width / 2,
2121
+ -thing.height / 2,
2122
+ thing.width,
2123
+ thing.height
2124
+ );
2125
+ ctx.restore();
2126
+ }
2127
+ /**
2128
+ * Animate things with a simple physics update
2129
+ */
2130
+ static animatePhysics(deltaTime, options) {
2131
+ const gravity = options?.gravity || 0;
2132
+ const friction = options?.friction || 0.99;
2133
+ const bounds = options?.bounds;
2134
+ this.things.forEach((thing) => {
2135
+ if (!thing.data.vx) thing.data.vx = 0;
2136
+ if (!thing.data.vy) thing.data.vy = 0;
2137
+ thing.data.vy += gravity;
2138
+ thing.data.vx *= friction;
2139
+ thing.data.vy *= friction;
2140
+ thing.x += thing.data.vx;
2141
+ thing.y += thing.data.vy;
2142
+ if (bounds) {
2143
+ const halfW = thing.width * thing.scale / 2;
2144
+ const halfH = thing.height * thing.scale / 2;
2145
+ if (thing.x - halfW < bounds.x) {
2146
+ thing.x = bounds.x + halfW;
2147
+ thing.data.vx *= -0.8;
2148
+ }
2149
+ if (thing.x + halfW > bounds.x + bounds.width) {
2150
+ thing.x = bounds.x + bounds.width - halfW;
2151
+ thing.data.vx *= -0.8;
2152
+ }
2153
+ if (thing.y - halfH < bounds.y) {
2154
+ thing.y = bounds.y + halfH;
2155
+ thing.data.vy *= -0.8;
2156
+ }
2157
+ if (thing.y + halfH > bounds.y + bounds.height) {
2158
+ thing.y = bounds.y + bounds.height - halfH;
2159
+ thing.data.vy *= -0.8;
2160
+ }
2161
+ }
2162
+ });
2163
+ }
2164
+ /**
2165
+ * Get the count of things
2166
+ */
2167
+ static count() {
2168
+ return this.things.size;
2169
+ }
2170
+ /**
2171
+ * Check if a thing exists
2172
+ */
2173
+ static has(id) {
2174
+ return this.things.has(id);
2175
+ }
2176
+ /**
2177
+ * Utility: Get distance between two things
2178
+ */
2179
+ static distance(id1, id2) {
2180
+ const thing1 = this.things.get(id1);
2181
+ const thing2 = this.things.get(id2);
2182
+ if (!thing1 || !thing2) return Infinity;
2183
+ const dx = thing2.x - thing1.x;
2184
+ const dy = thing2.y - thing1.y;
2185
+ return Math.sqrt(dx * dx + dy * dy);
2186
+ }
2187
+ /**
2188
+ * Utility: Check collision between two things
2189
+ */
2190
+ static collides(id1, id2) {
2191
+ const thing1 = this.things.get(id1);
2192
+ const thing2 = this.things.get(id2);
2193
+ if (!thing1 || !thing2) return false;
2194
+ const halfW1 = thing1.width * thing1.scale / 2;
2195
+ const halfH1 = thing1.height * thing1.scale / 2;
2196
+ const halfW2 = thing2.width * thing2.scale / 2;
2197
+ const halfH2 = thing2.height * thing2.scale / 2;
2198
+ return Math.abs(thing1.x - thing2.x) < halfW1 + halfW2 && Math.abs(thing1.y - thing2.y) < halfH1 + halfH2;
2199
+ }
2200
+ };
2201
+ Things.things = /* @__PURE__ */ new Map();
2202
+ Things.config = {
2203
+ maxThings: 1e3,
2204
+ defaultSize: 50,
2205
+ defaultColor: "#ffffff"
2206
+ };
2207
+ Things.idCounter = 0;
2208
+
2209
+ // src/plugins/Sprites.tsx
2210
+ var Sprites = class {
2211
+ /**
2212
+ * Load one or more spritesheets
2213
+ * @param configs - Sprite configuration(s) to load
2214
+ * @returns Promise that resolves when all sprites are loaded
2215
+ */
2216
+ static async load(...configs) {
2217
+ const promises = configs.map((config) => this.loadSingle(config));
2218
+ await Promise.all(promises);
2219
+ }
2220
+ /**
2221
+ * Load a single spritesheet
2222
+ * @param config - Sprite configuration
2223
+ * @returns Promise that resolves when sprite is loaded
2224
+ */
2225
+ static async loadSingle(config) {
2226
+ const { name, url, spriteWidth, spriteHeight, gap = 0 } = config;
2227
+ if (this.loadingPromises.has(name)) {
2228
+ return this.loadingPromises.get(name);
2229
+ }
2230
+ if (this.spritesheets.has(name)) {
2231
+ return Promise.resolve();
2232
+ }
2233
+ const loadPromise = new Promise((resolve, reject) => {
2234
+ const img = new Image();
2235
+ img.onload = () => {
2236
+ const cols = Math.floor((img.width + gap) / (spriteWidth + gap));
2237
+ const rows = Math.floor((img.height + gap) / (spriteHeight + gap));
2238
+ const numSprites = cols * rows;
2239
+ const spritesheet = {
2240
+ image: img,
2241
+ srcNaturalWidth: img.width,
2242
+ srcNaturalHeight: img.height,
2243
+ numSprites,
2244
+ spriteWidth,
2245
+ spriteHeight,
2246
+ gap,
2247
+ cols,
2248
+ rows
2249
+ };
2250
+ this.spritesheets.set(name, spritesheet);
2251
+ this.loadingPromises.delete(name);
2252
+ resolve();
2253
+ };
2254
+ img.onerror = () => {
2255
+ this.loadingPromises.delete(name);
2256
+ reject(new Error(`Failed to load sprite: ${url}`));
2257
+ };
2258
+ img.crossOrigin = "anonymous";
2259
+ img.src = url;
2260
+ });
2261
+ this.loadingPromises.set(name, loadPromise);
2262
+ return loadPromise;
2263
+ }
2264
+ /**
2265
+ * Get spritesheet information
2266
+ * @param name - Name of the spritesheet
2267
+ * @returns Spritesheet data or undefined if not loaded
2268
+ */
2269
+ static sheet(name) {
2270
+ return this.spritesheets.get(name);
2271
+ }
2272
+ /**
2273
+ * Draw a sprite from a spritesheet
2274
+ * @param ctx - Klint context
2275
+ * @param sheetName - Name of the spritesheet
2276
+ * @param sprite - Sprite index to draw
2277
+ * @param x - X position to draw at
2278
+ * @param y - Y position to draw at
2279
+ * @param options - Drawing options
2280
+ */
2281
+ static draw(ctx, sheetName, sprite, x, y, options) {
2282
+ const sheet = this.spritesheets.get(sheetName);
2283
+ if (!sheet) {
2284
+ console.warn(`Spritesheet '${sheetName}' not loaded`);
2285
+ return;
2286
+ }
2287
+ const spriteIndex = Math.floor(sprite) % sheet.numSprites;
2288
+ const col = spriteIndex % sheet.cols;
2289
+ const row = Math.floor(spriteIndex / sheet.cols);
2290
+ const sx = col * (sheet.spriteWidth + sheet.gap);
2291
+ const sy = row * (sheet.spriteHeight + sheet.gap);
2292
+ const drawWidth = options?.width || sheet.spriteWidth;
2293
+ const drawHeight = options?.height || sheet.spriteHeight;
2294
+ const scale = options?.scale || 1;
2295
+ const rotation = options?.rotation || 0;
2296
+ const flipX = options?.flipX || false;
2297
+ const flipY = options?.flipY || false;
2298
+ const alpha = options?.alpha !== void 0 ? options.alpha : 1;
2299
+ ctx.save();
2300
+ ctx.translate(x, y);
2301
+ if (rotation !== 0) {
2302
+ ctx.rotate(rotation);
2303
+ }
2304
+ if (flipX || flipY) {
2305
+ ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
2306
+ }
2307
+ if (scale !== 1) {
2308
+ ctx.scale(scale, scale);
2309
+ }
2310
+ if (alpha !== 1) {
2311
+ ctx.globalAlpha = alpha;
2312
+ }
2313
+ ctx.drawImage(
2314
+ sheet.image,
2315
+ sx,
2316
+ sy,
2317
+ sheet.spriteWidth,
2318
+ sheet.spriteHeight,
2319
+ -drawWidth / 2,
2320
+ -drawHeight / 2,
2321
+ drawWidth,
2322
+ drawHeight
2323
+ );
2324
+ ctx.restore();
2325
+ }
2326
+ /**
2327
+ * Draw a sprite at its actual position without centering
2328
+ * @param ctx - Klint context
2329
+ * @param sheetName - Name of the spritesheet
2330
+ * @param sprite - Sprite index to draw
2331
+ * @param x - X position to draw at
2332
+ * @param y - Y position to draw at
2333
+ * @param width - Width to draw (optional)
2334
+ * @param height - Height to draw (optional)
2335
+ */
2336
+ static drawCorner(ctx, sheetName, sprite, x, y, width, height) {
2337
+ const sheet = this.spritesheets.get(sheetName);
2338
+ if (!sheet) {
2339
+ console.warn(`Spritesheet '${sheetName}' not loaded`);
2340
+ return;
2341
+ }
2342
+ const spriteIndex = Math.floor(sprite) % sheet.numSprites;
2343
+ const col = spriteIndex % sheet.cols;
2344
+ const row = Math.floor(spriteIndex / sheet.cols);
2345
+ const sx = col * (sheet.spriteWidth + sheet.gap);
2346
+ const sy = row * (sheet.spriteHeight + sheet.gap);
2347
+ const drawWidth = width || sheet.spriteWidth;
2348
+ const drawHeight = height || sheet.spriteHeight;
2349
+ ctx.drawImage(
2350
+ sheet.image,
2351
+ sx,
2352
+ sy,
2353
+ sheet.spriteWidth,
2354
+ sheet.spriteHeight,
2355
+ x,
2356
+ y,
2357
+ drawWidth,
2358
+ drawHeight
2359
+ );
2360
+ }
2361
+ /**
2362
+ * Create an animation from a range of sprites
2363
+ * @param sheetName - Name of the spritesheet
2364
+ * @param startSprite - Starting sprite index
2365
+ * @param endSprite - Ending sprite index
2366
+ * @param frameDuration - Duration of each frame in milliseconds
2367
+ * @returns Animation object
2368
+ */
2369
+ static animation(sheetName, startSprite, endSprite, frameDuration = 100) {
2370
+ return new SpriteAnimation(
2371
+ sheetName,
2372
+ startSprite,
2373
+ endSprite,
2374
+ frameDuration
2375
+ );
2376
+ }
2377
+ /**
2378
+ * Check if a spritesheet is loaded
2379
+ * @param name - Name of the spritesheet
2380
+ * @returns True if loaded, false otherwise
2381
+ */
2382
+ static hasSheet(name) {
2383
+ return this.spritesheets.has(name);
2384
+ }
2385
+ /**
2386
+ * Unload a spritesheet
2387
+ * @param name - Name of the spritesheet to unload
2388
+ */
2389
+ static unload(name) {
2390
+ this.spritesheets.delete(name);
2391
+ this.loadingPromises.delete(name);
2392
+ }
2393
+ /**
2394
+ * Unload all spritesheets
2395
+ */
2396
+ static clear() {
2397
+ this.spritesheets.clear();
2398
+ this.loadingPromises.clear();
2399
+ }
2400
+ /**
2401
+ * Get the number of loaded spritesheets
2402
+ * @returns Number of loaded spritesheets
2403
+ */
2404
+ static count() {
2405
+ return this.spritesheets.size;
2406
+ }
2407
+ /**
2408
+ * Get all loaded spritesheet names
2409
+ * @returns Array of spritesheet names
2410
+ */
2411
+ static getSheetNames() {
2412
+ return Array.from(this.spritesheets.keys());
2413
+ }
2414
+ };
2415
+ Sprites.spritesheets = /* @__PURE__ */ new Map();
2416
+ Sprites.loadingPromises = /* @__PURE__ */ new Map();
2417
+ var SpriteAnimation = class {
2418
+ constructor(sheetName, startSprite, endSprite, frameDuration) {
2419
+ this.currentFrame = 0;
2420
+ this.lastFrameTime = 0;
2421
+ this.playing = true;
2422
+ this.loop = true;
2423
+ this.sheetName = sheetName;
2424
+ this.startSprite = startSprite;
2425
+ this.endSprite = endSprite;
2426
+ this.frameDuration = frameDuration;
2427
+ this.currentFrame = startSprite;
2428
+ }
2429
+ /**
2430
+ * Update animation frame
2431
+ * @param deltaTime - Time since last update in milliseconds
2432
+ */
2433
+ update(deltaTime) {
2434
+ if (!this.playing) return;
2435
+ this.lastFrameTime += deltaTime;
2436
+ if (this.lastFrameTime >= this.frameDuration) {
2437
+ this.lastFrameTime = 0;
2438
+ this.currentFrame++;
2439
+ if (this.currentFrame > this.endSprite) {
2440
+ if (this.loop) {
2441
+ this.currentFrame = this.startSprite;
2442
+ } else {
2443
+ this.currentFrame = this.endSprite;
2444
+ this.playing = false;
2445
+ }
2446
+ }
2447
+ }
2448
+ }
2449
+ /**
2450
+ * Draw the current animation frame
2451
+ * @param ctx - Klint context
2452
+ * @param x - X position
2453
+ * @param y - Y position
2454
+ * @param options - Drawing options
2455
+ */
2456
+ draw(ctx, x, y, options) {
2457
+ Sprites.draw(ctx, this.sheetName, this.currentFrame, x, y, options);
2458
+ }
2459
+ /**
2460
+ * Play the animation
2461
+ */
2462
+ play() {
2463
+ this.playing = true;
2464
+ }
2465
+ /**
2466
+ * Pause the animation
2467
+ */
2468
+ pause() {
2469
+ this.playing = false;
2470
+ }
2471
+ /**
2472
+ * Stop the animation and reset to start
2473
+ */
2474
+ stop() {
2475
+ this.playing = false;
2476
+ this.currentFrame = this.startSprite;
2477
+ this.lastFrameTime = 0;
2478
+ }
2479
+ /**
2480
+ * Set whether the animation should loop
2481
+ * @param loop - True to loop, false to play once
2482
+ */
2483
+ setLoop(loop) {
2484
+ this.loop = loop;
2485
+ }
2486
+ /**
2487
+ * Get the current frame
2488
+ * @returns Current frame index
2489
+ */
2490
+ getCurrentFrame() {
2491
+ return this.currentFrame;
2492
+ }
2493
+ /**
2494
+ * Set the current frame
2495
+ * @param frame - Frame index to set
2496
+ */
2497
+ setFrame(frame) {
2498
+ this.currentFrame = Math.max(
2499
+ this.startSprite,
2500
+ Math.min(this.endSprite, frame)
2501
+ );
2502
+ }
2503
+ /**
2504
+ * Check if animation is playing
2505
+ * @returns True if playing, false otherwise
2506
+ */
2507
+ isPlaying() {
2508
+ return this.playing;
2509
+ }
2510
+ };
2511
+ // Annotate the CommonJS export names for ESM import in node:
2512
+ 0 && (module.exports = {
2513
+ CatmullRom,
2514
+ Delaunay,
2515
+ FontParser,
2516
+ Sprites,
2517
+ Things
2518
+ });