@lightningjs/renderer 0.6.0 → 0.6.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 (40) hide show
  1. package/README.md +1 -1
  2. package/dist/src/core/CoreNode.d.ts +63 -15
  3. package/dist/src/core/CoreNode.js +238 -118
  4. package/dist/src/core/CoreNode.js.map +1 -1
  5. package/dist/src/core/CoreTextNode.d.ts +1 -0
  6. package/dist/src/core/CoreTextNode.js +13 -0
  7. package/dist/src/core/CoreTextNode.js.map +1 -1
  8. package/dist/src/core/Stage.d.ts +6 -2
  9. package/dist/src/core/Stage.js +24 -21
  10. package/dist/src/core/Stage.js.map +1 -1
  11. package/dist/src/core/animations/CoreAnimation.js +11 -2
  12. package/dist/src/core/animations/CoreAnimation.js.map +1 -1
  13. package/dist/src/core/lib/ContextSpy.d.ts +12 -0
  14. package/dist/src/core/lib/ContextSpy.js +38 -0
  15. package/dist/src/core/lib/ContextSpy.js.map +1 -0
  16. package/dist/src/core/lib/WebGlContext.d.ts +414 -0
  17. package/dist/src/core/lib/WebGlContext.js +640 -0
  18. package/dist/src/core/lib/WebGlContext.js.map +1 -0
  19. package/dist/src/core/lib/WebGlContextWrapper.d.ts +496 -0
  20. package/dist/src/core/lib/WebGlContextWrapper.js +779 -0
  21. package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -0
  22. package/dist/src/core/platform.js +4 -0
  23. package/dist/src/core/platform.js.map +1 -1
  24. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +9 -8
  25. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  26. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js +4 -5
  27. package/dist/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.js.map +1 -1
  28. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +15 -13
  29. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  30. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  31. package/package.json +1 -1
  32. package/src/core/CoreNode.ts +293 -149
  33. package/src/core/CoreTextNode.ts +16 -0
  34. package/src/core/Stage.ts +28 -31
  35. package/src/core/animations/CoreAnimation.ts +11 -2
  36. package/src/core/platform.ts +5 -0
  37. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +9 -40
  38. package/src/core/text-rendering/font-face-types/SdfTrFontFace/SdfTrFontFace.ts +4 -5
  39. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +19 -15
  40. package/src/core/scene/Scene.ts +0 -120
@@ -17,7 +17,7 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import { assertTruthy } from '../utils.js';
20
+ import { assertTruthy, mergeColorAlphaPremultiplied } from '../utils.js';
21
21
  import type { ShaderMap } from './CoreShaderManager.js';
22
22
  import type {
23
23
  ExtractProps,
@@ -37,7 +37,11 @@ import type {
37
37
  NodeTextureLoadedPayload,
38
38
  } from '../common/CommonTypes.js';
39
39
  import { EventEmitter } from '../common/EventEmitter.js';
40
- import { intersectRect, type Rect } from './lib/utils.js';
40
+ import {
41
+ getNormalizedAlphaComponent,
42
+ intersectRect,
43
+ type Rect,
44
+ } from './lib/utils.js';
41
45
  import { Matrix3d } from './lib/Matrix3d.js';
42
46
 
43
47
  export interface CoreNodeProps {
@@ -82,17 +86,83 @@ type ICoreNode = Omit<
82
86
  'texture' | 'textureOptions' | 'shader' | 'shaderProps'
83
87
  >;
84
88
 
89
+ enum UpdateType {
90
+ /**
91
+ * Child updates
92
+ */
93
+ Children = 1,
94
+
95
+ /**
96
+ * Scale/Rotate transform update
97
+ */
98
+ ScaleRotate = 2,
99
+
100
+ /**
101
+ * Translate transform update (x/y/width/height/pivot/mount)
102
+ */
103
+ Local = 4,
104
+
105
+ /**
106
+ * Global transform update
107
+ */
108
+ Global = 8,
109
+
110
+ /**
111
+ * Clipping rect update
112
+ */
113
+ Clipping = 16,
114
+
115
+ /**
116
+ * Calculated ZIndex update
117
+ */
118
+ CalculatedZIndex = 32,
119
+
120
+ /**
121
+ * Z-Index Sorted Children update
122
+ */
123
+ ZIndexSortedChildren = 64,
124
+
125
+ /**
126
+ * Premultiplied Colors
127
+ */
128
+ PremultipliedColors = 128,
129
+
130
+ /**
131
+ * World Alpha
132
+ *
133
+ * @remarks
134
+ * World Alpha = Parent World Alpha * Alpha
135
+ */
136
+ WorldAlpha = 256,
137
+
138
+ /**
139
+ * None
140
+ */
141
+ None = 0,
142
+
143
+ /**
144
+ * All
145
+ */
146
+ All = 511,
147
+ }
148
+
85
149
  export class CoreNode extends EventEmitter implements ICoreNode {
86
150
  readonly children: CoreNode[] = [];
87
151
  protected props: Required<CoreNodeProps>;
88
152
 
89
- public recalculationType = 0;
90
- public hasUpdates = true;
153
+ public updateType = UpdateType.All;
91
154
  public globalTransform?: Matrix3d;
92
155
  public scaleRotateTransform?: Matrix3d;
93
156
  public localTransform?: Matrix3d;
94
157
  public clippingRect: Rect | null = null;
158
+ public isRenderable = false;
95
159
  private parentClippingRect: Rect | null = null;
160
+ public worldAlpha = 1;
161
+ public premultipliedColorTl = 0;
162
+ public premultipliedColorTr = 0;
163
+ public premultipliedColorBl = 0;
164
+ public premultipliedColorBr = 0;
165
+ public calcZIndex = 0;
96
166
 
97
167
  private isComplex = false;
98
168
 
@@ -122,6 +192,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
122
192
 
123
193
  this.props.texture = texture;
124
194
  this.props.textureOptions = options;
195
+ this.checkIsRenderable();
125
196
 
126
197
  // If texture is already loaded / failed, trigger loaded event manually
127
198
  // so that users get a consistent event experience.
@@ -145,9 +216,13 @@ export class CoreNode extends EventEmitter implements ICoreNode {
145
216
  }
146
217
  this.props.texture = null;
147
218
  this.props.textureOptions = null;
219
+ this.checkIsRenderable();
148
220
  }
149
221
 
150
222
  private onTextureLoaded: TextureLoadedEventHandler = (target, dimensions) => {
223
+ // Texture was loaded. In case the RAF loop has already stopped, we request
224
+ // a render to ensure the texture is rendered.
225
+ this.stage.requestRender();
151
226
  this.emit('loaded', {
152
227
  type: 'texture',
153
228
  dimensions,
@@ -171,66 +246,41 @@ export class CoreNode extends EventEmitter implements ICoreNode {
171
246
  const { shader, props: p } = shManager.loadShader(shaderType, props);
172
247
  this.props.shader = shader;
173
248
  this.props.shaderProps = p;
174
- }
175
-
176
- setHasUpdates(): void {
177
- this.hasUpdates = true;
178
- }
179
-
180
- setChildrenHasUpdates(): void {
181
- this.children.forEach((child) => {
182
- child.setRecalculationType(2);
183
- });
184
- }
185
-
186
- setParentHasUpdates(): void {
187
- if (!this.props.parent) {
188
- return;
189
- }
190
-
191
- this.props.parent.setRecalculationType(1);
249
+ this.checkIsRenderable();
192
250
  }
193
251
 
194
252
  /**
195
253
  * Change types types is used to determine the scope of the changes being applied
196
- * 1 - alpha recalculation
197
- * 2 - translate recalculation
198
- * 4 - transform recalculation
199
- * 8 - z-index recalculation
254
+ *
255
+ * @remarks
256
+ * See {@link UpdateType} for more information on each type
200
257
  *
201
258
  * @param type
202
259
  */
203
- setRecalculationType(type: number): void {
204
- this.recalculationType |= type;
205
- this.setHasUpdates();
206
-
207
- // always forcing parent updates so the root will have an hasUpdates flag
208
- this.setParentHasUpdates();
209
-
210
- if (type & 4) {
211
- this.setChildrenHasUpdates();
260
+ setUpdateType(type: UpdateType): void {
261
+ this.updateType |= type;
262
+
263
+ // If we're updating this node at all, we need to inform the parent
264
+ // (and all ancestors) that their children need updating as well
265
+ const parent = this.props.parent;
266
+ if (parent && !(parent.updateType & UpdateType.Children)) {
267
+ parent.setUpdateType(UpdateType.Children);
212
268
  }
213
269
  }
214
270
 
215
271
  sortChildren() {
216
- this.children.sort((a, b) => a.zIndex - b.zIndex);
272
+ this.children.sort((a, b) => a.calcZIndex - b.calcZIndex);
217
273
  }
218
274
 
219
275
  updateScaleRotateTransform() {
220
- this.setRecalculationType(4);
221
-
222
276
  this.scaleRotateTransform = Matrix3d.rotate(
223
277
  this.props.rotation,
224
278
  this.scaleRotateTransform,
225
279
  ).scale(this.props.scaleX, this.props.scaleY);
226
-
227
- // do transformations when matrix is implemented
228
- this.updateLocalTransform();
229
280
  }
230
281
 
231
282
  updateLocalTransform() {
232
283
  assertTruthy(this.scaleRotateTransform);
233
- this.setRecalculationType(2);
234
284
  const pivotTranslateX = this.props.pivotX * this.props.width;
235
285
  const pivotTranslateY = this.props.pivotY * this.props.height;
236
286
  const mountTranslateX = this.props.mountX * this.props.width;
@@ -243,6 +293,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
243
293
  )
244
294
  .multiply(this.scaleRotateTransform)
245
295
  .translate(-pivotTranslateX, -pivotTranslateY);
296
+ this.setUpdateType(UpdateType.Global);
246
297
  }
247
298
 
248
299
  /**
@@ -250,45 +301,145 @@ export class CoreNode extends EventEmitter implements ICoreNode {
250
301
  * @param delta
251
302
  */
252
303
  update(delta: number, parentClippingRect: Rect | null = null): void {
253
- assertTruthy(this.localTransform);
254
- const parentGlobalTransform = this.parent?.globalTransform;
255
- if (parentGlobalTransform) {
256
- this.globalTransform = Matrix3d.copy(
257
- parentGlobalTransform,
258
- this.globalTransform,
259
- ).multiply(this.localTransform);
260
- } else {
304
+ if (this.updateType & UpdateType.ScaleRotate) {
305
+ this.updateScaleRotateTransform();
306
+ this.setUpdateType(UpdateType.Local);
307
+ }
308
+
309
+ if (this.updateType & UpdateType.Local) {
310
+ this.updateLocalTransform();
311
+ this.setUpdateType(UpdateType.Global);
312
+ }
313
+
314
+ const parent = this.props.parent;
315
+ let childUpdateType = UpdateType.None;
316
+ if (this.updateType & UpdateType.Global) {
317
+ assertTruthy(this.localTransform);
261
318
  this.globalTransform = Matrix3d.copy(
262
- this.localTransform,
319
+ parent?.globalTransform || this.localTransform,
263
320
  this.globalTransform,
264
321
  );
322
+
323
+ if (parent) {
324
+ this.globalTransform.multiply(this.localTransform);
325
+ }
326
+
327
+ this.setUpdateType(UpdateType.Clipping | UpdateType.Children);
328
+ childUpdateType |= UpdateType.Global;
329
+ }
330
+
331
+ if (this.updateType & UpdateType.Clipping) {
332
+ this.calculateClippingRect(parentClippingRect);
333
+ this.checkIsRenderable();
334
+ this.setUpdateType(UpdateType.Children);
335
+ childUpdateType |= UpdateType.Clipping;
265
336
  }
266
337
 
267
- this.calculateClippingRect(parentClippingRect);
338
+ if (this.updateType & UpdateType.WorldAlpha) {
339
+ if (parent) {
340
+ this.worldAlpha = parent.worldAlpha * this.props.alpha;
341
+ } else {
342
+ this.worldAlpha = this.props.alpha;
343
+ }
344
+ this.setUpdateType(UpdateType.Children | UpdateType.PremultipliedColors);
345
+ childUpdateType |= UpdateType.WorldAlpha;
346
+ }
347
+
348
+ if (this.updateType & UpdateType.PremultipliedColors) {
349
+ this.premultipliedColorTl = mergeColorAlphaPremultiplied(
350
+ this.props.colorTl,
351
+ this.worldAlpha,
352
+ true,
353
+ );
354
+ this.premultipliedColorTr = mergeColorAlphaPremultiplied(
355
+ this.props.colorTr,
356
+ this.worldAlpha,
357
+ true,
358
+ );
359
+ this.premultipliedColorBl = mergeColorAlphaPremultiplied(
360
+ this.props.colorBl,
361
+ this.worldAlpha,
362
+ true,
363
+ );
364
+ this.premultipliedColorBr = mergeColorAlphaPremultiplied(
365
+ this.props.colorBr,
366
+ this.worldAlpha,
367
+ true,
368
+ );
369
+
370
+ this.checkIsRenderable();
371
+ this.setUpdateType(UpdateType.Children);
372
+ childUpdateType |= UpdateType.PremultipliedColors;
373
+ }
268
374
 
269
- if (this.children.length) {
375
+ // No need to update zIndex if there is no parent
376
+ if (parent && this.updateType & UpdateType.CalculatedZIndex) {
377
+ this.calculateZIndex();
378
+ // Tell parent to re-sort children
379
+ parent.setUpdateType(UpdateType.ZIndexSortedChildren);
380
+ }
381
+
382
+ if (this.updateType & UpdateType.Children && this.children.length) {
270
383
  this.children.forEach((child) => {
384
+ // Trigger the depenedent update types on the child
385
+ child.setUpdateType(childUpdateType);
386
+ // If child has no updates, skip
387
+ if (child.updateType === 0) {
388
+ return;
389
+ }
271
390
  child.update(delta, this.clippingRect);
272
391
  });
273
392
  }
274
393
 
275
- if (this.recalculationType & 8) {
394
+ // Sorting children MUST happen after children have been updated so
395
+ // that they have the oppotunity to update their calculated zIndex.
396
+ if (this.updateType & UpdateType.ZIndexSortedChildren) {
276
397
  // reorder z-index
277
398
  this.sortChildren();
278
399
  }
279
400
 
280
- // reset update flag
281
- this.hasUpdates = false;
401
+ // reset update type
402
+ this.updateType = 0;
403
+ }
404
+
405
+ // This function checks if the current node is renderable based on certain properties.
406
+ // It returns true if any of the specified properties are truthy or if any color property is not 0, otherwise it returns false.
407
+ checkIsRenderable(): boolean {
408
+ if (this.props.texture) {
409
+ return (this.isRenderable = true);
410
+ }
411
+
412
+ if (this.props.shader) {
413
+ return (this.isRenderable = true);
414
+ }
415
+
416
+ if (this.props.clipping) {
417
+ return (this.isRenderable = true);
418
+ }
419
+
420
+ const colors = [
421
+ 'color',
422
+ 'colorTop',
423
+ 'colorBottom',
424
+ 'colorLeft',
425
+ 'colorRight',
426
+ 'colorTl',
427
+ 'colorTr',
428
+ 'colorBl',
429
+ 'colorBr',
430
+ ];
431
+ if (
432
+ colors.some((color) => this.props[color as keyof CoreNodeProps] !== 0)
433
+ ) {
434
+ return (this.isRenderable = true);
435
+ }
282
436
 
283
- // reset recalculation type
284
- this.recalculationType = 0;
437
+ return (this.isRenderable = false);
285
438
  }
286
439
 
287
440
  /**
288
441
  * This function calculates the clipping rectangle for a node.
289
442
  *
290
- * If the node's globalTransform is not set, the function returns immediately.
291
- * If the node's props do not require clipping and there is no parent clipping rectangle, the node's clipping rectangle is set to null.
292
443
  * If the parent clipping rectangle has not changed and the node's clipping rectangle is already set, the function returns immediately.
293
444
  *
294
445
  * The function then checks if the node is rotated. If the node requires clipping and is not rotated, a new clipping rectangle is created based on the node's global transform and dimensions.
@@ -297,14 +448,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
297
448
  * Finally, the node's parentClippingRect and clippingRect properties are updated.
298
449
  */
299
450
  calculateClippingRect(parentClippingRect: Rect | null = null) {
300
- if (!this.globalTransform) {
301
- return;
302
- }
303
-
304
- if (!this.props.clipping && !parentClippingRect) {
305
- this.clippingRect = null;
306
- return;
307
- }
451
+ assertTruthy(this.globalTransform);
308
452
 
309
453
  if (this.parentClippingRect === parentClippingRect && this.clippingRect) {
310
454
  return;
@@ -332,19 +476,28 @@ export class CoreNode extends EventEmitter implements ICoreNode {
332
476
  this.clippingRect = clippingRect;
333
477
  }
334
478
 
479
+ calculateZIndex(): void {
480
+ const props = this.props;
481
+ const z = props.zIndex || 0;
482
+ const p = props.parent?.zIndex || 0;
483
+
484
+ let zIndex = z;
485
+ if (props.parent?.zIndexLocked) {
486
+ zIndex = z < p ? z : p;
487
+ }
488
+ this.calcZIndex = zIndex;
489
+ }
490
+
335
491
  renderQuads(renderer: CoreRenderer): void {
492
+ const { width, height, texture, textureOptions, shader, shaderProps } =
493
+ this.props;
336
494
  const {
337
- width,
338
- height,
339
- colorTl,
340
- colorTr,
341
- colorBl,
342
- colorBr,
343
- texture,
344
- textureOptions,
345
- shader,
346
- shaderProps,
347
- } = this.props;
495
+ premultipliedColorTl,
496
+ premultipliedColorTr,
497
+ premultipliedColorBl,
498
+ premultipliedColorBr,
499
+ } = this;
500
+
348
501
  const { zIndex, worldAlpha, globalTransform: gt, clippingRect } = this;
349
502
 
350
503
  assertTruthy(gt);
@@ -353,10 +506,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
353
506
  renderer.addQuad({
354
507
  width,
355
508
  height,
356
- colorTl,
357
- colorTr,
358
- colorBl,
359
- colorBr,
509
+ colorTl: premultipliedColorTl,
510
+ colorTr: premultipliedColorTr,
511
+ colorBl: premultipliedColorBl,
512
+ colorBr: premultipliedColorBr,
360
513
  texture,
361
514
  textureOptions,
362
515
  zIndex,
@@ -388,7 +541,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
388
541
  set x(value: number) {
389
542
  if (this.props.x !== value) {
390
543
  this.props.x = value;
391
- this.updateLocalTransform();
544
+ this.setUpdateType(UpdateType.Local);
392
545
  }
393
546
  }
394
547
 
@@ -410,7 +563,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
410
563
  set y(value: number) {
411
564
  if (this.props.y !== value) {
412
565
  this.props.y = value;
413
- this.updateLocalTransform();
566
+ this.setUpdateType(UpdateType.Local);
414
567
  }
415
568
  }
416
569
 
@@ -421,7 +574,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
421
574
  set width(value: number) {
422
575
  if (this.props.width !== value) {
423
576
  this.props.width = value;
424
- this.updateLocalTransform();
577
+ this.setUpdateType(UpdateType.Local);
425
578
  }
426
579
  }
427
580
 
@@ -432,7 +585,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
432
585
  set height(value: number) {
433
586
  if (this.props.height !== value) {
434
587
  this.props.height = value;
435
- this.updateLocalTransform();
588
+ this.setUpdateType(UpdateType.Local);
436
589
  }
437
590
  }
438
591
 
@@ -456,7 +609,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
456
609
  set scaleX(value: number) {
457
610
  if (this.props.scaleX !== value) {
458
611
  this.props.scaleX = value;
459
- this.updateScaleRotateTransform();
612
+ this.setUpdateType(UpdateType.ScaleRotate);
460
613
  }
461
614
  }
462
615
 
@@ -467,35 +620,21 @@ export class CoreNode extends EventEmitter implements ICoreNode {
467
620
  set scaleY(value: number) {
468
621
  if (this.props.scaleY !== value) {
469
622
  this.props.scaleY = value;
470
- this.updateScaleRotateTransform();
623
+ this.setUpdateType(UpdateType.ScaleRotate);
471
624
  }
472
625
  }
473
626
 
474
- get worldScaleX(): number {
475
- return (
476
- this.props.scaleX * (this.props.parent?.worldScaleX ?? 1) ||
477
- this.props.scaleX
478
- );
479
- }
480
-
481
- get worldScaleY(): number {
482
- return (
483
- this.props.scaleY * (this.props.parent?.worldScaleY ?? 1) ||
484
- this.props.scaleY
485
- );
486
- }
487
-
488
627
  get mount(): number {
489
628
  return this.props.mount;
490
629
  }
491
630
 
492
631
  set mount(value: number) {
493
- // if (this.props.mountX !== value || this.props.mountY !== value) {
494
- this.props.mountX = value;
495
- this.props.mountY = value;
496
- this.props.mount = value;
497
- this.updateLocalTransform();
498
- // }
632
+ if (this.props.mountX !== value || this.props.mountY !== value) {
633
+ this.props.mountX = value;
634
+ this.props.mountY = value;
635
+ this.props.mount = value;
636
+ this.setUpdateType(UpdateType.Local);
637
+ }
499
638
  }
500
639
 
501
640
  get mountX(): number {
@@ -503,8 +642,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
503
642
  }
504
643
 
505
644
  set mountX(value: number) {
506
- this.props.mountX = value;
507
- this.updateLocalTransform();
645
+ if (this.props.mountX !== value) {
646
+ this.props.mountX = value;
647
+ this.setUpdateType(UpdateType.Local);
648
+ }
508
649
  }
509
650
 
510
651
  get mountY(): number {
@@ -512,8 +653,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
512
653
  }
513
654
 
514
655
  set mountY(value: number) {
515
- this.props.mountY = value;
516
- this.updateLocalTransform();
656
+ if (this.props.mountY !== value) {
657
+ this.props.mountY = value;
658
+ this.setUpdateType(UpdateType.Local);
659
+ }
517
660
  }
518
661
 
519
662
  get pivot(): number {
@@ -524,7 +667,8 @@ export class CoreNode extends EventEmitter implements ICoreNode {
524
667
  if (this.props.pivotX !== value || this.props.pivotY !== value) {
525
668
  this.props.pivotX = value;
526
669
  this.props.pivotY = value;
527
- this.updateLocalTransform();
670
+ this.props.pivot = value;
671
+ this.setUpdateType(UpdateType.Local);
528
672
  }
529
673
  }
530
674
 
@@ -533,8 +677,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
533
677
  }
534
678
 
535
679
  set pivotX(value: number) {
536
- this.props.pivotX = value;
537
- this.updateLocalTransform();
680
+ if (this.props.pivotX !== value) {
681
+ this.props.pivotX = value;
682
+ this.setUpdateType(UpdateType.Local);
683
+ }
538
684
  }
539
685
 
540
686
  get pivotY(): number {
@@ -542,8 +688,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
542
688
  }
543
689
 
544
690
  set pivotY(value: number) {
545
- this.props.pivotY = value;
546
- this.updateLocalTransform();
691
+ if (this.props.pivotY !== value) {
692
+ this.props.pivotY = value;
693
+ this.setUpdateType(UpdateType.Local);
694
+ }
547
695
  }
548
696
 
549
697
  get rotation(): number {
@@ -553,7 +701,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
553
701
  set rotation(value: number) {
554
702
  if (this.props.rotation !== value) {
555
703
  this.props.rotation = value;
556
- this.updateScaleRotateTransform();
704
+ this.setUpdateType(UpdateType.ScaleRotate);
557
705
  }
558
706
  }
559
707
 
@@ -563,14 +711,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
563
711
 
564
712
  set alpha(value: number) {
565
713
  this.props.alpha = value;
566
- this.setRecalculationType(1);
567
- }
568
-
569
- get worldAlpha(): number {
570
- const props = this.props;
571
- const parent = props.parent;
572
-
573
- return props.alpha * (parent?.worldAlpha || 1);
714
+ this.setUpdateType(UpdateType.PremultipliedColors | UpdateType.WorldAlpha);
574
715
  }
575
716
 
576
717
  get clipping(): boolean {
@@ -579,8 +720,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
579
720
 
580
721
  set clipping(value: boolean) {
581
722
  this.props.clipping = value;
582
- this.clippingRect = null;
583
- this.setRecalculationType(4);
723
+ this.setUpdateType(UpdateType.Clipping);
584
724
  }
585
725
 
586
726
  get color(): number {
@@ -601,7 +741,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
601
741
  }
602
742
  this.props.color = value;
603
743
 
604
- this.setRecalculationType(2);
744
+ this.setUpdateType(UpdateType.PremultipliedColors);
605
745
  }
606
746
 
607
747
  get colorTop(): number {
@@ -614,7 +754,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
614
754
  this.colorTr = value;
615
755
  }
616
756
  this.props.colorTop = value;
617
- this.setRecalculationType(2);
757
+ this.setUpdateType(UpdateType.PremultipliedColors);
618
758
  }
619
759
 
620
760
  get colorBottom(): number {
@@ -627,7 +767,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
627
767
  this.colorBr = value;
628
768
  }
629
769
  this.props.colorBottom = value;
630
- this.setRecalculationType(2);
770
+ this.setUpdateType(UpdateType.PremultipliedColors);
631
771
  }
632
772
 
633
773
  get colorLeft(): number {
@@ -640,7 +780,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
640
780
  this.colorBl = value;
641
781
  }
642
782
  this.props.colorLeft = value;
643
- this.setRecalculationType(2);
783
+ this.setUpdateType(UpdateType.PremultipliedColors);
644
784
  }
645
785
 
646
786
  get colorRight(): number {
@@ -653,7 +793,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
653
793
  this.colorBr = value;
654
794
  }
655
795
  this.props.colorRight = value;
656
- this.setRecalculationType(2);
796
+ this.setUpdateType(UpdateType.PremultipliedColors);
657
797
  }
658
798
 
659
799
  get colorTl(): number {
@@ -662,7 +802,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
662
802
 
663
803
  set colorTl(value: number) {
664
804
  this.props.colorTl = value;
665
- this.setRecalculationType(2);
805
+ this.setUpdateType(UpdateType.PremultipliedColors);
666
806
  }
667
807
 
668
808
  get colorTr(): number {
@@ -671,7 +811,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
671
811
 
672
812
  set colorTr(value: number) {
673
813
  this.props.colorTr = value;
674
- this.setRecalculationType(2);
814
+ this.setUpdateType(UpdateType.PremultipliedColors);
675
815
  }
676
816
 
677
817
  get colorBl(): number {
@@ -680,7 +820,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
680
820
 
681
821
  set colorBl(value: number) {
682
822
  this.props.colorBl = value;
683
- this.setRecalculationType(2);
823
+ this.setUpdateType(UpdateType.PremultipliedColors);
684
824
  }
685
825
 
686
826
  get colorBr(): number {
@@ -689,7 +829,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
689
829
 
690
830
  set colorBr(value: number) {
691
831
  this.props.colorBr = value;
692
- this.setRecalculationType(2);
832
+ this.setUpdateType(UpdateType.PremultipliedColors);
693
833
  }
694
834
 
695
835
  // we're only interested in parent zIndex to test
@@ -700,22 +840,22 @@ export class CoreNode extends EventEmitter implements ICoreNode {
700
840
 
701
841
  set zIndexLocked(value: number) {
702
842
  this.props.zIndexLocked = value;
843
+ this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
844
+ this.children.forEach((child) => {
845
+ child.setUpdateType(UpdateType.CalculatedZIndex);
846
+ });
703
847
  }
704
848
 
705
849
  get zIndex(): number {
706
- const props = this.props;
707
- const z = props.zIndex || 0;
708
- const p = props.parent?.zIndex || 0;
709
-
710
- if (props.parent?.zIndexLocked) {
711
- return z < p ? z : p;
712
- }
713
- return z;
850
+ return this.props.zIndex;
714
851
  }
715
852
 
716
853
  set zIndex(value: number) {
717
854
  this.props.zIndex = value;
718
- this.props.parent?.setRecalculationType(8);
855
+ this.setUpdateType(UpdateType.CalculatedZIndex | UpdateType.Children);
856
+ this.children.forEach((child) => {
857
+ child.setUpdateType(UpdateType.CalculatedZIndex);
858
+ });
719
859
  }
720
860
 
721
861
  get parent(): CoreNode | null {
@@ -738,8 +878,12 @@ export class CoreNode extends EventEmitter implements ICoreNode {
738
878
  }
739
879
  if (newParent) {
740
880
  newParent.children.push(this);
741
- // force parent to recalculate z-index for its children
742
- newParent.setRecalculationType(8);
881
+ // Since this node has a new parent, to be safe, have it do a full update.
882
+ this.setUpdateType(UpdateType.All);
883
+ // Tell parent that it's children need to be updated and sorted.
884
+ newParent.setUpdateType(
885
+ UpdateType.Children | UpdateType.ZIndexSortedChildren,
886
+ );
743
887
  }
744
888
 
745
889
  this.updateScaleRotateTransform();