Nodes/Melite Poser Nodes/Fit Row (assemble the meld attachment row from knobs)
ComfyUI Node

Fit Row (assemble the meld attachment row from knobs)

Build the prop-attachment row from knobs instead of hand-writing JSON

By JayDataEngineer·Created 2 months ago·Updated 4 days ago· 1
Fit Row (assemble the meld attachment row from knobs)
    • row_json
    ◄model_nameprop►
    ◄bone►
    ◄alignsocket►
    ◄position0,0,0►
    ◄rotation0,0,0►
    ◄fit_ratio0.00►
    ◄fit_ref_bone►
    ◄fit_ref_bone2►
    ◄seat_ratio0.22►
    ◄finger_bone►
    ◄thumb_bone►
    ◄target_width0.00►

    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 > 0 or naming a fit_ref_bone.
    • grip - for things a hand holds. Triggered by align: "grip", and it wants finger_bone/thumb_bone so 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 (default prop) - 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.
    • position and rotation - "x,y,z" strings (default "0,0,0"). The dock conversion comes from the row's Position/Rotation fields.
    • 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 to fit_ratio for 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.

    CategoryTechNoir/Mesh

    Inputs (12)

    NameTypeDefaultDescription
    model_nameSTRINGprop—
    boneSTRING—
    alignCOMBOsocket3 options: socket, fit, grip
    positionSTRING0,0,0—
    rotationSTRING0,0,0—
    fit_ratioFLOAT0.00—
    fit_ref_boneSTRING—
    fit_ref_bone2STRING—
    seat_ratioFLOAT0.220–1—
    finger_boneSTRING—
    thumb_boneSTRING—
    target_widthFLOAT0.00—

    Outputs (1)

    NameTypeDescription
    row_jsonSTRING—