@datamoc/mw_games 0.38.0 → 0.40.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 (92) hide show
  1. package/CHANGELOG.md +75 -0
  2. package/api-surface.json +500 -2
  3. package/data/microgm-opt.PROVENANCE.md +20 -0
  4. package/data/microgm-opt.sf2 +0 -0
  5. package/dist/assets/SvgParts.d.ts +31 -0
  6. package/dist/assets/SvgParts.d.ts.map +1 -0
  7. package/dist/assets/SvgParts.js +54 -0
  8. package/dist/assets/SvgParts.js.map +1 -0
  9. package/dist/assets/SvgSource.d.ts +3 -0
  10. package/dist/assets/SvgSource.d.ts.map +1 -0
  11. package/dist/assets/SvgSource.js +22 -0
  12. package/dist/assets/SvgSource.js.map +1 -0
  13. package/dist/assets/index.d.ts +2 -0
  14. package/dist/assets/index.d.ts.map +1 -1
  15. package/dist/assets/index.js +1 -0
  16. package/dist/assets/index.js.map +1 -1
  17. package/dist/assets/svg.d.ts.map +1 -1
  18. package/dist/assets/svg.js +9 -5
  19. package/dist/assets/svg.js.map +1 -1
  20. package/dist/mw_games.global.js +110 -110
  21. package/dist/mwl/presentation.d.ts.map +1 -1
  22. package/dist/mwl/presentation.js +25 -18
  23. package/dist/mwl/presentation.js.map +1 -1
  24. package/dist/physics/PhysicsWorld.d.ts +49 -17
  25. package/dist/physics/PhysicsWorld.d.ts.map +1 -1
  26. package/dist/physics/PhysicsWorld.js +297 -59
  27. package/dist/physics/PhysicsWorld.js.map +1 -1
  28. package/dist/physics/XpbdWorld.d.ts +1 -1
  29. package/dist/physics/XpbdWorld.js +1 -1
  30. package/dist/physics/cloth/index.d.ts +98 -0
  31. package/dist/physics/cloth/index.d.ts.map +1 -0
  32. package/dist/physics/cloth/index.js +207 -0
  33. package/dist/physics/cloth/index.js.map +1 -0
  34. package/dist/physics/index.d.ts +4 -0
  35. package/dist/physics/index.d.ts.map +1 -1
  36. package/dist/physics/index.js +2 -0
  37. package/dist/physics/index.js.map +1 -1
  38. package/dist/physics/spring/index.d.ts +90 -0
  39. package/dist/physics/spring/index.d.ts.map +1 -0
  40. package/dist/physics/spring/index.js +97 -0
  41. package/dist/physics/spring/index.js.map +1 -0
  42. package/dist/skeleton/index.d.ts +94 -13
  43. package/dist/skeleton/index.d.ts.map +1 -1
  44. package/dist/skeleton/index.js +192 -73
  45. package/dist/skeleton/index.js.map +1 -1
  46. package/dist/two-d/presentation/MwlPresentation.d.ts +12 -0
  47. package/dist/two-d/presentation/MwlPresentation.d.ts.map +1 -1
  48. package/dist/two-d/presentation/MwlPresentation.js +204 -153
  49. package/dist/two-d/presentation/MwlPresentation.js.map +1 -1
  50. package/dist/two-d/render/ClothSprite.d.ts +83 -0
  51. package/dist/two-d/render/ClothSprite.d.ts.map +1 -0
  52. package/dist/two-d/render/ClothSprite.js +105 -0
  53. package/dist/two-d/render/ClothSprite.js.map +1 -0
  54. package/dist/two-d/render/HazeLayer.d.ts +112 -0
  55. package/dist/two-d/render/HazeLayer.d.ts.map +1 -0
  56. package/dist/two-d/render/HazeLayer.js +185 -0
  57. package/dist/two-d/render/HazeLayer.js.map +1 -0
  58. package/dist/two-d/render/NeonGlow.d.ts +100 -0
  59. package/dist/two-d/render/NeonGlow.d.ts.map +1 -0
  60. package/dist/two-d/render/NeonGlow.js +183 -0
  61. package/dist/two-d/render/NeonGlow.js.map +1 -0
  62. package/dist/two-d/render/SkeletalSprite.d.ts +5 -2
  63. package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
  64. package/dist/two-d/render/SkeletalSprite.js +27 -3
  65. package/dist/two-d/render/SkeletalSprite.js.map +1 -1
  66. package/dist/two-d/render/SpringSprite.d.ts +40 -0
  67. package/dist/two-d/render/SpringSprite.d.ts.map +1 -0
  68. package/dist/two-d/render/SpringSprite.js +39 -0
  69. package/dist/two-d/render/SpringSprite.js.map +1 -0
  70. package/dist/two-d/render/SpriteDeform.d.ts +156 -0
  71. package/dist/two-d/render/SpriteDeform.d.ts.map +1 -0
  72. package/dist/two-d/render/SpriteDeform.js +278 -0
  73. package/dist/two-d/render/SpriteDeform.js.map +1 -0
  74. package/dist/two-d/render/SvgSkeleton.d.ts +54 -0
  75. package/dist/two-d/render/SvgSkeleton.d.ts.map +1 -0
  76. package/dist/two-d/render/SvgSkeleton.js +112 -0
  77. package/dist/two-d/render/SvgSkeleton.js.map +1 -0
  78. package/dist/two-d/render/index.d.ts +12 -0
  79. package/dist/two-d/render/index.d.ts.map +1 -1
  80. package/dist/two-d/render/index.js +6 -0
  81. package/dist/two-d/render/index.js.map +1 -1
  82. package/dist/version.d.ts +1 -1
  83. package/dist/version.js +1 -1
  84. package/package.json +11 -7
  85. package/sbom.cdx.json +6 -58
  86. package/tools/asset-report.d.mts +37 -0
  87. package/tools/asset-report.mjs +157 -0
  88. package/tools/compile-resources.d.mts +24 -0
  89. package/tools/compile-resources.mjs +138 -5
  90. package/tools/emit-page.d.mts +9 -1
  91. package/tools/emit-page.mjs +31 -3
  92. package/tools/vite.mjs +6 -1
@@ -0,0 +1,40 @@
1
+ import { Graphics } from 'pixi.js';
2
+ import type { Spring } from '../../physics/spring/index.ts';
3
+ export interface SpringSpriteOptions {
4
+ /** the spring to draw */
5
+ spring: Spring;
6
+ /** line colour */
7
+ color?: number;
8
+ /** line opacity */
9
+ alpha?: number;
10
+ /** line width */
11
+ lineWidth?: number;
12
+ }
13
+ /**
14
+ * A debug line from a spring's anchor to its bob: one straight segment that stretches,
15
+ * overshoots and settles with the simulation. A game draws hair or hems by placing its
16
+ * own sprites with `Spring.place`, and keeps this around while tuning stiffness and
17
+ * damping. Call `sync` after stepping the spring; the line never steps anything itself.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import { Spring } from '@datamoc/mw_games/physics';
22
+ * import { SpringSprite } from '@datamoc/mw_games/two-d/render';
23
+ *
24
+ * const spring = new Spring({ stiffness: 120, damping: 8 });
25
+ * const line = new SpringSprite({ spring });
26
+ * spring.setAnchor(10, 0);
27
+ * spring.update(1 / 60);
28
+ * line.sync(); // the segment now leans toward the anchor
29
+ * ```
30
+ */
31
+ export declare class SpringSprite extends Graphics {
32
+ private readonly spring;
33
+ private readonly color;
34
+ private readonly opacity;
35
+ private readonly lineWidth;
36
+ constructor(options: SpringSpriteOptions);
37
+ /** redraw the anchor-to-bob segment from the spring's current state */
38
+ sync(): void;
39
+ }
40
+ //# sourceMappingURL=SpringSprite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpringSprite.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/SpringSprite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,+BAA+B,CAAC;AAE5D,MAAM,WAAW,mBAAmB;IACnC,yBAAyB;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAa,SAAQ,QAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC,YAAY,OAAO,EAAE,mBAAmB,EAOvC;IAED,uEAAuE;IACvE,IAAI,IAAI,IAAI,CAOX;CACD"}
@@ -0,0 +1,39 @@
1
+ import { Graphics } from 'pixi.js';
2
+ /**
3
+ * A debug line from a spring's anchor to its bob: one straight segment that stretches,
4
+ * overshoots and settles with the simulation. A game draws hair or hems by placing its
5
+ * own sprites with `Spring.place`, and keeps this around while tuning stiffness and
6
+ * damping. Call `sync` after stepping the spring; the line never steps anything itself.
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import { Spring } from '@datamoc/mw_games/physics';
11
+ * import { SpringSprite } from '@datamoc/mw_games/two-d/render';
12
+ *
13
+ * const spring = new Spring({ stiffness: 120, damping: 8 });
14
+ * const line = new SpringSprite({ spring });
15
+ * spring.setAnchor(10, 0);
16
+ * spring.update(1 / 60);
17
+ * line.sync(); // the segment now leans toward the anchor
18
+ * ```
19
+ */
20
+ export class SpringSprite extends Graphics {
21
+ constructor(options) {
22
+ super();
23
+ this.spring = options.spring;
24
+ this.color = options.color ?? 0xffffff;
25
+ this.opacity = options.alpha ?? 1;
26
+ this.lineWidth = options.lineWidth ?? 2;
27
+ this.sync();
28
+ }
29
+ /** redraw the anchor-to-bob segment from the spring's current state */
30
+ sync() {
31
+ const anchor = this.spring.anchorPosition;
32
+ const bob = this.spring.position;
33
+ this.clear();
34
+ this.moveTo(anchor.x, anchor.y)
35
+ .lineTo(bob.x, bob.y)
36
+ .stroke({ color: this.color, width: this.lineWidth, alpha: this.opacity });
37
+ }
38
+ }
39
+ //# sourceMappingURL=SpringSprite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpringSprite.js","sourceRoot":"","sources":["../../../src/two-d/render/SpringSprite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAcnC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,YAAa,SAAQ,QAAQ;IAMzC,YAAY,OAA4B;QACvC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,CAAC,CAAC;QAClC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,EAAE,CAAC;IACb,CAAC;IAED,uEAAuE;IACvE,IAAI;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC;QAC1C,MAAM,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;aAC7B,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;aACpB,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7E,CAAC;CACD"}
@@ -0,0 +1,156 @@
1
+ import { MeshPlane } from 'pixi.js';
2
+ import type { Texture2D } from './Types2D.ts';
3
+ /** A single control point on a deformation grid, in local pixels. */
4
+ export interface DeformPoint {
5
+ x: number;
6
+ y: number;
7
+ }
8
+ /** Grid dimensions and rest size for {@link PinnedDeform}. */
9
+ export interface PinnedDeformOptions {
10
+ /** control points across; an integer of at least 2 */
11
+ columns: number;
12
+ /** control points down; an integer of at least 2 */
13
+ rows: number;
14
+ /** rest width in local pixels; must be finite and above 0 */
15
+ width: number;
16
+ /** rest height in local pixels; must be finite and above 0 */
17
+ height: number;
18
+ }
19
+ /**
20
+ * A pinned-point deformation grid: the renderer-free math behind roadmap item 414.
21
+ *
22
+ * The grid holds `columns * rows` control points over a `width * height` rest
23
+ * rectangle. Every point stays exactly at its rest position until the game drags
24
+ * it with {@link PinnedDeform.drag} (or {@link PinnedDeform.nudge}); there is no
25
+ * falloff onto neighbours, no smoothing pass and no whole-sprite rotation, so a
26
+ * Pontifex wingtip can move while the wing root stays bit-identical. UVs are not
27
+ * modelled here at all: this class only tracks positions, and the GPU side
28
+ * (`DeformableSprite`) leaves its UV buffer untouched.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { PinnedDeform } from '@datamoc/mw_games/two-d/render';
33
+ *
34
+ * const wing = new PinnedDeform({ columns: 4, rows: 3, width: 96, height: 64 });
35
+ * wing.drag(wing.indexOf(3, 0), 99, -6); // the wingtip follows the bone
36
+ * console.log(wing.at(wing.indexOf(0, 1))); // { x: 0, y: 32 } - the pinned root never moves
37
+ * console.log(wing.movedCount()); // 1 - only the dragged tip left its rest position
38
+ * ```
39
+ */
40
+ export declare class PinnedDeform {
41
+ readonly columns: number;
42
+ readonly rows: number;
43
+ readonly width: number;
44
+ readonly height: number;
45
+ private readonly rest;
46
+ private readonly offsets;
47
+ constructor(options: PinnedDeformOptions);
48
+ /** how many control points the grid holds */
49
+ get count(): number;
50
+ /** the flat index of the point at `(column, row)`; throws a RangeError outside the grid */
51
+ indexOf(column: number, row: number): number;
52
+ private checkIndex;
53
+ /** the rest position of one point; the value pinned points always report */
54
+ restAt(index: number): DeformPoint;
55
+ /** the current position of one point: rest plus whatever `drag`/`nudge` applied */
56
+ at(index: number): DeformPoint;
57
+ /** whether a point currently sits away from its rest position */
58
+ isMoved(index: number): boolean;
59
+ /** how many points currently sit away from their rest positions */
60
+ movedCount(): number;
61
+ /**
62
+ * Move one control point to an absolute local position. Only that point
63
+ * changes; every other point stays pinned at rest.
64
+ */
65
+ drag(index: number, x: number, y: number): void;
66
+ /** Move a batch of control points, validating the entire batch before changing any. */
67
+ dragMany(points: ReadonlyArray<DeformPoint & {
68
+ index: number;
69
+ }>): void;
70
+ /** Shift one control point by a delta; the rest stay pinned, as with `drag`. */
71
+ nudge(index: number, dx: number, dy: number): void;
72
+ /**
73
+ * Return one point to rest, or every point when called without an index.
74
+ */
75
+ reset(index?: number): void;
76
+ /**
77
+ * Adopt externally built rest positions, clearing every offset. `DeformableSprite`
78
+ * uses this to adopt the GPU plane's own floats, so pinned vertices stay
79
+ * bit-identical to what the mesh already renders instead of nearly equal.
80
+ */
81
+ rebase(rest: Float32Array): void;
82
+ /** a copy of every current position, row-major `(x, y)` pairs for a mesh buffer */
83
+ positions(): Float32Array;
84
+ /**
85
+ * Write every current position into `into`, which must hold exactly
86
+ * `count * 2` floats. The mesh side calls this with its GPU position array.
87
+ */
88
+ writePositions(into: Float32Array): void;
89
+ }
90
+ /** Grid density for {@link DeformableSprite}; both default to 4. */
91
+ export interface DeformableSpriteOptions {
92
+ /** vertices across; an integer of at least 2, default 4 */
93
+ columns?: number;
94
+ /** vertices down; an integer of at least 2, default 4 */
95
+ rows?: number;
96
+ }
97
+ /**
98
+ * A sprite that deforms by pinned control points on the existing GPU mesh path.
99
+ *
100
+ * This is Pixi's `MeshPlane` with a `PinnedDeform` driving its position buffer:
101
+ * `dragVertex` moves one vertex while the rest stay pinned, and the mesh batch
102
+ * renders the result, so a Pontifex wing bends without the game touching Pixi's
103
+ * mesh directly. The routine itself never rotates, scales or repositions the
104
+ * sprite (`rotation` stays 0 through every drag) and never rewrites UVs; games
105
+ * that need a turned sprite rotate the texture art, not this mesh. Colour-batch
106
+ * and shader internals are untouched: this stays out of `ColorTransformBatcher`.
107
+ *
108
+ * @example
109
+ * ```ts
110
+ * import { DeformableSprite } from '@datamoc/mw_games/two-d/render';
111
+ * import type { Texture2D } from '@datamoc/mw_games/two-d/render';
112
+ *
113
+ * declare const wingTexture: Texture2D;
114
+ *
115
+ * const wing = new DeformableSprite(wingTexture, { columns: 4, rows: 4 });
116
+ * wing.dragVertex(wing.indexOf(3, 0), 99, -6); // the wingtip bends on the GPU mesh
117
+ * console.log(wing.vertexAt(wing.indexOf(0, 1)).x); // 0 - the pinned root stays put
118
+ * console.log(wing.rotation); // 0 - dragging never turns the whole sprite
119
+ * ```
120
+ */
121
+ export declare class DeformableSprite extends MeshPlane {
122
+ private readonly deform;
123
+ constructor(texture: Texture2D, options?: DeformableSpriteOptions);
124
+ private positionArray;
125
+ private sync;
126
+ /** vertices across */
127
+ get columns(): number;
128
+ /** vertices down */
129
+ get rows(): number;
130
+ /** how many control vertices the mesh holds */
131
+ get count(): number;
132
+ /** the flat index of the vertex at `(column, row)` */
133
+ indexOf(column: number, row: number): number;
134
+ /** the rest position of one vertex */
135
+ restAt(index: number): DeformPoint;
136
+ /** the current position of one vertex */
137
+ vertexAt(index: number): DeformPoint;
138
+ /** whether a vertex currently sits away from its rest position */
139
+ isMoved(index: number): boolean;
140
+ /** how many vertices currently sit away from their rest positions */
141
+ movedCount(): number;
142
+ /**
143
+ * Move one mesh vertex to an absolute local position and re-upload the
144
+ * position buffer. Only that vertex changes; the rest stay pinned.
145
+ */
146
+ dragVertex(index: number, x: number, y: number): void;
147
+ /** Move several vertices with a single GPU buffer upload. Other vertices stay pinned. */
148
+ dragVertices(points: ReadonlyArray<DeformPoint & {
149
+ index: number;
150
+ }>): void;
151
+ /** Shift one mesh vertex by a delta and re-upload the position buffer. */
152
+ nudgeVertex(index: number, dx: number, dy: number): void;
153
+ /** Return one vertex to rest, or every vertex when called without an index. */
154
+ resetVertices(index?: number): void;
155
+ }
156
+ //# sourceMappingURL=SpriteDeform.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpriteDeform.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/SpriteDeform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,278 @@
1
+ import { MeshPlane } from 'pixi.js';
2
+ function assertGrid(options) {
3
+ for (const key of ['columns', 'rows']) {
4
+ if (!Number.isInteger(options[key]) || options[key] < 2) {
5
+ throw new RangeError(`a pinned deform grid needs integer ${key} of at least 2, saw ${options[key]}`);
6
+ }
7
+ }
8
+ for (const key of ['width', 'height']) {
9
+ if (!Number.isFinite(options[key]) || options[key] <= 0) {
10
+ throw new RangeError(`a pinned deform grid needs ${key} above 0, saw ${options[key]}`);
11
+ }
12
+ }
13
+ }
14
+ function assertPoint(x, y) {
15
+ if (!Number.isFinite(x) || !Number.isFinite(y)) {
16
+ throw new Error(`a deform control point needs finite coordinates, saw (${x}, ${y})`);
17
+ }
18
+ }
19
+ /**
20
+ * A pinned-point deformation grid: the renderer-free math behind roadmap item 414.
21
+ *
22
+ * The grid holds `columns * rows` control points over a `width * height` rest
23
+ * rectangle. Every point stays exactly at its rest position until the game drags
24
+ * it with {@link PinnedDeform.drag} (or {@link PinnedDeform.nudge}); there is no
25
+ * falloff onto neighbours, no smoothing pass and no whole-sprite rotation, so a
26
+ * Pontifex wingtip can move while the wing root stays bit-identical. UVs are not
27
+ * modelled here at all: this class only tracks positions, and the GPU side
28
+ * (`DeformableSprite`) leaves its UV buffer untouched.
29
+ *
30
+ * @example
31
+ * ```ts
32
+ * import { PinnedDeform } from '@datamoc/mw_games/two-d/render';
33
+ *
34
+ * const wing = new PinnedDeform({ columns: 4, rows: 3, width: 96, height: 64 });
35
+ * wing.drag(wing.indexOf(3, 0), 99, -6); // the wingtip follows the bone
36
+ * console.log(wing.at(wing.indexOf(0, 1))); // { x: 0, y: 32 } - the pinned root never moves
37
+ * console.log(wing.movedCount()); // 1 - only the dragged tip left its rest position
38
+ * ```
39
+ */
40
+ export class PinnedDeform {
41
+ constructor(options) {
42
+ assertGrid(options);
43
+ this.columns = options.columns;
44
+ this.rows = options.rows;
45
+ this.width = options.width;
46
+ this.height = options.height;
47
+ this.rest = new Float32Array(this.count * 2);
48
+ this.offsets = new Float32Array(this.count * 2);
49
+ for (let row = 0; row < this.rows; row++) {
50
+ for (let column = 0; column < this.columns; column++) {
51
+ const index = (row * this.columns + column) * 2;
52
+ this.rest[index] = (column / (this.columns - 1)) * this.width;
53
+ this.rest[index + 1] = (row / (this.rows - 1)) * this.height;
54
+ }
55
+ }
56
+ }
57
+ /** how many control points the grid holds */
58
+ get count() {
59
+ return this.columns * this.rows;
60
+ }
61
+ /** the flat index of the point at `(column, row)`; throws a RangeError outside the grid */
62
+ indexOf(column, row) {
63
+ if (!Number.isInteger(column) ||
64
+ !Number.isInteger(row) ||
65
+ column < 0 ||
66
+ row < 0 ||
67
+ column >= this.columns ||
68
+ row >= this.rows) {
69
+ throw new RangeError(`control point (${column}, ${row}) is outside a ${this.columns}x${this.rows} grid`);
70
+ }
71
+ return row * this.columns + column;
72
+ }
73
+ checkIndex(index) {
74
+ if (!Number.isInteger(index) || index < 0 || index >= this.count) {
75
+ throw new RangeError(`control point ${index} is outside a grid of ${this.count} points`);
76
+ }
77
+ }
78
+ /** the rest position of one point; the value pinned points always report */
79
+ restAt(index) {
80
+ this.checkIndex(index);
81
+ return { x: this.rest[index * 2], y: this.rest[index * 2 + 1] };
82
+ }
83
+ /** the current position of one point: rest plus whatever `drag`/`nudge` applied */
84
+ at(index) {
85
+ this.checkIndex(index);
86
+ return {
87
+ x: this.rest[index * 2] + this.offsets[index * 2],
88
+ y: this.rest[index * 2 + 1] + this.offsets[index * 2 + 1],
89
+ };
90
+ }
91
+ /** whether a point currently sits away from its rest position */
92
+ isMoved(index) {
93
+ this.checkIndex(index);
94
+ return this.offsets[index * 2] !== 0 || this.offsets[index * 2 + 1] !== 0;
95
+ }
96
+ /** how many points currently sit away from their rest positions */
97
+ movedCount() {
98
+ let moved = 0;
99
+ for (let index = 0; index < this.count; index++) {
100
+ if (this.offsets[index * 2] !== 0 || this.offsets[index * 2 + 1] !== 0)
101
+ moved++;
102
+ }
103
+ return moved;
104
+ }
105
+ /**
106
+ * Move one control point to an absolute local position. Only that point
107
+ * changes; every other point stays pinned at rest.
108
+ */
109
+ drag(index, x, y) {
110
+ this.checkIndex(index);
111
+ assertPoint(x, y);
112
+ this.offsets[index * 2] = x - this.rest[index * 2];
113
+ this.offsets[index * 2 + 1] = y - this.rest[index * 2 + 1];
114
+ }
115
+ /** Move a batch of control points, validating the entire batch before changing any. */
116
+ dragMany(points) {
117
+ for (const point of points) {
118
+ this.checkIndex(point.index);
119
+ assertPoint(point.x, point.y);
120
+ }
121
+ for (const point of points)
122
+ this.drag(point.index, point.x, point.y);
123
+ }
124
+ /** Shift one control point by a delta; the rest stay pinned, as with `drag`. */
125
+ nudge(index, dx, dy) {
126
+ this.checkIndex(index);
127
+ assertPoint(dx, dy);
128
+ this.offsets[index * 2] += dx;
129
+ this.offsets[index * 2 + 1] += dy;
130
+ }
131
+ /**
132
+ * Return one point to rest, or every point when called without an index.
133
+ */
134
+ reset(index) {
135
+ if (index === undefined) {
136
+ this.offsets.fill(0);
137
+ return;
138
+ }
139
+ this.checkIndex(index);
140
+ this.offsets[index * 2] = 0;
141
+ this.offsets[index * 2 + 1] = 0;
142
+ }
143
+ /**
144
+ * Adopt externally built rest positions, clearing every offset. `DeformableSprite`
145
+ * uses this to adopt the GPU plane's own floats, so pinned vertices stay
146
+ * bit-identical to what the mesh already renders instead of nearly equal.
147
+ */
148
+ rebase(rest) {
149
+ if (!(rest instanceof Float32Array) || rest.length !== this.rest.length) {
150
+ throw new Error(`rebase needs a Float32Array of ${this.rest.length} floats, saw length ${rest?.length}`);
151
+ }
152
+ for (const value of rest) {
153
+ if (!Number.isFinite(value))
154
+ throw new Error('rebase needs finite rest positions');
155
+ }
156
+ this.rest.set(rest);
157
+ this.offsets.fill(0);
158
+ }
159
+ /** a copy of every current position, row-major `(x, y)` pairs for a mesh buffer */
160
+ positions() {
161
+ const out = new Float32Array(this.rest.length);
162
+ this.writePositions(out);
163
+ return out;
164
+ }
165
+ /**
166
+ * Write every current position into `into`, which must hold exactly
167
+ * `count * 2` floats. The mesh side calls this with its GPU position array.
168
+ */
169
+ writePositions(into) {
170
+ if (!(into instanceof Float32Array) || into.length !== this.rest.length) {
171
+ throw new Error(`a deform target needs a Float32Array of ${this.rest.length} floats, saw length ${into?.length}`);
172
+ }
173
+ for (let index = 0; index < this.rest.length; index++) {
174
+ into[index] = this.rest[index] + this.offsets[index];
175
+ }
176
+ }
177
+ }
178
+ /**
179
+ * A sprite that deforms by pinned control points on the existing GPU mesh path.
180
+ *
181
+ * This is Pixi's `MeshPlane` with a `PinnedDeform` driving its position buffer:
182
+ * `dragVertex` moves one vertex while the rest stay pinned, and the mesh batch
183
+ * renders the result, so a Pontifex wing bends without the game touching Pixi's
184
+ * mesh directly. The routine itself never rotates, scales or repositions the
185
+ * sprite (`rotation` stays 0 through every drag) and never rewrites UVs; games
186
+ * that need a turned sprite rotate the texture art, not this mesh. Colour-batch
187
+ * and shader internals are untouched: this stays out of `ColorTransformBatcher`.
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * import { DeformableSprite } from '@datamoc/mw_games/two-d/render';
192
+ * import type { Texture2D } from '@datamoc/mw_games/two-d/render';
193
+ *
194
+ * declare const wingTexture: Texture2D;
195
+ *
196
+ * const wing = new DeformableSprite(wingTexture, { columns: 4, rows: 4 });
197
+ * wing.dragVertex(wing.indexOf(3, 0), 99, -6); // the wingtip bends on the GPU mesh
198
+ * console.log(wing.vertexAt(wing.indexOf(0, 1)).x); // 0 - the pinned root stays put
199
+ * console.log(wing.rotation); // 0 - dragging never turns the whole sprite
200
+ * ```
201
+ */
202
+ export class DeformableSprite extends MeshPlane {
203
+ constructor(texture, options) {
204
+ const columns = options?.columns ?? 4;
205
+ const rows = options?.rows ?? 4;
206
+ const deform = new PinnedDeform({ columns, rows, width: texture.width, height: texture.height });
207
+ super({ texture, verticesX: columns, verticesY: rows });
208
+ this.deform = deform;
209
+ //a later texture resize must not rebuild the plane under the offsets
210
+ this.autoResize = false;
211
+ this.deform.rebase(Float32Array.from(this.positionArray()));
212
+ this.sync();
213
+ }
214
+ positionArray() {
215
+ return this.geometry.getAttribute('aPosition').buffer.data;
216
+ }
217
+ sync() {
218
+ const attribute = this.geometry.getAttribute('aPosition');
219
+ this.deform.writePositions(attribute.buffer.data);
220
+ attribute.buffer.update();
221
+ }
222
+ /** vertices across */
223
+ get columns() {
224
+ return this.deform.columns;
225
+ }
226
+ /** vertices down */
227
+ get rows() {
228
+ return this.deform.rows;
229
+ }
230
+ /** how many control vertices the mesh holds */
231
+ get count() {
232
+ return this.deform.count;
233
+ }
234
+ /** the flat index of the vertex at `(column, row)` */
235
+ indexOf(column, row) {
236
+ return this.deform.indexOf(column, row);
237
+ }
238
+ /** the rest position of one vertex */
239
+ restAt(index) {
240
+ return this.deform.restAt(index);
241
+ }
242
+ /** the current position of one vertex */
243
+ vertexAt(index) {
244
+ return this.deform.at(index);
245
+ }
246
+ /** whether a vertex currently sits away from its rest position */
247
+ isMoved(index) {
248
+ return this.deform.isMoved(index);
249
+ }
250
+ /** how many vertices currently sit away from their rest positions */
251
+ movedCount() {
252
+ return this.deform.movedCount();
253
+ }
254
+ /**
255
+ * Move one mesh vertex to an absolute local position and re-upload the
256
+ * position buffer. Only that vertex changes; the rest stay pinned.
257
+ */
258
+ dragVertex(index, x, y) {
259
+ this.deform.drag(index, x, y);
260
+ this.sync();
261
+ }
262
+ /** Move several vertices with a single GPU buffer upload. Other vertices stay pinned. */
263
+ dragVertices(points) {
264
+ this.deform.dragMany(points);
265
+ this.sync();
266
+ }
267
+ /** Shift one mesh vertex by a delta and re-upload the position buffer. */
268
+ nudgeVertex(index, dx, dy) {
269
+ this.deform.nudge(index, dx, dy);
270
+ this.sync();
271
+ }
272
+ /** Return one vertex to rest, or every vertex when called without an index. */
273
+ resetVertices(index) {
274
+ this.deform.reset(index);
275
+ this.sync();
276
+ }
277
+ }
278
+ //# sourceMappingURL=SpriteDeform.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SpriteDeform.js","sourceRoot":"","sources":["../../../src/two-d/render/SpriteDeform.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import { type Mixer, type SkeletonBones } from '../../skeleton/index.ts';
2
+ import { SkeletalSprite } from './SkeletalSprite.ts';
3
+ import type { Texture2D } from './Types2D.ts';
4
+ export interface SvgBoneBinding {
5
+ bone: string;
6
+ /** SVG group id; defaults to the bone name. */
7
+ group?: string;
8
+ /** Pivot in root SVG user coordinates. Overrides options.pivots and SVG data attributes. */
9
+ pivot?: {
10
+ x: number;
11
+ y: number;
12
+ };
13
+ /** Artwork's rest angle in degrees; defaults to the bone's resolved rest angle. */
14
+ rotation?: number;
15
+ }
16
+ export interface SvgSkeletonOptions {
17
+ skeleton: SkeletonBones;
18
+ /** Draw order. Omit to bind matching named groups in SVG document order. */
19
+ bindings?: readonly SvgBoneBinding[];
20
+ /** Root SVG user coordinates, keyed by group id (e.g. from a game's art manifest). */
21
+ pivots?: Readonly<Record<string, {
22
+ x: number;
23
+ y: number;
24
+ }>>;
25
+ resolution?: number;
26
+ mixer?: Mixer;
27
+ }
28
+ export interface LoadedSvgSkeleton {
29
+ sprite: SkeletalSprite;
30
+ /** Borrowed cached textures. Destroy sprites first, then releaseSvgParts once all users are done. */
31
+ textures: Readonly<Record<string, Texture2D>>;
32
+ }
33
+ /**
34
+ * Load authored SVG limbs, normalize each around its pivot, and bind them to a skeleton.
35
+ * Uses SVG data-pivot-x / data-pivot-y attributes or pivots supplied by the game. Art is
36
+ * rotated back from its rest angle before rasterization, so rest poses reconstruct the drawing
37
+ * and posed limbs turn around their joints. Separately bound descendant groups are removed
38
+ * from parent parts, avoiding duplicate limbs; ancestor transforms and shared defs/styles survive.
39
+ * Requires browser DOMParser and XMLSerializer. Compiled asset paths work directly under file://.
40
+ * @example
41
+ * ```ts
42
+ * import { create } from '@datamoc/mw_games/skeleton';
43
+ * import { loadSvgSkeleton } from '@datamoc/mw_games/two-d/render';
44
+ * import { releaseSvgParts } from '@datamoc/mw_games/assets';
45
+ * const loaded = await loadSvgSkeleton('<svg xmlns="http://www.w3.org/2000/svg" width="30" height="10"><g id="arm" data-pivot-x="0" data-pivot-y="5"><rect width="30" height="10"/></g></svg>', {
46
+ * skeleton: create([{ name: 'arm', length: 30, origin: { x: 0, y: 5 } }]), resolution: 2,
47
+ * });
48
+ * loaded.sprite.apply({ arm: 45 });
49
+ * loaded.sprite.destroy({ children: true });
50
+ * await releaseSvgParts(loaded.textures);
51
+ * ```
52
+ */
53
+ export declare function loadSvgSkeleton(source: string, options: SvgSkeletonOptions): Promise<LoadedSvgSkeleton>;
54
+ //# sourceMappingURL=SvgSkeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SvgSkeleton.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/SvgSkeleton.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,KAAK,EAAE,KAAK,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,KAAK,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjC,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACrC,sFAAsF;IACtF,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,cAAc,CAAC;IACvB,qGAAqG;IACrG,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAgF7G"}