Fit Row (assemble the meld attachment row from knobs)
Build the prop-attachment row from knobs instead of hand-writing JSON
- row_json
Here's a workflow problem you don't think about until you hit it: ComfyUI's graph format doesn't have a clean way to build a JSON string out of several widgets. FitProp needs a structured attachment row - which prop, which bone, how it aligns, how big it should be - and you're not going to hand-type that as one blob of JSON in a text widget and keep it in sync with a UI.
Fit Row is the plumbing answer. Twelve knobs go in, one JSON string comes out, and you wire that into FitProp's row_json input. It's a pure function with no side effects, no file I/O, and it isn't an output node - it only runs because something downstream needs it.
What the row means
The "meld attachment row" describes how a static prop (hat, sword, coat) gets seated on a rigged body. Three alignment modes are available, and the mode is inferred from the knobs you fill in rather than picked from a dropdown:
- socket - the default. The prop is bolted to a bone at an offset with a fixed rotation. No measuring. This is the row you want about 80% of the time.
- fit - the prop gets scaled to a target body dimension. You get here by setting
fit_ratio > 0or naming afit_ref_bone. - grip - for things a hand holds. Triggered by
align: "grip", and it wantsfinger_bone/thumb_boneso it can derive a blade axis.
That inference is deliberate, and so are the refusals around it. From the source: socket rows that carry fit_ratio or a fit_ref_bone are rejected as conflicting ("socket rows never measure"), fit rows without a FitRefBone or with a FitRatio of zero are rejected, and grip rows with a Position or Rotation set are rejected because "the grip law owns orientation". Grip with no FingerBone is rejected too. Nothing is silently clamped - the docstring on the numeric helpers says it outright: negative is nonsense and gets refused rather than fixed for you.
What you set
model_name(defaultprop) - which prop this row is for. Required; the gate refuses an empty model.bone- which bone carries it, e.g. a head bone for a hat. Also required, also refused if empty.align-socket/fit/grip.positionandrotation-"x,y,z"strings (default"0,0,0"). The dock conversion comes from the row'sPosition/Rotationfields.fit_ratio,fit_ref_bone,fit_ref_bone2- the fit-mode trio. A second reference bone is there for two-ended fits.seat_ratio(default 0.22, [0, 1]) - where along the fit axis the prop seats.finger_bone,thumb_bone- grip mode only.target_width- an alternative tofit_ratiofor socket rows. Setting both is a conflict the gate refuses.
Output is a single STRING named row_json. Wire it into FitProp.
Install
cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/JayDataEngineer/melite-poser-nodes.git
Restart ComfyUI, or search melite-poser-nodes in ComfyUI Manager. The pack installs py-soma-x[anny], trimesh and xatlas; Fit Row itself needs none of them - it's pure Python string and float handling, so it'll run fine even on a broken environment while you sort dependencies out.
Where people get burned
Refusals arrive as exceptions at queue time, not as validation errors. The row gate raises with a message like FitRow refused: fit needs FitRefBone (nothing to measure). Nothing goes red in the UI beforehand. Read the message - it tells you which knob to add or clear.
Empty strings aren't the same as unset for bone and model_name. Both are required by the gate, and both default to an empty string for bone. If you forget to name a bone, you get a refusal rather than a default.
Don't try to skip this node and type the JSON. You can - FitProp accepts any JSON with the right field names - but then you've hard-coded a row next to a graph that's supposed to derive it. The author is explicit that hardcoded rows are banned and there should be "ONE runtime derivation". Save yourself the debugging session when a knob changes and the JSON doesn't.
It's not an output node. Add it alone and queue: nothing happens. That's correct behaviour.
Mode inference can surprise you. Fill in fit_ratio intending "socket, but bigger" and you've switched the row to fit mode, which now demands a fit_ref_bone. If you want a fixed-size prop, use target_width on a socket row, or scale the prop mesh itself.
Inputs (12)
| Name | Type | Default | Description |
|---|---|---|---|
| model_name | STRING | prop | — |
| bone | STRING | — | |
| align | COMBO | socket | 3 options: socket, fit, grip |
| position | STRING | 0,0,0 | — |
| rotation | STRING | 0,0,0 | — |
| fit_ratio | FLOAT | 0.00 | — |
| fit_ref_bone | STRING | — | |
| fit_ref_bone2 | STRING | — | |
| seat_ratio | FLOAT | 0.220–1 | — |
| finger_bone | STRING | — | |
| thumb_bone | STRING | — | |
| target_width | FLOAT | 0.00 | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| row_json | STRING | — |