@vectojs/core 0.2.4 → 0.2.6

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.
@@ -1,8 +1,10 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2; var _class3; var _class4; var _class5; var _class6; var _class7;
2
2
 
3
- var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
3
+ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
4
4
 
5
5
  // src/math/SpringPhysics.ts
6
+ var MAX_FRAME_DT = 0.25;
7
+ var MAX_STEP_DT = 1 / 120;
6
8
  var SpringPhysics = (_class = class {
7
9
 
8
10
 
@@ -22,11 +24,22 @@ var SpringPhysics = (_class = class {
22
24
  this.velocity = 0;
23
25
  return;
24
26
  }
25
- const forceSpring = -this.stiffness * (this.value - this.target);
26
- const forceDamping = -this.damping * this.velocity;
27
- const acceleration = (forceSpring + forceDamping) / this.mass;
28
- this.velocity += acceleration * dt;
29
- this.value += this.velocity * dt;
27
+ if (!(dt > 0)) return;
28
+ let remaining = dt < MAX_FRAME_DT ? dt : MAX_FRAME_DT;
29
+ while (remaining > 0) {
30
+ const step = remaining < MAX_STEP_DT ? remaining : MAX_STEP_DT;
31
+ const forceSpring = -this.stiffness * (this.value - this.target);
32
+ const forceDamping = -this.damping * this.velocity;
33
+ const acceleration = (forceSpring + forceDamping) / this.mass;
34
+ this.velocity += acceleration * step;
35
+ this.value += this.velocity * step;
36
+ remaining -= step;
37
+ if (this.isAtRest()) {
38
+ this.value = this.target;
39
+ this.velocity = 0;
40
+ return;
41
+ }
42
+ }
30
43
  }
31
44
  isAtRest() {
32
45
  return Math.abs(this.value - this.target) < this.valEpsilon && Math.abs(this.velocity) < this.velEpsilon;
@@ -117,6 +130,14 @@ var SpringDriver = class {
117
130
  };
118
131
 
119
132
  // src/tree/Entity.ts
133
+ var ANIMATABLE_PROPS = /* @__PURE__ */ new Set([
134
+ "x",
135
+ "y",
136
+ "scaleX",
137
+ "scaleY",
138
+ "rotation",
139
+ "opacity"
140
+ ]);
120
141
  var VectoJSEvent = (_class3 = class {
121
142
  /** The event name. */
122
143
 
@@ -568,7 +589,12 @@ var Entity = (_class4 = class {
568
589
  const end = anim.target[key];
569
590
  if (typeof start === "number" && typeof end === "number") {
570
591
  const easeOut = progress * (2 - progress);
571
- this[key] = start + (end - start) * easeOut;
592
+ const value = start + (end - start) * easeOut;
593
+ if (ANIMATABLE_PROPS.has(key)) {
594
+ this._applyAnimated(key, value);
595
+ } else {
596
+ this[key] = value;
597
+ }
572
598
  }
573
599
  }
574
600
  if (progress >= 1) {
@@ -619,6 +645,10 @@ var Entity = (_class4 = class {
619
645
  */
620
646
  destroy() {
621
647
  this.animations = [];
648
+ for (const driver of this._drivers.values()) {
649
+ this._settleDriver(driver);
650
+ }
651
+ this._drivers.clear();
622
652
  this.listeners.clear();
623
653
  this.captureListeners.clear();
624
654
  if (this.parent) {
@@ -989,6 +1019,8 @@ var MSDFTextEntity = (_class6 = class extends Entity {
989
1019
 
990
1020
 
991
1021
 
1022
+
1023
+
992
1024
  __init35() {this.text = ""}
993
1025
  __init36() {this.lastRenderedSeqId = 0}
994
1026
  __init37() {this.rgbColorCache = /* @__PURE__ */ new Map()}
@@ -1003,17 +1035,27 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1003
1035
  this.color = _nullishCoalesce(options.color, () => ( "#ffffff"));
1004
1036
  this.letterSpacing = _nullishCoalesce(options.letterSpacing, () => ( 0));
1005
1037
  this.lineHeight = options.lineHeight;
1038
+ this.maxWidth = _nullishCoalesce(options.maxWidth, () => ( 1e3));
1039
+ this.maxHeight = _nullishCoalesce(options.maxHeight, () => ( 1e3));
1006
1040
  this.setText(text);
1007
1041
  }
1042
+ /** Change the wrap boundary and re-run layout for the current text. */
1043
+ setMaxWidth(maxWidth) {
1044
+ if (this.maxWidth === maxWidth) return;
1045
+ this.maxWidth = maxWidth;
1046
+ this.queueLayout();
1047
+ }
1008
1048
  setText(text) {
1009
1049
  if (this.text === text && this.layoutResult) return;
1010
1050
  this.text = text;
1011
- _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1051
+ this.queueLayout();
1052
+ }
1053
+ queueLayout() {
1054
+ _chunkCTZQOM5Zjs.LayoutWorkerManager.getInstance().queueLayout(this.id, this.text, {
1012
1055
  fontId: this.font.id,
1013
1056
  fontSize: this.fontSize,
1014
- maxWidth: 1e3,
1015
- // standard wrap boundary
1016
- maxHeight: 1e3,
1057
+ maxWidth: this.maxWidth,
1058
+ maxHeight: this.maxHeight,
1017
1059
  fontData: this.font.data,
1018
1060
  letterSpacing: this.letterSpacing,
1019
1061
  lineHeight: this.lineHeight,
@@ -1042,6 +1084,8 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1042
1084
  if (scene && scene.pointRenderer && scene.glCanvas && canUsePointGlyphs) {
1043
1085
  scene.pointRenderer.setMSDFTexture(this.texture, this.font.distanceRange);
1044
1086
  const worldRot = Math.atan2(world.b, world.a);
1087
+ let worldOpacity = this.opacity;
1088
+ for (let p = this.parent; p; p = p.parent) worldOpacity *= p.opacity;
1045
1089
  const len2 = this.layoutResult.codePoints.length;
1046
1090
  for (let i = 0; i < len2; i++) {
1047
1091
  const code = this.layoutResult.codePoints[i];
@@ -1080,7 +1124,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1080
1124
  ab.right / aw,
1081
1125
  v1,
1082
1126
  runColor,
1083
- this.opacity,
1127
+ worldOpacity,
1084
1128
  worldRot
1085
1129
  );
1086
1130
  }
@@ -1112,7 +1156,7 @@ var MSDFTextEntity = (_class6 = class extends Entity {
1112
1156
  }
1113
1157
  }
1114
1158
  destroy() {
1115
- _chunk76NMTLYLjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1159
+ _chunkCTZQOM5Zjs.LayoutWorkerManager.getInstance().cancelLayout(this.id);
1116
1160
  super.destroy();
1117
1161
  }
1118
1162
  }, _class6);
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class; var _class2;
2
2
 
3
3
 
4
- var _chunk76NMTLYLjs = require('./chunk-76NMTLYL.js');
4
+ var _chunkCTZQOM5Zjs = require('./chunk-CTZQOM5Z.js');
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -79,6 +79,12 @@ var LayoutEngine = (_class = class {
79
79
  if (this.measurer) return this.measurer.measure(char, fontSize);
80
80
  return fontSize * 0.5;
81
81
  }
82
+ glyphKeyFor(grapheme, fontAtlas) {
83
+ if (fontAtlas[grapheme]) return grapheme;
84
+ const firstCodePoint = Array.from(grapheme)[0];
85
+ if (firstCodePoint && fontAtlas[firstCodePoint]) return firstCodePoint;
86
+ return grapheme;
87
+ }
82
88
  getGraphemes(word) {
83
89
  const cached = this.graphemeCache.get(word);
84
90
  if (cached) return cached;
@@ -142,8 +148,8 @@ var LayoutEngine = (_class = class {
142
148
  offset += paragraph.length + 1;
143
149
  continue;
144
150
  }
145
- const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
146
- const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
151
+ const { shapedText, indexMap } = _chunkCTZQOM5Zjs.ArabicShaper.shapeArabic(paragraph);
152
+ const levels = _chunkCTZQOM5Zjs.BidiResolver.resolveLevels(shapedText);
147
153
  const words = [];
148
154
  let shapedCharIdx = 0;
149
155
  let pFallback = false;
@@ -158,25 +164,20 @@ var LayoutEngine = (_class = class {
158
164
  const rawEnd = visualEnd === shapedText.length ? paragraph.length : indexMap[visualEnd];
159
165
  const sourceIndex = offset + rawStart;
160
166
  const sourceLength = rawEnd - rawStart;
161
- const baseChar = char[0];
167
+ const glyphKey = this.glyphKeyFor(char, fontAtlas);
162
168
  const level = levels[visualStart];
163
- const hasGlyph = !!fontAtlas[char] || !!fontAtlas[baseChar];
169
+ const hasGlyph = !!fontAtlas[glyphKey];
164
170
  if (char.trim().length > 0 && !hasGlyph) {
165
171
  pFallback = true;
166
172
  fallbackToCanvas = true;
167
173
  }
168
- const w = this.glyphWidth(baseChar, fontAtlas, fontSize);
169
- const combining = [];
170
- for (let cIdx = 1; cIdx < char.length; cIdx++) {
171
- combining.push(char[cIdx]);
172
- }
174
+ const w = this.glyphWidth(glyphKey, fontAtlas, fontSize);
173
175
  glyphs.push({
174
- char: baseChar,
176
+ char,
175
177
  width: w,
176
178
  level,
177
179
  sourceIndex,
178
- sourceLength,
179
- combining: combining.length > 0 ? combining : void 0
180
+ sourceLength
180
181
  });
181
182
  width += w;
182
183
  shapedCharIdx += char.length;
@@ -192,7 +193,7 @@ var LayoutEngine = (_class = class {
192
193
  words,
193
194
  isEmpty: false,
194
195
  fallbackToCanvas: pFallback || void 0,
195
- baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
196
+ baseLevel: _chunkCTZQOM5Zjs.BidiResolver.getBaseLevel(shapedText)
196
197
  };
197
198
  if (this.paragraphCache.size > 1e3) this.paragraphCache.clear();
198
199
  this.paragraphCache.set(key, prepared);
@@ -264,8 +265,8 @@ var LayoutEngine = (_class = class {
264
265
  offset += paragraph.length + 1;
265
266
  continue;
266
267
  }
267
- const { shapedText, indexMap } = _chunk76NMTLYLjs.ArabicShaper.shapeArabic(paragraph);
268
- const levels = _chunk76NMTLYLjs.BidiResolver.resolveLevels(shapedText);
268
+ const { shapedText, indexMap } = _chunkCTZQOM5Zjs.ArabicShaper.shapeArabic(paragraph);
269
+ const levels = _chunkCTZQOM5Zjs.BidiResolver.resolveLevels(shapedText);
269
270
  const words = [];
270
271
  let shapedCharIdx = 0;
271
272
  let pFallback = false;
@@ -280,28 +281,23 @@ var LayoutEngine = (_class = class {
280
281
  const rawEnd = visualEnd === shapedText.length ? paragraph.length : indexMap[visualEnd];
281
282
  const sourceIndex = offset + rawStart;
282
283
  const sourceLength = rawEnd - rawStart;
283
- const baseChar = char[0];
284
+ const glyphKey = this.glyphKeyFor(char, fontAtlas);
284
285
  const level = levels[visualStart];
285
286
  const style = styleAt[offset + rawStart];
286
287
  const gfs = _nullishCoalesce(_optionalChain([style, 'optionalAccess', _ => _.fontSize]), () => ( baseFontSize));
287
- const hasGlyph = !!fontAtlas[char] || !!fontAtlas[baseChar];
288
+ const hasGlyph = !!fontAtlas[glyphKey];
288
289
  if (char.trim().length > 0 && !hasGlyph) {
289
290
  pFallback = true;
290
291
  fallbackToCanvas = true;
291
292
  }
292
- const w = this.glyphWidth(baseChar, fontAtlas, gfs);
293
- const combining = [];
294
- for (let cIdx = 1; cIdx < char.length; cIdx++) {
295
- combining.push(char[cIdx]);
296
- }
293
+ const w = this.glyphWidth(glyphKey, fontAtlas, gfs);
297
294
  glyphs.push({
298
- char: baseChar,
295
+ char,
299
296
  width: w,
300
297
  style,
301
298
  level,
302
299
  sourceIndex,
303
- sourceLength,
304
- combining: combining.length > 0 ? combining : void 0
300
+ sourceLength
305
301
  });
306
302
  width += w;
307
303
  shapedCharIdx += char.length;
@@ -317,7 +313,7 @@ var LayoutEngine = (_class = class {
317
313
  words,
318
314
  isEmpty: false,
319
315
  fallbackToCanvas: pFallback || void 0,
320
- baseLevel: _chunk76NMTLYLjs.BidiResolver.getBaseLevel(shapedText)
316
+ baseLevel: _chunkCTZQOM5Zjs.BidiResolver.getBaseLevel(shapedText)
321
317
  };
322
318
  if (this.richParagraphCache.size > 1e3) this.richParagraphCache.clear();
323
319
  this.richParagraphCache.set(key, prepared);
@@ -372,7 +368,7 @@ var LayoutEngine = (_class = class {
372
368
  }
373
369
  for (const run of runs) {
374
370
  const runStartX = run[0].x;
375
- _chunk76NMTLYLjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
371
+ _chunkCTZQOM5Zjs.BidiResolver.reorderVisual(run, paragraphBaseLevel);
376
372
  let x = runStartX;
377
373
  for (const node of run) {
378
374
  node.x = x;
@@ -296,7 +296,7 @@ var BidiResolver = class _BidiResolver {
296
296
  };
297
297
 
298
298
  // src/layout/LayoutWorkerSource.ts
299
- var WORKER_SOURCE_STRING = '"use strict";(()=>{var k=new Map;function n(t){return typeof t=="number"&&Number.isFinite(t)}function H(t){return t.origin?t.origin===self.location.origin:!0}function M(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&n(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&n(e.maxWidth)&&n(e.maxHeight)&&n(e.fontSize)&&(e.lineHeight===void 0||n(e.lineHeight))&&(e.letterSpacing===void 0||n(e.letterSpacing))}self.onmessage=t=>{if(!H(t)||!M(t.data))return;let{id:e,seqId:F,text:q,fontId:d,fontData:f,maxWidth:y,maxHeight:I,fontSize:i,lineHeight:A,letterSpacing:D}=t.data;f&&k.set(d,f);let s=k.get(d);if(!s)return;let g=[],l=[],p=[],m=[],o=0,a=0,h=s.metrics?.ascender??.8,W=s.metrics?.descender??-.2,b=A??i*(h-W),x=Array.from(q);for(let c=0;c<x.length;c++){let u=x[c].codePointAt(0),S=(s.glyphs?.find(C=>C.unicode===u)?.advance??1)*i;o+S>y&&u===32&&(o=0,a++),g.push(u),l.push(o);let w=a*b+h*i;p.push(w),m.push(-256),o+=S+(D??0)}let r={id:e,seqId:F,width:Math.min(o,y),height:(a+1)*b,codePoints:new Uint32Array(g),xCoords:new Float32Array(l),yCoords:new Float32Array(p),packedStyles:new Uint32Array(m)};self.postMessage(r,[r.codePoints.buffer,r.xCoords.buffer,r.yCoords.buffer,r.packedStyles.buffer])};})();\n';
299
+ var WORKER_SOURCE_STRING = '"use strict";(()=>{var C=new Map;function f(t){return typeof t=="number"&&Number.isFinite(t)}function U(t){return t.origin?t.origin===self.location.origin:!0}function j(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.id=="string"&&f(e.seqId)&&typeof e.text=="string"&&typeof e.fontId=="string"&&(e.fontData===void 0||typeof e.fontData=="object")&&f(e.maxWidth)&&f(e.maxHeight)&&f(e.fontSize)&&(e.lineHeight===void 0||f(e.lineHeight))&&(e.letterSpacing===void 0||f(e.letterSpacing))}self.onmessage=t=>{if(!U(t)||!j(t.data))return;let{id:e,seqId:D,text:L,fontId:k,fontData:S,maxWidth:H,maxHeight:z,fontSize:d,lineHeight:I,letterSpacing:M}=t.data;S&&C.set(k,S);let g=C.get(k);if(!g)return;let m=[],i=[],h=[],w=[],n=0,c=0,r=0,o=-1,p=g.metrics?.ascender??.8,R=g.metrics?.descender??-.2,b=I??d*(p-R),P=M??0,F=new Map;for(let s of g.glyphs??[])F.set(s.unicode,s.advance);let x=()=>{n>r&&(r=n),n=0,c++,o=-1},A=Array.from(L);for(let s=0;s<A.length;s++){let a=A[s].codePointAt(0);if(a===10){x();continue}let W=(F.get(a)??1)*d,q=a>=11904;if(q&&(o=-1),n+W>H&&n>0){if(a===32){x();continue}if(o>=0&&i[o]>0){let l=i[o];l>r&&(r=l),c++;let v=c*b+p*d;for(let y=o;y<i.length;y++)i[y]-=l,h[y]=v;n-=l}else x()}a===32?o=-1:o===-1&&!q&&(o=m.length),m.push(a),i.push(n),h.push(c*b+p*d),w.push(-256),n+=W+P}n>r&&(r=n);let u={id:e,seqId:D,width:r,height:(c+1)*b,codePoints:new Uint32Array(m),xCoords:new Float32Array(i),yCoords:new Float32Array(h),packedStyles:new Uint32Array(w)};self.postMessage(u,[u.codePoints.buffer,u.xCoords.buffer,u.yCoords.buffer,u.packedStyles.buffer])};})();\n';
300
300
 
301
301
  // src/layout/LayoutWorkerManager.ts
302
302
  var LayoutWorkerManager = (_class2 = class _LayoutWorkerManager {
@@ -20,10 +20,18 @@ var CanvasRenderer = (_class = class _CanvasRenderer {
20
20
  __init2() {this.batchColor = ""}
21
21
  __init3() {this.batchAlpha = 1}
22
22
  __init4() {this.batchCount = 0}
23
- constructor(canvas) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
23
+ /**
24
+ * @param canvas - The target canvas. Its backing store is resized to the
25
+ * logical size × devicePixelRatio.
26
+ * @param size - Explicit logical size. Without it the renderer assumes a
27
+ * fullscreen canvas and sizes to the window — pass this for embedded /
28
+ * custom-container canvases (the Scene does when `disableWindowResize` is
29
+ * set) so the canvas's own dimensions aren't clobbered by the window's.
30
+ */
31
+ constructor(canvas, size) {;_class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);
24
32
  const dpr = getDevicePixelRatio();
25
- this.width = typeof window !== "undefined" ? window.innerWidth : canvas.width || 0;
26
- this.height = typeof window !== "undefined" ? window.innerHeight : canvas.height || 0;
33
+ this.width = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _ => _.width]), () => ( (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0)));
34
+ this.height = _nullishCoalesce(_optionalChain([size, 'optionalAccess', _2 => _2.height]), () => ( (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0)));
27
35
  canvas.width = this.width * dpr;
28
36
  canvas.height = this.height * dpr;
29
37
  const ctx = canvas.getContext("2d");
@@ -414,18 +422,19 @@ var SVGRenderer = (_class2 = class {
414
422
  } else {
415
423
  this.currentPath.push(`L ${xs} ${ys}`);
416
424
  }
417
- const deltaAngle = Math.abs(endAngle - startAngle);
418
- if (deltaAngle >= Math.PI * 2 - 1e-4) {
425
+ const TWO_PI2 = Math.PI * 2;
426
+ const directedDelta = ccw ? startAngle - endAngle : endAngle - startAngle;
427
+ const sweepAngle = directedDelta >= TWO_PI2 ? TWO_PI2 : (directedDelta % TWO_PI2 + TWO_PI2) % TWO_PI2;
428
+ const sweep = ccw ? 0 : 1;
429
+ if (sweepAngle >= TWO_PI2 - 1e-4) {
419
430
  const xm = x - r * Math.cos(startAngle);
420
431
  const ym = y - r * Math.sin(startAngle);
421
- const sweep = ccw ? 0 : 1;
422
432
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xm} ${ym}`);
423
433
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xs} ${ys}`);
424
434
  } else {
425
435
  const xe = x + r * Math.cos(endAngle);
426
436
  const ye = y + r * Math.sin(endAngle);
427
- const largeArc = deltaAngle > Math.PI ? 1 : 0;
428
- const sweep = ccw ? 0 : 1;
437
+ const largeArc = sweepAngle > Math.PI ? 1 : 0;
429
438
  this.currentPath.push(`A ${r} ${r} 0 ${largeArc} ${sweep} ${xe} ${ye}`);
430
439
  }
431
440
  }
@@ -537,8 +546,8 @@ var SVGRenderer = (_class2 = class {
537
546
  }
538
547
  drawImage(source, dx, dy, dw, dh) {
539
548
  this.flush();
540
- const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _ => _.toDataURL]) === "function";
541
- const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _2 => _2.src]) || "";
549
+ const fromCanvas2 = typeof _optionalChain([source, 'optionalAccess', _3 => _3.toDataURL]) === "function";
550
+ const rawHref = fromCanvas2 ? source.toDataURL() : _optionalChain([source, 'optionalAccess', _4 => _4.src]) || "";
542
551
  const href = fromCanvas2 && this.isSafeRasterDataUrl(rawHref) ? rawHref : sanitizeUrl(String(rawHref));
543
552
  const transformStr = `matrix(${this.ma},${this.mb},${this.mc},${this.md},${this.me},${this.mf})`;
544
553
  if (href) {
@@ -723,6 +732,7 @@ function fromCanvas(css) {
723
732
  if (typeof document === "undefined") return null;
724
733
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
725
734
  if (!fallbackCtx) return null;
735
+ fallbackCtx.clearRect(0, 0, 1, 1);
726
736
  fallbackCtx.fillStyle = css;
727
737
  fallbackCtx.fillRect(0, 0, 1, 1);
728
738
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
@@ -869,7 +879,9 @@ function grow(data, needed) {
869
879
  return grown;
870
880
  }
871
881
  function createWebGLPointRenderer(canvas) {
872
- const gl = canvas.getContext("webgl2");
882
+ const gl = canvas.getContext("webgl2", {
883
+ premultipliedAlpha: false
884
+ });
873
885
  if (!gl) return null;
874
886
  const pointProgram = link(gl, POINT_VERT, POINT_FRAG);
875
887
  const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
@@ -937,7 +949,9 @@ function createWebGLPointRenderer(canvas) {
937
949
  gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
938
950
  gl.bindVertexArray(null);
939
951
  let texture = null;
952
+ let textureSource = null;
940
953
  let msdfTexture = null;
954
+ let msdfSource = null;
941
955
  let distanceRange = 4;
942
956
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
943
957
  let pointCount = 0;
@@ -951,6 +965,22 @@ function createWebGLPointRenderer(canvas) {
951
965
  let logicalH = 0;
952
966
  let dpr = 1;
953
967
  let destroyed = false;
968
+ const drawGlyphs = () => {
969
+ if (glyphCount === 0 || !msdfTexture) return;
970
+ const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
971
+ gl.useProgram(msdfProgram);
972
+ gl.bindVertexArray(glyphVAO);
973
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
974
+ gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
975
+ gl.activeTexture(gl.TEXTURE0);
976
+ gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
977
+ gl.uniform1i(gUTex, 0);
978
+ gl.uniform2f(gURes, logicalW, logicalH);
979
+ gl.uniform1f(gURange, distanceRange);
980
+ gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
981
+ gl.bindVertexArray(null);
982
+ glyphCount = 0;
983
+ };
954
984
  return {
955
985
  resize(width, height) {
956
986
  logicalW = width;
@@ -967,8 +997,11 @@ function createWebGLPointRenderer(canvas) {
967
997
  rectCount = 0;
968
998
  spriteCount = 0;
969
999
  glyphCount = 0;
1000
+ gl.clearColor(0, 0, 0, 0);
1001
+ gl.clear(gl.COLOR_BUFFER_BIT);
970
1002
  },
971
1003
  setTexture(source) {
1004
+ if (source === textureSource && texture) return;
972
1005
  if (!texture) {
973
1006
  texture = gl.createTexture();
974
1007
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -980,6 +1013,7 @@ function createWebGLPointRenderer(canvas) {
980
1013
  gl.bindTexture(gl.TEXTURE_2D, texture);
981
1014
  }
982
1015
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1016
+ textureSource = source;
983
1017
  },
984
1018
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
985
1019
  if (!texture) return;
@@ -1016,6 +1050,11 @@ function createWebGLPointRenderer(canvas) {
1016
1050
  spriteCount++;
1017
1051
  },
1018
1052
  setMSDFTexture(source, range) {
1053
+ if (source === msdfSource && msdfTexture) {
1054
+ distanceRange = range;
1055
+ return;
1056
+ }
1057
+ drawGlyphs();
1019
1058
  distanceRange = range;
1020
1059
  if (!msdfTexture) {
1021
1060
  msdfTexture = gl.createTexture();
@@ -1028,6 +1067,7 @@ function createWebGLPointRenderer(canvas) {
1028
1067
  gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1029
1068
  }
1030
1069
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1070
+ msdfSource = source;
1031
1071
  },
1032
1072
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1033
1073
  if (!msdfTexture) return;
@@ -1105,8 +1145,6 @@ function createWebGLPointRenderer(canvas) {
1105
1145
  rectCount++;
1106
1146
  },
1107
1147
  flush() {
1108
- gl.clearColor(0, 0, 0, 0);
1109
- gl.clear(gl.COLOR_BUFFER_BIT);
1110
1148
  if (rectCount > 0) {
1111
1149
  const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1112
1150
  gl.useProgram(rectProgram);
@@ -1141,20 +1179,8 @@ function createWebGLPointRenderer(canvas) {
1141
1179
  gl.uniform2f(sURes, logicalW, logicalH);
1142
1180
  gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1143
1181
  }
1144
- if (glyphCount > 0 && msdfTexture) {
1145
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1146
- gl.useProgram(msdfProgram);
1147
- gl.bindVertexArray(glyphVAO);
1148
- gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
1149
- gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
1150
- gl.activeTexture(gl.TEXTURE0);
1151
- gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1152
- gl.uniform1i(gUTex, 0);
1153
- gl.uniform2f(gURes, logicalW, logicalH);
1154
- gl.uniform1f(gURange, distanceRange);
1155
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1156
- }
1157
1182
  gl.bindVertexArray(null);
1183
+ drawGlyphs();
1158
1184
  },
1159
1185
  destroy() {
1160
1186
  if (destroyed) return;
@@ -20,10 +20,18 @@ var CanvasRenderer = class _CanvasRenderer {
20
20
  batchColor = "";
21
21
  batchAlpha = 1;
22
22
  batchCount = 0;
23
- constructor(canvas) {
23
+ /**
24
+ * @param canvas - The target canvas. Its backing store is resized to the
25
+ * logical size × devicePixelRatio.
26
+ * @param size - Explicit logical size. Without it the renderer assumes a
27
+ * fullscreen canvas and sizes to the window — pass this for embedded /
28
+ * custom-container canvases (the Scene does when `disableWindowResize` is
29
+ * set) so the canvas's own dimensions aren't clobbered by the window's.
30
+ */
31
+ constructor(canvas, size) {
24
32
  const dpr = getDevicePixelRatio();
25
- this.width = typeof window !== "undefined" ? window.innerWidth : canvas.width || 0;
26
- this.height = typeof window !== "undefined" ? window.innerHeight : canvas.height || 0;
33
+ this.width = size?.width ?? (typeof window !== "undefined" ? window.innerWidth : canvas.width || 0);
34
+ this.height = size?.height ?? (typeof window !== "undefined" ? window.innerHeight : canvas.height || 0);
27
35
  canvas.width = this.width * dpr;
28
36
  canvas.height = this.height * dpr;
29
37
  const ctx = canvas.getContext("2d");
@@ -414,18 +422,19 @@ var SVGRenderer = class {
414
422
  } else {
415
423
  this.currentPath.push(`L ${xs} ${ys}`);
416
424
  }
417
- const deltaAngle = Math.abs(endAngle - startAngle);
418
- if (deltaAngle >= Math.PI * 2 - 1e-4) {
425
+ const TWO_PI2 = Math.PI * 2;
426
+ const directedDelta = ccw ? startAngle - endAngle : endAngle - startAngle;
427
+ const sweepAngle = directedDelta >= TWO_PI2 ? TWO_PI2 : (directedDelta % TWO_PI2 + TWO_PI2) % TWO_PI2;
428
+ const sweep = ccw ? 0 : 1;
429
+ if (sweepAngle >= TWO_PI2 - 1e-4) {
419
430
  const xm = x - r * Math.cos(startAngle);
420
431
  const ym = y - r * Math.sin(startAngle);
421
- const sweep = ccw ? 0 : 1;
422
432
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xm} ${ym}`);
423
433
  this.currentPath.push(`A ${r} ${r} 0 0 ${sweep} ${xs} ${ys}`);
424
434
  } else {
425
435
  const xe = x + r * Math.cos(endAngle);
426
436
  const ye = y + r * Math.sin(endAngle);
427
- const largeArc = deltaAngle > Math.PI ? 1 : 0;
428
- const sweep = ccw ? 0 : 1;
437
+ const largeArc = sweepAngle > Math.PI ? 1 : 0;
429
438
  this.currentPath.push(`A ${r} ${r} 0 ${largeArc} ${sweep} ${xe} ${ye}`);
430
439
  }
431
440
  }
@@ -723,6 +732,7 @@ function fromCanvas(css) {
723
732
  if (typeof document === "undefined") return null;
724
733
  if (!fallbackCtx) fallbackCtx = document.createElement("canvas").getContext("2d");
725
734
  if (!fallbackCtx) return null;
735
+ fallbackCtx.clearRect(0, 0, 1, 1);
726
736
  fallbackCtx.fillStyle = css;
727
737
  fallbackCtx.fillRect(0, 0, 1, 1);
728
738
  const d = fallbackCtx.getImageData(0, 0, 1, 1).data;
@@ -869,7 +879,9 @@ function grow(data, needed) {
869
879
  return grown;
870
880
  }
871
881
  function createWebGLPointRenderer(canvas) {
872
- const gl = canvas.getContext("webgl2");
882
+ const gl = canvas.getContext("webgl2", {
883
+ premultipliedAlpha: false
884
+ });
873
885
  if (!gl) return null;
874
886
  const pointProgram = link(gl, POINT_VERT, POINT_FRAG);
875
887
  const rectProgram = link(gl, RECT_VERT, RECT_FRAG);
@@ -937,7 +949,9 @@ function createWebGLPointRenderer(canvas) {
937
949
  gl.vertexAttribPointer(gATint, 4, gl.FLOAT, false, SPRITE_VERT_STRIDE, 16);
938
950
  gl.bindVertexArray(null);
939
951
  let texture = null;
952
+ let textureSource = null;
940
953
  let msdfTexture = null;
954
+ let msdfSource = null;
941
955
  let distanceRange = 4;
942
956
  let pointData = new Float32Array(FLOATS_PER_POINT * 1024);
943
957
  let pointCount = 0;
@@ -951,6 +965,22 @@ function createWebGLPointRenderer(canvas) {
951
965
  let logicalH = 0;
952
966
  let dpr = 1;
953
967
  let destroyed = false;
968
+ const drawGlyphs = () => {
969
+ if (glyphCount === 0 || !msdfTexture) return;
970
+ const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
971
+ gl.useProgram(msdfProgram);
972
+ gl.bindVertexArray(glyphVAO);
973
+ gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
974
+ gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
975
+ gl.activeTexture(gl.TEXTURE0);
976
+ gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
977
+ gl.uniform1i(gUTex, 0);
978
+ gl.uniform2f(gURes, logicalW, logicalH);
979
+ gl.uniform1f(gURange, distanceRange);
980
+ gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
981
+ gl.bindVertexArray(null);
982
+ glyphCount = 0;
983
+ };
954
984
  return {
955
985
  resize(width, height) {
956
986
  logicalW = width;
@@ -967,8 +997,11 @@ function createWebGLPointRenderer(canvas) {
967
997
  rectCount = 0;
968
998
  spriteCount = 0;
969
999
  glyphCount = 0;
1000
+ gl.clearColor(0, 0, 0, 0);
1001
+ gl.clear(gl.COLOR_BUFFER_BIT);
970
1002
  },
971
1003
  setTexture(source) {
1004
+ if (source === textureSource && texture) return;
972
1005
  if (!texture) {
973
1006
  texture = gl.createTexture();
974
1007
  gl.bindTexture(gl.TEXTURE_2D, texture);
@@ -980,6 +1013,7 @@ function createWebGLPointRenderer(canvas) {
980
1013
  gl.bindTexture(gl.TEXTURE_2D, texture);
981
1014
  }
982
1015
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1016
+ textureSource = source;
983
1017
  },
984
1018
  addSprite(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
985
1019
  if (!texture) return;
@@ -1016,6 +1050,11 @@ function createWebGLPointRenderer(canvas) {
1016
1050
  spriteCount++;
1017
1051
  },
1018
1052
  setMSDFTexture(source, range) {
1053
+ if (source === msdfSource && msdfTexture) {
1054
+ distanceRange = range;
1055
+ return;
1056
+ }
1057
+ drawGlyphs();
1019
1058
  distanceRange = range;
1020
1059
  if (!msdfTexture) {
1021
1060
  msdfTexture = gl.createTexture();
@@ -1028,6 +1067,7 @@ function createWebGLPointRenderer(canvas) {
1028
1067
  gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1029
1068
  }
1030
1069
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, source);
1070
+ msdfSource = source;
1031
1071
  },
1032
1072
  addGlyph(x, y, width, height, u0, v0, u1, v1, color = "#ffffff", alpha = 1, rotation = 0) {
1033
1073
  if (!msdfTexture) return;
@@ -1105,8 +1145,6 @@ function createWebGLPointRenderer(canvas) {
1105
1145
  rectCount++;
1106
1146
  },
1107
1147
  flush() {
1108
- gl.clearColor(0, 0, 0, 0);
1109
- gl.clear(gl.COLOR_BUFFER_BIT);
1110
1148
  if (rectCount > 0) {
1111
1149
  const floats = rectCount * VERTS_PER_RECT * FLOATS_PER_RECT_VERT;
1112
1150
  gl.useProgram(rectProgram);
@@ -1141,20 +1179,8 @@ function createWebGLPointRenderer(canvas) {
1141
1179
  gl.uniform2f(sURes, logicalW, logicalH);
1142
1180
  gl.drawArrays(gl.TRIANGLES, 0, spriteCount * VERTS_PER_SPRITE);
1143
1181
  }
1144
- if (glyphCount > 0 && msdfTexture) {
1145
- const floats = glyphCount * VERTS_PER_SPRITE * FLOATS_PER_SPRITE_VERT;
1146
- gl.useProgram(msdfProgram);
1147
- gl.bindVertexArray(glyphVAO);
1148
- gl.bindBuffer(gl.ARRAY_BUFFER, glyphBuffer);
1149
- gl.bufferData(gl.ARRAY_BUFFER, glyphData.subarray(0, floats), gl.DYNAMIC_DRAW);
1150
- gl.activeTexture(gl.TEXTURE0);
1151
- gl.bindTexture(gl.TEXTURE_2D, msdfTexture);
1152
- gl.uniform1i(gUTex, 0);
1153
- gl.uniform2f(gURes, logicalW, logicalH);
1154
- gl.uniform1f(gURange, distanceRange);
1155
- gl.drawArrays(gl.TRIANGLES, 0, glyphCount * VERTS_PER_SPRITE);
1156
- }
1157
1182
  gl.bindVertexArray(null);
1183
+ drawGlyphs();
1158
1184
  },
1159
1185
  destroy() {
1160
1186
  if (destroyed) return;