ExperimentalOptional ExperimentalcursorCSS cursor to show while hovering this region. Read by the hover-cursor
pump in useGestureDispatcher via AffordanceHit.cursor, which
buildAffordanceAt fills in from the region the walk landed on.
Optional ExperimentalhitDiscriminator a press on this region reports as AffordanceHit.kind —
the string routing specs match on ('handle:top-left',
'rotate-handle', 'anchor:3'). Omit for regions that only exist
inside a consumer-registered layer, where the layer id is the
discriminator; buildAffordanceAt falls back to
<affordanceId>:<regionId>.
ExperimentalidStable id, e.g. corner-min-min. Used for debug overlays + a11y.
Optional ExperimentalpaintOptional paint. World position is derived from shape + target
transform; visual size stays in screen pixels (so handles don't
warp under zoom or non-uniform scale). Omit for hit-only regions.
square — small fixed-size square (only valid over point shapes).annulus — fill + stroke the annulus ring (only valid over
annulus shapes). Uses even-odd fill rule to punch the inner-rect
cutout.custom — emit arbitrary draw commands; receives a CustomPaintContext.ExperimentalshapeRegion geometry, expressed in the target's local frame.
point — circular hit (hitRadiusPx is screen-space).rect — axis-aligned rect (target rotation applies).annulus — outer ellipse minus inner rect cutout. Used for
invisible zones that sit around the AABB (e.g. rotate-on-
hover band). The outer ellipse is defined by world-space
semi-axes rx / ry around (cx, cy); the inner rect is the
same target-local rect that defines the AABB. Hit-test:
inside outer ellipse AND outside inner rect.Outer-ellipse center (target-local).
Inner rect (target-local) — the cutout. Typically the selection's AABB.
OptionalminBandPx?: numberMinimum band thickness outside the inner rect, in screen
pixels. The framework widens rx/ry to at least
innerHalfExtent + minBandPx / meanScale(view.scale) for both
paint and hit-test.
This exists because the clamp has to know the view and the
affordance doesn't: ChromeState carries no scale. Expressing the
floor in world units instead (which is what the rotate ring used
to do) makes the band shrink on screen as you zoom in, until the
ring around a small shape is too thin to hover.
Outer-ellipse semi-axes (target-local).
ExperimentaltargetTarget id whose state.boundsOf(targetId) defines this region's
local frame. bounds.rotation (if present) is the only transform
applied — translation is the AABB origin; scale is identity. Pass
null for affordances anchored to the viewport / world frame
(identity transform).
ExperimentalDrag binding produced when this region is hit. Lazily called so
affordances don't pay binding-construction cost on every paint frame —
state snapshots (e.g., capturing per-leaf poses at click time) belong
inside bind(), not inside regions().
One interactive region produced by an affordance. The framework owns the local↔world transform for
targetId(when non-null), soshape,paint.sizePx, andhitRadiusPxare always specified in coordinates the affordance can reason about directly.