@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
@@ -17,8 +17,6 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- // import { Colors } from '../../../Colors.js';
21
- // import type { Renderer } from '../../../Renderer.js';
22
20
  import { intersectBound, type Bound, type Rect } from '../../../lib/utils.js';
23
21
  import {
24
22
  TextRenderer,
@@ -44,6 +42,7 @@ import type {
44
42
  } from '../../../renderers/webgl/shaders/SdfShader.js';
45
43
  import type { WebGlCoreCtxTexture } from '../../../renderers/webgl/WebGlCoreCtxTexture.js';
46
44
  import { EventEmitter } from '../../../../common/EventEmitter.js';
45
+ import type { Matrix3d } from '../../../lib/Matrix3d.js';
47
46
 
48
47
  declare module '../TextRenderer.js' {
49
48
  interface TextRendererMap {
@@ -148,9 +147,6 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
148
147
  color: (state, value) => {
149
148
  state.props.color = value;
150
149
  },
151
- alpha: (state, value) => {
152
- state.props.alpha = value;
153
- },
154
150
  x: (state, value) => {
155
151
  state.props.x = value;
156
152
  },
@@ -269,6 +265,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
269
265
  this.setStatus(state, 'loading');
270
266
  }
271
267
 
268
+ // Resolve font face if we haven't yet
272
269
  if (!trFontFace) {
273
270
  trFontFace = this.resolveFontFace(state.props);
274
271
  state.trFontFace = trFontFace;
@@ -280,7 +277,8 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
280
277
  }
281
278
  }
282
279
 
283
- // If the font hasn't been loaded yet, don't do anything
280
+ // If the font hasn't been loaded yet, stop here.
281
+ // Listen for the 'loaded' event and forward fontLoaded event
284
282
  if (!trFontFace.loaded) {
285
283
  trFontFace.on('loaded', function loadedHandler() {
286
284
  state.emitter.emit('fontLoaded', {});
@@ -431,7 +429,9 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
431
429
 
432
430
  override renderQuads(
433
431
  state: SdfTextRendererState,
432
+ transform: Matrix3d,
434
433
  clippingRect: Rect | null,
434
+ alpha: number,
435
435
  ): void {
436
436
  if (!state.vertexBuffer) {
437
437
  // Nothing to draw
@@ -446,19 +446,7 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
446
446
 
447
447
  const { appWidth, appHeight } = this.stage.options;
448
448
 
449
- const {
450
- fontSize,
451
- color,
452
- alpha,
453
- x,
454
- y,
455
- contain,
456
- width,
457
- height,
458
- scrollable,
459
- zIndex,
460
- debug,
461
- } = state.props;
449
+ const { fontSize, color, contain, scrollable, zIndex, debug } = state.props;
462
450
 
463
451
  // scrollY only has an effect when contain === 'both' and scrollable === true
464
452
  const scrollY = contain === 'both' && scrollable ? state.props.scrollY : 0;
@@ -527,12 +515,13 @@ export class SdfTextRenderer extends TextRenderer<SdfTextRendererState> {
527
515
  webGlBuffers,
528
516
  this.sdfShader,
529
517
  {
518
+ transform: transform.data,
530
519
  // IMPORTANT: The SDF Shader expects the color NOT to be premultiplied
531
520
  // for the best blending results. Which is why we use `mergeColorAlpha`
532
521
  // instead of `mergeColorAlphaPremultiplied` here.
533
522
  color: mergeColorAlpha(color, alpha),
534
523
  size: fontSize / (trFontFace.data?.info.size || 0),
535
- offset: [x, y - scrollY],
524
+ scrollY,
536
525
  distanceRange,
537
526
  debug: debug.sdfShaderDebug,
538
527
  } satisfies SdfShaderProps,
@@ -26,7 +26,6 @@ import type {
26
26
  import type { TrProps, TextRendererState } from '../../TextRenderer.js';
27
27
  import type { SdfTextRendererState } from '../SdfTextRenderer.js';
28
28
  import { PeekableIterator } from './PeekableGenerator.js';
29
- import { FLOATS_PER_GLYPH } from './constants.js';
30
29
  import { getUnicodeCodepoints } from './getUnicodeCodepoints.js';
31
30
  import { measureText } from './measureText.js';
32
31
 
@@ -19,6 +19,7 @@
19
19
 
20
20
  import type { EventEmitter } from '../../../common/EventEmitter.js';
21
21
  import type { Stage } from '../../Stage.js';
22
+ import type { Matrix3d } from '../../lib/Matrix3d.js';
22
23
  import type { Rect } from '../../lib/utils.js';
23
24
  import type {
24
25
  TrFontFace,
@@ -179,7 +180,6 @@ export interface TrProps extends TrFontProps {
179
180
  * @default 0xffffffff (opaque white)
180
181
  */
181
182
  color: number;
182
- alpha: number;
183
183
  x: number;
184
184
  y: number;
185
185
  /**
@@ -198,6 +198,14 @@ export interface TrProps extends TrFontProps {
198
198
  contain: 'none' | 'width' | 'both';
199
199
  width: number;
200
200
  height: number;
201
+ /**
202
+ * X-Axis scaling factor for text
203
+ */
204
+ scaleX: number;
205
+ /**
206
+ * Y-Axis scaling factor for text
207
+ */
208
+ scaleY: number;
201
209
  /**
202
210
  * Whether or not the text is scrollable
203
211
  *
@@ -263,9 +271,6 @@ const trPropSetterDefaults: TrPropSetters = {
263
271
  color: (state, value) => {
264
272
  state.props.color = value;
265
273
  },
266
- alpha: (state, value) => {
267
- state.props.alpha = value;
268
- },
269
274
  zIndex: (state, value) => {
270
275
  state.props.zIndex = value;
271
276
  },
@@ -284,6 +289,12 @@ const trPropSetterDefaults: TrPropSetters = {
284
289
  fontSize: (state, value) => {
285
290
  state.props.fontSize = value;
286
291
  },
292
+ scaleX: (state, value) => {
293
+ state.props.scaleX = value;
294
+ },
295
+ scaleY: (state, value) => {
296
+ state.props.scaleY = value;
297
+ },
287
298
  text: (state, value) => {
288
299
  state.props.text = value;
289
300
  },
@@ -310,6 +321,22 @@ const trPropSetterDefaults: TrPropSetters = {
310
321
  },
311
322
  };
312
323
 
324
+ /**
325
+ * Event handler for when text is loaded
326
+ *
327
+ * @remarks
328
+ * Emitted by state.emitter
329
+ */
330
+ export type TrLoadedEventHandler = (target: any) => void;
331
+
332
+ /**
333
+ * Event handler for when text failed to load
334
+ *
335
+ * @remarks
336
+ * Emitted by state.emitter
337
+ */
338
+ export type TrFailedEventHandler = (target: any, error: Error) => void;
339
+
313
340
  export abstract class TextRenderer<
314
341
  StateT extends TextRendererState = TextRendererState,
315
342
  > {
@@ -367,5 +394,10 @@ export abstract class TextRenderer<
367
394
 
368
395
  abstract updateState(state: StateT): void;
369
396
 
370
- abstract renderQuads(state: StateT, clippingRect: Rect | null): void;
397
+ abstract renderQuads(
398
+ state: StateT,
399
+ transform: Matrix3d,
400
+ clippingRect: Rect | null,
401
+ alpha: number,
402
+ ): void;
371
403
  }
@@ -17,13 +17,14 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import type {
21
- TextureFailedEventHandler,
22
- TextureLoadedEventHandler,
23
- } from '../../common/CommonTypes.js';
24
20
  import type { TextureRef } from '../../main-api/RendererMain.js';
25
21
  import type { CoreTextureManager } from '../CoreTextureManager.js';
26
- import { Texture, type TextureData } from './Texture.js';
22
+ import {
23
+ Texture,
24
+ type TextureData,
25
+ type TextureFailedEventHandler,
26
+ type TextureLoadedEventHandler,
27
+ } from './Texture.js';
27
28
 
28
29
  /**
29
30
  * Properties of the {@link SubTexture}
@@ -19,14 +19,27 @@
19
19
 
20
20
  import type { CoreTextureManager } from '../CoreTextureManager.js';
21
21
  import type { SubTextureProps } from './SubTexture.js';
22
- import type {
23
- Dimensions,
24
- TextureFailedEventHandler,
25
- TextureLoadedEventHandler,
26
- TextureLoadingEventHandler,
27
- } from '../../common/CommonTypes.js';
22
+ import type { Dimensions } from '../../common/CommonTypes.js';
28
23
  import { EventEmitter } from '../../common/EventEmitter.js';
29
24
 
25
+ /**
26
+ * Event handler for when a Texture is loading
27
+ */
28
+ export type TextureLoadingEventHandler = (target: any) => void;
29
+
30
+ /**
31
+ * Event handler for when a Texture is loaded
32
+ */
33
+ export type TextureLoadedEventHandler = (
34
+ target: any,
35
+ dimensions: Readonly<Dimensions>,
36
+ ) => void;
37
+
38
+ /**
39
+ * Event handler for when a Texture fails to load
40
+ */
41
+ export type TextureFailedEventHandler = (target: any, error: Error) => void;
42
+
30
43
  /**
31
44
  * TextureData that is used to populate a CoreContextTexture
32
45
  */
@@ -246,12 +246,39 @@ export interface INodeWritableProps {
246
246
  *
247
247
  * @remarks
248
248
  * The scale value multiplies the provided {@link width} and {@link height}
249
- * of the Node around the Node's Mount Point (defined by the {@link mount}
249
+ * of the Node around the Node's Pivot Point (defined by the {@link pivot}
250
250
  * props).
251
251
  *
252
+ * Behind the scenes, setting this property sets both the {@link scaleX} and
253
+ * {@link scaleY} props to the same value.
254
+ *
255
+ * NOTE: When the scaleX and scaleY props are explicitly set to different values,
256
+ * this property returns `null`. Setting `null` on this property will have no
257
+ * effect.
258
+ *
259
+ * @default 1.0
260
+ */
261
+ scale: number | null;
262
+ /**
263
+ * Scale to render the Node at (X-Axis)
264
+ *
265
+ * @remarks
266
+ * The scaleX value multiplies the provided {@link width} of the Node around
267
+ * the Node's Pivot Point (defined by the {@link pivot} props).
268
+ *
269
+ * @default 1.0
270
+ */
271
+ scaleX: number;
272
+ /**
273
+ * Scale to render the Node at (Y-Axis)
274
+ *
275
+ * @remarks
276
+ * The scaleY value multiplies the provided {@link height} of the Node around
277
+ * the Node's Pivot Point (defined by the {@link pivot} props).
278
+ *
252
279
  * @default 1.0
253
280
  */
254
- scale: number;
281
+ scaleY: number;
255
282
  /**
256
283
  * Combined position of the Node's Mount Point
257
284
  *
@@ -365,12 +392,12 @@ export interface INodeWritableProps {
365
392
  * - `2 * Math.PI`: 360 rotation clockwise
366
393
  */
367
394
  rotation: number;
368
- worldX?: number;
369
- worldY?: number;
370
395
  }
371
396
 
372
397
  export type INodeAnimatableProps = {
373
- [Key in keyof INodeWritableProps as INodeWritableProps[Key] extends number
398
+ [Key in keyof INodeWritableProps as NonNullable<
399
+ INodeWritableProps[Key]
400
+ > extends number
374
401
  ? Key
375
402
  : never]: number;
376
403
  };
@@ -436,7 +436,9 @@ export class RendererMain {
436
436
  // Since setting the `src` will trigger a texture load, we need to set it after
437
437
  // we set the texture. Otherwise, problems happen.
438
438
  src: props.src ?? '',
439
- scale: props.scale ?? 1,
439
+ scale: props.scale ?? null,
440
+ scaleX: props.scaleX ?? props.scale ?? 1,
441
+ scaleY: props.scaleY ?? props.scale ?? 1,
440
442
  mount: props.mount ?? 0,
441
443
  mountX: props.mountX ?? props.mount ?? 0,
442
444
  mountY: props.mountY ?? props.mount ?? 0,
@@ -36,8 +36,8 @@ import type {
36
36
  import type { AnimationSettings } from '../../core/animations/CoreAnimation.js';
37
37
  import { EventEmitter } from '../../common/EventEmitter.js';
38
38
  import type {
39
- TextureFailedEventHandler,
40
- TextureLoadedEventHandler,
39
+ NodeLoadedEventHandler,
40
+ NodeFailedEventHandler,
41
41
  } from '../../common/CommonTypes.js';
42
42
 
43
43
  let nextId = 1;
@@ -86,7 +86,8 @@ export class MainOnlyNode extends EventEmitter implements INode {
86
86
  colorBr: props.colorBr,
87
87
  zIndex: props.zIndex,
88
88
  zIndexLocked: props.zIndexLocked,
89
- scale: props.scale,
89
+ scaleX: props.scaleX,
90
+ scaleY: props.scaleY,
90
91
  mountX: props.mountX,
91
92
  mountY: props.mountY,
92
93
  mount: props.mount,
@@ -100,9 +101,9 @@ export class MainOnlyNode extends EventEmitter implements INode {
100
101
  texture: null,
101
102
  textureOptions: null,
102
103
  });
103
- // Forward texture events
104
- this.coreNode.on('txLoaded', this.onTextureLoaded);
105
- this.coreNode.on('txFailed', this.onTextureFailed);
104
+ // Forward loaded/failed events
105
+ this.coreNode.on('loaded', this.onTextureLoaded);
106
+ this.coreNode.on('failed', this.onTextureFailed);
106
107
 
107
108
  // Assign properties to this object
108
109
  this.parent = props.parent as MainOnlyNode;
@@ -127,14 +128,6 @@ export class MainOnlyNode extends EventEmitter implements INode {
127
128
  this.coreNode.y = value;
128
129
  }
129
130
 
130
- get worldX(): number {
131
- return this.coreNode.worldX;
132
- }
133
-
134
- get worldY(): number {
135
- return this.coreNode.worldY;
136
- }
137
-
138
131
  get width(): number {
139
132
  return this.coreNode.width;
140
133
  }
@@ -239,12 +232,36 @@ export class MainOnlyNode extends EventEmitter implements INode {
239
232
  this.coreNode.colorBr = value;
240
233
  }
241
234
 
242
- get scale(): number {
243
- return this.coreNode.scale;
235
+ get scale(): number | null {
236
+ if (this.scaleX !== this.scaleY) {
237
+ return null;
238
+ }
239
+ return this.coreNode.scaleX;
240
+ }
241
+
242
+ set scale(value: number | null) {
243
+ // We ignore `null` when it's set.
244
+ if (value === null) {
245
+ return;
246
+ }
247
+ this.coreNode.scaleX = value;
248
+ this.coreNode.scaleY = value;
249
+ }
250
+
251
+ get scaleX(): number {
252
+ return this.coreNode.scaleX;
253
+ }
254
+
255
+ set scaleX(value: number) {
256
+ this.coreNode.scaleX = value;
257
+ }
258
+
259
+ get scaleY(): number {
260
+ return this.coreNode.scaleY;
244
261
  }
245
262
 
246
- set scale(value: number) {
247
- this.coreNode.scale = value;
263
+ set scaleY(value: number) {
264
+ this.coreNode.scaleY = value;
248
265
  }
249
266
 
250
267
  get mount(): number {
@@ -385,12 +402,12 @@ export class MainOnlyNode extends EventEmitter implements INode {
385
402
  }
386
403
  }
387
404
 
388
- private onTextureLoaded: TextureLoadedEventHandler = (target, dimensions) => {
389
- this.emit('txLoaded', dimensions);
405
+ private onTextureLoaded: NodeLoadedEventHandler = (target, payload) => {
406
+ this.emit('loaded', payload);
390
407
  };
391
408
 
392
- private onTextureFailed: TextureFailedEventHandler = (target, error) => {
393
- this.emit('txFailed', error);
409
+ private onTextureFailed: NodeFailedEventHandler = (target, payload) => {
410
+ this.emit('failed', payload);
394
411
  };
395
412
  //#endregion Texture
396
413
 
@@ -25,10 +25,6 @@ import type { Stage } from '../../core/Stage.js';
25
25
  import type { RendererMain } from '../../main-api/RendererMain.js';
26
26
  import { MainOnlyNode, getNewId } from './MainOnlyNode.js';
27
27
  import { CoreTextNode } from '../../core/CoreTextNode.js';
28
- import type {
29
- TextFailedEventHandler,
30
- TextLoadedEventHandler,
31
- } from '../../common/CommonTypes.js';
32
28
 
33
29
  export class MainOnlyTextNode extends MainOnlyNode implements ITextNode {
34
30
  protected declare coreNode: CoreTextNode;
@@ -61,7 +57,8 @@ export class MainOnlyTextNode extends MainOnlyNode implements ITextNode {
61
57
  colorBr: props.colorBr,
62
58
  zIndex: props.zIndex,
63
59
  zIndexLocked: props.zIndexLocked,
64
- scale: props.scale,
60
+ scaleX: props.scaleX,
61
+ scaleY: props.scaleY,
65
62
  mountX: props.mountX,
66
63
  mountY: props.mountY,
67
64
  mount: props.mount,
@@ -94,18 +91,8 @@ export class MainOnlyTextNode extends MainOnlyNode implements ITextNode {
94
91
  shaderProps: null,
95
92
  }),
96
93
  );
97
- this.coreNode.on('textLoaded', this.onTextLoaded);
98
- this.coreNode.on('textFailed', this.onTextFailed);
99
94
  }
100
95
 
101
- private onTextLoaded: TextLoadedEventHandler = (target, dimensions) => {
102
- this.emit('textLoaded', dimensions);
103
- };
104
-
105
- private onTextFailed: TextFailedEventHandler = (target, error) => {
106
- this.emit('textFailed', error);
107
- };
108
-
109
96
  get text(): string {
110
97
  return this.coreNode.text;
111
98
  }
@@ -38,7 +38,8 @@ export interface NodeStructWritableProps {
38
38
  parentId: number;
39
39
  zIndex: number;
40
40
  zIndexLocked: number;
41
- scale: number;
41
+ scaleX: number;
42
+ scaleY: number;
42
43
  mount: number;
43
44
  mountX: number;
44
45
  mountY: number;
@@ -190,11 +191,20 @@ export class NodeStruct
190
191
  }
191
192
 
192
193
  @structProp('number')
193
- get scale(): number {
194
+ get scaleX(): number {
194
195
  return 1;
195
196
  }
196
197
 
197
- set scale(value: number) {
198
+ set scaleX(value: number) {
199
+ // Decorator will handle this
200
+ }
201
+
202
+ @structProp('number')
203
+ get scaleY(): number {
204
+ return 1;
205
+ }
206
+
207
+ set scaleY(value: number) {
198
208
  // Decorator will handle this
199
209
  }
200
210
 
@@ -53,7 +53,8 @@ export class SharedNode extends SharedObject {
53
53
  parentId: sharedNodeStruct.parentId,
54
54
  zIndex: sharedNodeStruct.zIndex,
55
55
  zIndexLocked: sharedNodeStruct.zIndexLocked,
56
- scale: sharedNodeStruct.scale,
56
+ scaleX: sharedNodeStruct.scaleX,
57
+ scaleY: sharedNodeStruct.scaleY,
57
58
  mount: sharedNodeStruct.mount,
58
59
  mountX: sharedNodeStruct.mountX,
59
60
  mountY: sharedNodeStruct.mountY,
@@ -81,7 +82,8 @@ export class SharedNode extends SharedObject {
81
82
  declare colorTr: number;
82
83
  declare colorBl: number;
83
84
  declare colorBr: number;
84
- declare scale: number;
85
+ declare scaleX: number;
86
+ declare scaleY: number;
85
87
  declare mountX: number;
86
88
  declare mountY: number;
87
89
  declare mount: number;
@@ -95,6 +95,22 @@ export class ThreadXMainNode extends SharedNode implements INode {
95
95
  }
96
96
  }
97
97
 
98
+ get scale(): number | null {
99
+ if (this.scaleX !== this.scaleY) {
100
+ return null;
101
+ }
102
+ return this.scaleX;
103
+ }
104
+
105
+ set scale(scale: number | null) {
106
+ // We ignore `null` when it's set.
107
+ if (scale === null) {
108
+ return;
109
+ }
110
+ this.scaleX = scale;
111
+ this.scaleY = scale;
112
+ }
113
+
98
114
  animate(
99
115
  props: Partial<INodeAnimatableProps>,
100
116
  settings: Partial<AnimationSettings>,
@@ -142,7 +142,8 @@ export class ThreadXRenderDriver implements IRenderDriver {
142
142
  alpha: props.alpha,
143
143
  zIndex: props.zIndex,
144
144
  zIndexLocked: props.zIndexLocked,
145
- scale: props.scale,
145
+ scaleX: props.scaleX,
146
+ scaleY: props.scaleY,
146
147
  mount: props.mount,
147
148
  mountX: props.mountX,
148
149
  mountY: props.mountY,
@@ -187,7 +188,8 @@ export class ThreadXRenderDriver implements IRenderDriver {
187
188
  alpha: props.alpha,
188
189
  zIndex: props.zIndex,
189
190
  zIndexLocked: props.zIndexLocked,
190
- scale: props.scale,
191
+ scaleX: props.scaleX,
192
+ scaleY: props.scaleY,
191
193
  mount: props.mount,
192
194
  mountX: props.mountX,
193
195
  mountY: props.mountY,
@@ -30,7 +30,10 @@ import type { Texture } from '../../../core/textures/Texture.js';
30
30
  import { CoreNode } from '../../../core/CoreNode.js';
31
31
  import type { ShaderRef, TextureRef } from '../../../main-api/RendererMain.js';
32
32
  import type { AnimationSettings } from '../../../core/animations/CoreAnimation.js';
33
- import type { Dimensions } from '../../../common/CommonTypes.js';
33
+ import type {
34
+ NodeLoadedPayload,
35
+ NodeFailedPayload,
36
+ } from '../../../common/CommonTypes.js';
34
37
 
35
38
  export class ThreadXRendererNode extends SharedNode {
36
39
  protected coreNode: CoreNode;
@@ -128,12 +131,18 @@ export class ThreadXRendererNode extends SharedNode {
128
131
  this.coreNode.unloadTexture();
129
132
  });
130
133
  // Forward on CoreNode events
131
- this.coreNode.on('txLoaded', (target: CoreNode, dimensions: Dimensions) => {
132
- this.emit('txLoaded', dimensions as unknown as Record<string, unknown>);
133
- });
134
- this.coreNode.on('txFailed', (target: CoreNode, error: Error) => {
135
- this.emit('txFailed', error as unknown as Record<string, unknown>);
136
- });
134
+ this.coreNode.on(
135
+ 'loaded',
136
+ (target: CoreNode, payload: NodeLoadedPayload) => {
137
+ this.emit('loaded', payload);
138
+ },
139
+ );
140
+ this.coreNode.on(
141
+ 'failed',
142
+ (target: CoreNode, payload: NodeFailedPayload) => {
143
+ this.emit('failed', payload);
144
+ },
145
+ );
137
146
  }
138
147
 
139
148
  override onPropertyChange<Key extends keyof this['z$__type__Props']>(
@@ -205,7 +214,8 @@ export class ThreadXRendererNode extends SharedNode {
205
214
  colorBr: sharedNodeStruct.colorBr,
206
215
  zIndex: sharedNodeStruct.zIndex,
207
216
  zIndexLocked: sharedNodeStruct.zIndexLocked,
208
- scale: sharedNodeStruct.scale,
217
+ scaleX: sharedNodeStruct.scaleX,
218
+ scaleY: sharedNodeStruct.scaleY,
209
219
  mount: sharedNodeStruct.mount,
210
220
  mountX: sharedNodeStruct.mountX,
211
221
  mountY: sharedNodeStruct.mountY,
@@ -17,7 +17,6 @@
17
17
  * limitations under the License.
18
18
  */
19
19
 
20
- import type { Dimensions } from '../../../common/CommonTypes.js';
21
20
  import { CoreTextNode } from '../../../core/CoreTextNode.js';
22
21
  import type { Stage } from '../../../core/Stage.js';
23
22
  import type { TrProps } from '../../../core/text-rendering/renderers/TextRenderer.js';
@@ -65,7 +64,8 @@ export class ThreadXRendererTextNode extends ThreadXRendererNode {
65
64
  pivot: sharedNodeStruct.pivot,
66
65
  pivotX: sharedNodeStruct.pivotX,
67
66
  pivotY: sharedNodeStruct.pivotY,
68
- scale: sharedNodeStruct.scale,
67
+ scaleX: sharedNodeStruct.scaleX,
68
+ scaleY: sharedNodeStruct.scaleY,
69
69
  rotation: sharedNodeStruct.rotation,
70
70
 
71
71
  // These are passed in via message handlers
@@ -113,18 +113,6 @@ export class ThreadXRendererTextNode extends ThreadXRendererNode {
113
113
  >,
114
114
  );
115
115
  // Forward on CoreNode events
116
- this.coreNode.on(
117
- 'textLoaded',
118
- (target: CoreTextNode, dimensions: Dimensions) => {
119
- this.emit(
120
- 'textLoaded',
121
- dimensions as unknown as Record<string, unknown>,
122
- );
123
- },
124
- );
125
- this.coreNode.on('textFailed', (target: CoreTextNode, error: Error) => {
126
- this.emit('textFailed', error as unknown as Record<string, unknown>);
127
- });
128
116
  this.on('debug', (target: ThreadXRendererNode, debug: TrProps['debug']) => {
129
117
  this.coreNode.debug = debug;
130
118
  });
@@ -97,7 +97,8 @@ const threadx = ThreadX.init({
97
97
  parentId: coreRootNode.parent?.id ?? 0,
98
98
  zIndex: coreRootNode.zIndex,
99
99
  zIndexLocked: coreRootNode.zIndexLocked,
100
- scale: coreRootNode.scale,
100
+ scaleX: coreRootNode.scaleX,
101
+ scaleY: coreRootNode.scaleY,
101
102
  mount: coreRootNode.mount,
102
103
  mountX: coreRootNode.mountX,
103
104
  mountY: coreRootNode.mountY,