@lightningjs/renderer 0.4.1 → 0.5.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.
Files changed (162) hide show
  1. package/README.md +13 -13
  2. package/dist/exports/utils.d.ts +1 -1
  3. package/dist/exports/utils.js +1 -1
  4. package/dist/exports/utils.js.map +1 -1
  5. package/dist/src/common/CommonTypes.d.ts +32 -12
  6. package/dist/src/core/CoreNode.d.ts +14 -18
  7. package/dist/src/core/CoreNode.js +90 -124
  8. package/dist/src/core/CoreNode.js.map +1 -1
  9. package/dist/src/core/CoreTextNode.d.ts +0 -2
  10. package/dist/src/core/CoreTextNode.js +39 -17
  11. package/dist/src/core/CoreTextNode.js.map +1 -1
  12. package/dist/src/core/Stage.js +11 -8
  13. package/dist/src/core/Stage.js.map +1 -1
  14. package/dist/src/core/lib/Matrix3d.d.ts +59 -0
  15. package/dist/src/core/lib/Matrix3d.js +253 -0
  16. package/dist/src/core/lib/Matrix3d.js.map +1 -0
  17. package/dist/src/core/lib/utils.d.ts +1 -0
  18. package/dist/src/core/lib/utils.js +3 -0
  19. package/dist/src/core/lib/utils.js.map +1 -1
  20. package/dist/src/core/renderers/CoreRenderer.d.ts +2 -4
  21. package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
  22. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +1 -1
  23. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +16 -16
  24. package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
  25. package/dist/src/core/renderers/webgl/WebGlCoreShader.d.ts +1 -1
  26. package/dist/src/core/renderers/webgl/WebGlCoreShader.js +2 -2
  27. package/dist/src/core/renderers/webgl/WebGlCoreShader.js.map +1 -1
  28. package/dist/src/core/renderers/webgl/shaders/SdfShader.d.ts +2 -1
  29. package/dist/src/core/renderers/webgl/shaders/SdfShader.js +15 -6
  30. package/dist/src/core/renderers/webgl/shaders/SdfShader.js.map +1 -1
  31. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -1
  32. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +27 -30
  33. package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
  34. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +2 -1
  35. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +7 -9
  36. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
  37. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +0 -1
  38. package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
  39. package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +24 -2
  40. package/dist/src/core/text-rendering/renderers/TextRenderer.js +6 -3
  41. package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
  42. package/dist/src/core/textures/SubTexture.js +1 -1
  43. package/dist/src/core/textures/SubTexture.js.map +1 -1
  44. package/dist/src/core/textures/Texture.d.ts +13 -1
  45. package/dist/src/core/textures/Texture.js.map +1 -1
  46. package/dist/src/main-api/INode.d.ts +30 -5
  47. package/dist/src/main-api/RendererMain.js +3 -1
  48. package/dist/src/main-api/RendererMain.js.map +1 -1
  49. package/dist/src/render-drivers/main/MainOnlyNode.d.ts +6 -4
  50. package/dist/src/render-drivers/main/MainOnlyNode.js +31 -16
  51. package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
  52. package/dist/src/render-drivers/main/MainOnlyTextNode.d.ts +0 -2
  53. package/dist/src/render-drivers/main/MainOnlyTextNode.js +3 -10
  54. package/dist/src/render-drivers/main/MainOnlyTextNode.js.map +1 -1
  55. package/dist/src/render-drivers/threadx/NodeStruct.d.ts +6 -3
  56. package/dist/src/render-drivers/threadx/NodeStruct.js +12 -3
  57. package/dist/src/render-drivers/threadx/NodeStruct.js.map +1 -1
  58. package/dist/src/render-drivers/threadx/SharedNode.d.ts +2 -1
  59. package/dist/src/render-drivers/threadx/SharedNode.js +2 -1
  60. package/dist/src/render-drivers/threadx/SharedNode.js.map +1 -1
  61. package/dist/src/render-drivers/threadx/ThreadXMainNode.d.ts +2 -0
  62. package/dist/src/render-drivers/threadx/ThreadXMainNode.js +14 -0
  63. package/dist/src/render-drivers/threadx/ThreadXMainNode.js.map +1 -1
  64. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js +4 -2
  65. package/dist/src/render-drivers/threadx/ThreadXRenderDriver.js.map +1 -1
  66. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +6 -5
  67. package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
  68. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js +2 -7
  69. package/dist/src/render-drivers/threadx/worker/ThreadXRendererTextNode.js.map +1 -1
  70. package/dist/src/render-drivers/threadx/worker/renderer.js +2 -1
  71. package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
  72. package/dist/src/utils.d.ts +7 -12
  73. package/dist/src/utils.js +9 -25
  74. package/dist/src/utils.js.map +1 -1
  75. package/dist/tsconfig.dist.tsbuildinfo +1 -1
  76. package/exports/utils.ts +1 -1
  77. package/package.json +22 -19
  78. package/src/common/CommonTypes.ts +42 -16
  79. package/src/core/CoreNode.ts +120 -158
  80. package/src/core/CoreTextNode.ts +54 -24
  81. package/src/core/Stage.ts +16 -11
  82. package/src/core/lib/Matrix3d.ts +290 -0
  83. package/src/core/lib/utils.ts +4 -0
  84. package/src/core/renderers/CoreRenderer.ts +2 -4
  85. package/src/core/renderers/webgl/WebGlCoreRenderer.ts +18 -21
  86. package/src/core/renderers/webgl/WebGlCoreShader.ts +2 -2
  87. package/src/core/renderers/webgl/shaders/SdfShader.ts +17 -8
  88. package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +30 -28
  89. package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +9 -20
  90. package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +0 -1
  91. package/src/core/text-rendering/renderers/TextRenderer.ts +37 -5
  92. package/src/core/textures/SubTexture.ts +6 -5
  93. package/src/core/textures/Texture.ts +19 -6
  94. package/src/main-api/INode.ts +32 -5
  95. package/src/main-api/RendererMain.ts +3 -1
  96. package/src/render-drivers/main/MainOnlyNode.ts +39 -22
  97. package/src/render-drivers/main/MainOnlyTextNode.ts +2 -15
  98. package/src/render-drivers/threadx/NodeStruct.ts +13 -3
  99. package/src/render-drivers/threadx/SharedNode.ts +4 -2
  100. package/src/render-drivers/threadx/ThreadXMainNode.ts +16 -0
  101. package/src/render-drivers/threadx/ThreadXRenderDriver.ts +4 -2
  102. package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +18 -8
  103. package/src/render-drivers/threadx/worker/ThreadXRendererTextNode.ts +2 -14
  104. package/src/render-drivers/threadx/worker/renderer.ts +2 -1
  105. package/src/utils.ts +10 -27
  106. package/dist/src/core/Matrix2DContext.d.ts +0 -15
  107. package/dist/src/core/Matrix2DContext.js +0 -45
  108. package/dist/src/core/Matrix2DContext.js.map +0 -1
  109. package/dist/src/core/lib/glm/common.d.ts +0 -162
  110. package/dist/src/core/lib/glm/common.js +0 -81
  111. package/dist/src/core/lib/glm/common.js.map +0 -1
  112. package/dist/src/core/lib/glm/index.d.ts +0 -11
  113. package/dist/src/core/lib/glm/index.js +0 -30
  114. package/dist/src/core/lib/glm/index.js.map +0 -1
  115. package/dist/src/core/lib/glm/mat2.d.ts +0 -219
  116. package/dist/src/core/lib/glm/mat2.js +0 -396
  117. package/dist/src/core/lib/glm/mat2.js.map +0 -1
  118. package/dist/src/core/lib/glm/mat2d.d.ts +0 -237
  119. package/dist/src/core/lib/glm/mat2d.js +0 -442
  120. package/dist/src/core/lib/glm/mat2d.js.map +0 -1
  121. package/dist/src/core/lib/glm/mat3.d.ts +0 -283
  122. package/dist/src/core/lib/glm/mat3.js +0 -680
  123. package/dist/src/core/lib/glm/mat3.js.map +0 -1
  124. package/dist/src/core/lib/glm/mat4.d.ts +0 -550
  125. package/dist/src/core/lib/glm/mat4.js +0 -1802
  126. package/dist/src/core/lib/glm/mat4.js.map +0 -1
  127. package/dist/src/core/lib/glm/quat.d.ts +0 -363
  128. package/dist/src/core/lib/glm/quat.js +0 -693
  129. package/dist/src/core/lib/glm/quat.js.map +0 -1
  130. package/dist/src/core/lib/glm/quat2.d.ts +0 -356
  131. package/dist/src/core/lib/glm/quat2.js +0 -754
  132. package/dist/src/core/lib/glm/quat2.js.map +0 -1
  133. package/dist/src/core/lib/glm/vec2.d.ts +0 -365
  134. package/dist/src/core/lib/glm/vec2.js +0 -569
  135. package/dist/src/core/lib/glm/vec2.js.map +0 -1
  136. package/dist/src/core/lib/glm/vec3.d.ts +0 -406
  137. package/dist/src/core/lib/glm/vec3.js +0 -720
  138. package/dist/src/core/lib/glm/vec3.js.map +0 -1
  139. package/dist/src/core/lib/glm/vec4.d.ts +0 -330
  140. package/dist/src/core/lib/glm/vec4.js +0 -608
  141. package/dist/src/core/lib/glm/vec4.js.map +0 -1
  142. package/dist/src/core/textures/utils.d.ts +0 -11
  143. package/dist/src/core/textures/utils.js +0 -32
  144. package/dist/src/core/textures/utils.js.map +0 -1
  145. package/dist/src/main-api/TextureRegistry.d.ts +0 -33
  146. package/dist/src/main-api/TextureRegistry.js +0 -97
  147. package/dist/src/main-api/TextureRegistry.js.map +0 -1
  148. package/dist/src/main-api/TextureUsageRegistry/TextureRegistry.d.ts +0 -33
  149. package/dist/src/main-api/TextureUsageRegistry/TextureRegistry.js +0 -97
  150. package/dist/src/main-api/TextureUsageRegistry/TextureRegistry.js.map +0 -1
  151. package/src/core/Matrix2DContext.ts +0 -52
  152. package/src/core/lib/glm/common.ts +0 -231
  153. package/src/core/lib/glm/index.ts +0 -31
  154. package/src/core/lib/glm/mat2.ts +0 -499
  155. package/src/core/lib/glm/mat2d.ts +0 -547
  156. package/src/core/lib/glm/mat3.ts +0 -849
  157. package/src/core/lib/glm/mat4.ts +0 -2169
  158. package/src/core/lib/glm/quat.ts +0 -828
  159. package/src/core/lib/glm/quat2.ts +0 -951
  160. package/src/core/lib/glm/vec2.ts +0 -671
  161. package/src/core/lib/glm/vec3.ts +0 -859
  162. package/src/core/lib/glm/vec4.ts +0 -708
@@ -24,17 +24,21 @@ import type {
24
24
  TextureMap,
25
25
  TextureOptions,
26
26
  } from './CoreTextureManager.js';
27
- import { Matrix2DContext } from './Matrix2DContext.js';
28
27
  import type { CoreRenderer } from './renderers/CoreRenderer.js';
29
28
  import type { CoreShader } from './renderers/CoreShader.js';
30
29
  import type { Stage } from './Stage.js';
31
- import type { Texture } from './textures/Texture.js';
32
30
  import type {
31
+ Texture,
33
32
  TextureFailedEventHandler,
34
33
  TextureLoadedEventHandler,
34
+ } from './textures/Texture.js';
35
+ import type {
36
+ NodeTextureFailedPayload,
37
+ NodeTextureLoadedPayload,
35
38
  } from '../common/CommonTypes.js';
36
39
  import { EventEmitter } from '../common/EventEmitter.js';
37
40
  import type { Rect } from './lib/utils.js';
41
+ import { Matrix3d } from './lib/Matrix3d.js';
38
42
 
39
43
  export interface CoreNodeProps {
40
44
  id: number;
@@ -62,7 +66,8 @@ export interface CoreNodeProps {
62
66
  shader: CoreShader | null;
63
67
  shaderProps: Record<string, unknown> | null;
64
68
  zIndexLocked: number;
65
- scale: number;
69
+ scaleX: number;
70
+ scaleY: number;
66
71
  mount: number;
67
72
  mountX: number;
68
73
  mountY: number;
@@ -70,14 +75,6 @@ export interface CoreNodeProps {
70
75
  pivotX: number;
71
76
  pivotY: number;
72
77
  rotation: number;
73
- worldX?: number;
74
- worldY?: number;
75
-
76
- // Internal properties that are resolved in CoreNode constructor (see below)
77
- ta?: number;
78
- tb?: number;
79
- tc?: number;
80
- td?: number;
81
78
  }
82
79
 
83
80
  type ICoreNode = Omit<
@@ -98,12 +95,9 @@ export class CoreNode extends EventEmitter implements ICoreNode {
98
95
  */
99
96
  public recalculationType = 6;
100
97
  public hasUpdates = true;
101
- public worldContext: Matrix2DContext = new Matrix2DContext();
102
-
103
- // local translation / transform updates
104
- // derived from x, y, w, h, scale, pivot, rotation
105
- public localPx = 0;
106
- public localPy = 0;
98
+ public globalTransform?: Matrix3d;
99
+ public scaleRotateTransform?: Matrix3d;
100
+ public localTransform?: Matrix3d;
107
101
 
108
102
  private isComplex = false;
109
103
 
@@ -112,16 +106,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
112
106
  this.props = {
113
107
  ...props,
114
108
  parent: null,
115
- ta: props.ta ?? 1,
116
- tb: props.tb ?? 0,
117
- tc: props.tc ?? 0,
118
- td: props.td ?? 1,
119
- worldX: props.worldX ?? 0,
120
- worldY: props.worldY ?? 0,
121
109
  };
122
110
  // Allow for parent to be processed appropriately
123
111
  this.parent = props.parent;
124
- this.updateLocalTransform();
112
+ this.updateScaleRotateTransform();
125
113
  }
126
114
 
127
115
  //#region Textures
@@ -165,11 +153,17 @@ export class CoreNode extends EventEmitter implements ICoreNode {
165
153
  }
166
154
 
167
155
  private onTextureLoaded: TextureLoadedEventHandler = (target, dimensions) => {
168
- this.emit('txLoaded', dimensions);
156
+ this.emit('loaded', {
157
+ type: 'texture',
158
+ dimensions,
159
+ } satisfies NodeTextureLoadedPayload);
169
160
  };
170
161
 
171
162
  private onTextureFailed: TextureFailedEventHandler = (target, error) => {
172
- this.emit('txFailed', error);
163
+ this.emit('failed', {
164
+ type: 'texture',
165
+ error,
166
+ } satisfies NodeTextureFailedPayload);
173
167
  };
174
168
  //#endregion Textures
175
169
 
@@ -208,58 +202,33 @@ export class CoreNode extends EventEmitter implements ICoreNode {
208
202
  this.setHasUpdates();
209
203
  }
210
204
 
211
- updateLocalTransform() {
212
- // if rotation is equal to previous render pass, we only need
213
- // to use sin and cosine of rotation to calculate new position
214
- if (this.props.rotation !== 0 && this.props.rotation % (Math.PI * 2)) {
215
- const sineRotation = Math.sin(this.props.rotation);
216
- const cosineRotation = Math.cos(this.props.rotation);
217
-
218
- this.setLocalTransform(
219
- cosineRotation * this.props.scale,
220
- -sineRotation * this.props.scale,
221
- sineRotation * this.props.scale,
222
- cosineRotation * this.props.scale,
223
- );
224
- } else {
225
- this.setLocalTransform(this.props.scale, 0, 0, this.props.scale);
226
- }
227
- // do transformations when matrix is implemented
228
- this.updateLocalTranslate();
229
- }
230
-
231
- // update 2x2 matrix
232
- setLocalTransform(a: number, b: number, c: number, d: number) {
205
+ updateScaleRotateTransform() {
233
206
  this.setRecalculationType(4);
234
- this.props.ta = a;
235
- this.props.tb = b;
236
- this.props.tc = c;
237
- this.props.td = d;
238
207
 
239
- // test if there is scaling or shearing in transformation ( b !== 0 || c !== 0 )
240
- // test if there is flipping or reflection in transformation ( a < 0 || d < 0 )
241
- this.isComplex = b !== 0 || c !== 0 || a < 0 || d < 0;
208
+ this.scaleRotateTransform = Matrix3d.rotate(
209
+ this.props.rotation,
210
+ this.scaleRotateTransform,
211
+ ).scale(this.props.scaleX, this.props.scaleY);
212
+
213
+ // do transformations when matrix is implemented
214
+ this.updateLocalTransform();
242
215
  }
243
216
 
244
- updateLocalTranslate() {
217
+ updateLocalTransform() {
218
+ assertTruthy(this.scaleRotateTransform);
245
219
  this.setRecalculationType(2);
246
- const pivotXMultiplier = this.props.pivotX * this.props.width;
247
- const pivotYMultiplier = this.props.pivotY * this.props.height;
248
-
249
- let px =
250
- this.props.x -
251
- (pivotXMultiplier * this.props.ta + pivotYMultiplier * this.props.tb) +
252
- pivotXMultiplier;
253
- let py =
254
- this.props.y -
255
- (pivotXMultiplier * this.props.tc + pivotYMultiplier * this.props.td) +
256
- pivotYMultiplier;
220
+ const pivotTranslateX = this.props.pivotX * this.props.width;
221
+ const pivotTranslateY = this.props.pivotY * this.props.height;
222
+ const mountTranslateX = this.props.mountX * this.props.width;
223
+ const mountTranslateY = this.props.mountY * this.props.height;
257
224
 
258
- px -= this.props.mountX * this.props.width;
259
- py -= this.props.mountY * this.props.height;
260
-
261
- this.localPx = px;
262
- this.localPy = py;
225
+ this.localTransform = Matrix3d.translate(
226
+ pivotTranslateX - mountTranslateX + this.props.x,
227
+ pivotTranslateY - mountTranslateY + this.props.y,
228
+ this.localTransform,
229
+ )
230
+ .multiply(this.scaleRotateTransform)
231
+ .translate(-pivotTranslateX, -pivotTranslateY);
263
232
  }
264
233
 
265
234
  /**
@@ -267,39 +236,20 @@ export class CoreNode extends EventEmitter implements ICoreNode {
267
236
  * @param delta
268
237
  */
269
238
  update(delta: number): void {
270
- const parentWorldContext = this.props.parent?.worldContext;
271
- const worldContext = this.worldContext;
272
-
273
- worldContext.px =
274
- (parentWorldContext?.px || 0) +
275
- this.localPx * (parentWorldContext?.ta || 1);
276
- worldContext.py =
277
- (parentWorldContext?.py || 0) +
278
- this.localPy * (parentWorldContext?.td || 1);
279
-
280
- if (parentWorldContext?.tb !== 0) {
281
- worldContext.px += this.localPy * (parentWorldContext?.tb || 0);
282
- }
283
-
284
- if (parentWorldContext?.tc !== 0) {
285
- worldContext.py += this.localPx * (parentWorldContext?.tc || 0);
286
- }
287
-
288
- worldContext.ta = this.props.ta * (parentWorldContext?.ta || 1);
289
- worldContext.tb = this.props.td * (parentWorldContext?.tb || 0);
290
- worldContext.tc = this.props.ta * (parentWorldContext?.tc || 0);
291
- worldContext.td = this.props.td * (parentWorldContext?.td || 1);
292
-
293
- if (this.isComplex) {
294
- worldContext.ta += this.props.tc * (parentWorldContext?.tb || 0);
295
- worldContext.tb += this.props.tb * (parentWorldContext?.ta || 1);
296
- worldContext.tc += this.props.tc * (parentWorldContext?.td || 1);
297
- worldContext.td += this.props.tb * (parentWorldContext?.tc || 0);
239
+ assertTruthy(this.localTransform);
240
+ const parentGlobalTransform = this.parent?.globalTransform;
241
+ if (parentGlobalTransform) {
242
+ this.globalTransform = Matrix3d.copy(
243
+ parentGlobalTransform,
244
+ this.globalTransform,
245
+ ).multiply(this.localTransform);
246
+ } else {
247
+ this.globalTransform = Matrix3d.copy(
248
+ this.localTransform,
249
+ this.globalTransform,
250
+ );
298
251
  }
299
252
 
300
- this.worldX = worldContext.px;
301
- this.worldY = worldContext.py;
302
-
303
253
  if (this.children.length) {
304
254
  this.children.forEach((child) => {
305
255
  child.update(delta);
@@ -325,9 +275,10 @@ export class CoreNode extends EventEmitter implements ICoreNode {
325
275
  textureOptions,
326
276
  shader,
327
277
  shaderProps,
328
- scale,
329
278
  } = this.props;
330
- const { zIndex, alpha, worldScale } = this;
279
+ const { zIndex, worldAlpha, globalTransform: gt } = this;
280
+
281
+ assertTruthy(gt);
331
282
 
332
283
  // add to list of renderables to be sorted before rendering
333
284
  renderer.addRenderable({
@@ -342,16 +293,14 @@ export class CoreNode extends EventEmitter implements ICoreNode {
342
293
  zIndex,
343
294
  shader,
344
295
  shaderProps,
345
- alpha,
346
- scale,
296
+ alpha: worldAlpha,
347
297
  clippingRect,
348
- wpx: this.worldContext.px,
349
- wpy: this.worldContext.py,
350
- worldScale,
351
- ta: this.worldContext.ta,
352
- tb: this.worldContext.tb,
353
- tc: this.worldContext.tc,
354
- td: this.worldContext.td,
298
+ tx: gt.tx,
299
+ ty: gt.ty,
300
+ ta: gt.ta,
301
+ tb: gt.tb,
302
+ tc: gt.tc,
303
+ td: gt.td,
355
304
  });
356
305
 
357
306
  // Calculate absolute X and Y based on all ancestors
@@ -370,30 +319,14 @@ export class CoreNode extends EventEmitter implements ICoreNode {
370
319
  set x(value: number) {
371
320
  if (this.props.x !== value) {
372
321
  this.props.x = value;
373
- this.updateLocalTranslate();
322
+ this.updateLocalTransform();
374
323
  }
375
324
  }
376
325
 
377
- get worldX(): number {
378
- return this.props.worldX || 0;
379
- }
380
-
381
- set worldX(value: number) {
382
- this.props.worldX = value;
383
- }
384
-
385
- get worldY(): number {
386
- return this.props.worldY || 0;
387
- }
388
-
389
- set worldY(value: number) {
390
- this.props.worldY = value;
391
- }
392
-
393
326
  get absX(): number {
394
327
  return (
395
328
  this.props.x +
396
- (this.props.parent?.absX || this.props.parent?.worldContext.px || 0)
329
+ (this.props.parent?.absX || this.props.parent?.globalTransform?.tx || 0)
397
330
  );
398
331
  }
399
332
 
@@ -408,7 +341,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
408
341
  set y(value: number) {
409
342
  if (this.props.y !== value) {
410
343
  this.props.y = value;
411
- this.updateLocalTranslate();
344
+ this.updateLocalTransform();
412
345
  }
413
346
  }
414
347
 
@@ -435,21 +368,51 @@ export class CoreNode extends EventEmitter implements ICoreNode {
435
368
  }
436
369
 
437
370
  get scale(): number {
438
- return this.props.scale;
371
+ // The CoreNode `scale` property is only used by Animations.
372
+ // Unlike INode, `null` should never be possibility for Animations.
373
+ return this.scaleX;
439
374
  }
440
375
 
441
- // @todo: implement scaleX and scaleY
442
376
  set scale(value: number) {
443
- if (this.props.scale !== value) {
444
- this.props.scale = value;
445
- this.updateLocalTransform();
377
+ // The CoreNode `scale` property is only used by Animations.
378
+ // Unlike INode, `null` should never be possibility for Animations.
379
+ this.scaleX = value;
380
+ this.scaleY = value;
381
+ }
382
+
383
+ get scaleX(): number {
384
+ return this.props.scaleX;
385
+ }
386
+
387
+ set scaleX(value: number) {
388
+ if (this.props.scaleX !== value) {
389
+ this.props.scaleX = value;
390
+ this.updateScaleRotateTransform();
446
391
  }
447
392
  }
448
393
 
449
- get worldScale(): number {
394
+ get scaleY(): number {
395
+ return this.props.scaleY;
396
+ }
397
+
398
+ set scaleY(value: number) {
399
+ if (this.props.scaleY !== value) {
400
+ this.props.scaleY = value;
401
+ this.updateScaleRotateTransform();
402
+ }
403
+ }
404
+
405
+ get worldScaleX(): number {
406
+ return (
407
+ this.props.scaleX * (this.props.parent?.worldScaleX ?? 1) ||
408
+ this.props.scaleX
409
+ );
410
+ }
411
+
412
+ get worldScaleY(): number {
450
413
  return (
451
- this.props.scale * (this.props.parent?.worldScale ?? 1) ||
452
- this.props.scale
414
+ this.props.scaleY * (this.props.parent?.worldScaleY ?? 1) ||
415
+ this.props.scaleY
453
416
  );
454
417
  }
455
418
 
@@ -462,7 +425,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
462
425
  this.props.mountX = value;
463
426
  this.props.mountY = value;
464
427
  this.props.mount = value;
465
- this.updateLocalTranslate();
428
+ this.updateLocalTransform();
466
429
  // }
467
430
  }
468
431
 
@@ -472,7 +435,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
472
435
 
473
436
  set mountX(value: number) {
474
437
  this.props.mountX = value;
475
- this.updateLocalTranslate();
438
+ this.updateLocalTransform();
476
439
  }
477
440
 
478
441
  get mountY(): number {
@@ -481,7 +444,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
481
444
 
482
445
  set mountY(value: number) {
483
446
  this.props.mountY = value;
484
- this.updateLocalTranslate();
447
+ this.updateLocalTransform();
485
448
  }
486
449
 
487
450
  get pivot(): number {
@@ -492,7 +455,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
492
455
  if (this.props.pivotX !== value || this.props.pivotY !== value) {
493
456
  this.props.pivotX = value;
494
457
  this.props.pivotY = value;
495
- this.updateLocalTranslate();
458
+ this.updateLocalTransform();
496
459
  }
497
460
  }
498
461
 
@@ -502,7 +465,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
502
465
 
503
466
  set pivotX(value: number) {
504
467
  this.props.pivotX = value;
505
- this.updateLocalTranslate();
468
+ this.updateLocalTransform();
506
469
  }
507
470
 
508
471
  get pivotY(): number {
@@ -511,7 +474,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
511
474
 
512
475
  set pivotY(value: number) {
513
476
  this.props.pivotY = value;
514
- this.updateLocalTranslate();
477
+ this.updateLocalTransform();
515
478
  }
516
479
 
517
480
  get rotation(): number {
@@ -521,26 +484,25 @@ export class CoreNode extends EventEmitter implements ICoreNode {
521
484
  set rotation(value: number) {
522
485
  if (this.props.rotation !== value) {
523
486
  this.props.rotation = value;
524
- this.updateLocalTransform();
487
+ this.updateScaleRotateTransform();
525
488
  }
526
489
  }
527
490
 
528
491
  get alpha(): number {
529
- const props = this.props;
530
- const parent = props.parent;
531
-
532
- // root always visible
533
- if (!parent) {
534
- return 1;
535
- }
536
-
537
- return props.alpha * parent.alpha;
492
+ return this.props.alpha;
538
493
  }
539
494
 
540
495
  set alpha(value: number) {
541
496
  this.props.alpha = value;
542
497
  }
543
498
 
499
+ get worldAlpha(): number {
500
+ const props = this.props;
501
+ const parent = props.parent;
502
+
503
+ return props.alpha * (parent?.worldAlpha || 1);
504
+ }
505
+
544
506
  get clipping(): boolean {
545
507
  return this.props.clipping;
546
508
  }
@@ -694,7 +656,7 @@ export class CoreNode extends EventEmitter implements ICoreNode {
694
656
  if (newParent) {
695
657
  newParent.children.push(this);
696
658
  }
697
- this.updateLocalTransform();
659
+ this.updateScaleRotateTransform();
698
660
  }
699
661
  //#endregion Properties
700
662
  }
@@ -22,15 +22,18 @@ import type {
22
22
  TextRendererMap,
23
23
  TrProps,
24
24
  TextRendererState,
25
+ TrFailedEventHandler,
26
+ TrLoadedEventHandler,
25
27
  } from './text-rendering/renderers/TextRenderer.js';
26
28
  import { CoreNode, type CoreNodeProps } from './CoreNode.js';
27
29
  import type { Stage } from './Stage.js';
28
30
  import type { CoreRenderer } from './renderers/CoreRenderer.js';
29
31
  import type {
30
- TextFailedEventHandler,
31
- TextLoadedEventHandler,
32
+ NodeTextFailedPayload,
33
+ NodeTextLoadedPayload,
32
34
  } from '../common/CommonTypes.js';
33
35
  import type { Rect } from './lib/utils.js';
36
+ import { assertTruthy } from '../utils.js';
34
37
 
35
38
  export interface CoreTextNodeProps extends CoreNodeProps, TrProps {
36
39
  text: string;
@@ -52,7 +55,6 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
52
55
  constructor(stage: Stage, props: CoreTextNodeProps) {
53
56
  super(stage, props);
54
57
  this.updateScheduled = false;
55
- // console.log('fontfamily', props.fontFamily)
56
58
  this._textRendererOverride = props.textRendererOverride;
57
59
  const { resolvedTextRenderer, textRendererState } =
58
60
  this.resolveTextRendererAndState(
@@ -63,9 +65,10 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
63
65
  height: props.height,
64
66
  textAlign: props.textAlign,
65
67
  color: props.color,
66
- alpha: props.alpha,
67
68
  zIndex: props.zIndex,
68
69
  contain: props.contain,
70
+ scaleX: props.scaleX,
71
+ scaleY: props.scaleY,
69
72
  scrollable: props.scrollable,
70
73
  scrollY: props.scrollY,
71
74
  offsetY: props.offsetY,
@@ -84,15 +87,39 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
84
87
  this.trState = textRendererState;
85
88
  }
86
89
 
87
- private onTextLoaded: TextLoadedEventHandler = () => {
88
- this.emit('textLoaded', {
89
- width: this.trState.textW,
90
- height: this.trState.textH,
91
- });
90
+ private onTextLoaded: TrLoadedEventHandler = () => {
91
+ const { contain } = this;
92
+ const setWidth = this.trState.props.width;
93
+ const setHeight = this.trState.props.height;
94
+ const calcWidth = this.trState.textW || 0;
95
+ const calcHeight = this.trState.textH || 0;
96
+
97
+ if (contain === 'both') {
98
+ this.props.width = setWidth;
99
+ this.props.height = setHeight;
100
+ } else if (contain === 'width') {
101
+ this.props.width = setWidth;
102
+ this.props.height = calcHeight;
103
+ } else if (contain === 'none') {
104
+ this.props.width = calcWidth;
105
+ this.props.height = calcHeight;
106
+ }
107
+ this.updateLocalTransform();
108
+
109
+ this.emit('loaded', {
110
+ type: 'text',
111
+ dimensions: {
112
+ width: this.trState.textW || 0,
113
+ height: this.trState.textH || 0,
114
+ },
115
+ } satisfies NodeTextLoadedPayload);
92
116
  };
93
117
 
94
- private onTextFailed: TextFailedEventHandler = (target, error) => {
95
- this.emit('textFailed', error);
118
+ private onTextFailed: TrFailedEventHandler = (target, error) => {
119
+ this.emit('failed', {
120
+ type: 'text',
121
+ error,
122
+ } satisfies NodeTextFailedPayload);
96
123
  };
97
124
 
98
125
  override get width(): number {
@@ -113,15 +140,6 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
113
140
  this.updateText();
114
141
  }
115
142
 
116
- override get alpha(): number {
117
- return this.trState.props.alpha;
118
- }
119
-
120
- override set alpha(value: number) {
121
- this.textRenderer.set.alpha(this.trState, value);
122
- this.updateText();
123
- }
124
-
125
143
  override get color(): number {
126
144
  return this.trState.props.color;
127
145
  }
@@ -263,10 +281,16 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
263
281
 
264
282
  override update(delta: number) {
265
283
  super.update(delta);
266
- this.textRenderer.set.x(this.trState, this.worldContext.px);
267
- this.textRenderer.set.y(this.trState, this.worldContext.py);
284
+
285
+ assertTruthy(this.globalTransform);
286
+
287
+ // globalTransform is updated in super.update(delta)
288
+ this.textRenderer.set.x(this.trState, this.globalTransform.tx);
289
+ this.textRenderer.set.y(this.trState, this.globalTransform.ty);
290
+
268
291
  if (this.trState.status === 'loading') {
269
- this.updateText();
292
+ // Update the text state now
293
+ this.textRenderer.updateState(this.trState);
270
294
  }
271
295
  }
272
296
 
@@ -282,7 +306,13 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
282
306
  }
283
307
 
284
308
  override renderQuads(renderer: CoreRenderer, clippingRect: Rect | null) {
285
- this.textRenderer.renderQuads(this.trState, clippingRect);
309
+ assertTruthy(this.globalTransform);
310
+ this.textRenderer.renderQuads(
311
+ this.trState,
312
+ this.globalTransform,
313
+ clippingRect,
314
+ this.worldAlpha,
315
+ );
286
316
  }
287
317
 
288
318
  /**
package/src/core/Stage.ts CHANGED
@@ -126,7 +126,8 @@ export class Stage {
126
126
  colorBr: 0x00000000,
127
127
  zIndex: 0,
128
128
  zIndexLocked: 0,
129
- scale: 1,
129
+ scaleX: 1,
130
+ scaleY: 1,
130
131
  mountX: 0,
131
132
  mountY: 0,
132
133
  mount: 0,
@@ -181,15 +182,19 @@ export class Stage {
181
182
  }
182
183
 
183
184
  addQuads(node: CoreNode, parentClippingRect: Rect | null = null) {
184
- assertTruthy(this.renderer);
185
- let clippingRect: Rect | null = node.clipping
186
- ? {
187
- x: node.worldContext.px,
188
- y: node.worldContext.py,
189
- width: node.width,
190
- height: node.height,
191
- }
192
- : null;
185
+ assertTruthy(this.renderer && node.globalTransform);
186
+ const gt = node.globalTransform;
187
+ const isRotated = gt.tb !== 0 || gt.tc !== 0;
188
+
189
+ let clippingRect: Rect | null =
190
+ node.clipping && !isRotated
191
+ ? {
192
+ x: gt.tx,
193
+ y: gt.ty,
194
+ width: node.width * gt.ta,
195
+ height: node.height * gt.td,
196
+ }
197
+ : null;
193
198
  if (parentClippingRect && clippingRect) {
194
199
  clippingRect = intersectRect(parentClippingRect, clippingRect);
195
200
  } else if (parentClippingRect) {
@@ -198,7 +203,7 @@ export class Stage {
198
203
 
199
204
  node.renderQuads(this.renderer, clippingRect);
200
205
  node.children.forEach((child) => {
201
- if (child.alpha === 0) {
206
+ if (child.worldAlpha === 0) {
202
207
  return;
203
208
  }
204
209
  this.addQuads(child, clippingRect);