@vectojs/core 0.2.5 → 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
  import {
2
2
  LayoutWorkerManager
3
- } from "./chunk-B3Z3JEJH.mjs";
3
+ } from "./chunk-STWPTWO4.mjs";
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 {
7
9
  value;
8
10
  target;
@@ -22,11 +24,22 @@ var SpringPhysics = 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 = class {
121
142
  /** The event name. */
122
143
  type;
@@ -568,7 +589,12 @@ var Entity = 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 = 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 = class extends Entity {
989
1019
  color;
990
1020
  letterSpacing;
991
1021
  lineHeight;
1022
+ maxWidth;
1023
+ maxHeight;
992
1024
  text = "";
993
1025
  lastRenderedSeqId = 0;
994
1026
  rgbColorCache = /* @__PURE__ */ new Map();
@@ -1003,17 +1035,27 @@ var MSDFTextEntity = class extends Entity {
1003
1035
  this.color = options.color ?? "#ffffff";
1004
1036
  this.letterSpacing = options.letterSpacing ?? 0;
1005
1037
  this.lineHeight = options.lineHeight;
1038
+ this.maxWidth = options.maxWidth ?? 1e3;
1039
+ this.maxHeight = 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;
1051
+ this.queueLayout();
1052
+ }
1053
+ queueLayout() {
1011
1054
  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 = 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 = 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
  }
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ArabicShaper,
3
3
  BidiResolver
4
- } from "./chunk-B3Z3JEJH.mjs";
4
+ } from "./chunk-STWPTWO4.mjs";
5
5
 
6
6
  // src/layout/LayoutEngine.ts
7
7
  function computeLineSegments(top, bottom, maxWidth, exclusions) {
@@ -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 = class _LayoutWorkerManager {
@@ -96,6 +96,11 @@ export declare class SplineEntity extends Entity {
96
96
  private bounds;
97
97
  private offscreen;
98
98
  private baked;
99
+ /** Logical (CSS-pixel) size of the baked bitmap — the blit destination size. */
100
+ private bakedWidth;
101
+ private bakedHeight;
102
+ /** Gradient strokes can't be baked to a solid-color bitmap; they render per-frame. */
103
+ private readonly containsGradient;
99
104
  /** Lazily-flattened polylines (one Float32Array of [x,y,...] per segment) for hit-testing. */
100
105
  private polylines;
101
106
  /**