spine-rigc 2.57.0 → 2.58.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/docs/AUTHORING.md +607 -263
- package/package.json +1 -1
- package/skills/rigc/SKILL.md +3 -1
- package/skills/rigc-face/SKILL.md +3 -1
- package/skills/rigc-ingest/SKILL.md +3 -1
- package/skills/rigc-motion/SKILL.md +3 -1
- package/skills/rigc-rigging/SKILL.md +3 -1
- package/src/atlas.ts +10 -10
- package/src/cli/shared.ts +2 -2
- package/src/compile.ts +333 -333
- package/src/deformgen.ts +28 -28
- package/src/emit_spine.ts +5 -5
- package/src/errors.ts +8 -0
- package/src/guide.ts +4 -9
- package/src/keys.ts +4 -4
- package/src/model.ts +10 -10
- package/src/motion.ts +6 -6
- package/src/rig.ts +76 -76
- package/src/stop.ts +30 -30
- package/src/trackgen.ts +11 -11
package/src/stop.ts
CHANGED
|
@@ -147,7 +147,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
147
147
|
const declared: unknown = spec.stops;
|
|
148
148
|
if (declared === undefined) return spec;
|
|
149
149
|
if (!Array.isArray(declared)) {
|
|
150
|
-
throw new CompileError(`${where}: "stops" is ${JSON.stringify(declared)}; it is an array of { "chain", "links", "against", "at", "inward", "outward", "lower"? }
|
|
150
|
+
throw new CompileError(`${where}: "stops" is ${JSON.stringify(declared)}; it is an array of { "chain", "links", "against", "at", "inward", "outward", "lower"? }`, 'CE_STOP_DECLARATIONS_ARRAY');
|
|
151
151
|
}
|
|
152
152
|
const authored = new Map<string, RigBone>();
|
|
153
153
|
for (const bone of spec.bones) if (isObj(bone) && typeof bone.name === 'string') authored.set(bone.name, bone);
|
|
@@ -157,44 +157,44 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
157
157
|
const stops: ReadStop[] = [];
|
|
158
158
|
const stoppedBy = new Map<string, string>();
|
|
159
159
|
for (const [i, entry] of declared.entries()) {
|
|
160
|
-
if (!isObj(entry)) throw new CompileError(`${where}: stops[${i}] is ${JSON.stringify(entry)}; a stop is an object
|
|
160
|
+
if (!isObj(entry)) throw new CompileError(`${where}: stops[${i}] is ${JSON.stringify(entry)}; a stop is an object`, 'CE_STOP_DECLARATION_OBJECT');
|
|
161
161
|
const chain = entry.chain;
|
|
162
162
|
if (typeof chain !== 'string' || chain.length === 0) {
|
|
163
|
-
throw new CompileError(`${where}: stops[${i}] needs a "chain" — the name its point, frame and aim are called after ("<chain>_touch", "<chain>_stop", "<chain>_stop_aim")
|
|
163
|
+
throw new CompileError(`${where}: stops[${i}] needs a "chain" — the name its point, frame and aim are called after ("<chain>_touch", "<chain>_stop", "<chain>_stop_aim")`, 'CE_STOP_CHAIN_NAME_REQUIRED');
|
|
164
164
|
}
|
|
165
165
|
const who = `${where}: stop "${chain}"`;
|
|
166
166
|
const links: unknown = entry.links;
|
|
167
167
|
if (!Array.isArray(links) || links.length === 0 || !links.every((l): l is string => typeof l === 'string')) {
|
|
168
|
-
throw new CompileError(`${who} states "links" ${JSON.stringify(links) ?? 'absent'}; it is the chain's swinging links, root first — a non-empty array of bone names
|
|
168
|
+
throw new CompileError(`${who} states "links" ${JSON.stringify(links) ?? 'absent'}; it is the chain's swinging links, root first — a non-empty array of bone names`, 'CE_STOP_LINKS_ARRAY');
|
|
169
169
|
}
|
|
170
170
|
for (const [k, link] of links.entries()) {
|
|
171
171
|
const bone = authored.get(link);
|
|
172
|
-
if (bone === undefined) throw new CompileError(`${who}: links[${k}] names bone "${link}", which this rig does not declare
|
|
172
|
+
if (bone === undefined) throw new CompileError(`${who}: links[${k}] names bone "${link}", which this rig does not declare`, 'CE_STOP_LINK_UNKNOWN');
|
|
173
173
|
const other = stoppedBy.get(link);
|
|
174
174
|
if (other !== undefined) {
|
|
175
175
|
throw new CompileError(
|
|
176
|
-
`${who}: links[${k}] "${link}" is already stopped by stop "${other}" — one drawn link "${link}_d" and one clamp "${link}_clamp" per link, so a link is stopped once`,
|
|
176
|
+
`${who}: links[${k}] "${link}" is already stopped by stop "${other}" — one drawn link "${link}_d" and one clamp "${link}_clamp" per link, so a link is stopped once`, 'CE_STOP_LINK_ALREADY_STOPPED',
|
|
177
177
|
);
|
|
178
178
|
}
|
|
179
179
|
stoppedBy.set(link, chain);
|
|
180
180
|
if (bone.from !== undefined) {
|
|
181
|
-
throw new CompileError(`${who}: links[${k}] "${link}" takes its place from the manifest ("from"), and a stop copies the link's stated x, y and rotation onto its drawn link — state them
|
|
181
|
+
throw new CompileError(`${who}: links[${k}] "${link}" takes its place from the manifest ("from"), and a stop copies the link's stated x, y and rotation onto its drawn link — state them`, 'CE_STOP_LINK_MANIFEST_POSITION');
|
|
182
182
|
}
|
|
183
183
|
if (bone.parent === undefined) {
|
|
184
|
-
throw new CompileError(`${who}: links[${k}] "${link}" is the skeleton's root, and a stop hangs its frame and its drawn link where the link hangs — a link has a parent
|
|
184
|
+
throw new CompileError(`${who}: links[${k}] "${link}" is the skeleton's root, and a stop hangs its frame and its drawn link where the link hangs — a link has a parent`, 'CE_STOP_LINK_ROOT');
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
187
|
const against = entry.against;
|
|
188
188
|
if (typeof against !== 'string' || against.length === 0) {
|
|
189
|
-
throw new CompileError(`${who} needs "against" — the bone the point rides on, the leg the chain is held off
|
|
189
|
+
throw new CompileError(`${who} needs "against" — the bone the point rides on, the leg the chain is held off`, 'CE_STOP_AGAINST_REQUIRED');
|
|
190
190
|
}
|
|
191
|
-
if (!authored.has(against)) throw new CompileError(`${who}: "against" names bone "${against}", which this rig does not declare
|
|
191
|
+
if (!authored.has(against)) throw new CompileError(`${who}: "against" names bone "${against}", which this rig does not declare`, 'CE_STOP_AGAINST_UNKNOWN');
|
|
192
192
|
const at: unknown = entry.at;
|
|
193
193
|
if (at === undefined) {
|
|
194
|
-
throw new CompileError(`${who} states no "at" — the point on "${against}" the frame aims at, [x, y] in "${against}"'s local space; the compiler places no point of its own
|
|
194
|
+
throw new CompileError(`${who} states no "at" — the point on "${against}" the frame aims at, [x, y] in "${against}"'s local space; the compiler places no point of its own`, 'CE_STOP_AT_REQUIRED');
|
|
195
195
|
}
|
|
196
196
|
if (!Array.isArray(at) || at.length !== 2 || !at.every((v): v is number => typeof v === 'number')) {
|
|
197
|
-
throw new CompileError(`${who} states "at" ${JSON.stringify(at)}; it is [x, y], two numbers in "${against}"'s local space
|
|
197
|
+
throw new CompileError(`${who} states "at" ${JSON.stringify(at)}; it is [x, y], two numbers in "${against}"'s local space`, 'CE_STOP_AT_PAIR');
|
|
198
198
|
}
|
|
199
199
|
const range = (node: Record<string, unknown>, field: string): { inward: number; outward: number } => {
|
|
200
200
|
const out: Record<'inward' | 'outward', number> = { inward: 0, outward: 0 };
|
|
@@ -202,14 +202,14 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
202
202
|
const v = node[end];
|
|
203
203
|
if (typeof v !== 'number') {
|
|
204
204
|
throw new CompileError(
|
|
205
|
-
`${who} states no "${field}${end}" — degrees from the link's rest angle, counted positive away from "${against}"; the range is [inward, outward], and the compiler states neither end for you`,
|
|
205
|
+
`${who} states no "${field}${end}" — degrees from the link's rest angle, counted positive away from "${against}"; the range is [inward, outward], and the compiler states neither end for you`, 'CE_STOP_RANGE_END_REQUIRED',
|
|
206
206
|
);
|
|
207
207
|
}
|
|
208
208
|
out[end] = v;
|
|
209
209
|
}
|
|
210
210
|
if (out.inward > out.outward) {
|
|
211
211
|
throw new CompileError(
|
|
212
|
-
`${who}: "${field}inward" ${out.inward} is greater than "${field}outward" ${out.outward} — the range is [inward, outward] in degrees counted positive away from "${against}", and this one holds no angle`,
|
|
212
|
+
`${who}: "${field}inward" ${out.inward} is greater than "${field}outward" ${out.outward} — the range is [inward, outward] in degrees counted positive away from "${against}", and this one holds no angle`, 'CE_STOP_RANGE_REVERSED',
|
|
213
213
|
);
|
|
214
214
|
}
|
|
215
215
|
return out;
|
|
@@ -219,11 +219,11 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
219
219
|
let lower: { inward: number; outward: number } | null = null;
|
|
220
220
|
if (links.length > 1) {
|
|
221
221
|
if (!isObj(lowerRaw)) {
|
|
222
|
-
throw new CompileError(`${who} states no "lower" — the range [inward, outward] each link below the first may turn through off its own rest bend; the chain has ${links.length - 1} of them
|
|
222
|
+
throw new CompileError(`${who} states no "lower" — the range [inward, outward] each link below the first may turn through off its own rest bend; the chain has ${links.length - 1} of them`, 'CE_STOP_LOWER_REQUIRED');
|
|
223
223
|
}
|
|
224
224
|
lower = range(lowerRaw, 'lower.');
|
|
225
225
|
} else if (lowerRaw !== undefined) {
|
|
226
|
-
throw new CompileError(`${who} states "lower" and its chain has one link — there is no link below the first for it to hold
|
|
226
|
+
throw new CompileError(`${who} states "lower" and its chain has one link — there is no link below the first for it to hold`, 'CE_STOP_LOWER_WITH_ONE_LINK');
|
|
227
227
|
}
|
|
228
228
|
stops.push({ who, chain, links, against, at: [at[0], at[1]], inward: first.inward, outward: first.outward, lower, names: stopNames(chain, links) });
|
|
229
229
|
}
|
|
@@ -236,16 +236,16 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
236
236
|
const { names } = stop;
|
|
237
237
|
const lowerDrawn = names.drawn.slice(1);
|
|
238
238
|
for (const name of [names.frame, names.rel, names.drawn[0], ...lowerDrawn, names.point]) {
|
|
239
|
-
if (authored.has(name)) throw new CompileError(`${stop.who} generates bone "${name}", which this rig already declares — rename the bone, or the stop's "chain"
|
|
239
|
+
if (authored.has(name)) throw new CompileError(`${stop.who} generates bone "${name}", which this rig already declares — rename the bone, or the stop's "chain"`, 'CE_STOP_GENERATED_BONE_DECLARED');
|
|
240
240
|
const by = generatedBones.get(name);
|
|
241
|
-
if (by !== undefined) throw new CompileError(`${stop.who} generates bone "${name}", which stop "${by}" generates too — rename one "chain"
|
|
241
|
+
if (by !== undefined) throw new CompileError(`${stop.who} generates bone "${name}", which stop "${by}" generates too — rename one "chain"`, 'CE_STOP_GENERATED_BONE_COLLISION');
|
|
242
242
|
generatedBones.set(name, stop.chain);
|
|
243
243
|
}
|
|
244
244
|
for (const [type, name] of [['ik', names.aim], ['transform', names.copy], ...names.clamp.map((c) => ['transform', c] as const)] as const) {
|
|
245
245
|
const key = `${type} ${name}`;
|
|
246
|
-
if (authoredConstraints.has(key)) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which this rig already declares — rename the constraint, or the stop's "chain"
|
|
246
|
+
if (authoredConstraints.has(key)) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which this rig already declares — rename the constraint, or the stop's "chain"`, 'CE_STOP_GENERATED_CONSTRAINT_DECLARED');
|
|
247
247
|
const by = generatedConstraints.get(key);
|
|
248
|
-
if (by !== undefined) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which stop "${by}" generates too — rename one "chain"
|
|
248
|
+
if (by !== undefined) throw new CompileError(`${stop.who} generates ${type} constraint "${name}", which stop "${by}" generates too — rename one "chain"`, 'CE_STOP_GENERATED_CONSTRAINT_COLLISION');
|
|
249
249
|
generatedConstraints.set(key, stop.chain);
|
|
250
250
|
}
|
|
251
251
|
}
|
|
@@ -265,7 +265,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
265
265
|
});
|
|
266
266
|
if (at < 0) {
|
|
267
267
|
throw new CompileError(
|
|
268
|
-
`${stop.who}: links[${k}] "${link}" is written by no constraint of this rig spec — a stop holds a link something swings (a physics, ik, transform or path constraint in "constraints"), and its copy and clamp run right after that. A physics constraint in the motion spec's table is emitted after every rig constraint, past the clamp; declare it here`,
|
|
268
|
+
`${stop.who}: links[${k}] "${link}" is written by no constraint of this rig spec — a stop holds a link something swings (a physics, ik, transform or path constraint in "constraints"), and its copy and clamp run right after that. A physics constraint in the motion spec's table is emitted after every rig constraint, past the clamp; declare it here`, 'CE_STOP_LINK_UNDRIVEN',
|
|
269
269
|
);
|
|
270
270
|
}
|
|
271
271
|
return at;
|
|
@@ -296,14 +296,14 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
296
296
|
for (let k = 1; k < links.length; k++) {
|
|
297
297
|
if (!ancestry(links[k]).includes(names.drawn[k - 1])) {
|
|
298
298
|
throw new CompileError(
|
|
299
|
-
`${stop.who}: links[${k}] "${links[k]}" does not hang below "${names.drawn[k - 1]}", the drawn link of links[${k - 1}] — a chain's next link hangs from the drawn link above it, so the clamp that holds that one carries this one; parent it there`,
|
|
299
|
+
`${stop.who}: links[${k}] "${links[k]}" does not hang below "${names.drawn[k - 1]}", the drawn link of links[${k - 1}] — a chain's next link hangs from the drawn link above it, so the clamp that holds that one carries this one; parent it there`, 'CE_STOP_LINK_PARENT_CHAIN',
|
|
300
300
|
);
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
const held = new Set([names.frame, names.rel, ...names.drawn, ...links]);
|
|
304
304
|
const under = [stop.against, ...ancestry(stop.against)].find((b) => held.has(b));
|
|
305
305
|
if (under !== undefined) {
|
|
306
|
-
throw new CompileError(`${stop.who}: "against" "${stop.against}" hangs below "${under}", which the stop moves — the point would swing with the chain it holds off; name the leg
|
|
306
|
+
throw new CompileError(`${stop.who}: "against" "${stop.against}" hangs below "${under}", which the stop moves — the point would swing with the chain it holds off; name the leg`, 'CE_STOP_AGAINST_MOVES_WITH_STOP');
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
|
|
@@ -338,7 +338,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
338
338
|
for (const [end, value] of [['outward', a], ['inward', b]] as const) {
|
|
339
339
|
if (Math.abs(value) > BOUND) {
|
|
340
340
|
throw new CompileError(
|
|
341
|
-
`${stop.who}: "${field}${end}" ${range[end]} puts ${what}'s bound at ${value}°, past ±${BOUND}° — the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holds`,
|
|
341
|
+
`${stop.who}: "${field}${end}" ${range[end]} puts ${what}'s bound at ${value}°, past ±${BOUND}° — the clamp reads a local rotation in (-180, 180], so a bound past it is never reached and the range it states is not the one that holds`, 'CE_STOP_LOCAL_BOUND_RANGE',
|
|
342
342
|
);
|
|
343
343
|
}
|
|
344
344
|
}
|
|
@@ -358,10 +358,10 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
358
358
|
if (listed.has(name)) continue;
|
|
359
359
|
const bone = known(name);
|
|
360
360
|
if (bone === undefined) {
|
|
361
|
-
throw new CompileError(`${who}: "${tip}" hangs below "${name}", which a later stop generates — declare that stop first, so its angles are known when this one is read
|
|
361
|
+
throw new CompileError(`${who}: "${tip}" hangs below "${name}", which a later stop generates — declare that stop first, so its angles are known when this one is read`, 'CE_STOP_DEPENDENCY_ORDER');
|
|
362
362
|
}
|
|
363
363
|
if (bone.from !== undefined) {
|
|
364
|
-
throw new CompileError(`${who}: "${name}" takes its place from the manifest ("from"), and a stop's angles are read off the setup pose this rig spec states — state its x, y and rotation
|
|
364
|
+
throw new CompileError(`${who}: "${name}" takes its place from the manifest ("from"), and a stop's angles are read off the setup pose this rig spec states — state its x, y and rotation`, 'CE_STOP_ANCESTOR_MANIFEST_POSITION');
|
|
365
365
|
}
|
|
366
366
|
listed.add(name);
|
|
367
367
|
chain.push(bone);
|
|
@@ -371,7 +371,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
371
371
|
const parentFrame = frames.get(parent) as BoneTransform;
|
|
372
372
|
if (!conformal(parentFrame)) {
|
|
373
373
|
throw new CompileError(
|
|
374
|
-
`${who}: "${parent}", where the frame hangs, is scaled unevenly, sheared or reflected at the setup pose — the frame's angles add to its parent's only through a rotation and an even scale, and inward is a turn's sense`,
|
|
374
|
+
`${who}: "${parent}", where the frame hangs, is scaled unevenly, sheared or reflected at the setup pose — the frame's angles add to its parent's only through a rotation and an even scale, and inward is a turn's sense`, 'CE_STOP_FRAME_PARENT_TRANSFORM',
|
|
375
375
|
);
|
|
376
376
|
}
|
|
377
377
|
for (let k = 1; k < links.length; k++) {
|
|
@@ -381,7 +381,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
381
381
|
const plain = (bone.scaleX ?? 1) === (bone.scaleY ?? 1) && (bone.scaleX ?? 1) > 0 && (bone.shearX ?? 0) === 0 && (bone.shearY ?? 0) === 0 && (bone.inherit ?? 'normal') === 'normal';
|
|
382
382
|
if (!plain) {
|
|
383
383
|
throw new CompileError(
|
|
384
|
-
`${who}: "${at}", between links[${k}] "${links[k]}" and the drawn link above it, is scaled unevenly, sheared, reflected or inherits other than normally — a lower clamp reads the link's turn in its parent, and inward is a turn's sense`,
|
|
384
|
+
`${who}: "${at}", between links[${k}] "${links[k]}" and the drawn link above it, is scaled unevenly, sheared, reflected or inherits other than normally — a lower clamp reads the link's turn in its parent, and inward is a turn's sense`, 'CE_STOP_LOWER_PARENT_TRANSFORM',
|
|
385
385
|
);
|
|
386
386
|
}
|
|
387
387
|
}
|
|
@@ -392,7 +392,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
392
392
|
const dx = target[0] - linkFrame.worldX;
|
|
393
393
|
const dy = target[1] - linkFrame.worldY;
|
|
394
394
|
if (dx === 0 && dy === 0) {
|
|
395
|
-
throw new CompileError(`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" sits on the root of "${links[0]}" at the setup pose — the frame aims from the root at the point, and there is no direction to a point it stands on
|
|
395
|
+
throw new CompileError(`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" sits on the root of "${links[0]}" at the setup pose — the frame aims from the root at the point, and there is no direction to a point it stands on`, 'CE_STOP_POINT_AT_ROOT');
|
|
396
396
|
}
|
|
397
397
|
const aim = directionDegrees(dx, dy);
|
|
398
398
|
// Each angle is brought into (-180, 180] by one whole turn at most, so a
|
|
@@ -401,7 +401,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
401
401
|
const rest = halfTurn(halfTurn(linkFrame.worldRotation) - aim);
|
|
402
402
|
if (rest === 0) {
|
|
403
403
|
throw new CompileError(
|
|
404
|
-
`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" lies on "${links[0]}"'s own line at the setup pose — inward is the turn from the link toward the point, and a point on the link's line has no side`,
|
|
404
|
+
`${who}: "at" [${stop.at.join(', ')}] on "${stop.against}" lies on "${links[0]}"'s own line at the setup pose — inward is the turn from the link toward the point, and a point on the link's line has no side`, 'CE_STOP_POINT_ON_LINK_LINE',
|
|
405
405
|
);
|
|
406
406
|
}
|
|
407
407
|
// Inward turns the link toward the aim line: counter-clockwise when the link
|
package/src/trackgen.ts
CHANGED
|
@@ -199,13 +199,13 @@ export function evaluateTrackDerive(
|
|
|
199
199
|
where: string,
|
|
200
200
|
): TrackDeriveReport {
|
|
201
201
|
if (derive === null || typeof derive !== 'object' || Array.isArray(derive)) {
|
|
202
|
-
throw new CompileError(`${where}: "derive" is ${JSON.stringify(derive)}; it is an object with a "kind"
|
|
202
|
+
throw new CompileError(`${where}: "derive" is ${JSON.stringify(derive)}; it is an object with a "kind"`, 'CE_TRACK_DERIVE_OBJECT');
|
|
203
203
|
}
|
|
204
204
|
const kind = (derive as { kind?: unknown }).kind;
|
|
205
205
|
if (typeof kind !== 'string' || !(TRACK_DERIVE_KINDS as readonly string[]).includes(kind)) {
|
|
206
206
|
throw new CompileError(
|
|
207
207
|
`${where}: "derive" has kind ${JSON.stringify(kind)}; this spec evaluates ${TRACK_DERIVE_KINDS.join(', ')}. ` +
|
|
208
|
-
'A model that is none of those is still authorable as a per-member "v" map, or as one track per member.',
|
|
208
|
+
'A model that is none of those is still authorable as a per-member "v" map, or as one track per member.', 'CE_TRACK_DERIVE_KIND',
|
|
209
209
|
);
|
|
210
210
|
}
|
|
211
211
|
const turn = derive as TrackDeriveTurn;
|
|
@@ -223,7 +223,7 @@ export function evaluateTrackDerive(
|
|
|
223
223
|
throw new CompileError(
|
|
224
224
|
`${where}: derive ${kind} states carried=${JSON.stringify(turn.carried)} on a "${property}" track, and the ` +
|
|
225
225
|
'foreshortening reads no depth difference — it is cos(α − t)/cos α, where α is the member\'s own angle off the ' +
|
|
226
|
-
`axis. "carried" belongs on the "${projections.shift}" track, whose shared shift is the thing it names.`,
|
|
226
|
+
`axis. "carried" belongs on the "${projections.shift}" track, whose shared shift is the thing it names.`, 'CE_TRACK_CARRIED_PROJECTION',
|
|
227
227
|
);
|
|
228
228
|
}
|
|
229
229
|
const carried = turn.carried === undefined ? 0 : num(turn.carried, 'carried', where);
|
|
@@ -249,7 +249,7 @@ export function evaluateTrackDerive(
|
|
|
249
249
|
`${where}: derive ${kind} projects onto "${property}" and member "${member.name}" states depth ${depth}. ` +
|
|
250
250
|
'Foreshortening is cos(α − t)/cos α with α = atan2(coordinate, depth), which needs the part to be IN FRONT ' +
|
|
251
251
|
'of the axis; at or behind it there is no front surface to narrow. A part behind the axis still takes the ' +
|
|
252
|
-
`"${projections.shift}" projection, where a negative depth is exactly what swings it the other way.`,
|
|
252
|
+
`"${projections.shift}" projection, where a negative depth is exactly what swings it the other way.`, 'CE_TRACK_FORESHORTEN_DEPTH',
|
|
253
253
|
);
|
|
254
254
|
}
|
|
255
255
|
const alpha = Math.atan2(u, depth);
|
|
@@ -260,7 +260,7 @@ export function evaluateTrackDerive(
|
|
|
260
260
|
`its own edge: α = ${round(alpha)} rad and the turn is ${round(rad)} rad, so cos(α − t) is ` +
|
|
261
261
|
`${round(Math.cos(alpha - rad))} and the scale would be ${round(scale)}. A scale at or below 0 mirrors the ` +
|
|
262
262
|
'drawing rather than narrowing it. The part has turned edge-on — the turn is past what this construction ' +
|
|
263
|
-
'carries, and FACE §8 is the page that picks a construction from the angle.',
|
|
263
|
+
'carries, and FACE §8 is the page that picks a construction from the angle.', 'CE_TRACK_FORESHORTEN_EDGE_ON',
|
|
264
264
|
);
|
|
265
265
|
}
|
|
266
266
|
values.push({ member: member.name, at: u, depth, value: round(scale) });
|
|
@@ -298,7 +298,7 @@ function refuseProperty(kind: string, property: string, where: string): never {
|
|
|
298
298
|
`${where}: derive ${kind} has no projection onto "${property}". It projects onto "${p.shift}" (the displacement, ` +
|
|
299
299
|
`FACE §3) and "${p.foreshorten}" (the narrowing, FACE §5), and onto nothing else — a turn about one axis moves ` +
|
|
300
300
|
`nothing along it, so a paired "translate"/"scale" would need a second channel this model does not state. ` +
|
|
301
|
-
'State the axis, or write the values as a per-member "v" map.',
|
|
301
|
+
'State the axis, or write the values as a per-member "v" map.', 'CE_TRACK_PROPERTY_PROJECTION',
|
|
302
302
|
);
|
|
303
303
|
}
|
|
304
304
|
|
|
@@ -322,7 +322,7 @@ function depthOf(
|
|
|
322
322
|
throw new CompileError(
|
|
323
323
|
`${where}: derive ${kind} states one depth ${depth} for ${members.length} members ` +
|
|
324
324
|
`(${members.map((m) => m.name).join(', ')}). One number for every member is the shared value a plain "v" ` +
|
|
325
|
-
'already keys — state a depth per member, as { "member": z, … }.',
|
|
325
|
+
'already keys — state a depth per member, as { "member": z, … }.', 'CE_TRACK_GROUP_SHARED_DEPTH',
|
|
326
326
|
);
|
|
327
327
|
}
|
|
328
328
|
return num(depth, 'depth', where);
|
|
@@ -330,7 +330,7 @@ function depthOf(
|
|
|
330
330
|
if (depth === null || typeof depth !== 'object' || Array.isArray(depth)) {
|
|
331
331
|
throw new CompileError(
|
|
332
332
|
`${where}: derive ${kind} has depth ${JSON.stringify(depth)}; it is a number on a bone track and ` +
|
|
333
|
-
'{ "member": z, … } on a group track',
|
|
333
|
+
'{ "member": z, … } on a group track', 'CE_TRACK_DEPTH_SHAPE',
|
|
334
334
|
);
|
|
335
335
|
}
|
|
336
336
|
const table = depth as Record<string, unknown>;
|
|
@@ -340,7 +340,7 @@ function depthOf(
|
|
|
340
340
|
if (!known.has(name)) {
|
|
341
341
|
throw new CompileError(
|
|
342
342
|
`${where}: derive ${kind} states a depth for "${name}", which this track does not key ` +
|
|
343
|
-
`(its members are: ${members.map((m) => m.name).join(', ')})`,
|
|
343
|
+
`(its members are: ${members.map((m) => m.name).join(', ')})`, 'CE_TRACK_DEPTH_MEMBER_UNKNOWN',
|
|
344
344
|
);
|
|
345
345
|
}
|
|
346
346
|
}
|
|
@@ -349,7 +349,7 @@ function depthOf(
|
|
|
349
349
|
`${where}: derive ${kind} states no depth for member "${member}" ` +
|
|
350
350
|
`(it states: ${stated.length ? stated.join(', ') : 'nothing'}). ` +
|
|
351
351
|
'A depth is refused rather than defaulted: a member silently at depth 0 would be keyed with a DIFFERENT ' +
|
|
352
|
-
'model from the ones beside it, and that is exactly the error a table of six is written to make visible.',
|
|
352
|
+
'model from the ones beside it, and that is exactly the error a table of six is written to make visible.', 'CE_TRACK_DEPTH_MEMBER_REQUIRED',
|
|
353
353
|
);
|
|
354
354
|
}
|
|
355
355
|
return num(table[member], `depth.${member}`, where);
|
|
@@ -358,7 +358,7 @@ function depthOf(
|
|
|
358
358
|
/** One required finite number, refused by field name rather than compiled as a NaN. */
|
|
359
359
|
function num(value: unknown, field: string, where: string): number {
|
|
360
360
|
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
361
|
-
throw new CompileError(`${where}: derive field "${field}" is ${JSON.stringify(value)}; it is a finite number the spec has to state
|
|
361
|
+
throw new CompileError(`${where}: derive field "${field}" is ${JSON.stringify(value)}; it is a finite number the spec has to state`, 'CE_TRACK_FIELD_NUMBER_FINITE');
|
|
362
362
|
}
|
|
363
363
|
return value;
|
|
364
364
|
}
|