partforge 0.48.0 → 0.50.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 +8 -6
- package/bin/cli.js +38 -11
- package/docs/AUTHORING-PARTS.md +102 -36
- package/package.json +1 -1
- package/src/framework/animation-controls.js +108 -48
- package/src/framework/animation.js +47 -18
- package/src/framework/app.css +89 -21
- package/src/framework/controls.js +1 -1
- package/src/framework/cutaway.js +14 -0
- package/src/framework/lint/rules-animations.js +177 -92
- package/src/framework/mount.js +36 -11
- package/src/framework/panel/info.js +10 -1
- package/src/framework/panel/render.js +39 -13
- package/src/framework/panel/widgets/checkbox.js +2 -1
- package/src/framework/panel/widgets/numeric.js +3 -1
- package/src/framework/panel/widgets/select.js +4 -2
- package/src/framework/panel/widgets/text.js +2 -1
- package/src/framework/viewer.js +155 -9
- package/src/parts/hinged-box.js +43 -27
- package/src/testing/render.js +43 -7
- package/types/index.d.ts +17 -7
- package/types/part.d.ts +38 -12
- package/types/testing.d.ts +8 -0
package/types/index.d.ts
CHANGED
|
@@ -146,8 +146,13 @@ export interface CaptureViewOptions {
|
|
|
146
146
|
export type AnimationStatus = "idle" | "intro" | "playing" | "paused";
|
|
147
147
|
|
|
148
148
|
export interface AnimationState {
|
|
149
|
-
/** The
|
|
150
|
-
|
|
149
|
+
/** The active view (tab) name — animations belong to a view. */
|
|
150
|
+
view: string;
|
|
151
|
+
/**
|
|
152
|
+
* The selected animation's key, or `null` while the active view declares no
|
|
153
|
+
* animations. Switching views re-selects that view's first animation.
|
|
154
|
+
*/
|
|
155
|
+
animation: string | null;
|
|
151
156
|
status: AnimationStatus;
|
|
152
157
|
/** Position on the timeline, 0..1 over the animation's total duration. */
|
|
153
158
|
t: number;
|
|
@@ -158,12 +163,16 @@ export interface AnimationState {
|
|
|
158
163
|
/**
|
|
159
164
|
* Part-declared animation playback — the same engine the viewer's transport bar
|
|
160
165
|
* drives. Playback writes real params, so exporting while paused exports the
|
|
161
|
-
* posed state, and any user or host param edit pauses it.
|
|
166
|
+
* posed state, and any user or host param edit pauses it. An animation's
|
|
167
|
+
* `opacity` tracks are the exception: display-only, never written to params and
|
|
168
|
+
* never visible to export.
|
|
162
169
|
*/
|
|
163
170
|
export interface AnimationRuntime {
|
|
164
171
|
/**
|
|
165
|
-
* Play, optionally switching to a named animation first.
|
|
166
|
-
*
|
|
172
|
+
* Play, optionally switching to a named animation first. The name resolves
|
|
173
|
+
* within the ACTIVE view — an animation declared by another view is not
|
|
174
|
+
* playable from here. An unknown name warns and does nothing rather than
|
|
175
|
+
* playing whatever is selected.
|
|
167
176
|
*/
|
|
168
177
|
play(name?: string): void;
|
|
169
178
|
pause(): void;
|
|
@@ -239,8 +248,9 @@ export interface PartRuntime {
|
|
|
239
248
|
*/
|
|
240
249
|
setHostPane(pane: HostPane): void;
|
|
241
250
|
/**
|
|
242
|
-
* Part-declared animation playback, or `null` when
|
|
243
|
-
* `animations` block.
|
|
251
|
+
* Part-declared animation playback, or `null` when NO view declares an
|
|
252
|
+
* `animations` block. Non-null while any view does — including while the
|
|
253
|
+
* active view has none, where `state().animation` reads `null`.
|
|
244
254
|
*/
|
|
245
255
|
animation: AnimationRuntime | null;
|
|
246
256
|
}
|
package/types/part.d.ts
CHANGED
|
@@ -308,6 +308,14 @@ export interface SubPartDefinition<P = ResolvedParams, D = Derived> {
|
|
|
308
308
|
|
|
309
309
|
export interface ViewDefinition {
|
|
310
310
|
label: string;
|
|
311
|
+
/**
|
|
312
|
+
* Named animations belonging to this view — keyframe data driving this view's
|
|
313
|
+
* params and sub-part opacity over time. See `AnimationSpec` below; the
|
|
314
|
+
* transport bar shows one view's animations at a time, and `play(name)`
|
|
315
|
+
* resolves within the active view. Animations live ONLY here: a top-level
|
|
316
|
+
* `part.animations` is a lint error and is ignored at runtime.
|
|
317
|
+
*/
|
|
318
|
+
animations?: Record<string, AnimationSpec>;
|
|
311
319
|
}
|
|
312
320
|
|
|
313
321
|
// --- the verify block -------------------------------------------------------
|
|
@@ -430,12 +438,20 @@ export interface AnimationStep {
|
|
|
430
438
|
/**
|
|
431
439
|
* Param key -> keyframes. A param tracked nowhere keeps its current value.
|
|
432
440
|
*
|
|
433
|
-
* Optional so a step can move only the camera — an
|
|
434
|
-
* holds the pose while the view swings round.
|
|
435
|
-
* that at least one step in the animation
|
|
436
|
-
* whole-animation rule the type
|
|
441
|
+
* Optional so a step can carry only `opacity`, or move only the camera — an
|
|
442
|
+
* establishing shot that holds the pose while the view swings round.
|
|
443
|
+
* `partforge lint` still requires that at least one step in the animation
|
|
444
|
+
* carries `tracks` or `opacity`, which is a whole-animation rule the type
|
|
445
|
+
* system can't express per step.
|
|
437
446
|
*/
|
|
438
447
|
tracks?: Record<string, Keyframes>;
|
|
448
|
+
/**
|
|
449
|
+
* Sub-part name → opacity keyframes (values 0–1; 0 = fully hidden, mesh and
|
|
450
|
+
* edge lines both; multiplies any static `display.opacity`). Same keyframe
|
|
451
|
+
* rules as `tracks`; the same hold rule applies across steps. Display-only:
|
|
452
|
+
* never affects params, export, measure, or verify.
|
|
453
|
+
*/
|
|
454
|
+
opacity?: Record<string, Keyframes>;
|
|
439
455
|
/** Swing the camera to this angle when the step begins. */
|
|
440
456
|
camera?: CameraCue;
|
|
441
457
|
}
|
|
@@ -460,7 +476,7 @@ export interface AnimationSpecCommon {
|
|
|
460
476
|
/**
|
|
461
477
|
* Start this animation automatically on first show and again on each view
|
|
462
478
|
* switch, until the user touches the transport. At most one animation per
|
|
463
|
-
*
|
|
479
|
+
* VIEW may set this; `partforge lint` enforces it
|
|
464
480
|
* (`animation-autoplay-invalid`). Not armed when the browser reports
|
|
465
481
|
* `prefers-reduced-motion: reduce`.
|
|
466
482
|
*/
|
|
@@ -468,10 +484,12 @@ export interface AnimationSpecCommon {
|
|
|
468
484
|
}
|
|
469
485
|
|
|
470
486
|
/**
|
|
471
|
-
* An animation is EITHER single-phase (`tracks`
|
|
472
|
-
* (`steps`) — never both, never neither. `partforge
|
|
473
|
-
* (`animation-tracks-or-steps`), and the union says the
|
|
474
|
-
* carrying both is rejected before it ever reaches lint.
|
|
487
|
+
* An animation is EITHER single-phase (`tracks` and/or `opacity`, plus a
|
|
488
|
+
* `duration`) OR stepped (`steps`) — never both, never neither. `partforge
|
|
489
|
+
* lint` enforces that (`animation-tracks-or-steps`), and the union says the
|
|
490
|
+
* same thing, so a block carrying both is rejected before it ever reaches lint.
|
|
491
|
+
* The first two arms are the two ways to satisfy "at least one of
|
|
492
|
+
* `tracks`/`opacity`".
|
|
475
493
|
*/
|
|
476
494
|
export type AnimationSpec =
|
|
477
495
|
| (AnimationSpecCommon & {
|
|
@@ -479,12 +497,22 @@ export type AnimationSpec =
|
|
|
479
497
|
duration: number;
|
|
480
498
|
/** Param key -> keyframes. */
|
|
481
499
|
tracks: Record<string, Keyframes>;
|
|
500
|
+
/** Sub-part name -> opacity keyframes (see AnimationStep.opacity). */
|
|
501
|
+
opacity?: Record<string, Keyframes>;
|
|
502
|
+
steps?: never;
|
|
503
|
+
})
|
|
504
|
+
| (AnimationSpecCommon & {
|
|
505
|
+
duration: number;
|
|
506
|
+
tracks?: Record<string, Keyframes>;
|
|
507
|
+
/** An opacity-only animation is legal — a pure fade. */
|
|
508
|
+
opacity: Record<string, Keyframes>;
|
|
482
509
|
steps?: never;
|
|
483
510
|
})
|
|
484
511
|
| (AnimationSpecCommon & {
|
|
485
512
|
/** The multi-step form; each step carries its own relative `duration`. */
|
|
486
513
|
steps: AnimationStep[];
|
|
487
514
|
tracks?: never;
|
|
515
|
+
opacity?: never;
|
|
488
516
|
duration?: never;
|
|
489
517
|
});
|
|
490
518
|
|
|
@@ -510,10 +538,8 @@ export interface PartDefinition<P = ResolvedParams, D = Derived> {
|
|
|
510
538
|
derive?: DeriveSpec<P, D>;
|
|
511
539
|
/** Named sub-parts; each builds exactly one solid. */
|
|
512
540
|
parts: Record<string, SubPartDefinition<P, D>>;
|
|
513
|
-
/** The view tabs. A view is a set of sub-parts
|
|
541
|
+
/** The view tabs. A view is a set of sub-parts, and owns its own `animations`. */
|
|
514
542
|
views: Record<string, ViewDefinition>;
|
|
515
543
|
/** Self-verification, co-located with the schema. */
|
|
516
544
|
verify?: VerifyBlock<P, D>;
|
|
517
|
-
/** Named animations: keyframe data driving existing params over time. */
|
|
518
|
-
animations?: Record<string, AnimationSpec>;
|
|
519
545
|
}
|
package/types/testing.d.ts
CHANGED
|
@@ -358,5 +358,13 @@ export function renderViews(
|
|
|
358
358
|
size?: [number, number];
|
|
359
359
|
edges?: boolean;
|
|
360
360
|
params?: ResolvedParams;
|
|
361
|
+
/** Frame suffix in the written filename (`<part>-<view>-<angle>-<tag>.png`). */
|
|
362
|
+
tag?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Per-sub-part opacity, keyed by sub-part name (an animation `evaluate()`
|
|
365
|
+
* result). `0` omits the sub-part entirely; `0 < v < 1` fades it toward the
|
|
366
|
+
* background. Absent keys render solid.
|
|
367
|
+
*/
|
|
368
|
+
opacity?: Record<string, number>;
|
|
361
369
|
},
|
|
362
370
|
): Promise<string[]>;
|