rig-c 0.0.0-stage → 2.20.4

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 (213) hide show
  1. package/.claude-plugin/marketplace.json +19 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/LICENSE +30 -0
  4. package/NOTICE.md +145 -0
  5. package/README.md +817 -3
  6. package/bin/rigc.cjs +83 -0
  7. package/cli.ts +61 -0
  8. package/cli_core.ts +46 -0
  9. package/docs/AUTHORING.md +9923 -0
  10. package/docs/FACE.md +1948 -0
  11. package/docs/INGEST.md +1488 -0
  12. package/docs/MOTION.md +1241 -0
  13. package/docs/PROMPTING.md +109 -0
  14. package/docs/RIGGING.md +1441 -0
  15. package/docs/SPEC_COVERAGE.md +357 -0
  16. package/package.json +108 -4
  17. package/skills/rigc/SKILL.md +133 -0
  18. package/skills/rigc-face/SKILL.md +60 -0
  19. package/skills/rigc-ingest/SKILL.md +78 -0
  20. package/skills/rigc-motion/SKILL.md +51 -0
  21. package/skills/rigc-rigging/SKILL.md +49 -0
  22. package/src/areaband.ts +159 -0
  23. package/src/assertions/bodies/a01.ts +23 -0
  24. package/src/assertions/bodies/a02.ts +21 -0
  25. package/src/assertions/bodies/a03.ts +27 -0
  26. package/src/assertions/bodies/a04.ts +40 -0
  27. package/src/assertions/bodies/a05.ts +56 -0
  28. package/src/assertions/bodies/a06.ts +245 -0
  29. package/src/assertions/bodies/a07.ts +68 -0
  30. package/src/assertions/bodies/a08.ts +76 -0
  31. package/src/assertions/bodies/a09.ts +82 -0
  32. package/src/assertions/bodies/a10.ts +116 -0
  33. package/src/assertions/bodies/a11.ts +15 -0
  34. package/src/assertions/bodies/a12.ts +30 -0
  35. package/src/assertions/bodies/a13.ts +51 -0
  36. package/src/assertions/bodies/a14.ts +35 -0
  37. package/src/assertions/bodies/a15.ts +97 -0
  38. package/src/assertions/bodies/a16.ts +24 -0
  39. package/src/assertions/bodies/a17.ts +26 -0
  40. package/src/assertions/bodies/a18.ts +62 -0
  41. package/src/assertions/bodies/a19.ts +404 -0
  42. package/src/assertions/bodies/a20.ts +122 -0
  43. package/src/assertions/bodies/a21.ts +190 -0
  44. package/src/assertions/bodies/a22.ts +39 -0
  45. package/src/assertions/bodies/a23.ts +305 -0
  46. package/src/assertions/bodies/a24.ts +68 -0
  47. package/src/assertions/bodies/a25.ts +39 -0
  48. package/src/assertions/bodies/a26.ts +61 -0
  49. package/src/assertions/bodies/a27.ts +33 -0
  50. package/src/assertions/bodies/a28.ts +70 -0
  51. package/src/assertions/bodies/a29.ts +34 -0
  52. package/src/assertions/bodies/a30.ts +50 -0
  53. package/src/assertions/bodies/a31.ts +61 -0
  54. package/src/assertions/bodies/a32.ts +44 -0
  55. package/src/assertions/bodies/a33.ts +110 -0
  56. package/src/assertions/bodies/a34.ts +133 -0
  57. package/src/assertions/bodies/a35.ts +160 -0
  58. package/src/assertions/bodies/a36.ts +81 -0
  59. package/src/assertions/bodies/a37.ts +77 -0
  60. package/src/assertions/bodies/a38.ts +73 -0
  61. package/src/assertions/bodies/a39.ts +303 -0
  62. package/src/assertions/bodies/a40.ts +128 -0
  63. package/src/assertions/bodies/a42.ts +97 -0
  64. package/src/assertions/bodies/a43.ts +181 -0
  65. package/src/assertions/bodies/a44.ts +23 -0
  66. package/src/assertions/bodies/a45.ts +172 -0
  67. package/src/assertions/bodies/a46.ts +224 -0
  68. package/src/assertions/bodies/a47.ts +126 -0
  69. package/src/assertions/bodies/a48.ts +83 -0
  70. package/src/assertions/bodies/a49.ts +81 -0
  71. package/src/assertions/bodies/a50.ts +97 -0
  72. package/src/assertions/constraint_words.ts +169 -0
  73. package/src/assertions/emitted/index.ts +148 -0
  74. package/src/assertions/facts/animated_bones.ts +30 -0
  75. package/src/assertions/facts/animation_durations.ts +37 -0
  76. package/src/assertions/facts/atlas_pages.ts +19 -0
  77. package/src/assertions/facts/atlas_regions.ts +52 -0
  78. package/src/assertions/facts/bone_timelines.ts +37 -0
  79. package/src/assertions/facts/constraint_targets.ts +56 -0
  80. package/src/assertions/facts/constraints.ts +155 -0
  81. package/src/assertions/facts/deform_survey.ts +27 -0
  82. package/src/assertions/facts/event_keys.ts +55 -0
  83. package/src/assertions/facts/linked_meshes.ts +38 -0
  84. package/src/assertions/facts/mesh_attachments.ts +100 -0
  85. package/src/assertions/facts/region_joins.ts +34 -0
  86. package/src/assertions/facts/sequences.ts +85 -0
  87. package/src/assertions/facts/skeleton_roster.ts +45 -0
  88. package/src/assertions/facts/skin_entries.ts +37 -0
  89. package/src/assertions/facts/skin_members.ts +53 -0
  90. package/src/assertions/facts/slider_composition.ts +78 -0
  91. package/src/assertions/facts/slot_colour.ts +43 -0
  92. package/src/assertions/facts/stage.ts +27 -0
  93. package/src/assertions/facts/stage_box.ts +65 -0
  94. package/src/assertions/facts/stepped_poses.ts +74 -0
  95. package/src/assertions/facts/two_colour.ts +52 -0
  96. package/src/assertions/facts/vertex_polygons.ts +53 -0
  97. package/src/assertions/footprints.ts +367 -0
  98. package/src/assertions/harness.ts +109 -0
  99. package/src/assertions/inward_advance.ts +58 -0
  100. package/src/assertions/kinds.ts +105 -0
  101. package/src/assertions/mesh_kinds.ts +56 -0
  102. package/src/assertions/model/animated_bones.ts +38 -0
  103. package/src/assertions/model/animation_durations.ts +57 -0
  104. package/src/assertions/model/atlas_pages.ts +15 -0
  105. package/src/assertions/model/atlas_regions.ts +76 -0
  106. package/src/assertions/model/bone_timelines.ts +58 -0
  107. package/src/assertions/model/constraint_targets.ts +82 -0
  108. package/src/assertions/model/constraints.ts +233 -0
  109. package/src/assertions/model/declared.ts +125 -0
  110. package/src/assertions/model/deform_survey.ts +24 -0
  111. package/src/assertions/model/event_keys.ts +45 -0
  112. package/src/assertions/model/given.ts +45 -0
  113. package/src/assertions/model/index.ts +398 -0
  114. package/src/assertions/model/linked_meshes.ts +24 -0
  115. package/src/assertions/model/mesh_attachments.ts +119 -0
  116. package/src/assertions/model/parse.ts +146 -0
  117. package/src/assertions/model/region_joins.ts +67 -0
  118. package/src/assertions/model/runtime_timelines.ts +78 -0
  119. package/src/assertions/model/sequences.ts +157 -0
  120. package/src/assertions/model/skeleton_roster.ts +23 -0
  121. package/src/assertions/model/skin_entries.ts +69 -0
  122. package/src/assertions/model/skin_members.ts +64 -0
  123. package/src/assertions/model/slider_composition.ts +193 -0
  124. package/src/assertions/model/slot_colour.ts +81 -0
  125. package/src/assertions/model/stage.ts +28 -0
  126. package/src/assertions/model/stage_box.ts +51 -0
  127. package/src/assertions/model/stepped_poses.ts +105 -0
  128. package/src/assertions/model/two_colour.ts +61 -0
  129. package/src/assertions/model/vertex_polygons.ts +72 -0
  130. package/src/assertions/reasons.ts +129 -0
  131. package/src/assertions/region_lookups.ts +61 -0
  132. package/src/assertions/report.ts +189 -0
  133. package/src/assertions/values.ts +39 -0
  134. package/src/atlas.ts +2870 -0
  135. package/src/ballot.ts +866 -0
  136. package/src/bonedist.ts +643 -0
  137. package/src/chainfit.ts +2752 -0
  138. package/src/chains.ts +170 -0
  139. package/src/check.ts +4303 -0
  140. package/src/checkpics.ts +295 -0
  141. package/src/cli/core_commands.ts +1627 -0
  142. package/src/cli/repack.ts +414 -0
  143. package/src/cli/shared.ts +2776 -0
  144. package/src/cli/spine_commands.ts +820 -0
  145. package/src/compile.ts +9414 -0
  146. package/src/core/additive.ts +458 -0
  147. package/src/core/animation.ts +1050 -0
  148. package/src/core/clipping.ts +696 -0
  149. package/src/core/constraints.ts +1876 -0
  150. package/src/core/constraints_path.ts +964 -0
  151. package/src/core/constraints_physics.ts +881 -0
  152. package/src/core/constraints_slider.ts +635 -0
  153. package/src/core/deform.ts +613 -0
  154. package/src/core/draw_order.ts +125 -0
  155. package/src/core/events.ts +135 -0
  156. package/src/core/hooks.ts +249 -0
  157. package/src/core/index.ts +1400 -0
  158. package/src/core/raw.ts +739 -0
  159. package/src/core/skins.ts +129 -0
  160. package/src/core/uvs.ts +469 -0
  161. package/src/core/vertices.ts +490 -0
  162. package/src/core/walk.ts +197 -0
  163. package/src/core/world.ts +289 -0
  164. package/src/correspondence.ts +15 -0
  165. package/src/deformbuild.ts +60 -0
  166. package/src/deformgen.ts +630 -0
  167. package/src/deformmeasure.ts +732 -0
  168. package/src/deformreport.ts +373 -0
  169. package/src/deformstructure.ts +386 -0
  170. package/src/deformsurvey.ts +2162 -0
  171. package/src/depth.ts +784 -0
  172. package/src/diff.ts +2252 -0
  173. package/src/emit.ts +134 -0
  174. package/src/emit_spine.ts +854 -0
  175. package/src/errors.ts +53 -0
  176. package/src/framing.ts +819 -0
  177. package/src/generation.ts +139 -0
  178. package/src/ingest.ts +2293 -0
  179. package/src/json-position.ts +253 -0
  180. package/src/keyorder.ts +587 -0
  181. package/src/keys.ts +486 -0
  182. package/src/ladder.ts +121 -0
  183. package/src/mesh.ts +2382 -0
  184. package/src/meshcompare.ts +1188 -0
  185. package/src/meshquality.ts +2042 -0
  186. package/src/meshrasters.ts +944 -0
  187. package/src/meshreduce.ts +1425 -0
  188. package/src/model.ts +1245 -0
  189. package/src/motion.ts +809 -0
  190. package/src/nonfinite.ts +54 -0
  191. package/src/package_meta.ts +48 -0
  192. package/src/png.ts +297 -0
  193. package/src/pose.ts +2324 -0
  194. package/src/preview.ts +434 -0
  195. package/src/region_joins.ts +54 -0
  196. package/src/render.ts +1013 -0
  197. package/src/render_core.ts +871 -0
  198. package/src/render_shared.ts +2958 -0
  199. package/src/repack.ts +495 -0
  200. package/src/rig.ts +2941 -0
  201. package/src/slots.ts +892 -0
  202. package/src/spine_side.ts +138 -0
  203. package/src/timelines.ts +837 -0
  204. package/src/trackgen.ts +364 -0
  205. package/src/transform.ts +310 -0
  206. package/src/types.ts +1797 -0
  207. package/src/validate.ts +3875 -0
  208. package/tools/contact.ts +126 -0
  209. package/tools/editor_roundtrip.ts +1641 -0
  210. package/tools/font5x7.ts +101 -0
  211. package/tools/measure_contact_depth.ts +105 -0
  212. package/tools/plate.ts +508 -0
  213. package/tools/png_probe.mjs +72 -0
@@ -0,0 +1,148 @@
1
+ /**
2
+ * The round trip's rules, restated over the text rigc emitted (issue #1060,
3
+ * step 4e of #380) — the half of the gate the entry that links none of
4
+ * spine-core runs beside the model side (`../model/index.ts`).
5
+ *
6
+ * ⭐ **Every one of the 50 is accounted for, by name.** The model side runs
7
+ * the moved assertions (`MOVED_ASSERTIONS`) over the document. What is left —
8
+ * the complement, read off `ASSERTION_KIND` rather than listed — is the
9
+ * round trip's own: A00, A01, A02, A05, A07, A16, A18, A31, A35 at the time of
10
+ * writing. Each of those but one reads the raw text, not what spine-core
11
+ * loaded: the atlas's line layout (A07), the skeleton JSON's draw-order
12
+ * offsets, deform runs, version label, legacy arrays, legacy bone key and
13
+ * curve arrays (A31, A35, A16, A01, A02, A05), and A18's second compile. So
14
+ * each is restated here over the same text, by a body under `../bodies/` that
15
+ * is the round trip's clause word for word, and prints the round trip's line
16
+ * (`RC28` holds the two on every recipe and on each assertion's mutants).
17
+ * The one that cannot be is **A00**: it is spine-core's parser loading the
18
+ * pair, and there is no second reader of the format to stand in for it. It
19
+ * SKIPs, naming spine-core and where the round trip does run — never a PASS
20
+ * on nothing. A code the complement grows by that this file does not know is
21
+ * an internal error, so a new round-trip rule cannot go unrun here in silence.
22
+ *
23
+ * Links nothing from the runtime.
24
+ */
25
+ import { ASSERTION_KIND, type AssertionProfile } from '../kinds.ts';
26
+ import { verdictHarness, type VerdictLists } from '../harness.ts';
27
+ import { MOVED_ASSERTIONS } from '../model/index.ts';
28
+ import type { Verdicts } from '../harness.ts';
29
+ import { isObj, type Json } from '../values.ts';
30
+ import { a01NoLegacyToplevelConstraintArrays } from '../bodies/a01.ts';
31
+ import { a02NoBoneTransformKey } from '../bodies/a02.ts';
32
+ import { a05CurveArrayLength } from '../bodies/a05.ts';
33
+ import { a07AtlasTextShape } from '../bodies/a07.ts';
34
+ import { a16SkeletonVersion43 } from '../bodies/a16.ts';
35
+ import { a18DeterministicEmit } from '../bodies/a18.ts';
36
+ import { a31DrawOrderOffsetsResolve } from '../bodies/a31.ts';
37
+ import { a35DeformKeysFitTheAttachment } from '../bodies/a35.ts';
38
+ import { parseAtlasText } from '../../atlas.ts';
39
+
40
+ /** What the restated rules read: the emitted texts, the document written beside them, and a second compile's three. */
41
+ export interface EmittedTextInput {
42
+ skeletonText: string;
43
+ atlasText: string;
44
+ /** The document `build` writes — A18 compares it with the second compile's. */
45
+ modelText: string;
46
+ /** A second, independent compile's texts; absent, A18 SKIPs as the round trip's does on a re-gate. */
47
+ reEmit?: { skeletonText: string; atlasText: string; modelText: string };
48
+ profile: AssertionProfile;
49
+ }
50
+
51
+ /** The emitted texts as the bodies read them: the skeleton JSON parsed, or `null` where it does not parse. */
52
+ interface EmittedTexts {
53
+ raw: Json | null;
54
+ input: EmittedTextInput;
55
+ }
56
+
57
+ /** A00's SKIP here: what did not run, and where it does. */
58
+ export const SKIP_NO_ROUND_TRIP =
59
+ "spine-core's parser is the subject of this rule and this entry links none of it — the entry that links it runs the round trip on build and validate " +
60
+ '(installed, the same `rigc` once @esotericsoftware/spine-core is installed beside the package; from a source checkout, `bun cli.ts`), ' +
61
+ 'and the selftest and CI run it on every corpus build';
62
+
63
+ /** The code this file SKIPs rather than restates. */
64
+ const A00 = 'A00_ROUNDTRIP_PARSE';
65
+
66
+ /**
67
+ * The restated rules, in `validate()`'s report order — A00's place among them
68
+ * (after A35, before A16) is the round trip's, so the report reads in the order
69
+ * the round trip's does.
70
+ */
71
+ export const EMITTED_TEXT_RULES: ReadonlyArray<{ code: string; run: (v: Verdicts, texts: EmittedTexts) => void }> = [
72
+ { code: 'A07_ATLAS_TEXT_SHAPE', run: (v, t) => a07AtlasTextShape(v, t.input.atlasText) },
73
+ { code: 'A31_DRAW_ORDER_OFFSETS_RESOLVE', run: (v, t) => a31DrawOrderOffsetsResolve(v, t.raw) },
74
+ { code: 'A35_DEFORM_KEYS_FIT_THE_ATTACHMENT', run: (v, t) => a35DeformKeysFitTheAttachment(v, t.raw) },
75
+ { code: A00, run: (v) => v.skip(A00, SKIP_NO_ROUND_TRIP) },
76
+ { code: 'A16_SKELETON_VERSION_4_3', run: (v, t) => a16SkeletonVersion43(v, t.raw) },
77
+ { code: 'A01_NO_LEGACY_TOPLEVEL_CONSTRAINT_ARRAYS', run: (v, t) => a01NoLegacyToplevelConstraintArrays(v, t.raw) },
78
+ { code: 'A02_NO_BONE_TRANSFORM_KEY', run: (v, t) => a02NoBoneTransformKey(v, t.raw) },
79
+ { code: 'A05_CURVE_ARRAY_LENGTH', run: (v, t) => a05CurveArrayLength(v, t.raw) },
80
+ {
81
+ code: 'A18_DETERMINISTIC_EMIT',
82
+ run: (v, { input }) => {
83
+ const again = input.reEmit;
84
+ const encoding =
85
+ again === undefined
86
+ ? []
87
+ : [
88
+ ...(again.skeletonText !== input.skeletonText ? ['recompiling produced a different skeleton.json'] : []),
89
+ ...(again.atlasText !== input.atlasText ? ['recompiling produced a different skeleton.atlas'] : []),
90
+ ];
91
+ a18DeterministicEmit(v, { again: again !== undefined, encoding, first: input.modelText, second: again?.modelText ?? '' });
92
+ },
93
+ },
94
+ ];
95
+
96
+ /**
97
+ * The figures `validate()` reads off the pair it loaded, read off the same two
98
+ * texts here (issue #1114), into `stats` first — the order the round trip's
99
+ * report holds them in: `pages` and `regions` from the atlas text by rigc's own
100
+ * reader (`parseAtlasText`, whose pages and regions are the runtime atlas's in
101
+ * file order), then `bones`, `slots`, `animations` and `version` from the
102
+ * skeleton JSON — the arrays and the animation map the runtime loads them
103
+ * from, and `skeleton.spine` as it states it, `(none)` where it states none.
104
+ * The skeleton's four are left out where its JSON does not parse, as the round
105
+ * trip leaves them out where it loaded nothing; nothing here is a value the
106
+ * texts do not state.
107
+ */
108
+ function pairFigures(stats: Record<string, number | string>, raw: Json | null, atlasText: string): void {
109
+ const atlas = parseAtlasText(atlasText);
110
+ stats.pages = atlas.pages.length;
111
+ stats.regions = atlas.regions.length;
112
+ if (!isObj(raw)) return;
113
+ const listed = (v: unknown): number => (Array.isArray(v) ? v.length : 0);
114
+ stats.bones = listed(raw.bones);
115
+ stats.slots = listed(raw.slots);
116
+ stats.animations = isObj(raw.animations) ? Object.keys(raw.animations).length : 0;
117
+ const declared = isObj(raw.skeleton) ? raw.skeleton.spine : undefined;
118
+ stats.version = declared === undefined || declared === null ? '(none)' : typeof declared === 'string' ? declared : JSON.stringify(declared);
119
+ }
120
+
121
+ /** Every code the model side does not run, read off the registry — what this file must account for. */
122
+ export function roundTripOnlyCodes(): string[] {
123
+ const moved = new Set(MOVED_ASSERTIONS.map((m) => m.code));
124
+ return Object.keys(ASSERTION_KIND).filter((code) => !moved.has(code));
125
+ }
126
+
127
+ /** The restated rules over one build's emitted texts, in `validate()`'s report shape. */
128
+ export function validateEmittedText(input: EmittedTextInput): VerdictLists & { profile: AssertionProfile } {
129
+ const known = new Set(EMITTED_TEXT_RULES.map((r) => r.code));
130
+ const unrun = roundTripOnlyCodes().filter((code) => !known.has(code));
131
+ if (unrun.length > 0) {
132
+ throw new Error(`internal: ${unrun.join(', ')} runs on neither side here — the model side does not run it and src/assertions/emitted/index.ts neither restates nor SKIPs it`);
133
+ }
134
+ const h = verdictHarness(input.profile, ASSERTION_KIND, 'the emitted text');
135
+ let raw: Json | null = null;
136
+ try {
137
+ raw = JSON.parse(input.skeletonText) as Json;
138
+ } catch (err) {
139
+ h.fail(A00, `skeleton JSON is not parseable: ${(err as Error).message}`);
140
+ }
141
+ pairFigures(h.stats, raw, input.atlasText);
142
+ for (const rule of EMITTED_TEXT_RULES) {
143
+ if (rule.code === A00 && raw === null) continue;
144
+ h.check(rule.code, () => rule.run(h.verdicts, { raw, input }));
145
+ }
146
+ const { failures, passed, skipped, profileSkipped, stats } = h;
147
+ return { failures, passed, skipped, profileSkipped, stats, profile: input.profile };
148
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Which bones an animation keys (issue #1025, step 4c of #380) — the roster
3
+ * half of the census's F18 that A15 reads: whether the skeleton holds an
4
+ * animation of a name, whether it keys any bone, and which bones, in the
5
+ * order the file keys them.
6
+ *
7
+ * ⭐ **Three states, because A15 says three different things.** No such
8
+ * animation, an animation that keys no bone, and the bones it keys are each a
9
+ * sentence of their own in A15 — one is a missing subject, the next a subject
10
+ * with nothing in it, the last the subject — so the fact keeps them apart
11
+ * rather than folding the first two into an empty list.
12
+ *
13
+ * The order is the file's: A15 prints one line per keyed bone in it. The file
14
+ * keys an animation's bones in an object filled in the order the emitter
15
+ * writes the model's (`emitAnimation`), which is the document's own list — so
16
+ * an array-index bone name (`5`, `10`) is listed first whatever its place in
17
+ * that list (issue #1039), and the model side reads it through `keyedOrder`.
18
+ *
19
+ * Links nothing from the runtime: `validate()` reads it off the skeleton JSON
20
+ * as A15 always did, and the model side off the document
21
+ * (`../model/animated_bones.ts`).
22
+ */
23
+ export interface AnimatedBoneFacts {
24
+ /**
25
+ * The bones `animation` keys, in the file's order — `undefined` when the
26
+ * skeleton holds no animation of that name, `null` when it holds one that
27
+ * carries no bone timeline at all.
28
+ */
29
+ bonesKeyedBy(animation: string): readonly string[] | null | undefined;
30
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The animations and how long each one runs (issue #1025, cut 4c-3 of step
3
+ * 4c of #380) — the census's F18 and F19 as A09 reads them: the animation
4
+ * roster in the file's order, each animation's duration as the runtime holds
5
+ * it, and each of its timelines' own duration.
6
+ *
7
+ * ⭐ **The runtime's durations, not the stated ones.** An animation's duration
8
+ * is the last key of every timeline it carries, as float32 — the number
9
+ * `SkeletonJson` computes and stores, which is what A09 holds against the
10
+ * declared duration — and a timeline's own duration is its last key, as
11
+ * float32 (`Timeline.getDuration`). A09 counts the timelines keyed past the
12
+ * declared duration in its message, so the list holds one entry per timeline
13
+ * the runtime builds, in no order a body may read: A09 counts them and
14
+ * nothing else.
15
+ *
16
+ * The order of the roster is the file's: A09's last clause prints one line per
17
+ * animation the spec does not declare, in it.
18
+ *
19
+ * Links nothing from the runtime: `validate()` reads it off the loaded
20
+ * skeleton (`spineAnimationDurations`), the model side off the document and
21
+ * the core (`../model/animation_durations.ts`).
22
+ */
23
+
24
+ /** One animation, as A09 reads it. */
25
+ export interface AnimationDuration {
26
+ readonly name: string;
27
+ /** The runtime's duration: the last key of every timeline, float32. */
28
+ readonly duration: number;
29
+ /** Each timeline's own duration — its last key, float32 — one per timeline the runtime builds. */
30
+ readonly timelineDurations: readonly number[];
31
+ }
32
+
33
+ /** What A09 reads. */
34
+ export interface AnimationDurationFacts {
35
+ /** Every animation the skeleton holds, in the file's order. */
36
+ readonly animations: readonly AnimationDuration[];
37
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The atlas's pages, as the bodies that read them see them (issue #1025, step
3
+ * 4c of #380) — the census's F20, without `pma`, which the model document does
4
+ * not hold and a later cut supplies from the caller.
5
+ *
6
+ * Links nothing from the runtime: a spine-core `TextureAtlas` satisfies the
7
+ * shape structurally, which is how `validate()` supplies it, and the model
8
+ * side supplies the document's `pages` section (`../model/atlas_pages.ts`).
9
+ */
10
+
11
+ /** One page: its name, a path relative to the directory the atlas sits in. */
12
+ export interface AtlasPage {
13
+ readonly name: string;
14
+ }
15
+
16
+ /** The atlas, or `null` when there is none to read — the round trip refused it, or the model side read no document. */
17
+ export interface AtlasPageFacts {
18
+ readonly atlas: { readonly pages: readonly AtlasPage[] } | null;
19
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * The atlas as the page rules read it (issue #1025, step 4c of #380) — the
3
+ * census's F20 and F21 whole: every page's name, declared size and `pma`, every
4
+ * region's rectangle and the page it sits on, and the region a name resolves
5
+ * to. A06, A19 and A27 read it; A17, which reads only the page names, keeps the
6
+ * narrower `./atlas_pages.ts`.
7
+ *
8
+ * ⭐ **The order is a fact, and it is the file's.** A06 and A27 print one line
9
+ * per offending page or region, and A06's overlap clause prints a pair in the
10
+ * order it meets them, so `pages` and `regions` hold the atlas's own order —
11
+ * pages as the file lists them, regions page by page in the file's order — and
12
+ * `findRegion` answers with the FIRST region of exactly that name, the one the
13
+ * runtime's lookup returns.
14
+ *
15
+ * Links nothing from the runtime: spine-core's `TextureAtlas` satisfies the
16
+ * shape structurally, which is how `validate()` supplies it. The model side
17
+ * supplies the document's `pages` section, with each page's `pma` given by its
18
+ * caller until the document states it (issue #1026) — `../model/atlas_regions.ts`.
19
+ */
20
+
21
+ /** One page: its name (a path relative to the directory the atlas sits in), the size it declares, and whether it claims premultiplied alpha. */
22
+ export interface AtlasRegionPage {
23
+ readonly name: string;
24
+ readonly width: number;
25
+ readonly height: number;
26
+ readonly pma: boolean;
27
+ }
28
+
29
+ /** One region: its name exactly as the atlas spells it, the rectangle in the page's texels, and the page it sits on. */
30
+ export interface AtlasRegionEntry {
31
+ readonly name: string;
32
+ readonly page: AtlasRegionPage;
33
+ readonly x: number;
34
+ readonly y: number;
35
+ readonly width: number;
36
+ readonly height: number;
37
+ readonly degrees: number;
38
+ readonly offsetX: number;
39
+ readonly offsetY: number;
40
+ readonly originalWidth: number;
41
+ readonly originalHeight: number;
42
+ }
43
+
44
+ /** The atlas, or `null` when there is none to read — the round trip refused it, or the model side read no document. */
45
+ export interface AtlasRegionFacts {
46
+ readonly atlas: {
47
+ readonly pages: readonly AtlasRegionPage[];
48
+ readonly regions: readonly AtlasRegionEntry[];
49
+ /** The first region of exactly this name, pages in order — or `null`. */
50
+ findRegion(name: string): AtlasRegionEntry | null;
51
+ } | null;
52
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Every animation's bone timelines, in the file's order and spelling (issue
3
+ * #1025, cut 4c-4 of #380) — the half of the census's F18/F19 that the stroke
4
+ * rules read: which bones each animation keys, with which timelines, and each
5
+ * key as the file states it.
6
+ *
7
+ * ⭐ **The file's order, and the file's spelling**, for the reason A45's slot
8
+ * timelines carry both (`./slot_colour.ts`): A24 and A30 print one line per
9
+ * bone and per timeline in the order the file keys them, and A24, A29 and A30
10
+ * print a key's `x`, `y` and `time` as the file states them. So the list holds,
11
+ * for every animation in the order the file keys them and every bone in the
12
+ * order the animation keys them, that bone's timelines — name to keys — as
13
+ * the file spells them. `validate()` reads it off the skeleton JSON
14
+ * (`rawBoneTimelines`); the model side produces it from the document by calling
15
+ * the emitter's own order and its parser-default and key-order passes
16
+ * (`../model/bone_timelines.ts`), so a key field the emitter would leave out at
17
+ * the parser's default is left out here too.
18
+ *
19
+ * ⚠️ `timelines` is `unknown` rather than an object because the raw JSON can
20
+ * hold anything there and A24 counts a keyed bone before it asks what the
21
+ * bone's timelines are; the document always holds an object.
22
+ *
23
+ * Links nothing from the runtime.
24
+ */
25
+
26
+ /** One animation's timelines on one bone: timeline name -> keys, as the file states them. */
27
+ export interface BoneTimelines {
28
+ readonly animation: string;
29
+ readonly bone: string;
30
+ readonly timelines: unknown;
31
+ }
32
+
33
+ /** What A24, A29 and A30 read. */
34
+ export interface BoneTimelineFacts {
35
+ /** Every (animation, bone) pair the file keys, in the header's order. */
36
+ readonly boneTimelines: readonly BoneTimelines[];
37
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * The constraint timelines of every animation, as A34 walks them (issue
3
+ * #1025, cut 4c-5 of step 4c of #380): the constraints the skeleton declares,
4
+ * and each animation's `ik`, `transform`, `path`, `physics` and `slider`
5
+ * groups as the Spine file states them — the constraint each entry names and
6
+ * the key arrays under it.
7
+ *
8
+ * ⭐ **The order is the file's.** The animations in the order the file keys
9
+ * them, in each the five groups in that fixed order, each group's entries in
10
+ * the order the file keys them, each entry's timelines likewise: A34 prints
11
+ * one line per finding, so the walk is a fact both suppliers state.
12
+ *
13
+ * 🔸 **Which constraints a physics timeline naming none reaches is a
14
+ * question, not a value** — the runtime answers it by building the timeline
15
+ * its parser builds for the name and asking each constraint's data whether it
16
+ * takes the key (`unnamedPhysicsReach`), the model side by the core's own
17
+ * reading (`posedPhysics`, through `unnamedReach`). A body asks for it
18
+ * (`reach`), and the selftest puts every question to both (`VF14`).
19
+ *
20
+ * Links nothing from the runtime.
21
+ */
22
+
23
+ /** One key array under an entry: the timeline's name (`''` for the `ik`/`transform` shape) and what it holds — how many keys, or the spelling of a value that is not a list. */
24
+ export interface TargetKeyArray {
25
+ readonly timeline: string;
26
+ readonly keys: { readonly count: number } | { readonly spelled: string };
27
+ }
28
+
29
+ /** One entry of a group: the constraint name the file keys it by, and either the spelling of a value that is not an object where named timelines go (`bare`) or its key arrays. */
30
+ export interface TargetEntry {
31
+ readonly name: string;
32
+ readonly bare: string | null;
33
+ readonly keyArrays: readonly TargetKeyArray[];
34
+ }
35
+
36
+ /** One animation: its name and the groups it states, in the walk's order. */
37
+ export interface TargetAnimation {
38
+ readonly name: string;
39
+ readonly groups: ReadonlyArray<{ readonly group: 'ik' | 'transform' | 'path' | 'physics' | 'slider'; readonly entries: readonly TargetEntry[] }>;
40
+ }
41
+
42
+ /** What a physics timeline naming no constraint reaches: whether it is a `reset`, and the constraints that take its key. */
43
+ export interface UnnamedReach {
44
+ readonly resets: boolean;
45
+ readonly reached: readonly string[];
46
+ }
47
+
48
+ /** What A34 reads. */
49
+ export interface ConstraintTargetFacts {
50
+ /** Every entry of the skeleton's `constraints` array that is an object: its name (null when it states none as a string), its type as spelled, and its name as the sentence spells it. */
51
+ readonly constraints: ReadonlyArray<{ readonly name: string | null; readonly type: string; readonly spelled: string }>;
52
+ /** Every animation, or `null` when the skeleton declares none (its `animations` is not an object). */
53
+ readonly groupsByAnimation: readonly TargetAnimation[] | null;
54
+ /** What a physics timeline named `timeline` that names no constraint reaches — or `null` for a name the parser builds no timeline for. */
55
+ reach(timeline: string): UnnamedReach | null;
56
+ }
@@ -0,0 +1,155 @@
1
+ /**
2
+ * The constraints and the timelines that key them, as the bodies that ask
3
+ * "does this constraint do anything" read them (issue #1025, step 4c of
4
+ * #380) — the census's F12 to F17 and F19 with R1 and R2, for A23, A36, A37,
5
+ * A42, A47 and A48.
6
+ *
7
+ * ⭐ **The order is the runtime's update order**, which is the file's
8
+ * `constraints` array and the document's: a body that prints one line per
9
+ * constraint prints them in this order, and `A42` compares two positions in
10
+ * it. A timeline's `constraint` is a position in the same list.
11
+ *
12
+ * ⭐ **The timelines are in the order the runtime builds them**: the
13
+ * animations in the file's order, and within one the groups `ik`,
14
+ * `transform`, `path`, `physics`, `slider` — whatever order the file keys
15
+ * them in [measured: an animation keying `slider`, `physics`, `transform`,
16
+ * `ik` in that key order loads ik, transform, physics, slider] — each group's
17
+ * constraints and their timelines in the file's order. A body printing one
18
+ * line per key prints them in this order.
19
+ *
20
+ * 🔸 **Values are the ones the runtime holds after its parse, and each
21
+ * derivation is a function rigc already runs**, called by the model side: a
22
+ * field the record leaves out reads the parser's value through the core's
23
+ * record readers (`readModel`); `massInverse` and `step` are the core
24
+ * physics record's `1 / mass` and `1 / fps`; a key's time and value are the
25
+ * float32 the core's timeline reader stores; a Bézier segment's samples are
26
+ * `bezierPolyline` (`src/core/animation.ts`), the runtime's curve the core
27
+ * reproduces; the constraints a physics timeline naming none reaches are
28
+ * read off the core's own `posedPhysics`, asked rather than restated.
29
+ *
30
+ * Links nothing from the runtime.
31
+ */
32
+
33
+ /** The five constraint kinds, in the motion spec's words. */
34
+ export type ConstraintKind = 'ik' | 'transform' | 'path' | 'physics' | 'slider';
35
+
36
+ /** The five components a physics constraint drives, by name. */
37
+ export interface PhysicsComponents {
38
+ readonly x: number;
39
+ readonly y: number;
40
+ readonly rotate: number;
41
+ readonly scaleX: number;
42
+ readonly shearX: number;
43
+ }
44
+
45
+ /** The four bounded values of a physics constraint's setup pose — the fields `PHYSICS_POSE_RULES` judges. */
46
+ export interface PhysicsSetup {
47
+ readonly mix: number;
48
+ readonly massInverse: number;
49
+ readonly strength: number;
50
+ readonly damping: number;
51
+ }
52
+
53
+ /** One constraint, in update order. Exactly one of the kind fields is set, the one its `kind` names. */
54
+ export interface ConstraintEntry {
55
+ readonly kind: ConstraintKind;
56
+ readonly name: string;
57
+ /** The runtime class the constraint updates as (`IkConstraint`, `Slider`, …) — the word A42's sentence names `update` on. */
58
+ readonly runtimeClass: string;
59
+ readonly physics?: {
60
+ readonly bone: string;
61
+ /**
62
+ * The bone's `length`, as the runtime holds it after its parse (0 where the
63
+ * file leaves it out) — the lever a `rotate`, `shearX` or `scaleX` drive is
64
+ * stepped with (issue #1195): the tip `length·(a, c)` the rotation chases,
65
+ * and the radius the along-bone motion is divided by for `scaleX`.
66
+ */
67
+ readonly boneLength: number;
68
+ readonly components: PhysicsComponents;
69
+ readonly setup: PhysicsSetup;
70
+ readonly step: number;
71
+ };
72
+ readonly path?: { readonly bones: readonly string[]; readonly slot: string; readonly setup: { readonly mixRotate: number; readonly mixX: number; readonly mixY: number } };
73
+ readonly slider?: {
74
+ /** The animation it applies — its name, how many timelines it carries and its duration — or `null` when it applies none. */
75
+ readonly animation: { readonly name: string; readonly timelines: number; readonly duration: number } | null;
76
+ /** The dial bone's name, or `null` for the bone-less form. */
77
+ readonly bone: string | null;
78
+ readonly loop: boolean;
79
+ readonly scale: number;
80
+ readonly mix: number;
81
+ };
82
+ readonly ik?: {
83
+ readonly bones: readonly string[];
84
+ readonly target: string;
85
+ readonly mix: number;
86
+ /**
87
+ * For an ik over exactly two bones, the second bone's ancestors, its
88
+ * parent first and the root last; empty for any other count (issue
89
+ * #1205). The two-bone solve places the second bone through the first's
90
+ * matrix from the second's own local offset, so it is the solve of the
91
+ * chain drawn only when the first entry here is the first bone.
92
+ */
93
+ readonly secondAncestors: readonly string[];
94
+ };
95
+ /**
96
+ * A transform constraint's six mix channels in frame order — `mixRotate`,
97
+ * `mixX`, `mixY`, `mixScaleX`, `mixScaleY`, `mixShearY` — each the field's
98
+ * name and its setup value where the constraint declares that property as
99
+ * a `to`, and `null` where it does not: a mix the constraint never reads.
100
+ */
101
+ readonly transform?: { readonly bones: readonly string[]; readonly mixes: ReadonlyArray<{ readonly field: string; readonly setup: number } | null> };
102
+ }
103
+
104
+ /** One frame of a constraint timeline: its time and its first channel, as the runtime stores them (float32). */
105
+ export interface ConstraintFrame {
106
+ readonly time: number;
107
+ readonly value: number;
108
+ }
109
+
110
+ /** One constraint timeline of one animation. */
111
+ export interface ConstraintTimeline {
112
+ readonly animation: string;
113
+ readonly kind: ConstraintKind;
114
+ /**
115
+ * What it keys, in the motion spec's word: `ik` or `transform` for those
116
+ * two; `position`, `spacing`, `mix`; `inertia` … `mix` or `reset`; `time`
117
+ * or `mix`.
118
+ */
119
+ readonly word: string;
120
+ /** The constraint it names, as a position in `constraints` — or −1 for a physics timeline naming none. */
121
+ readonly constraint: number;
122
+ /** The constraints it writes into, as positions in `constraints`. */
123
+ readonly reach: readonly number[];
124
+ /** Its frames; empty for `reset`. */
125
+ readonly frames: readonly ConstraintFrame[];
126
+ /**
127
+ * Every value one channel poses while it plays: each key's own and every
128
+ * sample of each Bézier segment between two keys, in frame order. Channel
129
+ * 0 is the first value a frame holds (an ik frame's `mix`, a transform
130
+ * frame's `mixRotate`, a path `mix` frame's `mixRotate`, the one value of
131
+ * the others).
132
+ */
133
+ channelValues(channel: number): readonly number[];
134
+ /**
135
+ * On a physics timeline whose value `PHYSICS_POSE_RULES` bounds, the number
136
+ * a keyed `value` becomes in the pose field the integrator reads — a `mass`
137
+ * key lands as its reciprocal (`massInverse`), the rest as they are: the
138
+ * census's R1. `validate()` asks the runtime's own `set`; the model side the
139
+ * rule's `toPose`, the compiler's reading, which a selftest control holds to
140
+ * the runtime's. Absent on every other timeline.
141
+ */
142
+ readonly posed?: (value: number) => number;
143
+ }
144
+
145
+ /** What A23, A36, A37, A42, A47 and A48 read. */
146
+ export interface ConstraintFacts {
147
+ /** How many animations the skeleton declares. */
148
+ readonly animations: number;
149
+ /** Every constraint, in update order. */
150
+ readonly constraints: readonly ConstraintEntry[];
151
+ /** Every constraint timeline of every animation, in the order the runtime builds them. */
152
+ readonly timelines: readonly ConstraintTimeline[];
153
+ /** The slots some skin gives a path attachment, each once. */
154
+ readonly pathSlots: readonly string[];
155
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * The deform survey (issue #1025, cut 4c-3 of step 4c of #380) — the
3
+ * census's R6 and the structure under it, as A39 reads them: every deform key
4
+ * of every animation measured in the frame its animation is reached in, every
5
+ * span between two keys scanned, and what was passed over.
6
+ *
7
+ * ⭐ **One fact, because it is one measurement.** The survey is what
8
+ * `explain`'s `DEFORM` block prints whole and A39 gates on, and the reason
9
+ * both read one survey rather than each posing for itself is that the
10
+ * report's reversal count and A39's are the same count
11
+ * (`src/deformmeasure.ts`'s header). So A39 is handed the survey, not the
12
+ * poses under it: `validate()` hands it spine-core's — the Spine skeleton read
13
+ * and posed by the runtime (`surveyDeformKeys`) — and the model side the core's
14
+ * — the model document's structure posed by rigc's core (`surveyOfModel`,
15
+ * `src/deformsurvey.ts`). `tools/survey_hashes.ts` holds the two to one survey
16
+ * on every corpus row, and the selftest on every call with a model in hand
17
+ * (`VF12`).
18
+ *
19
+ * Links nothing from the runtime.
20
+ */
21
+ import type { DeformSurvey } from '../../deformsurvey.ts';
22
+
23
+ /** What A39 reads. */
24
+ export interface DeformSurveyFacts {
25
+ /** The survey, with the slots in `exempt` passed over unmeasured (the rig's `deformMayFold`). */
26
+ survey(exempt: ReadonlySet<string>): DeformSurvey;
27
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Every event key, in the file's order, with the declaration it fires (issue
3
+ * #1025, cut 4c-4 of #380) — what A32 reads.
4
+ *
5
+ * ⭐ **Split per clause, and the split is in this interface.** A32 has five
6
+ * clauses. Four of them — a key with no string `name`, a key firing an event
7
+ * the skeleton does not declare, a key whose time is not a finite number, and
8
+ * a key earlier than the one before it — are states `readModel` refuses by
9
+ * name (`readEventKeys` in `src/core/events.ts`, measured on forged documents:
10
+ * each one is refused at the key's address), so no readable document reaches
11
+ * them and they stay with the round trip: `validate()` runs them over the
12
+ * skeleton JSON and hands their findings in as `kept`, in the order they
13
+ * print, with `stopped` where the clause ended the key's reading. The fifth —
14
+ * `volume` or `balance` on a key whose event declares no audio — is the rig's:
15
+ * the document holds the key's fields and the declaration's `audio`, and a
16
+ * readable document can carry it. That clause is the body's
17
+ * (`../bodies/a32.ts`), and the model side supplies `kept` empty.
18
+ *
19
+ * The order is the file's: animations in the order the file keys them (the
20
+ * emitter's `editorAnimationOrder`), and an animation's keys in the order the
21
+ * document lists them, which is the order the emitter writes them
22
+ * (`emitAnimation`'s `events`). `timelines` counts the animations carrying an
23
+ * event timeline at all, which is what A32 SKIPs on when it is zero.
24
+ *
25
+ * Links nothing from the runtime.
26
+ */
27
+
28
+ /** One event key: where it is, what the kept clauses found, and what the moved clause reads. */
29
+ export interface EventKeyEntry {
30
+ readonly animation: string;
31
+ /** The key's index in its animation's event timeline. */
32
+ readonly index: number;
33
+ /** The findings of the clauses that stay with the round trip, in the order they print — always empty on the model side. */
34
+ readonly kept: readonly string[];
35
+ /** Whether a kept clause ended the key's reading, so the moved clause does not run on it. */
36
+ readonly stopped: boolean;
37
+ /** The event the key fires. */
38
+ readonly name: string;
39
+ /** Which of the two audio fields the key states. */
40
+ readonly sets: { readonly volume: boolean; readonly balance: boolean };
41
+ /** Whether the event the key fires declares an audio path (a string). */
42
+ readonly audio: boolean;
43
+ }
44
+
45
+ /** What A32 reads. */
46
+ export interface EventKeyFacts {
47
+ /** Whether the skeleton JSON parsed — always true on the model side, whose parse is the reader's. */
48
+ readonly parsed: boolean;
49
+ /** Whether the skeleton declares an `animations` object at all. */
50
+ readonly animations: boolean;
51
+ /** How many animations carry an event timeline. */
52
+ readonly timelines: number;
53
+ /** Every event key, in the file's order. */
54
+ readonly keys: readonly EventKeyEntry[];
55
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The linked meshes the file declares, as `A44` reads them (issue #1025, step
3
+ * 4c of #380): the census's F10, the subject half.
4
+ *
5
+ * ⭐ **The subject is the file's links, not the loaded ones** (A44's own ⚠️):
6
+ * a link whose region is missing loads as nothing and is in no skin, so the
7
+ * walk is over what the file declares — the skins in the file's order, each
8
+ * skin's slot keys as the file keys them, each slot's entries in table order.
9
+ * The model side walks the document's `linkedmesh` records in that order, by
10
+ * calling the emitter's own orders (`editorSkinOrder`, `editorSlotKeyOrder`).
11
+ *
12
+ * 🔒 **Everything A44 refuses is the encoding's.** A linked mesh's record
13
+ * holds no geometry field at all — `uvs`, `triangles`, `vertices`, `hull` and
14
+ * `edges` written on a link exist only in the Spine text, which is the
15
+ * card's census row for A44 ("a link record has no geometry field"). So the
16
+ * clause that refuses them stays in `src/validate.ts`, which hands its finding
17
+ * about each link here; the model side, which cannot spell such a link, hands
18
+ * none. What moved is the roster — which links there are, so a rig with none
19
+ * SKIPs on both sides and a rig with some is measured on both.
20
+ *
21
+ * Links nothing from the runtime.
22
+ */
23
+
24
+ /** One link the file declares. */
25
+ export interface LinkEntry {
26
+ readonly skin: string;
27
+ readonly slot: string;
28
+ readonly placeholder: string;
29
+ /** The placeholder of the mesh it draws. */
30
+ readonly source: string;
31
+ /** The kept clause's finding about this link — the geometry keys it states — in order; never on the model side. */
32
+ readonly encoding: readonly string[];
33
+ }
34
+
35
+ /** What A44 reads. */
36
+ export interface LinkFacts {
37
+ readonly links: readonly LinkEntry[];
38
+ }