@rig-c/core 2.58.1 → 2.60.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 +2 -1
- package/docs/AUTHORING.md +145 -1
- package/package.json +1 -1
- package/src/cli/core_commands.ts +140 -5
- package/src/cli/shared.ts +60 -3
- package/src/mesh.ts +17 -0
- package/src/passthrough.ts +3 -3
- package/src/seam.ts +576 -0
- package/src/stop.ts +33 -3
package/src/stop.ts
CHANGED
|
@@ -119,6 +119,13 @@ const TWIN_FIELDS = ['length', 'x', 'y', 'rotation', 'scaleX', 'scaleY', 'shearX
|
|
|
119
119
|
/** A degree bound a clamp can reach: a local rotation read off a world copy is in (-180, 180]. */
|
|
120
120
|
const BOUND = 180;
|
|
121
121
|
|
|
122
|
+
/** What the first link's bound refusal names: the link's hang and root, and the point — world, at the setup pose. */
|
|
123
|
+
interface FirstLink {
|
|
124
|
+
hang: number;
|
|
125
|
+
root: readonly [number, number];
|
|
126
|
+
point: readonly [number, number];
|
|
127
|
+
}
|
|
128
|
+
|
|
122
129
|
/** An angle in (-540, 540) brought into (-180, 180] by at most one whole turn. */
|
|
123
130
|
function halfTurn(deg: number): number {
|
|
124
131
|
if (deg > 180) return deg - 360;
|
|
@@ -332,13 +339,36 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
332
339
|
properties: { rotate: { offset: lo, to: { rotate: { offset: lo, max: hi, scale: 1 } } } },
|
|
333
340
|
mixRotate: 1,
|
|
334
341
|
});
|
|
335
|
-
|
|
342
|
+
/**
|
|
343
|
+
* The first link's half of a bound refusal (issue #1455): where its rest
|
|
344
|
+
* angle came from, and what to move. Every figure is one the expansion
|
|
345
|
+
* already read — the link's root and hang and the point, in the exact frame's
|
|
346
|
+
* world (Spine world, y up) at the setup pose — and the bound is
|
|
347
|
+
* `|rest| + end`, since inward's sign is the opposite of rest's.
|
|
348
|
+
*/
|
|
349
|
+
const aimed = (stop: ReadStop, rest: number, end: 'inward' | 'outward', at: FirstLink): string => {
|
|
350
|
+
const e = stop[end];
|
|
351
|
+
const off = Math.abs(rest);
|
|
352
|
+
const sum = off + e;
|
|
353
|
+
const geometry =
|
|
354
|
+
`. "${stop.links[0]}" hangs at ${at.hang}° from its root at [${at.root.join(', ')}], and "${stop.names.point}" ("at" [${stop.at.join(', ')}] on "${stop.against}") ` +
|
|
355
|
+
`sits at [${at.point.join(', ')}] — Spine world, y up, at the setup pose — so the aim from that root to the point lies ${off}° off the hang (rest ${rest}°): ` +
|
|
356
|
+
`|rest| ${off}° + "${end}" ${e}° is ${sum}°`;
|
|
357
|
+
if (sum < -BOUND) return `${geometry}, below -${BOUND}°. State "${end}" at least ${-BOUND - off}°`;
|
|
358
|
+
if (e >= BOUND) return `${geometry}, above ${BOUND}°, and no point lowers it: |rest| is above 0° for every point a stop accepts. State "${end}" below ${BOUND}°`;
|
|
359
|
+
return (
|
|
360
|
+
`${geometry}, above ${BOUND}°. Move "${stop.names.point}" to within ${BOUND - e}° (${BOUND}° − "${end}") of the direction "${stop.links[0]}" hangs in from its root, ${at.hang}°, ` +
|
|
361
|
+
`or state "${end}" at most ${BOUND - off}°`
|
|
362
|
+
);
|
|
363
|
+
};
|
|
364
|
+
const bounds = (stop: ReadStop, rest: number, sign: number, range: { inward: number; outward: number }, field: string, what: string, first?: FirstLink): [number, number] => {
|
|
336
365
|
const a = rest - sign * range.outward;
|
|
337
366
|
const b = rest - sign * range.inward;
|
|
338
367
|
for (const [end, value] of [['outward', a], ['inward', b]] as const) {
|
|
339
368
|
if (Math.abs(value) > BOUND) {
|
|
369
|
+
const why = first === undefined ? '' : aimed(stop, rest, end, first);
|
|
340
370
|
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`, 'CE_STOP_LOCAL_BOUND_RANGE',
|
|
371
|
+
`${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${why}`, 'CE_STOP_LOCAL_BOUND_RANGE',
|
|
342
372
|
);
|
|
343
373
|
}
|
|
344
374
|
}
|
|
@@ -407,7 +437,7 @@ export function expandStops(spec: RigSpec, where: string, plant: StopPlant = {})
|
|
|
407
437
|
// Inward turns the link toward the aim line: counter-clockwise when the link
|
|
408
438
|
// lies clockwise of it (a negative rest angle), clockwise otherwise.
|
|
409
439
|
const sign = (rest < 0 ? 1 : -1) * (plant.swapRange ? -1 : 1);
|
|
410
|
-
const [lo, hi] = bounds(stop, rest, sign, stop, '', `"${names.drawn[0]}"
|
|
440
|
+
const [lo, hi] = bounds(stop, rest, sign, stop, '', `"${names.drawn[0]}"`, { hang: halfTurn(linkFrame.worldRotation), root: [linkFrame.worldX, linkFrame.worldY], point });
|
|
411
441
|
|
|
412
442
|
const frame: RigBone = { name: names.frame, parent };
|
|
413
443
|
if (link.x !== undefined) frame.x = link.x;
|