rig-parts 1.69.0 → 1.70.1
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 +6 -3
- package/docs/AUTHORING.md +221 -2
- package/docs/STABILITY.md +2 -1
- package/package.json +1 -1
- package/src/config.ts +29 -1
- package/src/mesh.ts +20 -1
- package/src/rig.ts +178 -15
- package/src/waist.ts +71 -0
- package/src/waistmesh.ts +553 -0
package/README.md
CHANGED
|
@@ -408,7 +408,7 @@ These are limits of the approach, stated so nobody reads more into a green run:
|
|
|
408
408
|
by models the user runs on their GPU or a web demo. No weights ship here; the
|
|
409
409
|
optional `comfy` adapter drives the user's own ComfyUI host and models.
|
|
410
410
|
- **Legacy turns use one ordering depth per layer.** See-through also supplies per-pixel
|
|
411
|
-
sheets,
|
|
411
|
+
sheets, consumed by the shipped 2.5D head-turn producer through [config.turn.roles](docs/AUTHORING.md#3-configjson-field-by-field). A layer that is in front of another in one place and behind it in another is drawn
|
|
412
412
|
right in the still (the painting's own pixels are projected onto it) and wrong once
|
|
413
413
|
it moves.
|
|
414
414
|
- **No expression or lip-sync, and no jiggle.** The mouth is one layer; there is no
|
|
@@ -586,8 +586,11 @@ missing declared file is an error, not a fallback. The ordering median remains
|
|
|
586
586
|
separate, and its agreement reading cannot certify an image's polarity.
|
|
587
587
|
|
|
588
588
|
Assembly emits registered part sheets and references; rig emits PAD4 sheets and
|
|
589
|
-
`depth_sheets.json`.
|
|
590
|
-
|
|
589
|
+
`depth_sheets.json`. Since 1.68.0 (#777), [config.turn.roles](docs/AUTHORING.md#3-configjson-field-by-field)
|
|
590
|
+
uses depth sheets for 2.5D head turns with grid surfaces and `yaw_dial`/`pitch_dial`
|
|
591
|
+
controls; see [`CONFIG_TURN_ROLE_UNKNOWN`](docs/AUTHORING.md#CONFIG_TURN_ROLE_UNKNOWN)
|
|
592
|
+
and [`CONFIG_TURN_SURFACE_REQUIRED`](docs/AUTHORING.md#CONFIG_TURN_SURFACE_REQUIRED).
|
|
593
|
+
Authored weighted body meshes do not consume depth sheets. No-image builds keep the
|
|
591
594
|
legacy output bytes. `comfy seethrough --config <file> --depth-run <full|head>`
|
|
592
595
|
uses the same opt-in; its default captures art only. Standalone authored part
|
|
593
596
|
references remain active declarations. See [the input contract and refusals](docs/AUTHORING.md#per-layer-depth-sheets-773).
|
package/docs/AUTHORING.md
CHANGED
|
@@ -1184,6 +1184,80 @@ lattice and contour modes a mesh may declare it:
|
|
|
1184
1184
|
"bottomwear": { "grid": 25, "r": 10, "segments": [["hip", [232, 425], [601, 425]], "skirt_r", "skirt_c", "skirt_l"], "influences": { "maxInfluences": 6, "floor": "continuous" } }
|
|
1185
1185
|
```
|
|
1186
1186
|
|
|
1187
|
+
**A topwear hem over a rigid bottomwear (issue #650).** Only an explicit `waist`
|
|
1188
|
+
declaration enables this construction. Without it, even an eligible pair keeps
|
|
1189
|
+
the original bindings, rigid region, graph, motion, reports and images. A distance lattice whose
|
|
1190
|
+
`topwear` provenance declares exactly two distinct segment bones receives a waist
|
|
1191
|
+
driver only when one is the explicit region binding of a `bottomwear` part and the
|
|
1192
|
+
other is its descendant in the declared bone graph. Part and bone spellings do not
|
|
1193
|
+
select this rule. More than one eligible bottomwear partner, or multiple topwear
|
|
1194
|
+
drivers of one bottomwear region, is `RIG_WAIST_PAIR`;
|
|
1195
|
+
an explicit declaration without that qualifying pair is refused by name.
|
|
1196
|
+
The declaration selects one pair; no other pair is changed.
|
|
1197
|
+
|
|
1198
|
+
Call the region bone H and the descendant C. The existing lattice art criterion is
|
|
1199
|
+
strictly alpha above 8, after any declared stray removal. The last such pixel row
|
|
1200
|
+
is the painted hem, rather than the padded image boundary. The lattice splits its
|
|
1201
|
+
original triangles at C's image-space origin, the H-only plateau and the hem row, retaining the
|
|
1202
|
+
original diagonal partition and rebuilding
|
|
1203
|
+
the outline, triangles, UVs and bindings together. At and above C's row, distance
|
|
1204
|
+
weights stay unchanged. Below it, the H-only plateau starts one source texel before the painted hem,
|
|
1205
|
+
with `t = min(1, (y - C.y) / (plateau - C.y))`. The
|
|
1206
|
+
uncut distance share `h` becomes `h + (1 - h) * t` for H and its complement for C;
|
|
1207
|
+
the declared cap and floor are then applied once. The whole last art pixel and the
|
|
1208
|
+
padding below it bind fully to H. `RIG_WAIST_HEIGHT` names a missing hem or a C
|
|
1209
|
+
origin not strictly above the plateau; `RIG_WAIST_BINDING` names a missing member of the
|
|
1210
|
+
declared pair. Alternate drawings use their own art and posed C origin. Heat,
|
|
1211
|
+
contour, automatic, influence and meshes with other binding sets keep their rules.
|
|
1212
|
+
The original triangle's native setup position and UV field are interpolated at
|
|
1213
|
+
each new point. Bind coordinates are inverted through the runtime frame, written
|
|
1214
|
+
with the existing six decimal places, then stored by the runtime as float32;
|
|
1215
|
+
UVs retain the interpolated value and the runtime's existing page-float32 step.
|
|
1216
|
+
This is the nearest representable field under those storage rules, not a promise
|
|
1217
|
+
of exact mathematical setup coordinates or universally identical raster pixels.
|
|
1218
|
+
Setup RGBA differences must be measured on the actual input, reporting differing
|
|
1219
|
+
pixels and the maximum absolute difference per channel. The waist acceptance
|
|
1220
|
+
allows at most one per channel. Literal whole-hem support is checked separately
|
|
1221
|
+
in the actual stored UV field; nominal source rows are not that proof.
|
|
1222
|
+
The stored-space check reads native page UVs through the promised renderer entry,
|
|
1223
|
+
requires integral unscaled page/source metadata at most 2048 on each edge, and
|
|
1224
|
+
intersects every complete last-row alpha>8 pixel cell with the UV triangles.
|
|
1225
|
+
Exact coverage and absence of positive non-H support are required; duplicate
|
|
1226
|
+
coverage cannot compensate for a hole, and an empty or missing read is refused.
|
|
1227
|
+
A source-coordinate rounding envelope alone is not an acceptance result.
|
|
1228
|
+
Paired alternate drawings retain their selected base or front slots, including
|
|
1229
|
+
hidden-base-slot semantics and deform target identities.
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
Only the paired bottomwear region becomes a mesh. Its whole existing padded
|
|
1233
|
+
image is partitioned by the top's actual triangle arrangement. Inside that domain
|
|
1234
|
+
each bone receives the barycentric stored moments `w`, `w*x`, `w*y`, divided by
|
|
1235
|
+
the resulting positive weight to recover local coordinates; cap/floor is not
|
|
1236
|
+
applied again. Common vertices copy complete bindings. Outside the top domain,
|
|
1237
|
+
the supplied region rectangle's nodes retain H, and the arrangement connects the
|
|
1238
|
+
fields. The bottom's mesh report names the top driver's grid and measures the
|
|
1239
|
+
resulting geometry and art coverage. Alternate sets select their own paired
|
|
1240
|
+
field; a subset without a new bottom drawing receives a derived attachment on
|
|
1241
|
+
the existing bottom slot/image. A set hiding the top retains the rigid bottom.
|
|
1242
|
+
Explicit still pieces remain separate and retain their own binding.
|
|
1243
|
+
|
|
1244
|
+
External numeric deforms on the changed top topology are expanded in the **old
|
|
1245
|
+
per-influence** index space, including sparse runs, odd `offset`, odd lengths,
|
|
1246
|
+
`fromVertex` and null setup keys. Per-bone delta moments are transferred by the
|
|
1247
|
+
same old-to-new barycentric correspondence. `RIG_WAIST_DEFORM_RUN` names an old
|
|
1248
|
+
run outside its array; `RIG_WAIST_DEFORM_SUPPORT` refuses a nonzero moment whose
|
|
1249
|
+
bone the new field no longer contains. Such a moment cannot be carried by another
|
|
1250
|
+
bone under arbitrary motion. `RIG_WAIST_DEFORM_REBIND` refuses nonzero numeric
|
|
1251
|
+
offsets after stops/turn changed those local binding frames without a proven
|
|
1252
|
+
correspondence. A whole-attachment transform is `RIG_WAIST_DEFORM_MODEL`: evaluating
|
|
1253
|
+
a nonlinear model at new nodes is not proven equivalent to its old piecewise
|
|
1254
|
+
field. `RIG_WAIST_DEFORM_REGION` refuses an authored timeline on the original
|
|
1255
|
+
bottom region, which had no mesh deform array. Refusals occur when external
|
|
1256
|
+
animations are merged, before the build gate or output. Other attachments,
|
|
1257
|
+
including unrelated face deforms, retain their existing path and are not refused.
|
|
1258
|
+
This changes the rig's response to motion; the consumer's motion and an owner's
|
|
1259
|
+
viewing of the result remain separate checks.
|
|
1260
|
+
|
|
1187
1261
|
**When to reach for it:** a part that folds where two chains meet, or at the end of a curl.
|
|
1188
1262
|
The default does not move; a fold is today found by looking at a build that passes every
|
|
1189
1263
|
bar, and this field is what there is to turn once one is seen.
|
|
@@ -3997,6 +4071,29 @@ says why each requirement exists and what breaks without it.
|
|
|
3997
4071
|
| <a id="RIG_PNG_PRESENT"></a><a id="RIG_PNG_MATCHES_BOX"></a><a id="RIG_PART_HAS_ART"></a>`RIG_PNG_PRESENT`, `RIG_PNG_MATCHES_BOX`, `RIG_PART_HAS_ART` | a part's PNG is missing, the wrong size, or has no art pixel | re-run assemble |
|
|
3998
4072
|
| <a id="RIG_NAME_RESOLVES"></a><a id="RIG_SEGMENT_DEFINED"></a><a id="RIG_CHAIN_POINTS"></a>`RIG_NAME_RESOLVES`, `RIG_SEGMENT_DEFINED`, `RIG_CHAIN_POINTS` | a segment names an undeclared bone, a bone with no `tip` and no next link, or a chain too short to make a link | that bone's `tip`, or write the segment out as `[bone, [x0,y0], [x1,y1]]` |
|
|
3999
4073
|
| <a id="RIG_INFLUENCES_KEPT"></a>`RIG_INFLUENCES_KEPT` | a mesh's declared `influences` leaves a vertex with no bone: every capped share is under 0.03 (at or under it, for the continuous floor). The largest of k shares is at least 1/k, so this needs 34 or more kept bones; the default cap of 4 never reaches it (issue #284). The detail names the first vertex, how many more, and its largest share | declare `maxInfluences` 33 or less, or name fewer bones in `segments` |
|
|
4074
|
+
| <a id="RIG_LATTICE_STATION"></a>`RIG_LATTICE_STATION` | a requested lattice subdivision row is not finite | supply finite source-image row coordinates |
|
|
4075
|
+
| <a id="RIG_WAIST_PIVOT_SOURCE"></a>`RIG_WAIST_PIVOT_SOURCE` | paired original mesh layout/masks are absent, contact is empty/disjoint, or no bilateral lower station precedes the painted hem | retain the source layout and nonempty contiguous alpha support; do not guess a band height |
|
|
4076
|
+
| <a id="RIG_WAIST_PIVOT_NAME"></a>`RIG_WAIST_PIVOT_NAME` | the appended bone name exists, or the constraint name is empty | choose a new explicit bone and nonempty constraint name; do not replace a graph node |
|
|
4077
|
+
| <a id="RIG_WAIST_PIVOT_FRAME"></a>`RIG_WAIST_PIVOT_FRAME` | exact authored upper/lower rest frames are not unturned unit frames, or the source graph has constraints, turn or stops | retain the unconstrained original graph; authored eligibility uses exact frames, while inverse binding uses runtime frames; no epsilon or graph rewrite |
|
|
4078
|
+
| <a id="RIG_WAIST_COPY_SOURCE"></a>`RIG_WAIST_COPY_SOURCE` | supplied paired meshes/images or an existing upper source row are missing, a triangle crosses that row, or original upper support is lost | retain the stored source topology and an existing row above the annotated copy boundary; no replacement lattice or upper refinement |
|
|
4079
|
+
| <a id="RIG_WAIST_COPY_FRAME"></a>`RIG_WAIST_COPY_FRAME` | the supplied waist frame is absent, already drives the original top, has an incompatible graph/property contract or authored non-unit rest frame | supply the existing upper-parented world rotate-only frame; do not rewrite graph or motion |
|
|
4080
|
+
| <a id="RIG_WAIST_COPY_DEFORM"></a>`RIG_WAIST_COPY_DEFORM` | a targeted original top deform carries a nonzero numeric run or attachment transform whose per-bone moment cannot be transferred to the new waist frame | retain the original field or provide a proved composition; no silent deformation reinterpretation |
|
|
4081
|
+
| <a id="RIG_WAIST_COPY_COVERAGE"></a>`RIG_WAIST_COPY_COVERAGE` | a mixed-support triangle has positive source area below the copy boundary, or actual native UV/support correspondence is unavailable | retain the constrained source arrangement and native whole-area proof; inserted points alone are insufficient |
|
|
4082
|
+
| <a id="RIG_WAIST_PAIR"></a>`RIG_WAIST_PAIR` | a topwear has multiple eligible bottomwear partners, or a bottomwear has multiple topwear drivers | declare one unambiguous binding pair; no first-match selection |
|
|
4083
|
+
| <a id="RIG_WAIST_HEIGHT"></a>`RIG_WAIST_HEIGHT` | the painted hem is missing, or the upper origin does not precede its one-texel plateau | retain nonempty art and an upper origin strictly above the plateau |
|
|
4084
|
+
| <a id="RIG_WAIST_BINDING"></a>`RIG_WAIST_BINDING` | the uncut distance field does not retain both declared waist bones | declare both members of the eligible binding pair |
|
|
4085
|
+
| <a id="RIG_WAIST_GEOMETRY"></a>`RIG_WAIST_GEOMETRY` | a waist arrangement coordinate is not finite | supply finite source geometry |
|
|
4086
|
+
| <a id="RIG_WAIST_TOPOLOGY"></a>`RIG_WAIST_TOPOLOGY` | the arrangement has an invalid edge incidence, a pinched boundary or multiple boundary loops | supply one coherent closed mesh domain |
|
|
4087
|
+
| <a id="RIG_WAIST_CORRESPONDENCE"></a>`RIG_WAIST_CORRESPONDENCE` | a subdivision point lies outside the original supplied triangle domain | preserve the source triangle domain and its correspondence |
|
|
4088
|
+
| <a id="RIG_WAIST_SET_BINDING"></a>`RIG_WAIST_SET_BINDING` | a selected set no longer resolves the paired source meshes on their declared slots | preserve the selected top and bottom attachment identities |
|
|
4089
|
+
| <a id="RIG_WAIST_STORED_METADATA"></a>`RIG_WAIST_STORED_METADATA` | native atlas metadata is not integral, unscaled, size-bounded and consistent with the original drawing | read back the actual page and region metadata; require matching original dimensions and page edges at most2048 |
|
|
4090
|
+
| <a id="RIG_WAIST_STORED_UV"></a>`RIG_WAIST_STORED_UV` | native UVs, support or triangles are missing, nonfinite, not float32 or inconsistent | supply complete native float32 UVs with parallel support and valid triangles |
|
|
4091
|
+
| <a id="RIG_WAIST_STORED_EMPTY"></a>`RIG_WAIST_STORED_EMPTY` | the stored-hem drawing has no painted row | supply nonempty original art; an empty read cannot pass |
|
|
4092
|
+
| <a id="RIG_WAIST_DEFORM_REGION"></a>`RIG_WAIST_DEFORM_REGION` | an authored deform targets the original rigid bottomwear region | remove the invalid region deform; conversion cannot reinterpret it as a mesh timeline |
|
|
4093
|
+
| <a id="RIG_WAIST_DEFORM_MODEL"></a>`RIG_WAIST_DEFORM_MODEL` | an unproved whole-attachment transform targets a subdivided waist mesh | supply a representable numeric old-topology run |
|
|
4094
|
+
| <a id="RIG_WAIST_DEFORM_RUN"></a>`RIG_WAIST_DEFORM_RUN` | an old-topology deform run has invalid values, offsets or array bounds | supply a finite numeric run within the original per-influence array |
|
|
4095
|
+
| <a id="RIG_WAIST_DEFORM_REBIND"></a>`RIG_WAIST_DEFORM_REBIND` | a nonzero old local deform follows a stop or turn that changed the waist binding frames | retain an established correspondence for the rebound field before supplying nonzero local deltas |
|
|
4096
|
+
| <a id="RIG_WAIST_DEFORM_SUPPORT"></a>`RIG_WAIST_DEFORM_SUPPORT` | a nonzero old bone moment has no corresponding influence on the new vertex | preserve that bone support; another bone cannot carry its moment under arbitrary motion |
|
|
4000
4097
|
| <a id="RIG_LATTICE_ONE_LOOP"></a>`RIG_LATTICE_ONE_LOOP` | the lattice over a part does not close into one outline even after the repair passes | that mesh's `grid` |
|
|
4001
4098
|
| <a id="RIG_CONTROL_NAME_FREE"></a>`RIG_CONTROL_NAME_FREE` | a keyed, mesh-weighted bone needs `<bone>_ctl` and that name is taken | rename the declared bone |
|
|
4002
4099
|
| <a id="RIG_HEAD_UP_NAME_FREE"></a>`RIG_HEAD_UP_NAME_FREE` | `config.bones` declares `head_up`, the bone the rig stage adds under `head` to carry the face's up axis (issue #250) | rename that bone |
|
|
@@ -5464,9 +5561,131 @@ actual padded drawing `size` [width,height], and its `depth` reference. A blink
|
|
|
5464
5561
|
still split has separate lower/upper scalar slices at the same row as the art;
|
|
5465
5562
|
both keep original source provenance. Its references describe inputs for Core's
|
|
5466
5563
|
`generator.depth = {image, near, zScale}`; sources are provenance, not Core
|
|
5467
|
-
fields.
|
|
5468
|
-
|
|
5564
|
+
fields. Since 1.68.0 (#777), [config.turn.roles](#3-configjson-field-by-field) consumes
|
|
5565
|
+
depth sheets for 2.5D head turns with grid surfaces and `yaw_dial`/`pitch_dial`
|
|
5566
|
+
controls; see [`CONFIG_TURN_ROLE_UNKNOWN`](#CONFIG_TURN_ROLE_UNKNOWN) and
|
|
5567
|
+
[`CONFIG_TURN_SURFACE_REQUIRED`](#CONFIG_TURN_SURFACE_REQUIRED).
|
|
5568
|
+
Authored weighted body meshes are unchanged and do not consume depth sheets. Measurement sheets are not packed
|
|
5469
5569
|
art. An authored `parts.json` depth reference remains an active standalone
|
|
5470
5570
|
`rig` declaration even without config opt-in: its file/size/color/opacity
|
|
5471
5571
|
guards still apply. With binding inactive there is no added reference, file or
|
|
5472
5572
|
legacy-byte change. #773 adds no image generation, geometry or motion model.
|
|
5573
|
+
### Explicit source-derived waist pivot stage
|
|
5574
|
+
|
|
5575
|
+
`withWaistPivot` is a pure, explicit candidate-stage operation over an already
|
|
5576
|
+
built paired waist. It leaves the ordinary builder unchanged. The complete top
|
|
5577
|
+
attachment, original bones, limb attachments, images and motion object remain
|
|
5578
|
+
unchanged; one bone is appended under the declared upper chain. Its translation
|
|
5579
|
+
is the parent-upper frame evaluated at the source band TOP centre. This is an
|
|
5580
|
+
explicit definition, not a claim that a spatially blended top field equals that
|
|
5581
|
+
single parent frame. The candidate's native readings must report their difference.
|
|
5582
|
+
|
|
5583
|
+
The source profile uses the existing alpha-above-eight masks: one contiguous
|
|
5584
|
+
shared column run, its central bottom upper cell edge, then the first later row
|
|
5585
|
+
whose bottom silhouette expands beyond both original side supports. That is the
|
|
5586
|
+
lower station, a conservative source boundary rather than an inferred semantic
|
|
5587
|
+
colour seam. The last painted bottom row is the end of a fixed linear waist-to-
|
|
5588
|
+
lower blend. Original triangles are split at those two rows, original UVs are
|
|
5589
|
+
interpolated through their original correspondence, and old stored setup points
|
|
5590
|
+
are inverse-bound through the fixed rest frames. The whole upper band uses the
|
|
5591
|
+
waist bone; the last painted hem has only lower-frame support. No coefficient,
|
|
5592
|
+
colour classifier, image edit or animation fitting is involved.
|
|
5593
|
+
|
|
5594
|
+
The generated core transform explicitly declares `rotate` to `rotate`,
|
|
5595
|
+
`mixRotate: 1`, and every translation, scale and shear mix zero. A mix without
|
|
5596
|
+
its matching property is dead data in the native 4.3 contract. With no declared
|
|
5597
|
+
destination property, the native gate rejects the constraint as
|
|
5598
|
+
`A48_TRANSFORM_CONSTRAINT_NOT_MUTED_THROUGHOUT`: it drives no property. The
|
|
5599
|
+
planted missing-property control requires that exact refusal for its named
|
|
5600
|
+
constraint as the only failed assertion; an arbitrary exit 1 or a skipped check
|
|
5601
|
+
does not pass. It does not build successfully and then reach a no-op angle
|
|
5602
|
+
probe. The positive control still reads native rotation and translation. Both
|
|
5603
|
+
controls carry their commands, full stdout/stderr and SHA-256 hashes in their
|
|
5604
|
+
case details or errors before temporary cleanup. Native gating is mandatory
|
|
5605
|
+
before this stage can be called a compiled candidate; it does not
|
|
5606
|
+
establish visual or motion acceptance. Nonzero bottom deformation remains subject
|
|
5607
|
+
to the original region/deform refusal; the operation adds no motion adapter.
|
|
5608
|
+
|
|
5609
|
+
The refusal rows are in the waist registry in section6. Eligibility measures
|
|
5610
|
+
authored unit frames with the core exact-frame evaluator; inverse binding keeps
|
|
5611
|
+
the core runtime-frame evaluator. No tolerance is applied to authored rotations.
|
|
5612
|
+
An existing constrained, turned or stopped graph remains ineligible.
|
|
5613
|
+
|
|
5614
|
+
`withWaistSharedCopy` is a separate explicit operation on a supplied pivot
|
|
5615
|
+
output. It preserves that output's bottom attachment, bones, constraints, limb
|
|
5616
|
+
attachments, art and motion. Its copy boundary is an operational annotation:
|
|
5617
|
+
the existing shared source columns' first bottom alpha cells, connected at
|
|
5618
|
+
column centres and continued at each end across the top hull. It is not a
|
|
5619
|
+
semantic cloth detector. The upper anchor is the last existing stored UV row
|
|
5620
|
+
strictly above that boundary, including its stored rounding rather than a
|
|
5621
|
+
nominal regenerated row. The fixed smoothstep between those source boundaries
|
|
5622
|
+
transfers original per-bone weights and first moments into the existing waist
|
|
5623
|
+
frame. Each new waist moment inverse-binds the original corresponding rest
|
|
5624
|
+
point; no geometry is snapped to the band's edge.
|
|
5625
|
+
|
|
5626
|
+
The lower original triangles are constrained along the boundary segments and
|
|
5627
|
+
existing edges. Boundary-interface points are propagated only below the upper
|
|
5628
|
+
anchor; original upper vertex UV/binding records and triangle fields are exact
|
|
5629
|
+
under the hull's required index permutation. Every triangle with positive area
|
|
5630
|
+
below the copy boundary must have only waist support, including side
|
|
5631
|
+
extensions. The native control repeats this test with actual runtime source
|
|
5632
|
+
UVs using exact binary arithmetic. A planted crossing triangle demonstrates why
|
|
5633
|
+
checking just inserted vertices is insufficient. Nonzero targeted top deforms
|
|
5634
|
+
and attachment transforms are refused by name until a composition into the new
|
|
5635
|
+
per-bone support is proved; original motion is never silently rewritten. A
|
|
5636
|
+
passing gate still does not establish picture, seam or motion acceptance.
|
|
5637
|
+
|
|
5638
|
+
The rigid copy support starts conservatively above that annotated boundary by
|
|
5639
|
+
`(source height + max absolute boundary slope * source width) * 2^-23` source
|
|
5640
|
+
pixels. This is a storage guard, not an authored height or fit coefficient:
|
|
5641
|
+
rig-c's `f32` writes the shortest decimal naming the same float32 as its input,
|
|
5642
|
+
and Spine retains that decimal. A complete float32 rounding cell on normalized
|
|
5643
|
+
UVs `[0,1]` is bounded by `2^-23`. Each actual new UV is checked with exact
|
|
5644
|
+
arithmetic against that source-coordinate bound, including arithmetic before
|
|
5645
|
+
the conversion. The boundary's slope bound then keeps the represented mixed
|
|
5646
|
+
triangles above the **original** boundary. Native whole-area coverage still
|
|
5647
|
+
tests the original boundary without tolerance; its failure remains a failure.
|
|
5648
|
+
|
|
5649
|
+
### Opting into the accepted shared waist
|
|
5650
|
+
|
|
5651
|
+
```json
|
|
5652
|
+
"waist": {
|
|
5653
|
+
"top": "shirt", "bottom": "skirt",
|
|
5654
|
+
"upper": "chest", "lower": "hip",
|
|
5655
|
+
"bone": "skirt_waist", "constraint": "skirt_waist_rotation"
|
|
5656
|
+
}
|
|
5657
|
+
```
|
|
5658
|
+
|
|
5659
|
+
All six nonempty names are required. `top` names a two-bone distance lattice
|
|
5660
|
+
with topwear provenance; `bottom` names a rigid region with bottomwear
|
|
5661
|
+
provenance bound to `lower`. `upper` is the other segment bone and a descendant
|
|
5662
|
+
of `lower`. `bone` is a new generated name; `constraint` names its rotate-only
|
|
5663
|
+
constraint. Origins and the canvas come from the actual parts, including their
|
|
5664
|
+
existing padding. There are no authored offsets, gains or search parameters.
|
|
5665
|
+
`buildRig` and the `rig`/`build` commands compose the preserved pair, pivot and
|
|
5666
|
+
shared-copy stages. The pure `buildWaistPairSource` intermediate is for stage
|
|
5667
|
+
composition and controls; `buildRig` is the complete declared construction.
|
|
5668
|
+
|
|
5669
|
+
This bounded construction requires one base pair without alternate or limb-owned
|
|
5670
|
+
replacements, unit rest frames and an unconstrained, unturned, unstopped graph.
|
|
5671
|
+
Unsupported input is refused, never silently changed to a different construction.
|
|
5672
|
+
Imported original keys remain unchanged; nonzero targeted top deforms or tracks
|
|
5673
|
+
driving the generated waist frame are refused before gated emission.
|
|
5674
|
+
|
|
5675
|
+
The accepted integrated-walk artifact in
|
|
5676
|
+
[the final issue disposition](https://github.com/rig-c/parts/issues/650#issuecomment-6408838090)
|
|
5677
|
+
reported native seam ghost 0 and gap 0 over 380 samples. Its maximum raster jut
|
|
5678
|
+
was **7 metric pixels at frame 1 against the previous limit of 6**: the raw bar
|
|
5679
|
+
failed and acceptance explicitly retained that exception. The separate fixed
|
|
5680
|
+
emission audit at frame 57 counted 375 all-bracketed background pixels, including
|
|
5681
|
+
26 in the dropping half. Native seam gap 0 is not a whole-picture no-background
|
|
5682
|
+
claim. These figures belong to the preserved artifact and its recorded toolchain;
|
|
5683
|
+
they do not certify a new toolchain, input or packaging build. No tuning to meet
|
|
5684
|
+
6 pixels is part of this declaration.
|
|
5685
|
+
|
|
5686
|
+
| rule | means | change |
|
|
5687
|
+
| --- | --- | --- |
|
|
5688
|
+
| <a id="CONFIG_WAIST_BINDING"></a>`CONFIG_WAIST_BINDING` | A declaration names an absent mesh/region/bone or repeats a pair member; supply the actual distinct bound objects | supply the actual distinct bound objects |
|
|
5689
|
+
| <a id="CONFIG_WAIST_NAME_FREE"></a>`CONFIG_WAIST_NAME_FREE` | The generated bone name already exists; declare an unused name | declare an unused name |
|
|
5690
|
+
| <a id="RIG_WAIST_DECLARATION"></a>`RIG_WAIST_DECLARATION` | Actual provenance, pair bindings or drawing ownership do not match the bounded declaration; supply the declared base two-bone mesh/rigid-region pair | supply the declared base pair |
|
|
5691
|
+
| <a id="RIG_WAIST_MOTION"></a>`RIG_WAIST_MOTION` | Imported tracks drive the generated waist frame; retain their original authored targets | retain the original authored targets |
|
package/docs/STABILITY.md
CHANGED
|
@@ -118,7 +118,7 @@ The fields, object by object (a bold path is where the object sits; `<part>` is
|
|
|
118
118
|
part name, `[i]` an entry of a list):
|
|
119
119
|
|
|
120
120
|
<!-- stable:config -->
|
|
121
|
-
- **config** — required by the full loader, in front of rig and build: `key`, `assemble`, `bones`, `meshes`, `regions`, `motion`; optional: `generation`, `seethrough`, `limbs`, `constraints`, `stops`, `turn`, `scene`, `cloth`, `proposed_rig_scale`, `requirements` (issue #556, additive: a path beside the config that build reads in place of its requirements flag, whose behaviour is unchanged), `motion_seams`, `depth`
|
|
121
|
+
- **config** — required by the full loader, in front of rig and build: `key`, `assemble`, `bones`, `meshes`, `regions`, `motion`; optional: `generation`, `seethrough`, `limbs`, `constraints`, `stops`, `turn`, `scene`, `cloth`, `proposed_rig_scale`, `requirements` (issue #556, additive: a path beside the config that build reads in place of its requirements flag, whose behaviour is unchanged), `motion_seams`, `depth`, `waist`
|
|
122
122
|
- **generation** — required: `checkpoint`, `loras`, `trigger`, `identity`, `sampler`, `costume`, `negative_extra`, `style`, `negative_pose`, `latent`, `seed`; optional: `pose`, `control`
|
|
123
123
|
- **generation.loras[i]** — required: `name`, `strength`; optional: `strength_clip`
|
|
124
124
|
- **generation.sampler** — required: `steps`, `cfg`, `sampler`, `scheduler`
|
|
@@ -189,6 +189,7 @@ part name, `[i]` an entry of a list):
|
|
|
189
189
|
- **depth** — optional: `zScale`, `full`, `head` (#773)
|
|
190
190
|
- **depth.full**, **depth.head** — optional: `layers`, `atlas` (one fallback route)
|
|
191
191
|
- **depth.full.layers.<layer>**, **depth.head.layers.<layer>**, **depth.full.atlas**, **depth.head.atlas** — required: `image`, `near`, `zScale`
|
|
192
|
+
- **waist** — required: `top`, `bottom`, `upper`, `lower`, `bone`, `constraint` (#650; absent keeps the ordinary bindings and graph)
|
|
192
193
|
<!-- /stable:config -->
|
|
193
194
|
|
|
194
195
|
`TY14` holds the set of field names in backticks above to the set of names the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rig-parts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.70.1",
|
|
4
4
|
"description": "AI-authored 2D character parts from one anime painting and its See-through layer decomposition: measured parts, weighted-mesh rig specs, verified through rig-c before anything is written. A CLI for agents that cannot see the image.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/config.ts
CHANGED
|
@@ -1042,7 +1042,18 @@ export type LimbLayout = { parts?: string[] } & (
|
|
|
1042
1042
|
{ before: LimbLayoutTarget; after?: never } | { after: LimbLayoutTarget; before?: never }
|
|
1043
1043
|
);
|
|
1044
1044
|
|
|
1045
|
+
export interface WaistDeclaration {
|
|
1046
|
+
top: string;
|
|
1047
|
+
bottom: string;
|
|
1048
|
+
upper: string;
|
|
1049
|
+
lower: string;
|
|
1050
|
+
bone: string;
|
|
1051
|
+
constraint: string;
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1045
1054
|
export interface CharacterConfig {
|
|
1055
|
+
/** Explicit shared-waist construction. Absent, no pair is detected or altered. */
|
|
1056
|
+
waist?: WaistDeclaration;
|
|
1046
1057
|
depth?: DepthConfig;
|
|
1047
1058
|
key: string;
|
|
1048
1059
|
note?: string;
|
|
@@ -1404,7 +1415,7 @@ export interface AssembleConfig {
|
|
|
1404
1415
|
proposed_rig_scale?: number;
|
|
1405
1416
|
}
|
|
1406
1417
|
|
|
1407
|
-
const TOP_KEYS = ['key', 'generation', 'seethrough', 'assemble', 'limbs', 'bones', 'meshes', 'regions', 'motion', 'constraints', 'stops', 'turn', 'scene', 'cloth', 'proposed_rig_scale', 'requirements', 'motion_seams', 'depth'] as const;
|
|
1418
|
+
const TOP_KEYS = ['key', 'generation', 'seethrough', 'assemble', 'limbs', 'bones', 'meshes', 'regions', 'motion', 'constraints', 'stops', 'turn', 'scene', 'cloth', 'proposed_rig_scale', 'requirements', 'motion_seams', 'depth', 'waist'] as const;
|
|
1408
1419
|
|
|
1409
1420
|
/**
|
|
1410
1421
|
* The one partial entry point. It validates exactly what its door requires —
|
|
@@ -1436,6 +1447,7 @@ function parseEarlyConfigFrom(raw: Json, door: EarlyDoor, base: string | null):
|
|
|
1436
1447
|
if (top === null) refuseIfAny(c.problems);
|
|
1437
1448
|
const t = top as Record<string, Json>;
|
|
1438
1449
|
if ('depth' in t) checkDepthConfig(c, t.depth);
|
|
1450
|
+
if ('waist' in t) checkWaistDeclaration(c, t.waist);
|
|
1439
1451
|
if ('key' in t) c.string('config.key', t.key);
|
|
1440
1452
|
if ('generation' in t) checkGeneration(c, t.generation);
|
|
1441
1453
|
else if (needed.includes('generation')) {
|
|
@@ -1660,6 +1672,7 @@ function parseConfigFrom(raw: Json, base: string | null): { config: CharacterCon
|
|
|
1660
1672
|
if (top === null) refuseIfAny(c.problems);
|
|
1661
1673
|
const t = top as Record<string, Json>;
|
|
1662
1674
|
if ('depth' in t) checkDepthConfig(c, t.depth);
|
|
1675
|
+
if ('waist' in t) checkWaistDeclaration(c, t.waist);
|
|
1663
1676
|
if ('key' in t) c.string('config.key', t.key);
|
|
1664
1677
|
if ('generation' in t) checkGeneration(c, t.generation);
|
|
1665
1678
|
if ('seethrough' in t) checkSeeThrough(c, t.seethrough);
|
|
@@ -1676,6 +1689,11 @@ function parseConfigFrom(raw: Json, base: string | null): { config: CharacterCon
|
|
|
1676
1689
|
if ('motion' in t) checkMotion(c, t.motion, names.bones, names.chains, 'regions' in t ? t.regions : undefined);
|
|
1677
1690
|
if ('constraints' in t) checkConstraints(c, t.constraints, names.bones, names.parents);
|
|
1678
1691
|
if ('stops' in t) checkStops(c, t.stops, names.bones, names.chains);
|
|
1692
|
+
if ('waist' in t && isPlainObject(t.waist)) {
|
|
1693
|
+
for (const field of ['upper', 'lower'] as const) if (typeof t.waist[field] === 'string' && !names.bones.has(t.waist[field])) c.fail('CONFIG_WAIST_BINDING', `config.waist.${field}`, `names ${JSON.stringify(t.waist[field])}; a declared bone is required`);
|
|
1694
|
+
for (const [field, map] of [['top', t.meshes], ['bottom', t.regions]] as const) if (typeof t.waist[field] === 'string' && (!isPlainObject(map) || !Object.hasOwn(map, t.waist[field]))) c.fail('CONFIG_WAIST_BINDING', `config.waist.${field}`, `names ${JSON.stringify(t.waist[field])}; ${field === 'top' ? 'a declared mesh' : 'a declared region'} is required`);
|
|
1695
|
+
if (typeof t.waist.bone === 'string' && names.bones.has(t.waist.bone)) c.fail('CONFIG_WAIST_NAME_FREE', 'config.waist.bone', `bone ${JSON.stringify(t.waist.bone)} already exists; an unused generated name is required`);
|
|
1696
|
+
}
|
|
1679
1697
|
const attached = attachedParts(t.meshes, t.regions);
|
|
1680
1698
|
if ('motion_seams' in t) c.problems.push(...motionSeamProblems(t.motion_seams, attached, 'config.motion_seams', 'CONFIG_MOTION_SEAM'));
|
|
1681
1699
|
const hasAltFace = hasAlternateFaceSet(t.assemble, [...parts, ...cuts]);
|
|
@@ -4136,6 +4154,7 @@ export function loadClothConfig(path: string): { key: string; cloth: ClothRecipe
|
|
|
4136
4154
|
if (top === null) refuseIfAny(c.problems);
|
|
4137
4155
|
const t = top as Record<string, Json>;
|
|
4138
4156
|
if ('depth' in t) checkDepthConfig(c, t.depth);
|
|
4157
|
+
if ('waist' in t) checkWaistDeclaration(c, t.waist);
|
|
4139
4158
|
if ('key' in t) c.string('config.key', t.key);
|
|
4140
4159
|
if ('cloth' in t) checkCloth(c, t.cloth);
|
|
4141
4160
|
refuseIfAny(c.problems);
|
|
@@ -4602,3 +4621,12 @@ function checkDepthConfig(c: Check, raw: Json): void {
|
|
|
4602
4621
|
if ('atlas' in q && isPlainObject(q.layers) && Object.keys(q.layers).length > 0) c.fail('DEPTH_SOURCE_AMBIGUOUS', path, 'declares canvas and per-layer sheets together; choose one fallback explicitly');
|
|
4603
4622
|
}
|
|
4604
4623
|
}
|
|
4624
|
+
|
|
4625
|
+
/** The operation has no implicit selector, fitted position or tuning coefficient. */
|
|
4626
|
+
function checkWaistDeclaration(c: Check, raw: Json): void {
|
|
4627
|
+
const fields = ['top', 'bottom', 'upper', 'lower', 'bone', 'constraint'] as const;
|
|
4628
|
+
const o = c.object('config.waist', raw, ['top', 'bottom', 'upper', 'lower', 'bone', 'constraint'], []);
|
|
4629
|
+
if (o === null) return;
|
|
4630
|
+
for (const field of fields) if (field in o) c.string(`config.waist.${field}`, o[field]);
|
|
4631
|
+
for (const [a, b] of [['top', 'bottom'], ['upper', 'lower']] as const) if (typeof o[a] === 'string' && o[a] === o[b]) c.fail('CONFIG_WAIST_BINDING', `config.waist.${a}`, `matches ${b} ${JSON.stringify(o[b])}; two distinct names are required`);
|
|
4632
|
+
}
|
package/src/mesh.ts
CHANGED
|
@@ -336,6 +336,12 @@ export function artCoverage(mesh: Pick<LatticeMesh, 'vertices' | 'triangles'>, a
|
|
|
336
336
|
* still not one closed outline after the passes is refused rather than handed on.
|
|
337
337
|
*/
|
|
338
338
|
export function latticeMesh(part: string, art: Mask, grid: number, maxPasses: number = ONE_LOOP_PASSES): LatticeMesh | Problem {
|
|
339
|
+
return latticeMeshRows(part, art, grid, [], maxPasses);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Refine the original kept cells at supplied image rows; no new mask/bridge decision or omitted cell. */
|
|
343
|
+
export function latticeMeshRows(part: string, art: Mask, grid: number, rows: readonly number[], maxPasses: number = ONE_LOOP_PASSES): LatticeMesh | Problem {
|
|
344
|
+
if (rows.some((y) => !Number.isFinite(y))) return { code: 'RIG_LATTICE_STATION', object: `mesh "${part}"`, detail: `station rows [${rows.join(', ')}] include a non-finite coordinate; supplied finite image rows are required` };
|
|
339
345
|
const lattice = latticeCells(art, grid);
|
|
340
346
|
let any = false;
|
|
341
347
|
for (let i = 0; i < lattice.cells.data.length && !any; i++) any = lattice.cells.data[i] !== 0;
|
|
@@ -347,7 +353,20 @@ export function latticeMesh(part: string, art: Mask, grid: number, maxPasses: nu
|
|
|
347
353
|
};
|
|
348
354
|
}
|
|
349
355
|
const loop = oneLoop(lattice.cells, maxPasses);
|
|
350
|
-
|
|
356
|
+
let refined = lattice;
|
|
357
|
+
let kept = loop.cells;
|
|
358
|
+
const extra = rows.filter((y) => y > 0 && y < art.height && !lattice.ys.includes(y));
|
|
359
|
+
if (extra.length > 0) {
|
|
360
|
+
const ys = [...new Set([...lattice.ys, ...extra])].sort((a, b) => a - b);
|
|
361
|
+
kept = newMask(lattice.nx, ys.length - 1);
|
|
362
|
+
let originalRow = 0;
|
|
363
|
+
for (let j = 0; j < ys.length - 1; j++) {
|
|
364
|
+
while (originalRow + 1 < lattice.ny && ys[j] >= lattice.ys[originalRow + 1]) originalRow++;
|
|
365
|
+
kept.data.set(loop.cells.data.subarray(originalRow * lattice.nx, (originalRow + 1) * lattice.nx), j * lattice.nx);
|
|
366
|
+
}
|
|
367
|
+
refined = { ...lattice, ys, ny: ys.length - 1, cells: kept };
|
|
368
|
+
}
|
|
369
|
+
const mesh = triangulate(refined, kept, loop.passes);
|
|
351
370
|
if (mesh.loops !== 1 || mesh.pinchedVertices !== 0) {
|
|
352
371
|
return {
|
|
353
372
|
code: 'RIG_LATTICE_ONE_LOOP',
|