# Poser Library Guide

`Poser` is a Node script that converts cursor motion into VM blend weights (`p1..p6`).
`PoserHitboxHelper` is an optional Path Effect script that publishes a path-shaped hitbox to `Poser`.

## Library Integration Model

This package is intended to be used as a library.

- Consumer projects should add/use only:
  - `Poser` (Node script)
  - `PoserHitboxHelper` (Path Effect script, optional)
- Internal files under `lib/` are implementation details used by those two scripts.
- Do not wire `lib/*` scripts directly to nodes in consuming projects.

## Pose Authoring Model (Up To 6 Timelines)

You author your visual poses in timelines/state machine, and `Poser` only drives the VM numbers.

Recommended mapping:

- `p1`: center/default
- `p2`: left extreme
- `p3`: right extreme
- `p4`: top extreme
- `p5`: bottom extreme
- `p6`: click pose (optional)

### Timeline Setup

1. Create local VM numbers named `p1..p6` (or custom names and map them in `Poser` inputs).
2. Pick one shared weight range:
   - `0..100` (common), or
   - `0..1`
3. Create up to 6 additive pose timelines:
   - Timeline for center pose (driven by `p1`)
   - Timeline for left pose (driven by `p2`)
   - Timeline for right pose (driven by `p3`)
   - Timeline for top pose (driven by `p4`)
   - Timeline for bottom pose (driven by `p5`)
   - Optional click pose (driven by `p6`)
4. In each timeline, key the properties for that pose.
5. Bind each timeline weight to its VM number (`p1..p6`).

Notes:

- Poses do not need identical keyed properties to run.
- For predictable blend quality, key the same property set across all related pose timelines.
- If a channel is missing (for example no `p4` timeline), that direction influence will be limited.

## Node Script Setup (`Poser`)

Attach `Poser` to a node in the target artboard, then configure:

### VM Name Inputs

- `p1Name`, `p2Name`, `p3Name`, `p4Name`, `p5Name`, `p6Name`
- Defaults: `p1..p6`

### Weight Range Inputs

- `weightMin`, `weightMax`
- Must match the range your timeline blend inputs expect.

### Hitbox Inputs

- Fixed hitbox mode:
  - `useDynamicHitbox = false`
  - Set `hitboxMinX`, `hitboxMinY`, `hitboxMaxX`, `hitboxMaxY`
- Dynamic path hitbox mode:
  - `useDynamicHitbox = true`
  - Set `hitboxBusKey` to a shared key string.
  - Add `PoserHitboxHelper` to a path and use the same key.

### Behavior Inputs

- `exitBehavior`
  - `hold_last`
  - `return_default`
  - accepted aliases: `return-default`, `return`
- `returnDuration` (seconds)
- `returnEase` (see Ease Options)
- `enterDuration` (seconds)
- `enterEase` (see Ease Options)
- `clickSubtractsBase` (recommended `true`)
- `debugPrint`

### Ease Options (All Supported Values)

For both `returnEase` and `enterEase`:

- `cubic` (default)
- `elastic`

Any other string currently falls back to `cubic`.

## Path Hitbox Setup (`PoserHitboxHelper`)

Attach `PoserHitboxHelper` as a Path Effect to a path shape.

Inputs:

- `key`: must match `Poser.hitboxBusKey`
- `centerAroundNode`:
  - `true`: publishes path centered around node origin
  - `false`: publishes path in its local authored coordinates

Behavior:

- Uses the actual path shape (contours), not only bounding box.
- Rounded corners and curved segments are handled via adaptive `cubicTo`/`quadTo` sampling.
- Bounds are still used for cursor normalization (`nx`, `ny`) and fast reject.

## Supported Channel Counts

- `p1..p6`: full hover + click behavior.
- `p1..p5`: full hover behavior, no click channel.
- `p1..p3` or other subsets: script still runs if at least one VM property exists, but missing channels reduce directional fidelity.

## Outputs (`Poser`)

- `outNormX`, `outNormY`
- `outClickWeight`
- `outP1..outP6`

Useful for debugging or driving additional logic.

## Troubleshooting

- No movement:
  - Verify VM names match `p*Name` inputs.
  - Verify timeline blend tracks are bound to those VM numbers.
- Click does nothing:
  - Ensure `p6` timeline exists and is bound.
  - Keep `clickSubtractsBase=true` to make click visible at center.
- Dynamic hitbox not active:
  - Confirm `useDynamicHitbox=true`
  - Confirm `hitboxBusKey == PoserHitboxHelper.key`
  - If key is missing, `Poser` falls back to fixed bounds.
- Debug spam:
  - Keep `debugPrint=false` for normal use.
