@vectojs/core 1.37.0 → 1.38.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.
@@ -134,6 +134,12 @@ var VectoJSEvent = class {
134
134
  }
135
135
  };
136
136
  var Entity = class {
137
+ /**
138
+ * Whether Scene may ask {@link getRenderChildRange} to prune this entity's
139
+ * visual child traversal. Off by default so ordinary containers pay no
140
+ * viewport-inversion cost.
141
+ */
142
+ viewportCullChildren = false;
137
143
  id;
138
144
  children = [];
139
145
  parent = null;
@@ -1094,6 +1100,25 @@ var Entity = class {
1094
1100
  getBounds() {
1095
1101
  return null;
1096
1102
  }
1103
+ /**
1104
+ * Select a contiguous subset of direct children for visual rendering.
1105
+ *
1106
+ * Return `null` (the default) to visit every child. Containers may override
1107
+ * this only when child order and geometry prove that every skipped child's
1108
+ * entire painted subtree lies outside `localViewport`. The Scene still keeps
1109
+ * all children resident, and hit testing, accessibility, and content
1110
+ * projection continue to see the complete tree. Skipped subtrees do not run
1111
+ * `update()` or `render()` in the visual pass, so opt in only for children that
1112
+ * are static while offscreen or whose state advances outside those methods.
1113
+ *
1114
+ * The returned range is half-open and is clamped to `children.length`.
1115
+ * `localViewport` is a conservative axis-aligned box obtained by mapping the
1116
+ * Scene viewport into this entity's local coordinate space.
1117
+ */
1118
+ getRenderChildRange(localViewport) {
1119
+ void localViewport;
1120
+ return null;
1121
+ }
1097
1122
  /**
1098
1123
  * Opt into the renderer's draw-call batching fast-path for point-cloud /
1099
1124
  * particle entities that draw as a single filled circle at their local origin.
@@ -136,9 +136,15 @@ var VectoJSEvent = (_class = class {
136
136
  }
137
137
  }, _class);
138
138
  var Entity = (_class2 = class {
139
+ /**
140
+ * Whether Scene may ask {@link getRenderChildRange} to prune this entity's
141
+ * visual child traversal. Off by default so ordinary containers pay no
142
+ * viewport-inversion cost.
143
+ */
144
+ __init3() {this.viewportCullChildren = false}
139
145
 
140
- __init3() {this.children = []}
141
- __init4() {this.parent = null}
146
+ __init4() {this.children = []}
147
+ __init5() {this.parent = null}
142
148
  /**
143
149
  * Walk up the parent chain to find the scene this entity is currently attached to.
144
150
  */
@@ -146,52 +152,52 @@ var Entity = (_class2 = class {
146
152
  if (this._scene) return this._scene;
147
153
  return this.parent ? this.parent.scene : null;
148
154
  }
149
- __init5() {this._x = 0}
150
- __init6() {this._y = 0}
151
- __init7() {this._scaleX = 1}
152
- __init8() {this._scaleY = 1}
153
- __init9() {this._rotation = 0}
154
- __init10() {this._opacity = 1}
155
+ __init6() {this._x = 0}
156
+ __init7() {this._y = 0}
157
+ __init8() {this._scaleX = 1}
158
+ __init9() {this._scaleY = 1}
159
+ __init10() {this._rotation = 0}
160
+ __init11() {this._opacity = 1}
155
161
  // Fast-path flag: false for the overwhelming majority of entities (incl. the
156
162
  // Danmaku hot loop), so a bare `entity.x = v` is one boolean check + field write.
157
- __init11() {this._hasTransitions = false}
158
- __init12() {this._transitions = null}
163
+ __init12() {this._hasTransitions = false}
164
+ __init13() {this._transitions = null}
159
165
  // Lazily allocated: null until first use. A scene of many passive entities
160
166
  // (particles, data points) never touches these, so paying an empty-Map/array
161
167
  // allocation per entity in the constructor is pure waste at scale.
162
- __init13() {this._drivers = null}
163
- __init14() {this._mounted = false}
164
- __init15() {this._destroyed = false}
168
+ __init14() {this._drivers = null}
169
+ __init15() {this._mounted = false}
170
+ __init16() {this._destroyed = false}
165
171
  // Frame this entity's active drivers were last advanced by Scene's batched
166
172
  // WASM animation pass (see Scene._tickBatchedDrivers), or -1 if never. When
167
173
  // it equals the current frame, tickDrivers() must skip its own tick loop —
168
174
  // otherwise a driver already advanced by the batch pass would be ticked a
169
175
  // second time by the normal per-entity update() walk in the same frame.
170
176
  // Irrelevant (and harmless) for entities never touched by WASM batching.
171
- __init16() {this._driversTickedFrame = -1}
177
+ __init17() {this._driversTickedFrame = -1}
172
178
  // Cached cos/sin, recomputed only when rotation actually changes. renderNode
173
179
  // and getWorldTransform() both read this instead of calling Math.cos/sin per
174
180
  // entity per frame (V8's are ~2.5x slower than other engines).
175
- __init17() {this._trig = { cos: 1, sin: 0 }}
176
- __init18() {this._trigRotation = Number.NaN}
181
+ __init18() {this._trig = { cos: 1, sin: 0 }}
182
+ __init19() {this._trigRotation = Number.NaN}
177
183
  // NaN !== any rotation -> first read computes
178
184
  // Per-frame world-matrix cache. Written by Scene during the render walk;
179
185
  // getWorldTransform() returns it only while `_worldFrame === scene.currentFrame`
180
186
  // and otherwise falls back to the full ancestor walk, so it can never return a
181
187
  // stale/wrong transform — only sometimes miss the fast path.
182
- __init19() {this._wa = 1}
183
- __init20() {this._wb = 0}
184
- __init21() {this._wc = 0}
185
- __init22() {this._wd = 1}
186
- __init23() {this._we = 0}
187
- __init24() {this._wf = 0}
188
- __init25() {this._worldFrame = -1}
188
+ __init20() {this._wa = 1}
189
+ __init21() {this._wb = 0}
190
+ __init22() {this._wc = 0}
191
+ __init23() {this._wd = 1}
192
+ __init24() {this._we = 0}
193
+ __init25() {this._wf = 0}
194
+ __init26() {this._worldFrame = -1}
189
195
  // Slot in the Scene's resident WASM transform store, or -1 when this entity is
190
196
  // not in that store (JS transform path, overlay/detached, or before the first
191
197
  // structural rebuild). Assigned by Scene on a structural rebuild; the Scene
192
198
  // validates it against its slot table before trusting it, so a stale value can
193
199
  // only cost a JS-path fallback, never a wrong read.
194
- __init26() {this._storeSlot = -1}
200
+ __init27() {this._storeSlot = -1}
195
201
  get x() {
196
202
  return this._x;
197
203
  }
@@ -234,8 +240,8 @@ var Entity = (_class2 = class {
234
240
  if (this._hasTransitions) this._animateProp("opacity", v);
235
241
  else this._opacity = v;
236
242
  }
237
- __init27() {this.isDOMPortal = false}
238
- __init28() {this._interactive = false}
243
+ __init28() {this.isDOMPortal = false}
244
+ __init29() {this._interactive = false}
239
245
  get interactive() {
240
246
  return this._interactive;
241
247
  }
@@ -249,10 +255,10 @@ var Entity = (_class2 = class {
249
255
  }
250
256
  }
251
257
  }
252
- __init29() {this.width = 0}
253
- __init30() {this.height = 0}
254
- __init31() {this.a11yOffsetX = 0}
255
- __init32() {this.a11yOffsetY = 0}
258
+ __init30() {this.width = 0}
259
+ __init31() {this.height = 0}
260
+ __init32() {this.a11yOffsetX = 0}
261
+ __init33() {this.a11yOffsetY = 0}
256
262
  /**
257
263
  * Opt in to a viewport-filling accessibility/automation shadow node even when
258
264
  * this entity has no intrinsic box (`width`/`height` of `0`). Use for
@@ -260,7 +266,7 @@ var Entity = (_class2 = class {
260
266
  * that need global pointer events. The node is mounted behind all other shadow
261
267
  * nodes, so on-top components stay clickable.
262
268
  */
263
- __init33() {this.a11yFullViewport = false}
269
+ __init34() {this.a11yFullViewport = false}
264
270
  /**
265
271
  * When this entity's a11y shadow node is materialized.
266
272
  *
@@ -307,7 +313,7 @@ var Entity = (_class2 = class {
307
313
  * `vectojs-native/danmaku`. Use `'onDemand'` to stop paying per entity, not as
308
314
  * the whole accessibility story.
309
315
  */
310
- __init34() {this.a11yProjection = "eager"}
316
+ __init35() {this.a11yProjection = "eager"}
311
317
  /**
312
318
  * Hide this entity AND its whole subtree from the accessibility/automation
313
319
  * projection, regardless of each node's own `interactive` flag.
@@ -325,14 +331,14 @@ var Entity = (_class2 = class {
325
331
  * `=== 0` test never fires; a threshold would instead silently un-project a
326
332
  * faint-but-live control.
327
333
  */
328
- __init35() {this.a11yHidden = false}
334
+ __init36() {this.a11yHidden = false}
329
335
  /**
330
336
  * Clip this node's children to its local box (`[0,0]–[width,height]`) while
331
337
  * rendering. Combined with translating a content child, this is how
332
338
  * scroll/overflow containers (e.g. `ScrollView`) keep their content inside a
333
339
  * fixed viewport. Off by default (children render unclipped). Canvas2D only.
334
340
  */
335
- __init36() {this.clipChildren = false}
341
+ __init37() {this.clipChildren = false}
336
342
  /**
337
343
  * Group this subtree's projected text into its own accessibility **region**,
338
344
  * without clipping anything.
@@ -359,14 +365,14 @@ var Entity = (_class2 = class {
359
365
  *
360
366
  * Off by default. Regions nest: the nearest enclosing region wins.
361
367
  */
362
- __init37() {this.a11yRegion = false}
368
+ __init38() {this.a11yRegion = false}
363
369
  // Lazily allocated (see _drivers above). Most entities never register a
364
370
  // listener or an imperative animate() tween.
365
- __init38() {this.listeners = null}
371
+ __init39() {this.listeners = null}
366
372
  /** Capture-phase listeners (fired root→target before bubble). */
367
- __init39() {this.captureListeners = null}
368
- __init40() {this.animations = null}
369
- constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);
373
+ __init40() {this.captureListeners = null}
374
+ __init41() {this.animations = null}
375
+ constructor(id) {;_class2.prototype.__init3.call(this);_class2.prototype.__init4.call(this);_class2.prototype.__init5.call(this);_class2.prototype.__init6.call(this);_class2.prototype.__init7.call(this);_class2.prototype.__init8.call(this);_class2.prototype.__init9.call(this);_class2.prototype.__init10.call(this);_class2.prototype.__init11.call(this);_class2.prototype.__init12.call(this);_class2.prototype.__init13.call(this);_class2.prototype.__init14.call(this);_class2.prototype.__init15.call(this);_class2.prototype.__init16.call(this);_class2.prototype.__init17.call(this);_class2.prototype.__init18.call(this);_class2.prototype.__init19.call(this);_class2.prototype.__init20.call(this);_class2.prototype.__init21.call(this);_class2.prototype.__init22.call(this);_class2.prototype.__init23.call(this);_class2.prototype.__init24.call(this);_class2.prototype.__init25.call(this);_class2.prototype.__init26.call(this);_class2.prototype.__init27.call(this);_class2.prototype.__init28.call(this);_class2.prototype.__init29.call(this);_class2.prototype.__init30.call(this);_class2.prototype.__init31.call(this);_class2.prototype.__init32.call(this);_class2.prototype.__init33.call(this);_class2.prototype.__init34.call(this);_class2.prototype.__init35.call(this);_class2.prototype.__init36.call(this);_class2.prototype.__init37.call(this);_class2.prototype.__init38.call(this);_class2.prototype.__init39.call(this);_class2.prototype.__init40.call(this);_class2.prototype.__init41.call(this);
370
376
  this.id = id || `entity_${Math.random().toString(36).substring(2, 9)}`;
371
377
  }
372
378
  /**
@@ -1096,6 +1102,25 @@ var Entity = (_class2 = class {
1096
1102
  getBounds() {
1097
1103
  return null;
1098
1104
  }
1105
+ /**
1106
+ * Select a contiguous subset of direct children for visual rendering.
1107
+ *
1108
+ * Return `null` (the default) to visit every child. Containers may override
1109
+ * this only when child order and geometry prove that every skipped child's
1110
+ * entire painted subtree lies outside `localViewport`. The Scene still keeps
1111
+ * all children resident, and hit testing, accessibility, and content
1112
+ * projection continue to see the complete tree. Skipped subtrees do not run
1113
+ * `update()` or `render()` in the visual pass, so opt in only for children that
1114
+ * are static while offscreen or whose state advances outside those methods.
1115
+ *
1116
+ * The returned range is half-open and is clamped to `children.length`.
1117
+ * `localViewport` is a conservative axis-aligned box obtained by mapping the
1118
+ * Scene viewport into this entity's local coordinate space.
1119
+ */
1120
+ getRenderChildRange(localViewport) {
1121
+ void localViewport;
1122
+ return null;
1123
+ }
1099
1124
  /**
1100
1125
  * Opt into the renderer's draw-call batching fast-path for point-cloud /
1101
1126
  * particle entities that draw as a single filled circle at their local origin.
@@ -1208,28 +1233,28 @@ var MSDFTextEntity = (_class3 = class extends Entity {
1208
1233
  // joined by soft hyphens (U+00AD); the worker then treats those as break
1209
1234
  // opportunities. `text` keeps the original string for a11y/content
1210
1235
  // projection; `layoutText` is the soft-hyphen-annotated string sent to layout.
1211
- __init41() {this.hyphenator = null}
1212
- __init42() {this.layoutText = ""}
1213
- __init43() {this.text = ""}
1214
- __init44() {this.lastRenderedSeqId = 0}
1236
+ __init42() {this.hyphenator = null}
1237
+ __init43() {this.layoutText = ""}
1238
+ __init44() {this.text = ""}
1239
+ __init45() {this.lastRenderedSeqId = 0}
1215
1240
  /** Bumped by {@link queueLayout}; read by `Scene` to skip an unchanged sync. */
1216
- __init45() {this.contentEpoch = 0}
1241
+ __init46() {this.contentEpoch = 0}
1217
1242
  // Atlas-decode subscription (see watchAtlasDecode). Held so `destroy()` can
1218
1243
  // release it: the handler closes over `this`, so leaving it attached to a
1219
1244
  // long-lived shared atlas image would retain the whole entity.
1220
- __init46() {this.atlasDecodeTarget = null}
1221
- __init47() {this.atlasDecodeHandler = null}
1222
- __init48() {this.rgbColorCache = /* @__PURE__ */ new Map()}
1223
- __init49() {this.fontStringCache = []}
1224
- __init50() {this.layoutResult = null}
1245
+ __init47() {this.atlasDecodeTarget = null}
1246
+ __init48() {this.atlasDecodeHandler = null}
1247
+ __init49() {this.rgbColorCache = /* @__PURE__ */ new Map()}
1248
+ __init50() {this.fontStringCache = []}
1249
+ __init51() {this.layoutResult = null}
1225
1250
  /**
1226
1251
  * Visual rows rebuilt from {@link layoutResult} (see
1227
1252
  * {@link rebuildProjectionLines}). Empty until a layout reply lands and the
1228
1253
  * reply's shaped glyphs can be mapped back to the source text 1:1.
1229
1254
  */
1230
- __init51() {this.projectionLines = []}
1255
+ __init52() {this.projectionLines = []}
1231
1256
  constructor(text, options) {
1232
- super();_class3.prototype.__init41.call(this);_class3.prototype.__init42.call(this);_class3.prototype.__init43.call(this);_class3.prototype.__init44.call(this);_class3.prototype.__init45.call(this);_class3.prototype.__init46.call(this);_class3.prototype.__init47.call(this);_class3.prototype.__init48.call(this);_class3.prototype.__init49.call(this);_class3.prototype.__init50.call(this);_class3.prototype.__init51.call(this);;
1257
+ super();_class3.prototype.__init42.call(this);_class3.prototype.__init43.call(this);_class3.prototype.__init44.call(this);_class3.prototype.__init45.call(this);_class3.prototype.__init46.call(this);_class3.prototype.__init47.call(this);_class3.prototype.__init48.call(this);_class3.prototype.__init49.call(this);_class3.prototype.__init50.call(this);_class3.prototype.__init51.call(this);_class3.prototype.__init52.call(this);;
1233
1258
  this.font = options.font;
1234
1259
  this.texture = options.texture;
1235
1260
  this.fallbackFont = _nullishCoalesce(options.fallbackFont, () => ( "sans-serif"));
@@ -1596,23 +1621,23 @@ var SVGEntity = (_class4 = class extends Entity {
1596
1621
  * rasterized. Set to `'transparent'` to opt out and keep the box empty.
1597
1622
  * Default `'rgba(248,113,113,0.9)'`.
1598
1623
  */
1599
- __init52() {this.fallbackStroke = "rgba(248,113,113,0.9)"}
1624
+ __init53() {this.fallbackStroke = "rgba(248,113,113,0.9)"}
1600
1625
  /** Fill behind the fallback marker. Default `'rgba(248,113,113,0.12)'`. */
1601
- __init53() {this.fallbackFill = "rgba(248,113,113,0.12)"}
1602
- __init54() {this.svgSource = ""}
1603
- __init55() {this.imageBitmap = null}
1604
- __init56() {this.imageElement = null}
1605
- __init57() {this.blobURL = null}
1606
- __init58() {this.currentImg = null}
1607
- __init59() {this.lodTimeout = null}
1608
- __init60() {this.rasterFailed = false}
1609
- __init61() {this.cachedDoc = null}
1610
- __init62() {this.baseWidth = 100}
1611
- __init63() {this.baseHeight = 100}
1612
- __init64() {this.lastRasterizedScale = 1}
1613
- __init65() {this.targetScale = 1}
1626
+ __init54() {this.fallbackFill = "rgba(248,113,113,0.12)"}
1627
+ __init55() {this.svgSource = ""}
1628
+ __init56() {this.imageBitmap = null}
1629
+ __init57() {this.imageElement = null}
1630
+ __init58() {this.blobURL = null}
1631
+ __init59() {this.currentImg = null}
1632
+ __init60() {this.lodTimeout = null}
1633
+ __init61() {this.rasterFailed = false}
1634
+ __init62() {this.cachedDoc = null}
1635
+ __init63() {this.baseWidth = 100}
1636
+ __init64() {this.baseHeight = 100}
1637
+ __init65() {this.lastRasterizedScale = 1}
1638
+ __init66() {this.targetScale = 1}
1614
1639
  constructor(svgSource, id) {
1615
- super(id);_class4.prototype.__init52.call(this);_class4.prototype.__init53.call(this);_class4.prototype.__init54.call(this);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);_class4.prototype.__init61.call(this);_class4.prototype.__init62.call(this);_class4.prototype.__init63.call(this);_class4.prototype.__init64.call(this);_class4.prototype.__init65.call(this);;
1640
+ super(id);_class4.prototype.__init53.call(this);_class4.prototype.__init54.call(this);_class4.prototype.__init55.call(this);_class4.prototype.__init56.call(this);_class4.prototype.__init57.call(this);_class4.prototype.__init58.call(this);_class4.prototype.__init59.call(this);_class4.prototype.__init60.call(this);_class4.prototype.__init61.call(this);_class4.prototype.__init62.call(this);_class4.prototype.__init63.call(this);_class4.prototype.__init64.call(this);_class4.prototype.__init65.call(this);_class4.prototype.__init66.call(this);;
1616
1641
  this.setSVGSource(svgSource);
1617
1642
  }
1618
1643
  setSVGSource(svgSource) {
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ var _chunkZYYP2M5Djs = require('./chunk-ZYYP2M5D.js');
19
19
 
20
20
 
21
21
 
22
- var _chunkDSYYQ6BQjs = require('./chunk-DSYYQ6BQ.js');
22
+ var _chunkSRXU3ZXKjs = require('./chunk-SRXU3ZXK.js');
23
23
 
24
24
  // src/tree/ComputeParticleEntity.ts
25
25
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -31,7 +31,7 @@ var PARTICLE_OFFSET_ORIGIN_X = 4;
31
31
  var PARTICLE_OFFSET_ORIGIN_Y = 5;
32
32
  var PARTICLE_OFFSET_SIZE = 6;
33
33
  var PARTICLE_OFFSET_LIFE = 7;
34
- var ComputeParticleEntity = (_class = class extends _chunkDSYYQ6BQjs.Entity {
34
+ var ComputeParticleEntity = (_class = class extends _chunkSRXU3ZXKjs.Entity {
35
35
 
36
36
 
37
37
 
@@ -4824,7 +4824,7 @@ var Scene = (_class15 = class _Scene {
4824
4824
  this.forcedColorsChangeHandler = () => this.markDirty();
4825
4825
  _optionalChain([this, 'access', _118 => _118.forcedColorsQuery, 'access', _119 => _119.addEventListener, 'optionalCall', _120 => _120("change", this.forcedColorsChangeHandler)]);
4826
4826
  }
4827
- this.root = new class RootEntity extends _chunkDSYYQ6BQjs.Entity {
4827
+ this.root = new class RootEntity extends _chunkSRXU3ZXKjs.Entity {
4828
4828
  isPointInside() {
4829
4829
  return false;
4830
4830
  }
@@ -4834,7 +4834,7 @@ var Scene = (_class15 = class _Scene {
4834
4834
  }("root");
4835
4835
  this.root._scene = this;
4836
4836
  this._wasmBackend = new WasmBackendFacade(this.root);
4837
- this.overlayRoot = new class OverlayRoot extends _chunkDSYYQ6BQjs.Entity {
4837
+ this.overlayRoot = new class OverlayRoot extends _chunkSRXU3ZXKjs.Entity {
4838
4838
  isPointInside() {
4839
4839
  return false;
4840
4840
  }
@@ -5154,7 +5154,7 @@ var Scene = (_class15 = class _Scene {
5154
5154
  }
5155
5155
  if (this.hoveredA11yElements.has(el)) {
5156
5156
  this.hoveredA11yElements.delete(el);
5157
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointerleave", node, void 0, false));
5157
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, void 0, false));
5158
5158
  }
5159
5159
  this.preserveFocusOnRemoval(el);
5160
5160
  el.remove();
@@ -5745,20 +5745,20 @@ var Scene = (_class15 = class _Scene {
5745
5745
  el.style.background = "transparent";
5746
5746
  }
5747
5747
  el.addEventListener("click", (e) => {
5748
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("click", node, e));
5748
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("click", node, e));
5749
5749
  });
5750
5750
  el.addEventListener("dblclick", (e) => {
5751
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("dblclick", node, e));
5751
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("dblclick", node, e));
5752
5752
  });
5753
5753
  el.addEventListener("mouseenter", (e) => {
5754
5754
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.2)";
5755
5755
  this.hoveredA11yElements.add(el);
5756
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("hover", node, e, false));
5756
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("hover", node, e, false));
5757
5757
  });
5758
5758
  el.addEventListener("mouseleave", (e) => {
5759
5759
  if (this.debugA11y) el.style.backgroundColor = "rgba(56, 189, 248, 0.05)";
5760
5760
  this.hoveredA11yElements.delete(el);
5761
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointerleave", node, e, false));
5761
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerleave", node, e, false));
5762
5762
  });
5763
5763
  const capEl = el;
5764
5764
  const releasePointer = (event) => {
@@ -5774,24 +5774,24 @@ var Scene = (_class15 = class _Scene {
5774
5774
  };
5775
5775
  el.addEventListener("pointerdown", (e) => {
5776
5776
  if (typeof capEl.setPointerCapture === "function") capEl.setPointerCapture(e.pointerId);
5777
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointerdown", node, e));
5777
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerdown", node, e));
5778
5778
  });
5779
5779
  el.addEventListener("pointerup", (e) => {
5780
5780
  releasePointer(e);
5781
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointerup", node, e));
5781
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointerup", node, e));
5782
5782
  });
5783
5783
  el.addEventListener("pointercancel", (e) => {
5784
5784
  releasePointer(e);
5785
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointercancel", node, e));
5785
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointercancel", node, e));
5786
5786
  });
5787
5787
  el.addEventListener(
5788
5788
  "pointermove",
5789
- (e) => node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("pointermove", node, e))
5789
+ (e) => node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("pointermove", node, e))
5790
5790
  );
5791
5791
  el.addEventListener(
5792
5792
  "wheel",
5793
5793
  (e) => {
5794
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("wheel", node, e));
5794
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e));
5795
5795
  },
5796
5796
  { passive: false }
5797
5797
  );
@@ -5816,10 +5816,10 @@ var Scene = (_class15 = class _Scene {
5816
5816
  );
5817
5817
  emitInitialScroll = emitScroll;
5818
5818
  el.addEventListener("keydown", (e) => {
5819
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("keydown", node, e));
5819
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keydown", node, e));
5820
5820
  });
5821
5821
  el.addEventListener("keyup", (e) => {
5822
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("keyup", node, e));
5822
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("keyup", node, e));
5823
5823
  });
5824
5824
  if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
5825
5825
  const input = el;
@@ -5885,7 +5885,7 @@ var Scene = (_class15 = class _Scene {
5885
5885
  el.addEventListener("keydown", (e) => {
5886
5886
  if (e.key === "Enter" || e.key === " ") {
5887
5887
  e.preventDefault();
5888
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("click", node, e));
5888
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("click", node, e));
5889
5889
  }
5890
5890
  });
5891
5891
  }
@@ -6322,7 +6322,7 @@ var Scene = (_class15 = class _Scene {
6322
6322
  el.addEventListener(
6323
6323
  "wheel",
6324
6324
  (e2) => {
6325
- node.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)("wheel", node, e2));
6325
+ node.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)("wheel", node, e2));
6326
6326
  },
6327
6327
  { passive: false }
6328
6328
  );
@@ -6991,7 +6991,7 @@ var Scene = (_class15 = class _Scene {
6991
6991
  let walkHadAnimation = false;
6992
6992
  let walkHadInteractive = false;
6993
6993
  const runUpdate = (node) => {
6994
- const overridesUpdate = node.update !== _chunkDSYYQ6BQjs.Entity.prototype.update;
6994
+ const overridesUpdate = node.update !== _chunkSRXU3ZXKjs.Entity.prototype.update;
6995
6995
  let pending = node.hasPendingAnimations();
6996
6996
  if (pending || overridesUpdate) {
6997
6997
  node.update(dt, time);
@@ -7000,7 +7000,7 @@ var Scene = (_class15 = class _Scene {
7000
7000
  if (pending) walkHadAnimation = true;
7001
7001
  if (!walkHadInteractive && node.interactive) walkHadInteractive = true;
7002
7002
  if (this._devActive && this._devFrameCount % 120 === 0) {
7003
- if (overridesUpdate && node.hasPendingAnimations === _chunkDSYYQ6BQjs.Entity.prototype.hasPendingAnimations) {
7003
+ if (overridesUpdate && node.hasPendingAnimations === _chunkSRXU3ZXKjs.Entity.prototype.hasPendingAnimations) {
7004
7004
  this._devWarn(
7005
7005
  `Entity "${node.id}" overrides update() but not hasPendingAnimations(). Custom motion in update() without overriding hasPendingAnimations() causes the idle throttle to slow the animation. Override hasPendingAnimations() to return true while motion is in flight.`
7006
7006
  );
@@ -7172,8 +7172,43 @@ var Scene = (_class15 = class _Scene {
7172
7172
  if (node.clipChildren) {
7173
7173
  renderer.clip(0, 0, node.width, node.height);
7174
7174
  }
7175
- for (const child of node.children) {
7176
- renderNode(child, a, b, c, d, te, tf, worldOpacity);
7175
+ const children = node.children;
7176
+ let childStart = 0;
7177
+ let childEnd = children.length;
7178
+ if (childEnd > 0 && node.viewportCullChildren) {
7179
+ const det = a * d - b * c;
7180
+ if (Number.isFinite(det) && Math.abs(det) > 1e-12) {
7181
+ const invDet = 1 / det;
7182
+ let minLocalX = Infinity;
7183
+ let minLocalY = Infinity;
7184
+ let maxLocalX = -Infinity;
7185
+ let maxLocalY = -Infinity;
7186
+ for (let i = 0; i < 4; i++) {
7187
+ const worldX = i & 1 ? vw : 0;
7188
+ const worldY = i & 2 ? vh : 0;
7189
+ const dx = worldX - te;
7190
+ const dy = worldY - tf;
7191
+ const localX = (d * dx - c * dy) * invDet;
7192
+ const localY = (-b * dx + a * dy) * invDet;
7193
+ if (localX < minLocalX) minLocalX = localX;
7194
+ if (localX > maxLocalX) maxLocalX = localX;
7195
+ if (localY < minLocalY) minLocalY = localY;
7196
+ if (localY > maxLocalY) maxLocalY = localY;
7197
+ }
7198
+ const range = node.getRenderChildRange({
7199
+ x: minLocalX,
7200
+ y: minLocalY,
7201
+ width: maxLocalX - minLocalX,
7202
+ height: maxLocalY - minLocalY
7203
+ });
7204
+ if (range) {
7205
+ childStart = Math.max(0, Math.min(childEnd, Math.floor(range.start)));
7206
+ childEnd = Math.max(childStart, Math.min(childEnd, Math.ceil(range.end)));
7207
+ }
7208
+ }
7209
+ }
7210
+ for (let i = childStart; i < childEnd; i++) {
7211
+ renderNode(children[i], a, b, c, d, te, tf, worldOpacity);
7177
7212
  }
7178
7213
  renderer.flush();
7179
7214
  renderer.restore();
@@ -7426,7 +7461,7 @@ function defaultMeasurer() {
7426
7461
  sharedMeasurer ??= _layout.resolveGlyphMeasurer.call(void 0, "sans-serif");
7427
7462
  return sharedMeasurer;
7428
7463
  }
7429
- var TextEntity = (_class16 = class extends _chunkDSYYQ6BQjs.Entity {
7464
+ var TextEntity = (_class16 = class extends _chunkSRXU3ZXKjs.Entity {
7430
7465
 
7431
7466
 
7432
7467
 
@@ -7630,7 +7665,7 @@ var TextEntity = (_class16 = class extends _chunkDSYYQ6BQjs.Entity {
7630
7665
  }, _class16);
7631
7666
 
7632
7667
  // src/components/GridTextEntity.ts
7633
- var GridTextEntity = (_class17 = class extends _chunkDSYYQ6BQjs.Entity {
7668
+ var GridTextEntity = (_class17 = class extends _chunkSRXU3ZXKjs.Entity {
7634
7669
 
7635
7670
  __init185() {this.fillStyle = "#ffffff"}
7636
7671
  __init186() {this.grid = []}
@@ -7724,7 +7759,7 @@ function distSqToSegment(px, py, x1, y1, x2, y2) {
7724
7759
  const ey = py - cy;
7725
7760
  return ex * ex + ey * ey;
7726
7761
  }
7727
- var SplineEntity = (_class18 = class extends _chunkDSYYQ6BQjs.Entity {
7762
+ var SplineEntity = (_class18 = class extends _chunkSRXU3ZXKjs.Entity {
7728
7763
 
7729
7764
 
7730
7765
 
@@ -8052,7 +8087,7 @@ async function loadSpline(url) {
8052
8087
  }
8053
8088
 
8054
8089
  // src/components/Rect.ts
8055
- var Rect = class extends _chunkDSYYQ6BQjs.Entity {
8090
+ var Rect = class extends _chunkSRXU3ZXKjs.Entity {
8056
8091
 
8057
8092
 
8058
8093
 
@@ -8108,7 +8143,7 @@ var Rect = class extends _chunkDSYYQ6BQjs.Entity {
8108
8143
  };
8109
8144
 
8110
8145
  // src/components/Circle.ts
8111
- var Circle = class extends _chunkDSYYQ6BQjs.Entity {
8146
+ var Circle = class extends _chunkSRXU3ZXKjs.Entity {
8112
8147
 
8113
8148
 
8114
8149
 
@@ -8172,7 +8207,7 @@ var Circle = class extends _chunkDSYYQ6BQjs.Entity {
8172
8207
  };
8173
8208
 
8174
8209
  // src/components/Group.ts
8175
- var Group = class extends _chunkDSYYQ6BQjs.Entity {
8210
+ var Group = class extends _chunkSRXU3ZXKjs.Entity {
8176
8211
  constructor(...children) {
8177
8212
  super();
8178
8213
  if (children.length > 0) this.add(...children);
@@ -8191,7 +8226,7 @@ var _math = require('@vectojs/math'); _createStarExport(_math);
8191
8226
 
8192
8227
 
8193
8228
  // src/tree/DOMPortalEntity.ts
8194
- var DOMPortalEntity = (_class19 = class extends _chunkDSYYQ6BQjs.Entity {
8229
+ var DOMPortalEntity = (_class19 = class extends _chunkSRXU3ZXKjs.Entity {
8195
8230
 
8196
8231
  __init196() {this.isDOMPortal = true}
8197
8232
  __init197() {this.domListeners = []}
@@ -8247,7 +8282,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkDSYYQ6BQjs.Entity {
8247
8282
  ];
8248
8283
  for (const type of events) {
8249
8284
  const handler = (e) => {
8250
- this.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)(type, this, e));
8285
+ this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e));
8251
8286
  };
8252
8287
  this.domElement.addEventListener(type, handler);
8253
8288
  this.domListeners.push({ type, handler, capture: false });
@@ -8258,7 +8293,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkDSYYQ6BQjs.Entity {
8258
8293
  ];
8259
8294
  for (const { native, vecto } of hoverEvents) {
8260
8295
  const handler = (e) => {
8261
- this.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)(vecto, this, e, false));
8296
+ this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(vecto, this, e, false));
8262
8297
  };
8263
8298
  this.domElement.addEventListener(native, handler);
8264
8299
  this.domListeners.push({ type: native, handler, capture: false });
@@ -8266,7 +8301,7 @@ var DOMPortalEntity = (_class19 = class extends _chunkDSYYQ6BQjs.Entity {
8266
8301
  const focusEvents = ["focus", "blur"];
8267
8302
  for (const type of focusEvents) {
8268
8303
  const handler = (e) => {
8269
- this.dispatchEvent(new (0, _chunkDSYYQ6BQjs.VectoJSEvent)(type, this, e, true));
8304
+ this.dispatchEvent(new (0, _chunkSRXU3ZXKjs.VectoJSEvent)(type, this, e, true));
8270
8305
  };
8271
8306
  this.domElement.addEventListener(type, handler, true);
8272
8307
  this.domListeners.push({ type, handler, capture: true });
@@ -8364,4 +8399,4 @@ Scene.registerWebGPUParticleSystemManager(_chunkZYYP2M5Djs.WebGPUParticleSystemM
8364
8399
 
8365
8400
 
8366
8401
 
8367
- exports.CanvasRenderer = _chunkZYYP2M5Djs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkDSYYQ6BQjs.Entity; exports.GlyphRasterAtlas = _chunkZYYP2M5Djs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkDSYYQ6BQjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkDSYYQ6BQjs.SVGEntity; exports.SVGRenderer = _chunkZYYP2M5Djs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkZYYP2M5Djs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkDSYYQ6BQjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkZYYP2M5Djs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkDSYYQ6BQjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkZYYP2M5Djs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkZYYP2M5Djs.installRendererDevTraps; exports.isRendererDevMode = _chunkZYYP2M5Djs.isRendererDevMode; exports.isSafeUrl = _chunkZYYP2M5Djs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkZYYP2M5Djs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkZYYP2M5Djs.sanitizeUrl; exports.setRendererDevMode = _chunkZYYP2M5Djs.setRendererDevMode;
8402
+ exports.CanvasRenderer = _chunkZYYP2M5Djs.CanvasRenderer; exports.Circle = Circle; exports.ComputeParticleEntity = ComputeParticleEntity; exports.DEFAULT_CONTENT_SEMANTIC_BUDGET = DEFAULT_CONTENT_SEMANTIC_BUDGET; exports.DOMPortalEntity = DOMPortalEntity; exports.Entity = _chunkSRXU3ZXKjs.Entity; exports.GlyphRasterAtlas = _chunkZYYP2M5Djs.GlyphRasterAtlas; exports.GridTextEntity = GridTextEntity; exports.Group = Group; exports.MSDFTextEntity = _chunkSRXU3ZXKjs.MSDFTextEntity; exports.PARTICLE_OFFSET_LIFE = PARTICLE_OFFSET_LIFE; exports.PARTICLE_OFFSET_ORIGIN_X = PARTICLE_OFFSET_ORIGIN_X; exports.PARTICLE_OFFSET_ORIGIN_Y = PARTICLE_OFFSET_ORIGIN_Y; exports.PARTICLE_OFFSET_POSITION_X = PARTICLE_OFFSET_POSITION_X; exports.PARTICLE_OFFSET_POSITION_Y = PARTICLE_OFFSET_POSITION_Y; exports.PARTICLE_OFFSET_SIZE = PARTICLE_OFFSET_SIZE; exports.PARTICLE_OFFSET_VELOCITY_X = PARTICLE_OFFSET_VELOCITY_X; exports.PARTICLE_OFFSET_VELOCITY_Y = PARTICLE_OFFSET_VELOCITY_Y; exports.PARTICLE_STRIDE_FLOATS = PARTICLE_STRIDE_FLOATS; exports.REDUCED_MOTION_FPS = REDUCED_MOTION_FPS; exports.Rect = Rect; exports.SCENE_OPTION_KEYS = SCENE_OPTION_KEYS; exports.SVGEntity = _chunkSRXU3ZXKjs.SVGEntity; exports.SVGRenderer = _chunkZYYP2M5Djs.SVGRenderer; exports.Scene = Scene; exports.SplineEntity = SplineEntity; exports.TextEntity = TextEntity; exports.TextRasterCache = _chunkZYYP2M5Djs.TextRasterCache; exports.VECTO_USER_TIMING = VECTO_USER_TIMING; exports.VectoJSEvent = _chunkSRXU3ZXKjs.VectoJSEvent; exports.WebGPUParticleSystemManager = _chunkZYYP2M5Djs.WebGPUParticleSystemManager; exports.beginVectoUserTiming = beginVectoUserTiming; exports.contentLineInHint = _chunkSRXU3ZXKjs.contentLineInHint; exports.createWebGLPointRenderer = _chunkZYYP2M5Djs.createWebGLPointRenderer; exports.endVectoUserTiming = endVectoUserTiming; exports.installRendererDevTraps = _chunkZYYP2M5Djs.installRendererDevTraps; exports.isRendererDevMode = _chunkZYYP2M5Djs.isRendererDevMode; exports.isSafeUrl = _chunkZYYP2M5Djs.isSafeUrl; exports.loadSpline = loadSpline; exports.measureVectoUserTiming = measureVectoUserTiming; exports.parseColorToRGBA = _chunkZYYP2M5Djs.parseColorToRGBA; exports.polySegmentToBezier = polySegmentToBezier; exports.sanitizeUrl = _chunkZYYP2M5Djs.sanitizeUrl; exports.setRendererDevMode = _chunkZYYP2M5Djs.setRendererDevMode;
package/dist/index.mjs CHANGED
@@ -18,7 +18,7 @@ import {
18
18
  SVGEntity,
19
19
  VectoJSEvent,
20
20
  contentLineInHint
21
- } from "./chunk-3MTVDXZQ.mjs";
21
+ } from "./chunk-QVTCKWDO.mjs";
22
22
 
23
23
  // src/tree/ComputeParticleEntity.ts
24
24
  var PARTICLE_STRIDE_FLOATS = 8;
@@ -7171,8 +7171,43 @@ var Scene = class _Scene {
7171
7171
  if (node.clipChildren) {
7172
7172
  renderer.clip(0, 0, node.width, node.height);
7173
7173
  }
7174
- for (const child of node.children) {
7175
- renderNode(child, a, b, c, d, te, tf, worldOpacity);
7174
+ const children = node.children;
7175
+ let childStart = 0;
7176
+ let childEnd = children.length;
7177
+ if (childEnd > 0 && node.viewportCullChildren) {
7178
+ const det = a * d - b * c;
7179
+ if (Number.isFinite(det) && Math.abs(det) > 1e-12) {
7180
+ const invDet = 1 / det;
7181
+ let minLocalX = Infinity;
7182
+ let minLocalY = Infinity;
7183
+ let maxLocalX = -Infinity;
7184
+ let maxLocalY = -Infinity;
7185
+ for (let i = 0; i < 4; i++) {
7186
+ const worldX = i & 1 ? vw : 0;
7187
+ const worldY = i & 2 ? vh : 0;
7188
+ const dx = worldX - te;
7189
+ const dy = worldY - tf;
7190
+ const localX = (d * dx - c * dy) * invDet;
7191
+ const localY = (-b * dx + a * dy) * invDet;
7192
+ if (localX < minLocalX) minLocalX = localX;
7193
+ if (localX > maxLocalX) maxLocalX = localX;
7194
+ if (localY < minLocalY) minLocalY = localY;
7195
+ if (localY > maxLocalY) maxLocalY = localY;
7196
+ }
7197
+ const range = node.getRenderChildRange({
7198
+ x: minLocalX,
7199
+ y: minLocalY,
7200
+ width: maxLocalX - minLocalX,
7201
+ height: maxLocalY - minLocalY
7202
+ });
7203
+ if (range) {
7204
+ childStart = Math.max(0, Math.min(childEnd, Math.floor(range.start)));
7205
+ childEnd = Math.max(childStart, Math.min(childEnd, Math.ceil(range.end)));
7206
+ }
7207
+ }
7208
+ }
7209
+ for (let i = childStart; i < childEnd; i++) {
7210
+ renderNode(children[i], a, b, c, d, te, tf, worldOpacity);
7176
7211
  }
7177
7212
  renderer.flush();
7178
7213
  renderer.restore();
package/dist/text.js CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkDSYYQ6BQjs = require('./chunk-DSYYQ6BQ.js');
5
+ var _chunkSRXU3ZXKjs = require('./chunk-SRXU3ZXK.js');
6
6
 
7
7
  // src/text/index.ts
8
8
  var _text = require('@vectojs/text'); _createStarExport(_text);
9
9
 
10
10
 
11
11
 
12
- exports.MSDFTextEntity = _chunkDSYYQ6BQjs.MSDFTextEntity; exports.SVGEntity = _chunkDSYYQ6BQjs.SVGEntity;
12
+ exports.MSDFTextEntity = _chunkSRXU3ZXKjs.MSDFTextEntity; exports.SVGEntity = _chunkSRXU3ZXKjs.SVGEntity;
package/dist/text.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MSDFTextEntity,
3
3
  SVGEntity
4
- } from "./chunk-3MTVDXZQ.mjs";
4
+ } from "./chunk-QVTCKWDO.mjs";
5
5
 
6
6
  // src/text/index.ts
7
7
  export * from "@vectojs/text";
@@ -29,6 +29,13 @@ export interface Bounds {
29
29
  width: number;
30
30
  height: number;
31
31
  }
32
+ /** Half-open child index range selected for the current render traversal. */
33
+ export interface RenderChildRange {
34
+ /** Index of the first child to visit. */
35
+ start: number;
36
+ /** Index after the last child to visit. */
37
+ end: number;
38
+ }
32
39
  /**
33
40
  * Describes an entity that renders as a single filled circle at its local
34
41
  * origin, returned from {@link Entity.getBatchCircle} to opt into the renderer's
@@ -577,6 +584,12 @@ export declare class VectoJSEvent<N = unknown> {
577
584
  * }
578
585
  */
579
586
  export declare abstract class Entity {
587
+ /**
588
+ * Whether Scene may ask {@link getRenderChildRange} to prune this entity's
589
+ * visual child traversal. Off by default so ordinary containers pay no
590
+ * viewport-inversion cost.
591
+ */
592
+ viewportCullChildren: boolean;
580
593
  id: string;
581
594
  children: Entity[];
582
595
  parent: Entity | null;
@@ -1057,6 +1070,22 @@ export declare abstract class Entity {
1057
1070
  * @returns The local bounds, or `null` to opt out of culling.
1058
1071
  */
1059
1072
  getBounds(): Bounds | null;
1073
+ /**
1074
+ * Select a contiguous subset of direct children for visual rendering.
1075
+ *
1076
+ * Return `null` (the default) to visit every child. Containers may override
1077
+ * this only when child order and geometry prove that every skipped child's
1078
+ * entire painted subtree lies outside `localViewport`. The Scene still keeps
1079
+ * all children resident, and hit testing, accessibility, and content
1080
+ * projection continue to see the complete tree. Skipped subtrees do not run
1081
+ * `update()` or `render()` in the visual pass, so opt in only for children that
1082
+ * are static while offscreen or whose state advances outside those methods.
1083
+ *
1084
+ * The returned range is half-open and is clamped to `children.length`.
1085
+ * `localViewport` is a conservative axis-aligned box obtained by mapping the
1086
+ * Scene viewport into this entity's local coordinate space.
1087
+ */
1088
+ getRenderChildRange(localViewport: Bounds): RenderChildRange | null;
1060
1089
  /**
1061
1090
  * Opt into the renderer's draw-call batching fast-path for point-cloud /
1062
1091
  * particle entities that draw as a single filled circle at their local origin.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vectojs/core",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },