@lightningjs/renderer 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/dist/src/core/CoreNode.d.ts +4 -7
  2. package/dist/src/core/CoreNode.js +29 -25
  3. package/dist/src/core/CoreNode.js.map +1 -1
  4. package/dist/src/core/CoreTextNode.d.ts +2 -2
  5. package/dist/src/core/CoreTextNode.js +1 -1
  6. package/dist/src/core/CoreTextNode.js.map +1 -1
  7. package/dist/src/core/Stage.js +1 -1
  8. package/dist/src/core/Stage.js.map +1 -1
  9. package/dist/src/core/animations/CoreAnimation.d.ts +1 -0
  10. package/dist/src/core/animations/CoreAnimation.js +7 -0
  11. package/dist/src/core/animations/CoreAnimation.js.map +1 -1
  12. package/dist/src/core/lib/utils.d.ts +9 -0
  13. package/dist/src/core/lib/utils.js +48 -1
  14. package/dist/src/core/lib/utils.js.map +1 -1
  15. package/dist/src/core/renderers/CoreRenderer.d.ts +2 -2
  16. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.d.ts +3 -3
  17. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js +1 -1
  18. package/dist/src/core/renderers/webgl/WebGlCoreRenderOp.js.map +1 -1
  19. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +5 -0
  20. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  21. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -2
  22. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  23. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +6 -10
  24. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +73 -46
  25. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  26. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js +1 -1
  27. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.js.map +1 -1
  28. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +2 -2
  29. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  30. package/package.json +1 -1
  31. package/src/core/CoreNode.ts +30 -29
  32. package/src/core/CoreTextNode.ts +2 -2
  33. package/src/core/Stage.ts +1 -1
  34. package/src/core/animations/CoreAnimation.ts +8 -0
  35. package/src/core/lib/utils.ts +68 -1
  36. package/src/core/renderers/CoreRenderer.ts +2 -2
  37. package/src/core/renderers/webgl/WebGlCoreRenderOp.ts +3 -3
  38. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +7 -1
  39. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +2 -1
  40. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +96 -58
  41. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/setRenderWindow.ts +1 -1
  42. package/src/core/text-rendering/renderers/TextRenderer.ts +2 -2
@@ -18,13 +18,15 @@
18
18
  */
19
19
 
20
20
  import {
21
- intersectBound,
22
21
  type Bound,
23
22
  type Rect,
24
23
  createBound,
25
24
  type BoundWithValid,
26
25
  intersectRect,
27
- isBoundPositive,
26
+ type RectWithValid,
27
+ copyRect,
28
+ boundsOverlap,
29
+ convertBoundToRect,
28
30
  } from '../../../lib/utils.js';
29
31
  import {
30
32
  TextRenderer,
@@ -84,7 +86,9 @@ export interface SdfTextRendererState extends TextRendererState {
84
86
 
85
87
  renderWindow: SdfRenderWindow;
86
88
 
87
- visibleWindow: BoundWithValid;
89
+ elementBounds: BoundWithValid;
90
+
91
+ clippingRect: RectWithValid;
88
92
 
89
93
  bufferNumFloats: number;
90
94
 
@@ -104,13 +108,14 @@ export interface SdfTextRendererState extends TextRendererState {
104
108
  }
105
109
 
106
110
  /**
107
- * Ephemeral bounds object used for intersection calculations
108
- *
109
- * @remarks
110
- * Used to avoid creating a new object every time we need to intersect
111
- * element bounds.
111
+ * Ephemeral rect object used for calculations
112
112
  */
113
- const tmpElementBounds = createBound(0, 0, 0, 0);
113
+ const tmpRect: Rect = {
114
+ x: 0,
115
+ y: 0,
116
+ width: 0,
117
+ height: 0,
118
+ };
114
119
 
115
120
  /**
116
121
  * Singleton class for rendering text using signed distance fields.
@@ -178,11 +183,31 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
178
183
  },
179
184
  x: (state, value) => {
180
185
  state.props.x = value;
181
- this.invalidateVisibleWindowCache(state);
186
+ if (state.elementBounds.valid) {
187
+ this.setElementBoundsX(state);
188
+ // Only schedule an update if the text is not already rendered
189
+ // (renderWindow is invalid) and the element possibly overlaps the screen
190
+ // This is to avoid unnecessary updates when we know text is off-screen
191
+ if (
192
+ !state.renderWindow.valid &&
193
+ boundsOverlap(state.elementBounds, this.rendererBounds)
194
+ ) {
195
+ this.scheduleUpdateState(state);
196
+ }
197
+ }
182
198
  },
183
199
  y: (state, value) => {
184
200
  state.props.y = value;
185
- this.invalidateVisibleWindowCache(state);
201
+ if (state.elementBounds.valid) {
202
+ this.setElementBoundsY(state);
203
+ // See x() for explanation
204
+ if (
205
+ !state.renderWindow.valid &&
206
+ boundsOverlap(state.elementBounds, this.rendererBounds)
207
+ ) {
208
+ this.scheduleUpdateState(state);
209
+ }
210
+ }
186
211
  },
187
212
  contain: (state, value) => {
188
213
  state.props.contain = value;
@@ -304,13 +329,20 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
304
329
  numLines: 0,
305
330
  valid: false,
306
331
  },
307
- visibleWindow: {
332
+ elementBounds: {
308
333
  x1: 0,
309
334
  y1: 0,
310
335
  x2: 0,
311
336
  y2: 0,
312
337
  valid: false,
313
338
  },
339
+ clippingRect: {
340
+ x: 0,
341
+ y: 0,
342
+ width: 0,
343
+ height: 0,
344
+ valid: false,
345
+ },
314
346
  bufferNumFloats: 0,
315
347
  bufferNumQuads: 0,
316
348
  vertexBuffer: undefined,
@@ -410,22 +442,11 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
410
442
  vertexBuffer = new Float32Array(neededLength * 2);
411
443
  }
412
444
 
413
- const visibleWindow = state.visibleWindow;
414
- // If the visibleWindow is not valid, calculate it
415
- if (!visibleWindow.valid) {
416
- // Figure out whats actually in the bounds of the renderer/canvas (visibleWindow)
417
- const elementBounds = createBound(
418
- x,
419
- y,
420
- contain !== 'none' ? x + width : Infinity,
421
- contain === 'both' ? y + height : Infinity,
422
- tmpElementBounds, // Prevent allocation by using this temp object
423
- );
424
- /**
425
- * Area that is visible on the screen.
426
- */
427
- intersectBound(this.rendererBounds, elementBounds, state.visibleWindow);
428
- visibleWindow.valid = true;
445
+ const elementBounds = state.elementBounds;
446
+ if (!elementBounds.valid) {
447
+ this.setElementBoundsX(state);
448
+ this.setElementBoundsY(state);
449
+ elementBounds.valid = true;
429
450
  }
430
451
 
431
452
  // Return early if we're still viewing inside the established render window
@@ -434,10 +455,10 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
434
455
  if (!forceFullLayoutCalc && renderWindow.valid) {
435
456
  const rwScreen = renderWindow.screen;
436
457
  if (
437
- x + rwScreen.x1 <= visibleWindow.x1 &&
438
- x + rwScreen.x2 >= visibleWindow.x2 &&
439
- y - scrollY + rwScreen.y1 <= visibleWindow.y1 &&
440
- y - scrollY + rwScreen.y2 >= visibleWindow.y2
458
+ x + rwScreen.x1 <= elementBounds.x1 &&
459
+ x + rwScreen.x2 >= elementBounds.x2 &&
460
+ y - scrollY + rwScreen.y1 <= elementBounds.y1 &&
461
+ y - scrollY + rwScreen.y2 >= elementBounds.y2
441
462
  ) {
442
463
  this.setStatus(state, 'loaded');
443
464
  return;
@@ -451,14 +472,24 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
451
472
 
452
473
  // Create a new renderWindow if needed
453
474
  if (!renderWindow.valid) {
475
+ const isPossiblyOnScreen = boundsOverlap(
476
+ elementBounds,
477
+ this.rendererBounds,
478
+ );
479
+
480
+ if (!isPossiblyOnScreen) {
481
+ // If the element is not possibly on screen, we can skip the layout and rendering completely
482
+ return;
483
+ }
484
+
454
485
  setRenderWindow(
455
486
  renderWindow,
456
487
  x,
457
488
  y,
458
489
  scrollY,
459
490
  lineHeight,
460
- visibleWindow.y2 - visibleWindow.y1,
461
- visibleWindow,
491
+ contain === 'both' ? elementBounds.y2 - elementBounds.y1 : 0,
492
+ elementBounds,
462
493
  fontSizeRatio,
463
494
  );
464
495
  // console.log('newRenderWindow', renderWindow);
@@ -531,7 +562,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
531
562
  override renderQuads(
532
563
  state: SdfTextRendererState,
533
564
  transform: Matrix3d,
534
- clippingRect: Rect | null,
565
+ clippingRect: Readonly<RectWithValid>,
535
566
  alpha: number,
536
567
  ): void {
537
568
  if (!state.vertexBuffer) {
@@ -553,6 +584,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
553
584
  vertexBuffer,
554
585
  bufferUploaded,
555
586
  trFontFace,
587
+ elementBounds,
556
588
  } = state;
557
589
 
558
590
  let { webGlBuffers } = state;
@@ -599,18 +631,21 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
599
631
  }
600
632
 
601
633
  assertTruthy(trFontFace);
602
-
603
634
  if (scrollable && contain === 'both') {
604
- const visibleWindowRect: Rect = {
605
- x: state.visibleWindow.x1,
606
- y: state.visibleWindow.y1,
607
- width: state.visibleWindow.x2 - state.visibleWindow.x1,
608
- height: state.visibleWindow.y2 - state.visibleWindow.y1,
609
- };
610
-
611
- clippingRect = clippingRect
612
- ? intersectRect(clippingRect, visibleWindowRect)
613
- : visibleWindowRect;
635
+ assertTruthy(elementBounds.valid);
636
+ const elementRect = convertBoundToRect(elementBounds, tmpRect);
637
+
638
+ if (clippingRect.valid) {
639
+ state.clippingRect.valid = true;
640
+ clippingRect = intersectRect(
641
+ clippingRect,
642
+ elementRect,
643
+ state.clippingRect,
644
+ );
645
+ } else {
646
+ state.clippingRect.valid = true;
647
+ clippingRect = copyRect(elementRect, state.clippingRect);
648
+ }
614
649
  }
615
650
 
616
651
  const renderOp = new WebGlCoreRenderOp(
@@ -707,17 +742,6 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
707
742
  ) as SdfTrFontFace | undefined;
708
743
  }
709
744
 
710
- /**
711
- * Invalidate the visible window stored in the state. This will cause a new
712
- * visible window to be calculated on the next update.
713
- *
714
- * @param state
715
- */
716
- protected invalidateVisibleWindowCache(state: SdfTextRendererState): void {
717
- state.visibleWindow.valid = false;
718
- this.scheduleUpdateState(state);
719
- }
720
-
721
745
  /**
722
746
  * Invalidate the layout cache stored in the state. This will cause the text
723
747
  * to be re-layed out on the next update.
@@ -728,12 +752,26 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
728
752
  * @param state
729
753
  */
730
754
  protected invalidateLayoutCache(state: SdfTextRendererState): void {
731
- state.visibleWindow.valid = false;
732
755
  state.renderWindow.valid = false;
756
+ state.elementBounds.valid = false;
733
757
  state.textH = undefined;
734
758
  state.textW = undefined;
735
759
  state.lineCache = [];
736
760
  this.setStatus(state, 'loading');
737
761
  this.scheduleUpdateState(state);
738
762
  }
763
+
764
+ protected setElementBoundsX(state: SdfTextRendererState): void {
765
+ const { x, contain, width } = state.props;
766
+ const { elementBounds } = state;
767
+ elementBounds.x1 = x;
768
+ elementBounds.x2 = contain !== 'none' ? x + width : Infinity;
769
+ }
770
+
771
+ protected setElementBoundsY(state: SdfTextRendererState): void {
772
+ const { y, contain, height } = state.props;
773
+ const { elementBounds } = state;
774
+ elementBounds.y1 = y;
775
+ elementBounds.y2 = contain === 'both' ? y + height : Infinity;
776
+ }
739
777
  }
@@ -87,7 +87,7 @@ export function setRenderWindow(
87
87
  sdf.y2 = y2 / fontSizeRatio;
88
88
 
89
89
  outRenderWindow.numLines = Math.ceil((y2 - y1) / lineHeight);
90
- outRenderWindow.firstLineIdx = Math.floor(y1 / lineHeight);
90
+ outRenderWindow.firstLineIdx = lineHeight ? Math.floor(y1 / lineHeight) : 0;
91
91
  }
92
92
  outRenderWindow.valid = true;
93
93
  }
@@ -20,7 +20,7 @@
20
20
  import type { EventEmitter } from '../../../common/EventEmitter.js';
21
21
  import type { Stage } from '../../Stage.js';
22
22
  import type { Matrix3d } from '../../lib/Matrix3d.js';
23
- import type { Rect } from '../../lib/utils.js';
23
+ import type { Rect, RectWithValid } from '../../lib/utils.js';
24
24
  import type {
25
25
  TrFontFace,
26
26
  TrFontFaceDescriptors,
@@ -498,7 +498,7 @@ export abstract class TextRenderer<
498
498
  abstract renderQuads(
499
499
  state: StateT,
500
500
  transform: Matrix3d,
501
- clippingRect: Rect | null,
501
+ clippingRect: RectWithValid,
502
502
  alpha: number,
503
503
  ): void;
504
504
  }