@vpmedia/phaser 1.123.0 → 1.125.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 (221) hide show
  1. package/dist/index.js +635 -623
  2. package/dist/index.js.map +1 -1
  3. package/dist/phaser/core/animation.d.ts +3 -3
  4. package/dist/phaser/core/animation.d.ts.map +1 -1
  5. package/dist/phaser/core/animation_manager.d.ts.map +1 -1
  6. package/dist/phaser/core/animation_parser.d.ts +27 -2
  7. package/dist/phaser/core/animation_parser.d.ts.map +1 -1
  8. package/dist/phaser/core/array_set.d.ts +11 -11
  9. package/dist/phaser/core/array_set.d.ts.map +1 -1
  10. package/dist/phaser/core/cache.d.ts.map +1 -1
  11. package/dist/phaser/core/callback.d.ts.map +1 -1
  12. package/dist/phaser/core/dom.d.ts +125 -19
  13. package/dist/phaser/core/dom.d.ts.map +1 -1
  14. package/dist/phaser/core/factory.d.ts +3 -2
  15. package/dist/phaser/core/factory.d.ts.map +1 -1
  16. package/dist/phaser/core/frame_data.d.ts.map +1 -1
  17. package/dist/phaser/core/input.d.ts +3 -2
  18. package/dist/phaser/core/input.d.ts.map +1 -1
  19. package/dist/phaser/core/input_handler.d.ts.map +1 -1
  20. package/dist/phaser/core/input_mouse.d.ts +5 -1
  21. package/dist/phaser/core/input_mouse.d.ts.map +1 -1
  22. package/dist/phaser/core/input_pointer.d.ts +3 -3
  23. package/dist/phaser/core/input_pointer.d.ts.map +1 -1
  24. package/dist/phaser/core/input_touch.d.ts +8 -8
  25. package/dist/phaser/core/input_touch.d.ts.map +1 -1
  26. package/dist/phaser/core/loader.d.ts +11 -2
  27. package/dist/phaser/core/loader.d.ts.map +1 -1
  28. package/dist/phaser/core/loader_parser.d.ts +14 -1
  29. package/dist/phaser/core/loader_parser.d.ts.map +1 -1
  30. package/dist/phaser/core/raf.d.ts +1 -1
  31. package/dist/phaser/core/raf.d.ts.map +1 -1
  32. package/dist/phaser/core/scale_manager.d.ts.map +1 -1
  33. package/dist/phaser/core/scene_manager.d.ts.map +1 -1
  34. package/dist/phaser/core/signal.d.ts.map +1 -1
  35. package/dist/phaser/core/sound.d.ts.map +1 -1
  36. package/dist/phaser/core/sound_manager.d.ts +1 -1
  37. package/dist/phaser/core/sound_manager.d.ts.map +1 -1
  38. package/dist/phaser/core/stage.d.ts +16 -6
  39. package/dist/phaser/core/stage.d.ts.map +1 -1
  40. package/dist/phaser/core/time.d.ts.map +1 -1
  41. package/dist/phaser/core/timer.d.ts +1 -1
  42. package/dist/phaser/core/timer.d.ts.map +1 -1
  43. package/dist/phaser/core/tween.d.ts +13 -5
  44. package/dist/phaser/core/tween.d.ts.map +1 -1
  45. package/dist/phaser/core/tween_data.d.ts +9 -7
  46. package/dist/phaser/core/tween_data.d.ts.map +1 -1
  47. package/dist/phaser/core/tween_manager.d.ts.map +1 -1
  48. package/dist/phaser/display/button.d.ts +6 -5
  49. package/dist/phaser/display/button.d.ts.map +1 -1
  50. package/dist/phaser/display/canvas/buffer.d.ts +1 -1
  51. package/dist/phaser/display/canvas/buffer.d.ts.map +1 -1
  52. package/dist/phaser/display/canvas/masker.d.ts +1 -1
  53. package/dist/phaser/display/canvas/masker.d.ts.map +1 -1
  54. package/dist/phaser/display/canvas/pool.d.ts +2 -2
  55. package/dist/phaser/display/canvas/pool.d.ts.map +1 -1
  56. package/dist/phaser/display/canvas/renderer.d.ts +9 -8
  57. package/dist/phaser/display/canvas/renderer.d.ts.map +1 -1
  58. package/dist/phaser/display/display_object.d.ts +5 -3
  59. package/dist/phaser/display/display_object.d.ts.map +1 -1
  60. package/dist/phaser/display/graphics.d.ts +1 -1
  61. package/dist/phaser/display/graphics.d.ts.map +1 -1
  62. package/dist/phaser/display/group.d.ts +1 -1
  63. package/dist/phaser/display/group.d.ts.map +1 -1
  64. package/dist/phaser/display/image.d.ts +6 -5
  65. package/dist/phaser/display/image.d.ts.map +1 -1
  66. package/dist/phaser/display/sprite_util.d.ts +3 -2
  67. package/dist/phaser/display/sprite_util.d.ts.map +1 -1
  68. package/dist/phaser/display/text.d.ts +2 -16
  69. package/dist/phaser/display/text.d.ts.map +1 -1
  70. package/dist/phaser/display/webgl/abstract_filter.d.ts +14 -8
  71. package/dist/phaser/display/webgl/abstract_filter.d.ts.map +1 -1
  72. package/dist/phaser/display/webgl/blend_manager.d.ts +2 -3
  73. package/dist/phaser/display/webgl/blend_manager.d.ts.map +1 -1
  74. package/dist/phaser/display/webgl/earcut.d.ts.map +1 -1
  75. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts +11 -5
  76. package/dist/phaser/display/webgl/fast_sprite_batch.d.ts.map +1 -1
  77. package/dist/phaser/display/webgl/filter_manager.d.ts +6 -4
  78. package/dist/phaser/display/webgl/filter_manager.d.ts.map +1 -1
  79. package/dist/phaser/display/webgl/filter_texture.d.ts +4 -4
  80. package/dist/phaser/display/webgl/filter_texture.d.ts.map +1 -1
  81. package/dist/phaser/display/webgl/graphics.d.ts +2 -2
  82. package/dist/phaser/display/webgl/graphics.d.ts.map +1 -1
  83. package/dist/phaser/display/webgl/mask_manager.d.ts +3 -2
  84. package/dist/phaser/display/webgl/mask_manager.d.ts.map +1 -1
  85. package/dist/phaser/display/webgl/render_texture.d.ts +11 -8
  86. package/dist/phaser/display/webgl/render_texture.d.ts.map +1 -1
  87. package/dist/phaser/display/webgl/renderer.d.ts +8 -4
  88. package/dist/phaser/display/webgl/renderer.d.ts.map +1 -1
  89. package/dist/phaser/display/webgl/shader/normal.d.ts +28 -3
  90. package/dist/phaser/display/webgl/shader/normal.d.ts.map +1 -1
  91. package/dist/phaser/display/webgl/sprite_batch.d.ts +6 -5
  92. package/dist/phaser/display/webgl/sprite_batch.d.ts.map +1 -1
  93. package/dist/phaser/display/webgl/stencil_manager.d.ts +3 -3
  94. package/dist/phaser/display/webgl/stencil_manager.d.ts.map +1 -1
  95. package/dist/phaser/display/webgl/texture.d.ts.map +1 -1
  96. package/dist/phaser/display/webgl/texture_util.d.ts +2 -2
  97. package/dist/phaser/display/webgl/texture_util.d.ts.map +1 -1
  98. package/dist/phaser/display/webgl/util.d.ts +1 -1
  99. package/dist/phaser/display/webgl/util.d.ts.map +1 -1
  100. package/dist/phaser/geom/circle.d.ts.map +1 -1
  101. package/dist/phaser/geom/line.d.ts +10 -1
  102. package/dist/phaser/geom/line.d.ts.map +1 -1
  103. package/dist/phaser/geom/rectangle.d.ts.map +1 -1
  104. package/dist/phaser/geom/util/circle.d.ts +2 -1
  105. package/dist/phaser/geom/util/circle.d.ts.map +1 -1
  106. package/dist/phaser/geom/util/point.d.ts +1 -1
  107. package/dist/phaser/geom/util/point.d.ts.map +1 -1
  108. package/dist/phaser/geom/util/rectangle.d.ts +1 -1
  109. package/dist/phaser/geom/util/rectangle.d.ts.map +1 -1
  110. package/dist/phaser/util/math.d.ts.map +1 -1
  111. package/package.json +1 -1
  112. package/src/phaser/core/animation.ts +11 -11
  113. package/src/phaser/core/animation_manager.ts +12 -12
  114. package/src/phaser/core/animation_parser.ts +39 -37
  115. package/src/phaser/core/array_set.ts +28 -28
  116. package/src/phaser/core/cache.test.ts +33 -33
  117. package/src/phaser/core/cache.ts +7 -7
  118. package/src/phaser/core/callback.ts +1 -0
  119. package/src/phaser/core/callback_context.test.ts +8 -8
  120. package/src/phaser/core/device_util.ts +4 -4
  121. package/src/phaser/core/dom.ts +38 -24
  122. package/src/phaser/core/factory.ts +9 -2
  123. package/src/phaser/core/frame_data.test.ts +23 -23
  124. package/src/phaser/core/frame_data.ts +2 -5
  125. package/src/phaser/core/game.ts +12 -12
  126. package/src/phaser/core/input.ts +11 -10
  127. package/src/phaser/core/input_handler.test.ts +18 -18
  128. package/src/phaser/core/input_handler.ts +14 -14
  129. package/src/phaser/core/input_mouse.ts +13 -12
  130. package/src/phaser/core/input_pointer.test.ts +26 -26
  131. package/src/phaser/core/input_pointer.ts +4 -4
  132. package/src/phaser/core/input_touch.ts +8 -8
  133. package/src/phaser/core/loader.test.ts +26 -26
  134. package/src/phaser/core/loader.ts +63 -75
  135. package/src/phaser/core/loader_parser.test.ts +16 -16
  136. package/src/phaser/core/loader_parser.ts +16 -6
  137. package/src/phaser/core/raf.ts +1 -1
  138. package/src/phaser/core/registry.test.ts +6 -6
  139. package/src/phaser/core/scale_manager.ts +12 -11
  140. package/src/phaser/core/scene_manager.test.ts +30 -30
  141. package/src/phaser/core/scene_manager.ts +3 -3
  142. package/src/phaser/core/signal.test.ts +8 -8
  143. package/src/phaser/core/signal.ts +3 -3
  144. package/src/phaser/core/signal_dispatch.test.ts +30 -30
  145. package/src/phaser/core/sound.ts +45 -40
  146. package/src/phaser/core/sound_manager.ts +18 -22
  147. package/src/phaser/core/sound_sprite.ts +1 -1
  148. package/src/phaser/core/stage.ts +22 -12
  149. package/src/phaser/core/time.ts +1 -1
  150. package/src/phaser/core/timer.test.ts +47 -47
  151. package/src/phaser/core/timer.ts +2 -2
  152. package/src/phaser/core/tween.ts +34 -36
  153. package/src/phaser/core/tween_data.test.ts +119 -0
  154. package/src/phaser/core/tween_data.ts +54 -39
  155. package/src/phaser/core/tween_easing.test.ts +5 -5
  156. package/src/phaser/core/tween_manager.ts +2 -2
  157. package/src/phaser/display/bitmap_text.test.ts +30 -30
  158. package/src/phaser/display/bitmap_text.ts +3 -3
  159. package/src/phaser/display/button.ts +20 -18
  160. package/src/phaser/display/canvas/buffer.ts +2 -2
  161. package/src/phaser/display/canvas/graphics.ts +2 -2
  162. package/src/phaser/display/canvas/masker.ts +1 -1
  163. package/src/phaser/display/canvas/pool.test.ts +20 -20
  164. package/src/phaser/display/canvas/pool.ts +6 -6
  165. package/src/phaser/display/canvas/renderer.ts +18 -16
  166. package/src/phaser/display/canvas/tinter.ts +1 -1
  167. package/src/phaser/display/canvas/util.test.ts +25 -25
  168. package/src/phaser/display/canvas/util.ts +2 -2
  169. package/src/phaser/display/display_object.ts +16 -15
  170. package/src/phaser/display/graphics.test.ts +70 -20
  171. package/src/phaser/display/graphics.ts +46 -42
  172. package/src/phaser/display/group.ts +13 -12
  173. package/src/phaser/display/image.ts +35 -31
  174. package/src/phaser/display/sprite_util.ts +4 -3
  175. package/src/phaser/display/text.test.ts +28 -25
  176. package/src/phaser/display/text.ts +56 -62
  177. package/src/phaser/display/webgl/abstract_filter.ts +21 -11
  178. package/src/phaser/display/webgl/blend_manager.ts +4 -5
  179. package/src/phaser/display/webgl/earcut.test.ts +20 -20
  180. package/src/phaser/display/webgl/earcut.ts +46 -42
  181. package/src/phaser/display/webgl/fast_sprite_batch.ts +21 -16
  182. package/src/phaser/display/webgl/filter_manager.ts +7 -4
  183. package/src/phaser/display/webgl/filter_texture.ts +5 -5
  184. package/src/phaser/display/webgl/graphics.ts +3 -3
  185. package/src/phaser/display/webgl/mask_manager.ts +9 -14
  186. package/src/phaser/display/webgl/render_texture.ts +15 -14
  187. package/src/phaser/display/webgl/renderer.ts +10 -9
  188. package/src/phaser/display/webgl/shader/normal.ts +45 -35
  189. package/src/phaser/display/webgl/shader/shader.test.ts +24 -24
  190. package/src/phaser/display/webgl/shader_manager.test.ts +18 -18
  191. package/src/phaser/display/webgl/shader_manager.ts +1 -1
  192. package/src/phaser/display/webgl/sprite_batch.ts +10 -10
  193. package/src/phaser/display/webgl/stencil_manager.ts +4 -4
  194. package/src/phaser/display/webgl/texture.ts +2 -1
  195. package/src/phaser/display/webgl/texture_util.ts +2 -2
  196. package/src/phaser/display/webgl/util.ts +4 -4
  197. package/src/phaser/geom/circle.test.ts +38 -38
  198. package/src/phaser/geom/circle.ts +7 -7
  199. package/src/phaser/geom/ellipse.test.ts +12 -12
  200. package/src/phaser/geom/line.test.ts +15 -15
  201. package/src/phaser/geom/line.ts +5 -2
  202. package/src/phaser/geom/matrix.test.ts +31 -31
  203. package/src/phaser/geom/point.test.ts +31 -31
  204. package/src/phaser/geom/polygon.test.ts +27 -27
  205. package/src/phaser/geom/polygon.ts +2 -2
  206. package/src/phaser/geom/rectangle.test.ts +87 -87
  207. package/src/phaser/geom/rectangle.ts +12 -13
  208. package/src/phaser/geom/rounded_rectangle.test.ts +9 -9
  209. package/src/phaser/geom/util/circle.test.ts +5 -5
  210. package/src/phaser/geom/util/circle.ts +8 -11
  211. package/src/phaser/geom/util/ellipse.test.ts +2 -2
  212. package/src/phaser/geom/util/line.test.ts +2 -2
  213. package/src/phaser/geom/util/matrix.test.ts +4 -4
  214. package/src/phaser/geom/util/point.test.ts +2 -2
  215. package/src/phaser/geom/util/point.ts +6 -8
  216. package/src/phaser/geom/util/polygon.test.ts +2 -2
  217. package/src/phaser/geom/util/rectangle.test.ts +2 -2
  218. package/src/phaser/geom/util/rectangle.ts +1 -1
  219. package/src/phaser/geom/util/rounded_rectangle.test.ts +2 -2
  220. package/src/phaser/util/math.test.ts +41 -41
  221. package/src/phaser/util/math.ts +7 -5
@@ -3,36 +3,36 @@ import { addToDOM, create, removeFromDOM, setBackgroundColor, setTouchAction, se
3
3
 
4
4
  const createCanvas = (): HTMLCanvasElement => create(null, 32, 16, '', true);
5
5
 
6
- describe('canvas util', () => {
7
- afterEach(() => {
6
+ describe('canvas util', (): void => {
7
+ afterEach((): void => {
8
8
  document.body.innerHTML = '';
9
9
  });
10
10
 
11
- describe('create', () => {
12
- it('sizes the canvas as asked', () => {
11
+ describe('create', (): void => {
12
+ it('sizes the canvas as asked', (): void => {
13
13
  const canvas = createCanvas();
14
14
  expect(canvas.width).toBe(32);
15
15
  expect(canvas.height).toBe(16);
16
16
  });
17
17
 
18
- it('falls back to 256 square for a zero size', () => {
18
+ it('falls back to 256 square for a zero size', (): void => {
19
19
  const canvas = create(null, 0, 0, '', true);
20
20
  expect(canvas.width).toBe(256);
21
21
  expect(canvas.height).toBe(256);
22
22
  });
23
23
 
24
- it('assigns the id it is given and leaves an empty one off', () => {
24
+ it('assigns the id it is given and leaves an empty one off', (): void => {
25
25
  expect(create(null, 32, 16, 'stage', true).id).toBe('stage');
26
26
  expect(createCanvas().id).toBe('');
27
27
  });
28
28
 
29
- it('takes the canvas out of the flow', () => {
29
+ it('takes the canvas out of the flow', (): void => {
30
30
  expect(createCanvas().style.display).toBe('block');
31
31
  });
32
32
  });
33
33
 
34
- describe('addToDOM', () => {
35
- it('appends to the element with the given id', () => {
34
+ describe('addToDOM', (): void => {
35
+ it('appends to the element with the given id', (): void => {
36
36
  const host = document.createElement('div');
37
37
  host.id = 'host';
38
38
  document.body.append(host);
@@ -41,7 +41,7 @@ describe('canvas util', () => {
41
41
  expect(canvas.parentElement).toBe(host);
42
42
  });
43
43
 
44
- it('appends to an element passed directly', () => {
44
+ it('appends to an element passed directly', (): void => {
45
45
  const host = document.createElement('div');
46
46
  document.body.append(host);
47
47
  const canvas = createCanvas();
@@ -49,26 +49,26 @@ describe('canvas util', () => {
49
49
  expect(canvas.parentElement).toBe(host);
50
50
  });
51
51
 
52
- it('falls back to the body for an id nothing matches', () => {
52
+ it('falls back to the body for an id nothing matches', (): void => {
53
53
  const canvas = createCanvas();
54
54
  addToDOM(canvas, 'nothing-here');
55
55
  expect(canvas.parentElement).toBe(document.body);
56
56
  });
57
57
 
58
- it('falls back to the body when given no parent', () => {
58
+ it('falls back to the body when given no parent', (): void => {
59
59
  const canvas = createCanvas();
60
60
  addToDOM(canvas, null);
61
61
  expect(canvas.parentElement).toBe(document.body);
62
62
  });
63
63
 
64
64
  // An empty id used to build the selector `#`, which throws before the body fallback is reached.
65
- it('falls back to the body for an empty id', () => {
65
+ it('falls back to the body for an empty id', (): void => {
66
66
  const canvas = createCanvas();
67
67
  addToDOM(canvas, '');
68
68
  expect(canvas.parentElement).toBe(document.body);
69
69
  });
70
70
 
71
- it('hides the parent overflow unless told not to', () => {
71
+ it('hides the parent overflow unless told not to', (): void => {
72
72
  const host = document.createElement('div');
73
73
  document.body.append(host);
74
74
  addToDOM(createCanvas(), host);
@@ -79,7 +79,7 @@ describe('canvas util', () => {
79
79
  expect(other.style.overflow).toBe('');
80
80
  });
81
81
 
82
- it('survives an id that is not a valid bare selector', () => {
82
+ it('survives an id that is not a valid bare selector', (): void => {
83
83
  const host = document.createElement('div');
84
84
  host.id = 'game.container';
85
85
  document.body.append(host);
@@ -89,40 +89,40 @@ describe('canvas util', () => {
89
89
  });
90
90
  });
91
91
 
92
- describe('removeFromDOM', () => {
93
- it('detaches an attached canvas', () => {
92
+ describe('removeFromDOM', (): void => {
93
+ it('detaches an attached canvas', (): void => {
94
94
  const canvas = createCanvas();
95
95
  document.body.append(canvas);
96
96
  removeFromDOM(canvas);
97
97
  expect(canvas.parentElement).toBeNull();
98
98
  });
99
99
 
100
- it('leaves a detached canvas alone', () => {
100
+ it('leaves a detached canvas alone', (): void => {
101
101
  const canvas = createCanvas();
102
- expect(() => {
102
+ expect((): void => {
103
103
  removeFromDOM(canvas);
104
104
  }).not.toThrow();
105
105
  });
106
106
  });
107
107
 
108
- describe('style helpers', () => {
109
- it('sets the background colour', () => {
108
+ describe('style helpers', (): void => {
109
+ it('sets the background colour', (): void => {
110
110
  expect(setBackgroundColor(createCanvas(), 'rgb(255, 0, 0)').style.backgroundColor).toBe('rgb(255, 0, 0)');
111
111
  });
112
112
 
113
- it('disables touch actions', () => {
113
+ it('disables touch actions', (): void => {
114
114
  expect(setTouchAction(createCanvas()).style.getPropertyValue('touch-action')).toBe('none');
115
115
  });
116
116
 
117
- it('falls back to none for an empty touch action', () => {
117
+ it('falls back to none for an empty touch action', (): void => {
118
118
  expect(setTouchAction(createCanvas(), '').style.getPropertyValue('touch-action')).toBe('none');
119
119
  });
120
120
 
121
- it('disables user selection', () => {
121
+ it('disables user selection', (): void => {
122
122
  expect(setUserSelect(createCanvas()).style.getPropertyValue('user-select')).toBe('none');
123
123
  });
124
124
 
125
- it('falls back to none for an empty user selection', () => {
125
+ it('falls back to none for an empty user selection', (): void => {
126
126
  expect(setUserSelect(createCanvas(), '').style.getPropertyValue('user-select')).toBe('none');
127
127
  });
128
128
  });
@@ -141,7 +141,7 @@ export const getSmoothingPrefix = (context: CanvasRenderingContext2D): string |
141
141
  const smoothingFlags = context as unknown as Record<string, boolean | undefined>;
142
142
  for (const vendor of VENDORS) {
143
143
  const flag = `${vendor}mageSmoothingEnabled`;
144
- if (smoothingFlags[flag]) {
144
+ if (smoothingFlags[flag] !== undefined) {
145
145
  return flag;
146
146
  }
147
147
  }
@@ -166,7 +166,7 @@ export const setSmoothing = (context: CanvasRenderingContext2D, property: string
166
166
  */
167
167
  export const setSmoothingEnabled = (context: CanvasRenderingContext2D, value: boolean): CanvasRenderingContext2D => {
168
168
  const s = getSmoothingPrefix(context);
169
- if (s) {
169
+ if (s !== null) {
170
170
  setSmoothing(context, s, value);
171
171
  }
172
172
  return context;
@@ -10,6 +10,7 @@ import type { UserData } from '../core/callback.js';
10
10
  import type { EventManager } from '../core/event_manager.js';
11
11
  import type { Image } from './image.js';
12
12
  import type { RenderSession } from './render_session.js';
13
+ import type { IdentifiedWebGLRenderingContext } from './webgl/util.js';
13
14
 
14
15
  export class DisplayObject {
15
16
  /** @type {boolean} */
@@ -123,9 +124,11 @@ export class DisplayObject {
123
124
 
124
125
  /**
125
126
  * Destroys this display object and cleans up resources.
127
+ * @param {boolean} _destroyChildren - Whether the subclass should destroy its children too.
128
+ * @param {boolean} _destroyTexture - Whether the subclass should destroy its texture too.
126
129
  */
127
- public destroy(): void {
128
- if (this.children) {
130
+ public destroy(_destroyChildren = true, _destroyTexture = false): void {
131
+ if (this.children as DisplayObject[] | undefined) {
129
132
  let i = this.children.length;
130
133
  while (i) {
131
134
  i -= 1;
@@ -282,12 +285,12 @@ export class DisplayObject {
282
285
  * @returns {DisplayObject[]} The array of removed child display objects.
283
286
  * @throws {Error} If the range is invalid.
284
287
  */
285
- public removeChildren(beginIndex: number, endIndex: number): DisplayObject[] {
286
- beginIndex ??= 0;
287
- endIndex ??= this.children.length;
288
- const range = endIndex - beginIndex;
289
- if (range > 0 && range <= endIndex) {
290
- const removed = this.children.splice(beginIndex, range);
288
+ public removeChildren(beginIndex?: number, endIndex?: number): DisplayObject[] {
289
+ const begin = beginIndex ?? 0;
290
+ const end = endIndex ?? this.children.length;
291
+ const range = end - begin;
292
+ if (range > 0 && range <= end) {
293
+ const removed = this.children.splice(begin, range);
291
294
  for (const child of removed) {
292
295
  child.parent = null;
293
296
  }
@@ -308,7 +311,7 @@ export class DisplayObject {
308
311
  if (!parent && !this.parent) {
309
312
  return this;
310
313
  }
311
- if (!this.game || !this.visible) {
314
+ if (!(this.game as Game | undefined) || !this.visible) {
312
315
  return this;
313
316
  }
314
317
  const p = parent ?? this.parent ?? this.game.world;
@@ -605,12 +608,10 @@ export class DisplayObject {
605
608
  * @param {DisplayObject} from - The display object to convert from (defaults to this).
606
609
  * @returns {Point} The converted local position.
607
610
  */
608
- public toLocal(position: Point, from: DisplayObject): Point {
609
- if (from) {
610
- position = from.toGlobal(position);
611
- }
611
+ public toLocal(position: Point, from: DisplayObject | null): Point {
612
+ const global = from ? from.toGlobal(position) : position;
612
613
  this.updateTransform();
613
- return this.worldTransform.applyInverse(position);
614
+ return this.worldTransform.applyInverse(global);
614
615
  }
615
616
 
616
617
  /**
@@ -622,7 +623,7 @@ export class DisplayObject {
622
623
  return;
623
624
  }
624
625
  this._cachedSprite.worldAlpha = this.worldAlpha;
625
- if (renderSession.gl) {
626
+ if (renderSession.gl as IdentifiedWebGLRenderingContext | undefined) {
626
627
  renderWebGL(this._cachedSprite, renderSession);
627
628
  } else {
628
629
  renderCanvas(this._cachedSprite, renderSession);
@@ -9,11 +9,11 @@ const createGame = (): Game => ({ renderer: { resolution: 1 } }) as unknown as G
9
9
 
10
10
  const createGraphics = (): Graphics => new Graphics(createGame());
11
11
 
12
- describe('Graphics', () => {
13
- describe('containsPoint', () => {
12
+ describe('Graphics', (): void => {
13
+ describe('containsPoint', (): void => {
14
14
  // The index never advanced here, so any filled shape that did not contain the point spun
15
15
  // forever. A vitest timeout is the only way a hang shows up as a failure.
16
- it('returns false for a point outside a filled shape instead of hanging', { timeout: 2000 }, () => {
16
+ it('returns false for a point outside a filled shape instead of hanging', { timeout: 2000 }, (): void => {
17
17
  const graphics = createGraphics();
18
18
  graphics.beginFill(0xff0000);
19
19
  graphics.drawRect(0, 0, 10, 10);
@@ -21,7 +21,7 @@ describe('Graphics', () => {
21
21
  expect(graphics.containsPoint(new Point(100, 100), new Point())).toBe(false);
22
22
  });
23
23
 
24
- it('returns true for a point inside a filled shape', { timeout: 2000 }, () => {
24
+ it('returns true for a point inside a filled shape', { timeout: 2000 }, (): void => {
25
25
  const graphics = createGraphics();
26
26
  graphics.beginFill(0xff0000);
27
27
  graphics.drawRect(0, 0, 10, 10);
@@ -29,7 +29,7 @@ describe('Graphics', () => {
29
29
  expect(graphics.containsPoint(new Point(5, 5), new Point())).toBe(true);
30
30
  });
31
31
 
32
- it('walks past a non-matching shape to a later matching one', { timeout: 2000 }, () => {
32
+ it('walks past a non-matching shape to a later matching one', { timeout: 2000 }, (): void => {
33
33
  const graphics = createGraphics();
34
34
  graphics.beginFill(0xff0000);
35
35
  graphics.drawRect(0, 0, 10, 10);
@@ -38,33 +38,33 @@ describe('Graphics', () => {
38
38
  expect(graphics.containsPoint(new Point(105, 105), new Point())).toBe(true);
39
39
  });
40
40
 
41
- it('ignores unfilled shapes', { timeout: 2000 }, () => {
41
+ it('ignores unfilled shapes', { timeout: 2000 }, (): void => {
42
42
  const graphics = createGraphics();
43
43
  graphics.lineStyle(1, 0x00ff00);
44
44
  graphics.drawRect(0, 0, 10, 10);
45
45
  expect(graphics.containsPoint(new Point(5, 5), new Point())).toBe(false);
46
46
  });
47
47
 
48
- it('returns false when nothing has been drawn', { timeout: 2000 }, () => {
48
+ it('returns false when nothing has been drawn', { timeout: 2000 }, (): void => {
49
49
  expect(createGraphics().containsPoint(new Point(0, 0), new Point())).toBe(false);
50
50
  });
51
51
  });
52
52
 
53
- describe('drawShape', () => {
54
- it('records a polygon as the current path', () => {
53
+ describe('drawShape', (): void => {
54
+ it('records a polygon as the current path', (): void => {
55
55
  const graphics = createGraphics();
56
56
  const data = graphics.drawShape(new Polygon([0, 0, 10, 0, 10, 10]));
57
57
  expect(graphics.currentPath).toBe(data);
58
58
  expect(data.shape).toBeInstanceOf(Polygon);
59
59
  });
60
60
 
61
- it('does not record a rectangle as the current path', () => {
61
+ it('does not record a rectangle as the current path', (): void => {
62
62
  const graphics = createGraphics();
63
63
  graphics.drawShape(new Rectangle(0, 0, 10, 10));
64
64
  expect(graphics.currentPath).toBeNull();
65
65
  });
66
66
 
67
- it('carries the current fill and line style onto the data', () => {
67
+ it('carries the current fill and line style onto the data', (): void => {
68
68
  const graphics = createGraphics();
69
69
  graphics.lineStyle(4, 0x00ff00, 0.5);
70
70
  graphics.beginFill(0xff0000, 0.25);
@@ -77,15 +77,15 @@ describe('Graphics', () => {
77
77
  expect(data.fill).toBe(true);
78
78
  });
79
79
 
80
- it('flattens a polygon so the builders see numbers', () => {
80
+ it('flattens a polygon so the builders see numbers', (): void => {
81
81
  const graphics = createGraphics();
82
82
  const data = graphics.drawShape(new Polygon([new Point(0, 0), new Point(10, 0), new Point(10, 10)]));
83
- expect((data.shape as Polygon)._points.every((value) => typeof value === 'number')).toBe(true);
83
+ expect((data.shape as Polygon)._points.every((value): boolean => typeof value === 'number')).toBe(true);
84
84
  });
85
85
  });
86
86
 
87
- describe('path building', () => {
88
- it('accumulates points through moveTo and lineTo', () => {
87
+ describe('path building', (): void => {
88
+ it('accumulates points through moveTo and lineTo', (): void => {
89
89
  const graphics = createGraphics();
90
90
  graphics.moveTo(0, 0);
91
91
  graphics.lineTo(10, 0);
@@ -93,15 +93,15 @@ describe('Graphics', () => {
93
93
  expect(graphics.currentPath?.shape.points).toStrictEqual([0, 0, 10, 0, 10, 10]);
94
94
  });
95
95
 
96
- it('starts a path at the origin when lineTo comes first', () => {
96
+ it('starts a path at the origin when lineTo comes first', (): void => {
97
97
  const graphics = createGraphics();
98
98
  graphics.lineTo(10, 10);
99
99
  expect(graphics.currentPath?.shape.points).toStrictEqual([0, 0, 10, 10]);
100
100
  });
101
101
  });
102
102
 
103
- describe('endFill', () => {
104
- it('stops filling subsequent shapes', () => {
103
+ describe('endFill', (): void => {
104
+ it('stops filling subsequent shapes', (): void => {
105
105
  const graphics = createGraphics();
106
106
  graphics.beginFill(0xff0000);
107
107
  graphics.endFill();
@@ -109,8 +109,58 @@ describe('Graphics', () => {
109
109
  });
110
110
  });
111
111
 
112
- describe('clear', () => {
113
- it('drops the recorded graphics data', () => {
112
+ describe('drawTriangle', (): void => {
113
+ it('records the triangle as a polygon', (): void => {
114
+ const graphics = createGraphics();
115
+ graphics.drawTriangle([new Point(0, 0), new Point(10, 0), new Point(0, 10)]);
116
+ expect(graphics.graphicsData).toHaveLength(1);
117
+ expect(graphics.graphicsData[0]?.shape).toBeInstanceOf(Polygon);
118
+ });
119
+
120
+ it('keeps a front-facing triangle when culling', (): void => {
121
+ const graphics = createGraphics();
122
+ graphics.drawTriangle([new Point(0, 0), new Point(10, 0), new Point(0, 10)], true);
123
+ graphics.drawTriangle([new Point(0, 0), new Point(0, 10), new Point(10, 0)], true);
124
+ expect(graphics.graphicsData).toHaveLength(1);
125
+ });
126
+
127
+ it('draws both windings when culling is off', (): void => {
128
+ const graphics = createGraphics();
129
+ graphics.drawTriangle([new Point(0, 0), new Point(10, 0), new Point(0, 10)]);
130
+ graphics.drawTriangle([new Point(0, 0), new Point(0, 10), new Point(10, 0)]);
131
+ expect(graphics.graphicsData).toHaveLength(2);
132
+ });
133
+ });
134
+
135
+ describe('drawTriangles', (): void => {
136
+ it('reads a flat coordinate list as triangles', (): void => {
137
+ const graphics = createGraphics();
138
+ graphics.drawTriangles([0, 0, 10, 0, 0, 10, 20, 20, 30, 20, 20, 30], null);
139
+ expect(graphics.graphicsData).toHaveLength(2);
140
+ });
141
+
142
+ it('reads a point list as triangles', (): void => {
143
+ const graphics = createGraphics();
144
+ graphics.drawTriangles([new Point(0, 0), new Point(10, 0), new Point(0, 10)], null);
145
+ expect(graphics.graphicsData).toHaveLength(1);
146
+ });
147
+
148
+ it('follows an index list over a flat coordinate list', (): void => {
149
+ const graphics = createGraphics();
150
+ graphics.drawTriangles([0, 0, 10, 0, 0, 10], [0, 1, 2]);
151
+ expect(graphics.graphicsData).toHaveLength(1);
152
+ expect((graphics.graphicsData[0]!.shape as Polygon).toNumberArray()).toStrictEqual([0, 0, 10, 0, 0, 10]);
153
+ });
154
+
155
+ it('follows an index list over a point list', (): void => {
156
+ const graphics = createGraphics();
157
+ graphics.drawTriangles([new Point(0, 0), new Point(10, 0), new Point(0, 10)], [0, 1, 2]);
158
+ expect(graphics.graphicsData).toHaveLength(1);
159
+ });
160
+ });
161
+
162
+ describe('clear', (): void => {
163
+ it('drops the recorded graphics data', (): void => {
114
164
  const graphics = createGraphics();
115
165
  graphics.beginFill(0xff0000);
116
166
  graphics.drawRect(0, 0, 10, 10);
@@ -38,6 +38,22 @@ export type GraphicsWebGLBucket = {
38
38
  gl: IdentifiedWebGLRenderingContext;
39
39
  };
40
40
 
41
+ /**
42
+ * Reports whether a triangle winds towards the viewer. In two dimensions the cross product is a
43
+ * scalar, so its sign alone says which way the face points.
44
+ * @param {Point[]} points - The three corners of the triangle, in order.
45
+ * @returns {boolean} True when the triangle faces the viewer.
46
+ */
47
+ const isFrontFacing = (points: Point[]): boolean => {
48
+ const [a, b, c] = points;
49
+ if (!a || !b || !c) {
50
+ return true;
51
+ }
52
+ const ab = new Point(b.x - a.x, b.y - a.y);
53
+ const cb = new Point(b.x - c.x, b.y - c.y);
54
+ return cb.cross(ab) > 0;
55
+ };
56
+
41
57
  export class Graphics extends DisplayObject {
42
58
  declare public type: number;
43
59
  declare public renderable: boolean;
@@ -327,19 +343,17 @@ export class Graphics extends DisplayObject {
327
343
  if (startAngle === endAngle) {
328
344
  return this;
329
345
  }
330
- if (!anticlockwise && endAngle <= startAngle) {
331
- endAngle += Math.PI * 2;
332
- } else if (anticlockwise && startAngle <= endAngle) {
333
- startAngle += Math.PI * 2;
334
- }
335
- const sweep = anticlockwise ? (startAngle - endAngle) * -1 : endAngle - startAngle;
346
+ const fullTurn = Math.PI * 2;
347
+ const from = anticlockwise && startAngle <= endAngle ? startAngle + fullTurn : startAngle;
348
+ const to = !anticlockwise && endAngle <= startAngle ? endAngle + fullTurn : endAngle;
349
+ const sweep = anticlockwise ? (from - to) * -1 : to - from;
336
350
  const segs = Math.ceil(Math.abs(sweep) / (Math.PI * 2)) * segments;
337
351
  // Sweep check - moved here because we don't want to do the moveTo below if the arc fails
338
352
  if (sweep === 0) {
339
353
  return this;
340
354
  }
341
- const startX = cx + Math.cos(startAngle) * radius;
342
- const startY = cy + Math.sin(startAngle) * radius;
355
+ const startX = cx + Math.cos(from) * radius;
356
+ const startY = cy + Math.sin(from) * radius;
343
357
  if (anticlockwise && this.filling) {
344
358
  this.moveTo(cx, cy);
345
359
  } else {
@@ -355,7 +369,7 @@ export class Graphics extends DisplayObject {
355
369
  const remainder = (segMinus % 1) / segMinus;
356
370
  for (let i = 0; i <= segMinus; i += 1) {
357
371
  const real = i + remainder * i;
358
- const angle = theta + startAngle + theta2 * real;
372
+ const angle = theta + from + theta2 * real;
359
373
  const c = Math.cos(angle);
360
374
  const s = -Math.sin(angle);
361
375
  points.push((cTheta * c + sTheta * s) * radius + cx, (cTheta * -s + sTheta * c) * radius + cy);
@@ -887,18 +901,10 @@ export class Graphics extends DisplayObject {
887
901
  */
888
902
  public drawTriangle(points: Point[], cull = false): void {
889
903
  const triangle = new Polygon(points);
890
- if (cull) {
891
- const [a, b, c] = points as [Point, Point, Point];
892
- const cameraToFace = new Point(0 - a.x, 0 - a.y);
893
- const ab = new Point(b.x - a.x, b.y - a.y);
894
- const cb = new Point(b.x - c.x, b.y - c.y);
895
- const faceNormal = cb.cross(ab);
896
- if (cameraToFace.dot(faceNormal as any) > 0) {
897
- this.drawPolygon(triangle);
898
- }
899
- } else {
900
- this.drawPolygon(triangle);
904
+ if (cull && !isFrontFacing(points)) {
905
+ return;
901
906
  }
907
+ this.drawPolygon(triangle);
902
908
  }
903
909
 
904
910
  /**
@@ -907,43 +913,41 @@ export class Graphics extends DisplayObject {
907
913
  * @param {number[]} indices - The indices of the vertices to use.
908
914
  * @param {boolean} cull - Whether to perform backface culling.
909
915
  */
910
- public drawTriangles(vertices: any, indices: number[], cull = false): void {
911
- const point1 = new Point();
912
- const point2 = new Point();
913
- const point3 = new Point();
914
- let points = [];
916
+ public drawTriangles(vertices: Point[] | number[], indices: number[] | null, cull = false): void {
917
+ let points: Point[] = [];
915
918
  let i;
916
- if (!indices) {
919
+ if (indices === null) {
917
920
  if (vertices[0] instanceof Point) {
918
- for (i = 0; i < vertices.length / 3; i += 1) {
919
- this.drawTriangle([vertices[i * 3], vertices[i * 3 + 1], vertices[i * 3 + 2]], cull);
921
+ const corners = vertices as Point[];
922
+ for (i = 0; i < corners.length / 3; i += 1) {
923
+ this.drawTriangle([corners[i * 3]!, corners[i * 3 + 1]!, corners[i * 3 + 2]!], cull);
920
924
  }
921
925
  } else {
922
- for (i = 0; i < vertices.length / 6; i += 1) {
923
- point1.x = vertices[i * 6 + 0]!;
924
- point1.y = vertices[i * 6 + 1]!;
925
- point2.x = vertices[i * 6 + 2]!;
926
- point2.y = vertices[i * 6 + 3]!;
927
- point3.x = vertices[i * 6 + 4]!;
928
- point3.y = vertices[i * 6 + 5]!;
929
- this.drawTriangle([point1, point2, point3], cull);
926
+ const coordinates = vertices as number[];
927
+ for (i = 0; i < coordinates.length / 6; i += 1) {
928
+ this.drawTriangle(
929
+ [
930
+ new Point(coordinates[i * 6 + 0], coordinates[i * 6 + 1]),
931
+ new Point(coordinates[i * 6 + 2], coordinates[i * 6 + 3]),
932
+ new Point(coordinates[i * 6 + 4], coordinates[i * 6 + 5]),
933
+ ],
934
+ cull
935
+ );
930
936
  }
931
937
  }
932
938
  } else if (vertices[0] instanceof Point) {
939
+ const corners = vertices as Point[];
933
940
  for (i = 0; i < indices.length / 3; i += 1) {
934
- points.push(vertices[indices[i * 3]!]);
935
- points.push(vertices[indices[i * 3 + 1]!]);
936
- points.push(vertices[indices[i * 3 + 2]!]);
941
+ points.push(corners[indices[i * 3]!]!, corners[indices[i * 3 + 1]!]!, corners[indices[i * 3 + 2]!]!);
937
942
  if (points.length === 3) {
938
943
  this.drawTriangle(points, cull);
939
944
  points = [];
940
945
  }
941
946
  }
942
947
  } else {
948
+ const coordinates = vertices as number[];
943
949
  for (i = 0; i < indices.length; i += 1) {
944
- point1.x = vertices[indices[i]! * 2]!;
945
- point1.y = vertices[indices[i]! * 2 + 1]!;
946
- points.push(point1.copyTo({} as any));
950
+ points.push(new Point(coordinates[indices[i]! * 2], coordinates[indices[i]! * 2 + 1]));
947
951
  if (points.length === 3) {
948
952
  this.drawTriangle(points, cull);
949
953
  points = [];
@@ -3,6 +3,7 @@ import { Signal } from '../core/signal.js';
3
3
  import { DisplayObject } from './display_object.js';
4
4
  import { Image } from './image.js';
5
5
  import type { Game } from '../core/game.js';
6
+ import type { EventManager } from '../core/event_manager.js';
6
7
 
7
8
  export const SORT_ASCENDING = -1;
8
9
  export const SORT_DESCENDING = 1;
@@ -33,7 +34,7 @@ export class Group extends DisplayObject {
33
34
  super(game);
34
35
  /** @type {number} */
35
36
  this.type = GROUP;
36
- parent ??= game.world;
37
+ const target = parent ?? game.world;
37
38
  /** @type {string} */
38
39
  this.name = name ?? 'group';
39
40
  /** @type {number} */
@@ -41,9 +42,9 @@ export class Group extends DisplayObject {
41
42
  if (addToStage) {
42
43
  this.game.stage.addChild(this);
43
44
  this.z = this.game.stage.children.length;
44
- } else if (parent) {
45
- parent.addChild(this);
46
- this.z = parent.children.length;
45
+ } else if (target as DisplayObject | undefined) {
46
+ target.addChild(this);
47
+ this.z = target.children.length;
47
48
  }
48
49
  /** @type {boolean} */
49
50
  this.ignoreDestroy = false;
@@ -112,10 +113,10 @@ export class Group extends DisplayObject {
112
113
  this.updateZ();
113
114
  }
114
115
  const inputChild = child as T & { input?: unknown; inputEnabled?: boolean };
115
- if (this.inputEnableChildren && (!inputChild.input || inputChild.inputEnabled)) {
116
+ if (this.inputEnableChildren && (inputChild.input === undefined || inputChild.inputEnabled === true)) {
116
117
  inputChild.inputEnabled = true;
117
118
  }
118
- if (!silent && child.events) {
119
+ if (!silent && (child.events as EventManager | undefined)) {
119
120
  child.events.onAddedToGroup$dispatch(child, this);
120
121
  }
121
122
  this.cursor ??= child;
@@ -252,7 +253,7 @@ export class Group extends DisplayObject {
252
253
  this.destroy();
253
254
  return;
254
255
  }
255
- if (!this.exists || !this.parent?.exists) {
256
+ if (!this.exists || this.parent?.exists !== true) {
256
257
  this.renderOrderID = -1;
257
258
  return;
258
259
  }
@@ -288,11 +289,11 @@ export class Group extends DisplayObject {
288
289
  * @param {boolean} silent - Whether to dispatch events.
289
290
  * @returns {boolean} True if the child was removed, false otherwise.
290
291
  */
291
- public remove(child: any, destroy = true, silent = false): boolean {
292
+ public remove(child: DisplayObject, destroy = true, silent = false): boolean {
292
293
  if (this.children.length === 0 || !this.children.includes(child)) {
293
294
  return false;
294
295
  }
295
- if (!silent && child.events && !child.destroyPhase) {
296
+ if (!silent && (child.events as EventManager | undefined)) {
296
297
  child.events.onRemovedFromGroup$dispatch(child, this);
297
298
  }
298
299
  const removed = this.removeChild(child);
@@ -301,7 +302,7 @@ export class Group extends DisplayObject {
301
302
  this.next();
302
303
  }
303
304
  if (destroy && removed) {
304
- (removed as any).destroy(true);
305
+ removed.destroy(true);
305
306
  }
306
307
  return true;
307
308
  }
@@ -318,12 +319,12 @@ export class Group extends DisplayObject {
318
319
  }
319
320
  do {
320
321
  const first = this.children[0]!;
321
- if (!silent && first.events) {
322
+ if (!silent && (first.events as EventManager | undefined)) {
322
323
  first.events.onRemovedFromGroup$dispatch(first, this);
323
324
  }
324
325
  const removed = this.removeChild(first);
325
326
  if (destroy && removed) {
326
- (removed as any).destroy(true, destroyTexture);
327
+ removed.destroy(true, destroyTexture);
327
328
  }
328
329
  } while (this.children.length > 0);
329
330
  this.cursor = null;