@rig-c/core 2.64.3 → 2.65.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.
- package/README.md +3 -3
- package/docs/AUTHORING.md +9 -1
- package/package.json +1 -1
- package/src/compile.ts +51 -18
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ published beside it with the same files at every version.
|
|
|
26
26
|
|
|
27
27
|
<p align="center"><em>Fourteen part PNGs drawn from scratch for this repo, one rig spec, one motion spec — the assembly,
|
|
28
28
|
the breathing and the wave are all rigc-compiled Spine animations, rendered with
|
|
29
|
-
<code>rigc render</code
|
|
29
|
+
<code>rigc render</code>. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-demo@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
|
|
30
30
|
|
|
31
31
|
Loose part PNGs and two small JSON files in; **Spine 4.3 skeleton data out** — a
|
|
32
32
|
`skeleton.json` and a `skeleton.atlas` that load in any Spine runtime and **import
|
|
@@ -526,7 +526,7 @@ a rig compiles green, because green says nothing at all about the picture.
|
|
|
526
526
|
conditions, <code>rigc pose</code> reads where every part sits in each picture, two
|
|
527
527
|
candidate in-betweenings are compiled from the same pair — and a real
|
|
528
528
|
<code>rigc vote</code> ballot picks the winner, because the movement between the poses
|
|
529
|
-
is the one thing no instrument here will grade.</em></p>
|
|
529
|
+
is the one thing no instrument here will grade. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-keypose@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
|
|
530
530
|
|
|
531
531
|
Three properties of `vote` are worth stating, because they are what make its ledger
|
|
532
532
|
usable by the next agent rather than by a reader: **a tie is a recorded outcome, not a
|
|
@@ -580,7 +580,7 @@ while <code>gaze</code> and <code>turn</code> return to rest, so the hand-offs m
|
|
|
580
580
|
differing pixels — and the composing is the consumer's. Authorable on plain Spine 4.3, no
|
|
581
581
|
plugin, no runtime patch; what the turn costs is authoring rather than runtime capability,
|
|
582
582
|
and that cost is one stated expression per key. Compiled and rendered entirely by the
|
|
583
|
-
published package.</em></p>
|
|
583
|
+
published package. A <a href="https://raw.githubusercontent.com/rig-c/core/main/assets/rigc-scene@2x.gif">2x version</a> (1200x810) sits beside it.</em></p>
|
|
584
584
|
|
|
585
585
|
🎞️ **How the three films on this page were made** is kept with them, one directory per
|
|
586
586
|
film in [`films/`](https://github.com/rig-c/core/tree/main/films) — a `run.sh` that
|
package/docs/AUTHORING.md
CHANGED
|
@@ -4993,6 +4993,14 @@ consumer's, decided by dressing the skeleton rather than by the animation.
|
|
|
4993
4993
|
Seconds, not frames: nothing requires a key to land on any frame grid, and a
|
|
4994
4994
|
reference rendered at some rate says nothing about where its keys are. Put keys
|
|
4995
4995
|
where the motion changes.
|
|
4996
|
+
⚠️ **A phrase generated cycle by cycle ends one key before its period.** Write
|
|
4997
|
+
each cycle of a 1 s phrase as t = 0…1 and the closing key of one cycle lands on
|
|
4998
|
+
the opening key of the next, so the track is refused at the first boundary; the
|
|
4999
|
+
refusal names both keys and, when the keys after the pair repeat the spacing of
|
|
5000
|
+
the keys before it, says so. Generate each cycle without its closing key and
|
|
5001
|
+
keep that key on the last cycle only: a looping player takes `time % duration`
|
|
5002
|
+
and nothing interpolates across the wrap, so a track that stops short of its
|
|
5003
|
+
period holds its last key until it (issue #1506).
|
|
4996
5004
|
- **Key times are emitted as float32s like every other number, and never stored
|
|
4997
5005
|
LATER than you wrote them.** Every emitted number is the shortest decimal naming
|
|
4998
5006
|
its float32 — the text the editor writes, and the precision the runtime keeps,
|
|
@@ -7213,7 +7221,7 @@ retain the minimal inputs and report that code rather than inventing author data
|
|
|
7213
7221
|
| <a id="CE_COMPILE_CURVE_VALUE_NONFINITE"></a>`CE_COMPILE_CURVE_VALUE_NONFINITE` | “<value> (t=<value>): raw curve holds a non-finite value <value>” | Supply finite numbers in the raw curve array. |
|
|
7214
7222
|
| <a id="CE_COMPILE_VALUE_TIMELINE_PROPERTY_UNKNOWN"></a>`CE_COMPILE_VALUE_TIMELINE_PROPERTY_UNKNOWN` | “animation "<value>": <value> "<value>" has no property "<value>"” | Use a property present on the named target's timeline family. |
|
|
7215
7223
|
| <a id="CE_COMPILE_TIMELINE_KEYS_EMPTY"></a>`CE_COMPILE_TIMELINE_KEYS_EMPTY` | “<value>: no keys”<br>Also: “animation "<value>" deform <value>/<value>/<value>”<br>“animation "<value>" events”<br>“animation "<value>" drawOrder”<br>“animation "<value>" slot "<value>" <value>”<br>“animation "<value>" <value> constraint "<value>"”<br>Here the values named in the message include: “default”. | Supply at least one key in the timeline. |
|
|
7216
|
-
| <a id="CE_COMPILE_KEY_TIMES_NOT_INCREASING"></a>`CE_COMPILE_KEY_TIMES_NOT_INCREASING` | “<value>: key times must strictly increase
|
|
7224
|
+
| <a id="CE_COMPILE_KEY_TIMES_NOT_INCREASING"></a>`CE_COMPILE_KEY_TIMES_NOT_INCREASING` | “<value>: key times must strictly increase — key <value> at t=<value> does not come after key <value> at t=<value> (keys counted from 0)”, and when the two keys share a time and the keys after them repeat the spacing of the keys before them: “. The keys from t=<value> repeat every <value>s, so key <value> closes one cycle and key <value> opens the next at the same time: a phrase that repeats every <value>s ends one key before its period. Generate each cycle without its closing key and keep that key on the last cycle only — a looping player wraps from it to t=0, and a track that stops short holds its last key until the wrap”<br>Also: “animation "<value>" deform <value>/<value>/<value>”<br>“animation "<value>" drawOrder”<br>“animation "<value>" slot "<value>" <value>”<br>“animation "<value>" <value> constraint "<value>"”<br>Here the values named in the message include: “default”. | Order keys by strictly increasing time; the message names both keys of the pair. A phrase generated cycle by cycle with each cycle written from its start to its end repeats every boundary key — drop each cycle's closing key and keep it on the last cycle only (§4.5). |
|
|
7217
7225
|
| <a id="CE_COMPILE_STEPPED_TIMELINE_EASING"></a>`CE_COMPILE_STEPPED_TIMELINE_EASING` | “<value> key at t=<value> carries [an easing ("<value>") / a curve], and this timeline is stepped by the format — its reader builds no curve, so the mode changes AT the key and holds until the next one. Remove it”<br>Also: “animation "<value>" <value> "<value>" <value>” | Remove easing/curve from the stepped mode timeline. |
|
|
7218
7226
|
| <a id="CE_COMPILE_TIMELINE_MODE_UNKNOWN"></a>`CE_COMPILE_TIMELINE_MODE_UNKNOWN` | “<value> key at t=<value> names mode <value>; <value>”<br>“known: <value> — the runtime folds the case of the first letter and of nothing else”<br>Also: “animation "<value>" <value> "<value>" <value>”<br>“, ”<br>Here the values named in the message include: “noScaleOrReflection”, “normal”, “onlyTranslation”, “noRotationOrReflection”. | Use a mode named in the timeline's allowed mode list. |
|
|
7219
7227
|
| <a id="CE_COMPILE_VALUELESS_TIMELINE_VALUE"></a>`CE_COMPILE_VALUELESS_TIMELINE_VALUE` | “<value>: this timeline takes no value; use null”<br>Also: “animation "<value>" <value> "<value>" <value>” | Use null for a timeline that takes no value. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rig-c/core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.65.0",
|
|
4
4
|
"description": "AI-authored 2D rigging and animation, verified before it is written. rigc compiles a rig spec into skeleton data in the Spine 4.3 format, gates it with named assertions held to a spine-core round trip's verdicts, and emits nothing that fails. Ships as an agent skill.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/compile.ts
CHANGED
|
@@ -1148,6 +1148,51 @@ function checkKeyTime(where: string, time: number, authored: number, duration: n
|
|
|
1148
1148
|
);
|
|
1149
1149
|
}
|
|
1150
1150
|
|
|
1151
|
+
/**
|
|
1152
|
+
* The one refusal for a key that does not come after the key before it, for
|
|
1153
|
+
* every timeline kind (issue #1506) — six call sites used to spell it six times.
|
|
1154
|
+
*
|
|
1155
|
+
* It names both keys by index and authored time, because the time alone does
|
|
1156
|
+
* not say which pair to look at: a generated phrase of four one-second cycles,
|
|
1157
|
+
* each written t = 0…1, collides at every cycle boundary, and `(at t=1)` read
|
|
1158
|
+
* the same as an ordinary slip. When the two keys share a time AND the keys
|
|
1159
|
+
* after the pair repeat the spacing of the keys before it, the period is read
|
|
1160
|
+
* off the keys themselves — the second key's time less the first key's — and
|
|
1161
|
+
* the message says that the closing key of one cycle is the opening key of the
|
|
1162
|
+
* next. Nothing else is inferred: a duplicate with no keys after it, or with
|
|
1163
|
+
* keys that do not repeat, carries the pair alone, and the declared `loop` is
|
|
1164
|
+
* not consulted because it is a player hint (§4.1) and the boundary is the same
|
|
1165
|
+
* either way. The fix is phrased from the runtime rather than from taste:
|
|
1166
|
+
* `Animation.apply` wraps a looping time as `time % duration` and the last
|
|
1167
|
+
* frame of every curve timeline is stepped, so a track that stops short of its
|
|
1168
|
+
* period holds its last key until the wrap rather than easing into t = 0.
|
|
1169
|
+
*
|
|
1170
|
+
* ⚠️ The repetition is compared in authored seconds within one float32 step of
|
|
1171
|
+
* the later key (`float32Step`) — `lag`/`stagger` shift every key of a track by
|
|
1172
|
+
* the same amount, so the spacing is the same before and after them.
|
|
1173
|
+
*/
|
|
1174
|
+
function refuseKeyOrder(where: string, keys: ReadonlyArray<{ t: number }>, i: number, duplicate: boolean): never {
|
|
1175
|
+
const at = keys[i].t;
|
|
1176
|
+
const before = keys[i - 1].t;
|
|
1177
|
+
const period = at - keys[0].t;
|
|
1178
|
+
const following = Math.min(i - 1, keys.length - 1 - i);
|
|
1179
|
+
let repeats = duplicate && period > 0 && following >= 1;
|
|
1180
|
+
for (let j = 1; repeats && j <= following; j++) {
|
|
1181
|
+
if (Math.abs(keys[i + j].t - keys[j].t - period) > float32Step(keys[i + j].t)) repeats = false;
|
|
1182
|
+
}
|
|
1183
|
+
const boundary = repeats
|
|
1184
|
+
? `. The keys from t=${keys[0].t} repeat every ${period}s, so key ${i - 1} closes one cycle and key ${i} opens ` +
|
|
1185
|
+
`the next at the same time: a phrase that repeats every ${period}s ends one key before its period. Generate ` +
|
|
1186
|
+
'each cycle without its closing key and keep that key on the last cycle only — a looping player wraps from ' +
|
|
1187
|
+
'it to t=0, and a track that stops short holds its last key until the wrap'
|
|
1188
|
+
: '';
|
|
1189
|
+
throw new CompileError(
|
|
1190
|
+
`${where}: key times must strictly increase — key ${i} at t=${at} does not come after key ${i - 1} at ` +
|
|
1191
|
+
`t=${before} (keys counted from 0)${boundary}`,
|
|
1192
|
+
'CE_COMPILE_KEY_TIMES_NOT_INCREASING',
|
|
1193
|
+
);
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1151
1196
|
function channelHex(v: number): string {
|
|
1152
1197
|
const clamped = Math.max(0, Math.min(1, v));
|
|
1153
1198
|
return Math.round(clamped * 255)
|
|
@@ -8106,9 +8151,7 @@ function compileValueTrack(
|
|
|
8106
8151
|
const key = track.keys[i];
|
|
8107
8152
|
const next = track.keys[i + 1];
|
|
8108
8153
|
const time = keyTime(key.t + shift);
|
|
8109
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
8110
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
8111
|
-
}
|
|
8154
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, track.keys, i, time === out[i - 1].time);
|
|
8112
8155
|
checkKeyTime(where, time, key.t, duration);
|
|
8113
8156
|
// A named-value timeline (`inherit`): the key's `v` is one name, stepped by
|
|
8114
8157
|
// the format. The name is resolved rather than copied, and what is WRITTEN
|
|
@@ -8237,9 +8280,7 @@ function compileDrawOrder(
|
|
|
8237
8280
|
for (let i = 0; i < keys.length; i++) {
|
|
8238
8281
|
const key = keys[i];
|
|
8239
8282
|
const time = keyTime(key.t);
|
|
8240
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
8241
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
8242
|
-
}
|
|
8283
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, keys, i, time === out[i - 1].time);
|
|
8243
8284
|
checkKeyTime(where, time, key.t, duration);
|
|
8244
8285
|
// No offsets = "back to the setup draw order", which is the parser's own
|
|
8245
8286
|
// encoding for it. An empty array means the same thing and is written the
|
|
@@ -8478,9 +8519,7 @@ function compileConstraintTrack(
|
|
|
8478
8519
|
const key = keys[i];
|
|
8479
8520
|
const next = keys[i + 1];
|
|
8480
8521
|
const time = keyTime(key.t);
|
|
8481
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
8482
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
8483
|
-
}
|
|
8522
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, keys, i, time === out[i - 1].time);
|
|
8484
8523
|
checkKeyTime(where, time, key.t, duration);
|
|
8485
8524
|
|
|
8486
8525
|
const entry: ModelKey = { time };
|
|
@@ -8906,9 +8945,7 @@ function compileSequenceTrack(
|
|
|
8906
8945
|
for (let i = 0; i < track.keys.length; i++) {
|
|
8907
8946
|
const key = track.keys[i];
|
|
8908
8947
|
const time = keyTime(key.t);
|
|
8909
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
8910
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
8911
|
-
}
|
|
8948
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, track.keys, i, time === out[i - 1].time);
|
|
8912
8949
|
checkKeyTime(where, time, key.t, duration);
|
|
8913
8950
|
if (key.index !== undefined && key.index >= series.count) {
|
|
8914
8951
|
throw new CompileError(
|
|
@@ -8946,9 +8983,7 @@ function compileDeformTrack(
|
|
|
8946
8983
|
for (let i = 0; i < keys.length; i++) {
|
|
8947
8984
|
const key = keys[i];
|
|
8948
8985
|
const time = keyTime(key.t);
|
|
8949
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
8950
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
8951
|
-
}
|
|
8986
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, keys, i, time === out[i - 1].time);
|
|
8952
8987
|
checkKeyTime(where, time, key.t, duration);
|
|
8953
8988
|
// -- a key that states a MODEL rather than a run (issue #294) ------------
|
|
8954
8989
|
//
|
|
@@ -9660,9 +9695,7 @@ function compileTrack(
|
|
|
9660
9695
|
const key = track.keys[i];
|
|
9661
9696
|
const next = track.keys[i + 1];
|
|
9662
9697
|
const time = keyTime(key.t + shift);
|
|
9663
|
-
if (i > 0 && time <= (out[i - 1].time as number))
|
|
9664
|
-
throw new CompileError(`${where}: key times must strictly increase (at t=${key.t})`, 'CE_COMPILE_KEY_TIMES_NOT_INCREASING');
|
|
9665
|
-
}
|
|
9698
|
+
if (i > 0 && time <= (out[i - 1].time as number)) refuseKeyOrder(where, track.keys, i, time === out[i - 1].time);
|
|
9666
9699
|
checkKeyTime(where, time, key.t, duration);
|
|
9667
9700
|
|
|
9668
9701
|
if (shape === 'attachment') {
|