@codexo/exojs 0.6.3 → 0.6.5

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 (46) hide show
  1. package/CHANGELOG.md +156 -0
  2. package/dist/esm/core/Application.d.ts +8 -1
  3. package/dist/esm/core/Application.js +17 -1
  4. package/dist/esm/core/Application.js.map +1 -1
  5. package/dist/esm/core/SceneManager.js +11 -12
  6. package/dist/esm/core/SceneManager.js.map +1 -1
  7. package/dist/esm/core/capabilities.d.ts +30 -29
  8. package/dist/esm/core/capabilities.js +163 -61
  9. package/dist/esm/core/capabilities.js.map +1 -1
  10. package/dist/esm/index.js +1 -6
  11. package/dist/esm/index.js.map +1 -1
  12. package/dist/esm/math/geometry.d.ts +12 -8
  13. package/dist/esm/math/geometry.js +119 -72
  14. package/dist/esm/math/geometry.js.map +1 -1
  15. package/dist/esm/rendering/index.d.ts +0 -5
  16. package/dist/esm/rendering/primitives/Graphics.d.ts +3 -2
  17. package/dist/esm/rendering/primitives/Graphics.js +33 -25
  18. package/dist/esm/rendering/primitives/Graphics.js.map +1 -1
  19. package/dist/esm/rendering/webgl2/WebGl2Backend.js +1 -4
  20. package/dist/esm/rendering/webgl2/WebGl2Backend.js.map +1 -1
  21. package/dist/esm/rendering/webgpu/WebGpuBackend.js +0 -3
  22. package/dist/esm/rendering/webgpu/WebGpuBackend.js.map +1 -1
  23. package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js +1 -2
  24. package/dist/esm/rendering/webgpu/WebGpuMaskCompositor.js.map +1 -1
  25. package/dist/exo.esm.js +710 -1280
  26. package/dist/exo.esm.js.map +1 -1
  27. package/package.json +1 -1
  28. package/dist/esm/rendering/primitives/CircleGeometry.d.ts +0 -4
  29. package/dist/esm/rendering/primitives/CircleGeometry.js +0 -21
  30. package/dist/esm/rendering/primitives/CircleGeometry.js.map +0 -1
  31. package/dist/esm/rendering/primitives/DrawableShape.d.ts +0 -11
  32. package/dist/esm/rendering/primitives/DrawableShape.js +0 -21
  33. package/dist/esm/rendering/primitives/DrawableShape.js.map +0 -1
  34. package/dist/esm/rendering/primitives/Geometry.d.ts +0 -13
  35. package/dist/esm/rendering/primitives/Geometry.js +0 -16
  36. package/dist/esm/rendering/primitives/Geometry.js.map +0 -1
  37. package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.d.ts +0 -26
  38. package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.js +0 -222
  39. package/dist/esm/rendering/webgl2/WebGl2PrimitiveRenderer.js.map +0 -1
  40. package/dist/esm/rendering/webgl2/glsl/primitive.frag.js +0 -4
  41. package/dist/esm/rendering/webgl2/glsl/primitive.frag.js.map +0 -1
  42. package/dist/esm/rendering/webgl2/glsl/primitive.vert.js +0 -4
  43. package/dist/esm/rendering/webgl2/glsl/primitive.vert.js.map +0 -1
  44. package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.d.ts +0 -38
  45. package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js +0 -488
  46. package/dist/esm/rendering/webgpu/WebGpuPrimitiveRenderer.js.map +0 -1
@@ -1,17 +1,99 @@
1
- // Browser feature-detection probes evaluated once at module load. The
2
- // resulting `capabilities` object is a `Readonly<Record<CapabilityName,
3
- // boolean>>` and can be inspected directly or queried via `isSupported`.
1
+ /// <reference types="@webgpu/types" />
2
+ // Browser-environment feature detection. Construction is private; the
3
+ // only public entry is `Capabilities.ready`, a lazy-cached `Promise<Capabilities>`
4
+ // that fires the (mostly) async probes on first access and returns the
5
+ // same Promise for every subsequent call. Once it resolves, the returned
6
+ // instance is frozen — every property is read once and never mutates.
4
7
  //
5
- // All probes are synchronous. Async questions ("can I actually acquire a
6
- // WebGPU adapter?", "can the audio decoder play this OGG file?") are
7
- // outside this module's scope — they're owned by the Application's
8
- // backend selection and the Loader respectively. `capabilities.webgpu`
9
- // returning `true` only guarantees that the browser advertises WebGPU,
10
- // not that an adapter request will succeed.
8
+ // Synchronous callsites should keep the resolved instance in scope (e.g.,
9
+ // `app.capabilities` after `await app.start(...)`); there is no global
10
+ // sync mirror, by design.
11
11
  const hasWindow = typeof window !== 'undefined';
12
12
  const hasDocument = typeof document !== 'undefined';
13
13
  const hasNavigator = typeof navigator !== 'undefined';
14
- const probeWebGl2 = () => {
14
+ class Capabilities {
15
+ static _readyPromise = null;
16
+ /**
17
+ * Lazy-cached Promise that resolves to a frozen Capabilities instance.
18
+ *
19
+ * The first read kicks off the async probes (currently just the WebGPU
20
+ * adapter request); every subsequent read returns the same Promise.
21
+ * Concurrent callers share the in-flight detection — no double work.
22
+ *
23
+ * Early-warmup pattern for callers who want to overlap detection with
24
+ * other startup work:
25
+ *
26
+ * ```ts
27
+ * void Capabilities.ready; // fire-and-forget; starts probes now
28
+ * // ... unrelated bootstrap ...
29
+ * const caps = await Capabilities.ready; // typically already resolved
30
+ * ```
31
+ */
32
+ static get ready() {
33
+ if (Capabilities._readyPromise === null) {
34
+ Capabilities._readyPromise = Capabilities._detect();
35
+ }
36
+ return Capabilities._readyPromise;
37
+ }
38
+ webgl2;
39
+ webgpu;
40
+ webgpuAdapter;
41
+ webgpuVendor;
42
+ webgpuArchitecture;
43
+ pointer;
44
+ keyboard;
45
+ gamepad;
46
+ touch;
47
+ maxTouchPoints;
48
+ audio;
49
+ fullscreen;
50
+ vibration;
51
+ offscreenCanvas;
52
+ webWorkers;
53
+ devicePixelRatio;
54
+ constructor(values) {
55
+ this.webgl2 = values.webgl2;
56
+ this.webgpu = values.webgpu;
57
+ this.webgpuAdapter = values.webgpuAdapter;
58
+ this.webgpuVendor = values.webgpuVendor;
59
+ this.webgpuArchitecture = values.webgpuArchitecture;
60
+ this.pointer = values.pointer;
61
+ this.keyboard = values.keyboard;
62
+ this.gamepad = values.gamepad;
63
+ this.touch = values.touch;
64
+ this.maxTouchPoints = values.maxTouchPoints;
65
+ this.audio = values.audio;
66
+ this.fullscreen = values.fullscreen;
67
+ this.vibration = values.vibration;
68
+ this.offscreenCanvas = values.offscreenCanvas;
69
+ this.webWorkers = values.webWorkers;
70
+ this.devicePixelRatio = values.devicePixelRatio;
71
+ Object.freeze(this);
72
+ }
73
+ static async _detect() {
74
+ const [webgpuAdapter, webgpuInfo] = await probeWebGpu();
75
+ return new Capabilities({
76
+ webgl2: probeWebGl2(),
77
+ webgpu: probeWebGpuApiSurface(),
78
+ webgpuAdapter,
79
+ webgpuVendor: webgpuInfo?.vendor ?? null,
80
+ webgpuArchitecture: webgpuInfo?.architecture ?? null,
81
+ pointer: probePointer(),
82
+ keyboard: probeKeyboard(),
83
+ gamepad: probeGamepad(),
84
+ touch: probeTouchSupported(),
85
+ maxTouchPoints: probeMaxTouchPoints(),
86
+ audio: probeAudio(),
87
+ fullscreen: probeFullscreen(),
88
+ vibration: probeVibration(),
89
+ offscreenCanvas: probeOffscreenCanvas(),
90
+ webWorkers: probeWebWorkers(),
91
+ devicePixelRatio: hasWindow ? window.devicePixelRatio : 1,
92
+ });
93
+ }
94
+ }
95
+ // --- probes ---------------------------------------------------------------
96
+ function probeWebGl2() {
15
97
  if (!hasDocument)
16
98
  return false;
17
99
  try {
@@ -22,75 +104,95 @@ const probeWebGl2 = () => {
22
104
  catch {
23
105
  return false;
24
106
  }
25
- };
26
- const probeWebGpu = () => {
107
+ }
108
+ function probeWebGpuApiSurface() {
27
109
  return hasNavigator && 'gpu' in navigator;
28
- };
29
- const probeAudio = () => {
30
- if (!hasWindow)
31
- return false;
32
- const w = window;
33
- return typeof w.AudioContext !== 'undefined' || typeof w.webkitAudioContext !== 'undefined';
34
- };
35
- const probePointer = () => {
110
+ }
111
+ async function probeWebGpu() {
112
+ if (!probeWebGpuApiSurface())
113
+ return [null, null];
114
+ const gpu = navigator.gpu;
115
+ if (!gpu || typeof gpu.requestAdapter !== 'function')
116
+ return [null, null];
117
+ try {
118
+ const adapter = await gpu.requestAdapter();
119
+ if (!adapter)
120
+ return [null, null];
121
+ // Modern path: GPUAdapter.info is a sync property (Chrome 116+,
122
+ // Safari 18+). Older browsers exposed a deprecated async
123
+ // requestAdapterInfo() instead. Try the modern path first, fall
124
+ // back if needed.
125
+ const adapterAny = adapter;
126
+ if (adapterAny.info) {
127
+ return [adapter, adapterAny.info];
128
+ }
129
+ if (typeof adapterAny.requestAdapterInfo === 'function') {
130
+ try {
131
+ return [adapter, await adapterAny.requestAdapterInfo()];
132
+ }
133
+ catch {
134
+ return [adapter, null];
135
+ }
136
+ }
137
+ return [adapter, null];
138
+ }
139
+ catch {
140
+ return [null, null];
141
+ }
142
+ }
143
+ function probePointer() {
36
144
  return hasWindow && 'PointerEvent' in window;
37
- };
38
- const probeTouch = () => {
145
+ }
146
+ function probeKeyboard() {
147
+ return hasWindow && 'KeyboardEvent' in window;
148
+ }
149
+ function probeGamepad() {
150
+ return hasNavigator && typeof navigator.getGamepads === 'function';
151
+ }
152
+ function probeTouchSupported() {
39
153
  if (!hasWindow)
40
154
  return false;
41
155
  if ('ontouchstart' in window)
42
156
  return true;
43
- if (hasNavigator && typeof navigator.maxTouchPoints === 'number' && navigator.maxTouchPoints > 0)
157
+ if (probeMaxTouchPoints() > 0)
44
158
  return true;
45
159
  return false;
46
- };
47
- const probeGamepad = () => {
48
- return hasNavigator && typeof navigator.getGamepads === 'function';
49
- };
50
- const probeKeyboard = () => {
51
- return hasWindow && 'KeyboardEvent' in window;
52
- };
53
- const probeFullscreen = () => {
160
+ }
161
+ function probeMaxTouchPoints() {
162
+ if (!hasNavigator)
163
+ return 0;
164
+ const points = navigator.maxTouchPoints;
165
+ return typeof points === 'number' ? points : 0;
166
+ }
167
+ function probeAudio() {
168
+ if (!hasWindow)
169
+ return false;
170
+ const w = window;
171
+ return typeof w.AudioContext !== 'undefined' || typeof w.webkitAudioContext !== 'undefined';
172
+ }
173
+ function probeFullscreen() {
54
174
  if (!hasDocument)
55
175
  return false;
56
176
  const el = document.documentElement;
57
177
  return typeof el.requestFullscreen === 'function' || typeof el.webkitRequestFullscreen === 'function';
58
- };
59
- const probeVibration = () => {
178
+ }
179
+ function probeVibration() {
60
180
  return hasNavigator && typeof navigator.vibrate === 'function';
61
- };
62
- const probeOffscreenCanvas = () => {
181
+ }
182
+ function probeOffscreenCanvas() {
63
183
  // The browser global is verbatim `OffscreenCanvas`; eslint's
64
184
  // strict-camelCase rule rejects the property name even though we
65
185
  // can't rename a web standard.
66
186
  // eslint-disable-next-line @typescript-eslint/naming-convention
67
187
  return hasWindow && typeof window.OffscreenCanvas !== 'undefined';
68
- };
69
- /**
70
- * Synchronous, one-shot feature-detection results. Computed once at
71
- * module load and frozen. Use either as a property bag (`capabilities.touch`)
72
- * or via {@link isSupported} for typed lookup.
73
- */
74
- const capabilities = Object.freeze({
75
- webgl2: probeWebGl2(),
76
- webgpu: probeWebGpu(),
77
- audio: probeAudio(),
78
- pointer: probePointer(),
79
- touch: probeTouch(),
80
- gamepad: probeGamepad(),
81
- keyboard: probeKeyboard(),
82
- fullscreen: probeFullscreen(),
83
- vibration: probeVibration(),
84
- offscreenCanvas: probeOffscreenCanvas(),
85
- });
86
- /**
87
- * Typed lookup over {@link capabilities}. Identical to
88
- * `capabilities[name]` but the function form gives clearer call-sites
89
- * when the name is computed.
90
- */
91
- function isSupported(name) {
92
- return capabilities[name];
188
+ }
189
+ function probeWebWorkers() {
190
+ // The browser global is verbatim `Worker`; eslint's strict-camelCase
191
+ // rule rejects the property name even though we can't rename a web
192
+ // standard.
193
+ // eslint-disable-next-line @typescript-eslint/naming-convention
194
+ return hasWindow && typeof window.Worker === 'function';
93
195
  }
94
196
 
95
- export { capabilities, isSupported };
197
+ export { Capabilities };
96
198
  //# sourceMappingURL=capabilities.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.js","sources":["../../../../src/core/capabilities.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"capabilities.js","sources":["../../../../src/core/capabilities.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { canvasSourceToDataUrl, emptyArrayBuffer, getCanvasSourceSize, getPreciseTime, getTextureSourceSize, hours, milliseconds, minutes, noop, rand, removeArrayItems, seconds, stopEvent, supportsCodec, supportsEventOptions, supportsIndexedDb, supportsPointerEvents, supportsTouchEvents, supportsWebAudio } from './core/utils.js';
2
2
  export { Application, ApplicationStatus } from './core/Application.js';
3
3
  export { Bounds } from './core/Bounds.js';
4
- export { capabilities, isSupported } from './core/capabilities.js';
4
+ export { Capabilities } from './core/capabilities.js';
5
5
  export { Clock } from './core/Clock.js';
6
6
  export { Color } from './core/Color.js';
7
7
  export { Quadtree } from './core/Quadtree.js';
@@ -63,9 +63,6 @@ export { UniversalEmitter } from './particles/emitters/UniversalEmitter.js';
63
63
  export { Particle } from './particles/Particle.js';
64
64
  export { ParticleSystem } from './particles/ParticleSystem.js';
65
65
  export { Mesh } from './rendering/mesh/Mesh.js';
66
- export { CircleGeometry } from './rendering/primitives/CircleGeometry.js';
67
- export { Geometry } from './rendering/primitives/Geometry.js';
68
- export { DrawableShape } from './rendering/primitives/DrawableShape.js';
69
66
  export { Graphics } from './rendering/primitives/Graphics.js';
70
67
  export { Shader } from './rendering/shader/Shader.js';
71
68
  export { ShaderAttribute } from './rendering/shader/ShaderAttribute.js';
@@ -87,7 +84,6 @@ export { webGl2PrimitiveArrayConstructors, webGl2PrimitiveByteSizeMapping, webGl
87
84
  export { WebGl2VertexArrayObject } from './rendering/webgl2/WebGl2VertexArrayObject.js';
88
85
  export { WebGl2MeshRenderer } from './rendering/webgl2/WebGl2MeshRenderer.js';
89
86
  export { WebGl2ParticleRenderer } from './rendering/webgl2/WebGl2ParticleRenderer.js';
90
- export { WebGl2PrimitiveRenderer } from './rendering/webgl2/WebGl2PrimitiveRenderer.js';
91
87
  export { WebGl2Backend } from './rendering/webgl2/WebGl2Backend.js';
92
88
  export { createWebGl2ShaderProgram } from './rendering/webgl2/WebGl2ShaderProgram.js';
93
89
  export { WebGl2SpriteRenderer } from './rendering/webgl2/WebGl2SpriteRenderer.js';
@@ -95,7 +91,6 @@ export { AbstractWebGpuRenderer } from './rendering/webgpu/AbstractWebGpuRendere
95
91
  export { getWebGpuBlendState } from './rendering/webgpu/WebGpuBlendState.js';
96
92
  export { WebGpuMeshRenderer } from './rendering/webgpu/WebGpuMeshRenderer.js';
97
93
  export { WebGpuParticleRenderer } from './rendering/webgpu/WebGpuParticleRenderer.js';
98
- export { WebGpuPrimitiveRenderer } from './rendering/webgpu/WebGpuPrimitiveRenderer.js';
99
94
  export { WebGpuBackend } from './rendering/webgpu/WebGpuBackend.js';
100
95
  export { WebGpuSpriteRenderer } from './rendering/webgpu/WebGpuSpriteRenderer.js';
101
96
  export { BlendModes, BufferTypes, BufferUsage, RenderingPrimitives, ScaleModes, ShaderPrimitives, WrapModes } from './rendering/types.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,8 +1,12 @@
1
- import { Geometry } from '@/rendering/primitives/Geometry';
2
- export declare const buildLine: (startX: number, startY: number, endX: number, endY: number, width: number, vertices?: Array<number>, indices?: Array<number>) => Geometry;
3
- export declare const buildPath: (points: Array<number>, width: number, vertices?: Array<number>, indices?: Array<number>) => Geometry;
4
- export declare const buildCircle: (centerX: number, centerY: number, radius: number, vertices?: Array<number>, indices?: Array<number>) => Geometry;
5
- export declare const buildEllipse: (centerX: number, centerY: number, radiusX: number, radiusY: number, vertices?: Array<number>, indices?: Array<number>) => Geometry;
6
- export declare const buildPolygon: (points: Array<number>, vertices?: Array<number>, indices?: Array<number>) => Geometry;
7
- export declare const buildRectangle: (x: number, y: number, width: number, height: number, vertices?: Array<number>, indices?: Array<number>) => Geometry;
8
- export declare const buildStar: (centerX: number, centerY: number, points: number, radius: number, innerRadius?: number, rotation?: number) => Geometry;
1
+ export interface MeshGeometryData {
2
+ readonly vertices: Float32Array;
3
+ readonly indices: Uint16Array;
4
+ readonly points: Array<number>;
5
+ }
6
+ export declare const buildLine: (startX: number, startY: number, endX: number, endY: number, width: number) => MeshGeometryData;
7
+ export declare const buildPath: (points: Array<number>, width: number) => MeshGeometryData;
8
+ export declare const buildCircle: (centerX: number, centerY: number, radius: number) => MeshGeometryData;
9
+ export declare const buildEllipse: (centerX: number, centerY: number, radiusX: number, radiusY: number) => MeshGeometryData;
10
+ export declare const buildPolygon: (points: Array<number>) => MeshGeometryData;
11
+ export declare const buildRectangle: (x: number, y: number, width: number, height: number) => MeshGeometryData;
12
+ export declare const buildStar: (centerX: number, centerY: number, points: number, radius: number, innerRadius?: number, rotation?: number) => MeshGeometryData;
@@ -1,26 +1,31 @@
1
1
  import earcut from 'earcut';
2
- import { Geometry } from '../rendering/primitives/Geometry.js';
3
2
  import { Vector } from './Vector.js';
4
3
  import { tau } from './utils.js';
5
4
 
6
- const buildLine = (startX, startY, endX, endY, width, vertices = [], indices = []) => {
5
+ const buildLine = (startX, startY, endX, endY, width) => {
7
6
  const points = [startX, startY, endX, endY];
8
7
  const distance = width / 2;
9
- const index = vertices.length / 6;
10
8
  const perpA = new Vector(startX - endX, startY - endY).perp().normalize().multiply(distance);
11
9
  const perpB = new Vector(endX - startX, endY - startY).perp().normalize().multiply(distance);
12
- vertices.push(startX - perpA.x, startY - perpA.y);
13
- vertices.push(startX + perpA.x, startY + perpA.y);
14
- vertices.push(endX - perpB.x, endY - perpB.y);
15
- vertices.push(endX + perpB.x, endY + perpB.y);
16
- indices.push(index, index, index + 1, index + 2, index + 3, index + 3);
17
- return new Geometry({ vertices, indices, points });
10
+ const vertices = new Float32Array([
11
+ startX - perpA.x, startY - perpA.y, // 0: start-left
12
+ startX + perpA.x, startY + perpA.y, // 1: start-right
13
+ endX - perpB.x, endY - perpB.y, // 2: end-left
14
+ endX + perpB.x, endY + perpB.y, // 3: end-right
15
+ ]);
16
+ perpA.destroy();
17
+ perpB.destroy();
18
+ const indices = new Uint16Array([0, 1, 3, 0, 3, 2]);
19
+ return { vertices, indices, points };
18
20
  };
19
- const buildPath = (points, width, vertices = [], indices = []) => {
21
+ const buildPath = (points, width) => {
20
22
  if (points.length < 4) {
21
23
  throw new Error('At least two X/Y pairs are required to build a line.');
22
24
  }
23
- const lineWidth = width / 2, firstPoint = new Vector(points[0], points[1]), lastPoint = new Vector(points[points.length - 2], points[points.length - 1]);
25
+ const lineWidth = width / 2;
26
+ const firstPoint = new Vector(points[0], points[1]);
27
+ const lastPoint = new Vector(points[points.length - 2], points[points.length - 1]);
28
+ const outlinePoints = points;
24
29
  if (firstPoint.x === lastPoint.x && firstPoint.y === lastPoint.y) {
25
30
  points = points.slice();
26
31
  points.pop();
@@ -31,9 +36,10 @@ const buildPath = (points, width, vertices = [], indices = []) => {
31
36
  points.unshift(midPointX, midPointY);
32
37
  points.push(midPointX, midPointY);
33
38
  }
39
+ firstPoint.destroy();
40
+ lastPoint.destroy();
34
41
  const length = points.length / 2;
35
- let indexCount = points.length;
36
- let indexStart = vertices.length / 6;
42
+ const stripVertices = [];
37
43
  let p1x = points[0];
38
44
  let p1y = points[1];
39
45
  let p2x = points[2];
@@ -51,8 +57,8 @@ const buildPath = (points, width, vertices = [], indices = []) => {
51
57
  perpy /= dist;
52
58
  perpx *= lineWidth;
53
59
  perpy *= lineWidth;
54
- vertices.push(p1x - perpx, p1y - perpy);
55
- vertices.push(p1x + perpx, p1y + perpy);
60
+ stripVertices.push(p1x - perpx, p1y - perpy);
61
+ stripVertices.push(p1x + perpx, p1y + perpy);
56
62
  for (let i = 1; i < length - 1; i++) {
57
63
  p1x = points[(i - 1) * 2];
58
64
  p1y = points[((i - 1) * 2) + 1];
@@ -83,8 +89,8 @@ const buildPath = (points, width, vertices = [], indices = []) => {
83
89
  let denom = (a1 * b2) - (a2 * b1);
84
90
  if (Math.abs(denom) < 0.1) {
85
91
  denom += 10.1;
86
- vertices.push(p2x - perpx, p2y - perpy);
87
- vertices.push(p2x + perpx, p2y + perpy);
92
+ stripVertices.push(p2x - perpx, p2y - perpy);
93
+ stripVertices.push(p2x + perpx, p2y + perpy);
88
94
  continue;
89
95
  }
90
96
  const px = ((b1 * c2) - (b2 * c1)) / denom;
@@ -98,14 +104,13 @@ const buildPath = (points, width, vertices = [], indices = []) => {
98
104
  perp3y /= dist;
99
105
  perp3x *= lineWidth;
100
106
  perp3y *= lineWidth;
101
- vertices.push(p2x - perp3x, p2y - perp3y);
102
- vertices.push(p2x + perp3x, p2y + perp3y);
103
- vertices.push(p2x - perp3x, p2y - perp3y);
104
- indexCount++;
107
+ stripVertices.push(p2x - perp3x, p2y - perp3y);
108
+ stripVertices.push(p2x + perp3x, p2y + perp3y);
109
+ stripVertices.push(p2x - perp3x, p2y - perp3y);
105
110
  }
106
111
  else {
107
- vertices.push(px, py);
108
- vertices.push(p2x - (px - p2x), p2y - (py - p2y));
112
+ stripVertices.push(px, py);
113
+ stripVertices.push(p2x - (px - p2x), p2y - (py - p2y));
109
114
  }
110
115
  }
111
116
  p1x = points[(length - 2) * 2];
@@ -119,70 +124,112 @@ const buildPath = (points, width, vertices = [], indices = []) => {
119
124
  perpy /= dist;
120
125
  perpx *= lineWidth;
121
126
  perpy *= lineWidth;
122
- vertices.push(p2x - perpx, p2y - perpy);
123
- vertices.push(p2x + perpx, p2y + perpy);
124
- indices.push(indexStart);
125
- for (let i = 0; i < indexCount; i++) {
126
- indices.push(indexStart++);
127
+ stripVertices.push(p2x - perpx, p2y - perpy);
128
+ stripVertices.push(p2x + perpx, p2y + perpy);
129
+ // Convert strip-style vertex sequence to triangle-list indices.
130
+ // For N strip vertices (N = stripVertices.length / 2), each i in [0, N-3]
131
+ // produces a triangle. Even i: (i, i+1, i+2). Odd i: (i+1, i, i+2).
132
+ // This preserves the same winding the original triangle-strip pipeline saw.
133
+ const stripVertexCount = stripVertices.length / 2;
134
+ const vertices = new Float32Array(stripVertices);
135
+ const triangleCount = stripVertexCount >= 3 ? stripVertexCount - 2 : 0;
136
+ const indices = new Uint16Array(triangleCount * 3);
137
+ for (let i = 0; i < triangleCount; i++) {
138
+ const base = i * 3;
139
+ if ((i & 1) === 0) {
140
+ indices[base] = i;
141
+ indices[base + 1] = i + 1;
142
+ indices[base + 2] = i + 2;
143
+ }
144
+ else {
145
+ indices[base] = i + 1;
146
+ indices[base + 1] = i;
147
+ indices[base + 2] = i + 2;
148
+ }
127
149
  }
128
- indices.push(indexStart - 1);
129
- return new Geometry({ vertices, indices, points });
150
+ return { vertices, indices, points: outlinePoints };
130
151
  };
131
- const buildCircle = (centerX, centerY, radius, vertices = [], indices = []) => {
132
- const length = Math.floor(15 * Math.sqrt(radius + radius)), segment = (Math.PI * 2) / length, points = [];
133
- let index = vertices.length / 6;
134
- indices.push(index);
135
- for (let i = 0; i < length + 1; i++) {
136
- const segmentX = centerX + (Math.sin(segment * i) * radius), segmentY = centerY + (Math.cos(segment * i) * radius);
152
+ const buildCircle = (centerX, centerY, radius) => {
153
+ const length = Math.floor(15 * Math.sqrt(radius + radius));
154
+ const segment = (Math.PI * 2) / length;
155
+ const points = [];
156
+ // 1 center vertex + N perimeter vertices.
157
+ const vertices = new Float32Array((length + 1) * 2);
158
+ vertices[0] = centerX;
159
+ vertices[1] = centerY;
160
+ for (let i = 0; i < length; i++) {
161
+ const segmentX = centerX + (Math.sin(segment * i) * radius);
162
+ const segmentY = centerY + (Math.cos(segment * i) * radius);
137
163
  points.push(segmentX, segmentY);
138
- vertices.push(centerX, centerY);
139
- vertices.push(segmentX, segmentY);
140
- indices.push(index++, index++);
164
+ const offset = (i + 1) * 2;
165
+ vertices[offset] = segmentX;
166
+ vertices[offset + 1] = segmentY;
141
167
  }
142
- indices.push(index - 1);
143
- return new Geometry({ vertices, indices, points });
168
+ const indices = new Uint16Array(length * 3);
169
+ for (let i = 0; i < length; i++) {
170
+ const base = i * 3;
171
+ indices[base] = 0;
172
+ indices[base + 1] = i + 1;
173
+ indices[base + 2] = i + 2 > length ? 1 : i + 2;
174
+ }
175
+ return { vertices, indices, points };
144
176
  };
145
- const buildEllipse = (centerX, centerY, radiusX, radiusY, vertices = [], indices = []) => {
146
- const length = Math.floor(15 * Math.sqrt(radiusX + radiusY)), segment = (Math.PI * 2) / length, points = [];
147
- let index = vertices.length / 6;
148
- indices.push(index);
149
- for (let i = 0; i < length + 1; i++) {
150
- const segmentX = centerX + (Math.sin(segment * i) * radiusX), segmentY = centerY + (Math.cos(segment * i) * radiusY);
177
+ const buildEllipse = (centerX, centerY, radiusX, radiusY) => {
178
+ const length = Math.floor(15 * Math.sqrt(radiusX + radiusY));
179
+ const segment = (Math.PI * 2) / length;
180
+ const points = [];
181
+ const vertices = new Float32Array((length + 1) * 2);
182
+ vertices[0] = centerX;
183
+ vertices[1] = centerY;
184
+ for (let i = 0; i < length; i++) {
185
+ const segmentX = centerX + (Math.sin(segment * i) * radiusX);
186
+ const segmentY = centerY + (Math.cos(segment * i) * radiusY);
151
187
  points.push(segmentX, segmentY);
152
- vertices.push(centerX, centerY);
153
- vertices.push(segmentX, segmentY);
154
- indices.push(index++, index++);
188
+ const offset = (i + 1) * 2;
189
+ vertices[offset] = segmentX;
190
+ vertices[offset + 1] = segmentY;
155
191
  }
156
- indices.push(index - 1);
157
- return new Geometry({ vertices, indices, points });
192
+ const indices = new Uint16Array(length * 3);
193
+ for (let i = 0; i < length; i++) {
194
+ const base = i * 3;
195
+ indices[base] = 0;
196
+ indices[base + 1] = i + 1;
197
+ indices[base + 2] = i + 2 > length ? 1 : i + 2;
198
+ }
199
+ return { vertices, indices, points };
158
200
  };
159
- const buildPolygon = (points, vertices = [], indices = []) => {
201
+ const buildPolygon = (points) => {
160
202
  if (points.length < 6) {
161
203
  throw new Error('At least three X/Y pairs are required to build a polygon.');
162
204
  }
163
- const index = vertices.length / 6, length = points.length / 2, triangles = earcut(points, [], 2);
164
- if (triangles) {
165
- for (let i = 0; i < triangles.length; i += 3) {
166
- indices.push(triangles[i] + index);
167
- indices.push(triangles[i] + index);
168
- indices.push(triangles[i + 1] + index);
169
- indices.push(triangles[i + 2] + index);
170
- indices.push(triangles[i + 2] + index);
171
- }
172
- for (let i = 0; i < length; i++) {
173
- vertices.push(points[i * 2], points[(i * 2) + 1]);
174
- }
205
+ const length = points.length / 2;
206
+ const triangles = earcut(points, [], 2);
207
+ const vertices = new Float32Array(points.length);
208
+ for (let i = 0; i < length; i++) {
209
+ vertices[i * 2] = points[i * 2];
210
+ vertices[(i * 2) + 1] = points[(i * 2) + 1];
175
211
  }
176
- return new Geometry({ vertices, indices, points });
212
+ const indices = triangles ? new Uint16Array(triangles) : new Uint16Array(0);
213
+ return { vertices, indices, points };
177
214
  };
178
- const buildRectangle = (x, y, width, height, vertices = [], indices = []) => {
179
- const points = [x, y, x + width, y, x, y + height, x + width, y + height], index = vertices.length / 6;
180
- vertices.push(...points);
181
- indices.push(index, index, index + 1, index + 2, index + 3, index + 3);
182
- return new Geometry({ vertices, indices, points });
215
+ const buildRectangle = (x, y, width, height) => {
216
+ // 4 vertices: TL, TR, BL, BR. Triangles [0, 1, 2, 1, 3, 2] (clockwise).
217
+ const vertices = new Float32Array([
218
+ x, y, // 0 TL
219
+ x + width, y, // 1 TR
220
+ x, y + height, // 2 BL
221
+ x + width, y + height, // 3 BR
222
+ ]);
223
+ const indices = new Uint16Array([0, 1, 2, 1, 3, 2]);
224
+ // Outline points walk the perimeter (TL -> TR -> BR -> BL).
225
+ const points = [x, y, x + width, y, x + width, y + height, x, y + height];
226
+ return { vertices, indices, points };
183
227
  };
184
228
  const buildStar = (centerX, centerY, points, radius, innerRadius = radius / 2, rotation = 0) => {
185
- const startAngle = (Math.PI / -2) + rotation, length = points * 2, delta = tau / length, path = [];
229
+ const startAngle = (Math.PI / -2) + rotation;
230
+ const length = points * 2;
231
+ const delta = tau / length;
232
+ const path = [];
186
233
  for (let i = 0; i < length; i++) {
187
234
  const angle = startAngle + (i * delta);
188
235
  const rad = i % 2 ? innerRadius : radius;