p5.tree/handle

Draggable 3D handles

Handles you drag with the mouse or a finger in a WEBGL sketch. Each one moves on a sphere, a plane, an axis or a dial ring, or freely in the view, and reports a point or a direction you read back as a Vector. Create one with createHandle, call Handle.update() first in draw() (it returns true while the handle is held, so a grab can win over orbitControl()), read it with Handle.value(), drive a vector or a camera with Handle.bind() and show it with Handle.draw().

Reach for createPointerRouter when several handles overlap on screen, such as the three rails of a translate gizmo: the router grabs only the nearest handle under a press and shares hover between its members.

p5

createHandle(opts)

Drag a point on a sphere, a plane, an axis or a dial ring, or freely in the view, with the mouse or a finger. Choose the constraint and whether it reports a POINT or a DIRECTION, place it with anchor, radius, axis or normal, and add snap, hover, a bound target or the onGrab, onChange and onRelease callbacks as needed (see the SPHERE, from: EYE and snap examples). Drive it from draw() with Handle.update() and read it with value(); needs a WEBGL canvas.

ParameterTypeDefaultDescription
opts { constraint: number | Object, report?: number, anchor?: p5.Vector | number[], radius?: number, axis?: p5.Vector | number[], normal?: p5.Vector | number[], zero?: p5.Vector | number[], from?: *, extent?: number[], grabPx?: number, snap?: number | number[], hover?: boolean, enabled?: boolean, bind?: p5.Vector | number[] | { get: Function, set: Function }, drawLocus?: Function, onGrab?: Function, onChange?: Function, onRelease?: Function, onCancel?: Function, }

Returns Handle|null The Handle, or null on an invalid constraint.

A SPHERE handle reporting a DIRECTION: drag it to aim the light
from: EYE, a screen-horizontal rail whatever the orbit
snap, hover and the hooks: a 25-unit grid, a lit hover, a fill that tracks the gesture

createPointerRouter(args)

Group handles that may overlap on screen, so a press grabs only the nearest one and hover is shared between them. Pass the handles, then an optional options object with hover (on by default), and drive the router from draw() with PointerRouter.update() (see the translate cluster example).

ParameterTypeDefaultDescription
args ...(Handle | { hover?: boolean }) Handles, then an optional options object last.

Returns PointerRouter

A translate cluster: three rails on one anchor, routed so exactly one grabs

Handle

update()

Turn a press on the handle into a grab and follow the pointer while it is held. Call it first in draw() every frame; it returns true while the handle is grabbed, so the orbit gate example uses it to decide whether orbitControl() runs. When the handle sits on a PointerRouter, the router's own PointerRouter.update() call covers it.

Returns boolean grabbed

The orbit gate: a press on the dot grabs, a miss orbits

cancel()

Abandon the drag in flight and put the handle back where it was when it was grabbed. Esc and a lost pointer trigger it automatically; call it yourself for rules of your own, such as the leash example. Chainable.

Returns Handle this

A leash: the drag reverts once it strays 120 units from the origin (Esc cancels too)

value([opts])

Read the handle's current value as a Vector, a position or a direction depending on what the handle reports. Pass to to read it in another space such as EYE or SCREEN (see the to: SCREEN and to: EYE examples). Pass out to write into a vector you already have instead of getting a fresh one.

ParameterTypeDefaultDescription
opts optional { to?: string | Float32Array | number[] | p5.Matrix, report?: number, out?: Float32Array | number[] | p5.Vector, mat4Eye?: *, mat4Proj?: *, mat4View?: *, mat4PV?: * }

Returns Float32Array | number[] | p5.Vector

to: SCREEN, a HUD label pinned to the handle; the default read is world
to: EYE, the same heading read in the camera's frame; out reuses a buffer

bind(target, [field])

Attach the handle to something it drives while dragged: a Vector moved in place, a Camera's eye, center or up, or your own get and set pair. The handle jumps to the target's current value right away (see the p5.Vector and camera lookat examples). Chainable.

ParameterTypeDefaultDescription
target p5.Vector | p5.Camera | number[] | { get: Function, set: Function }
field optional string Camera lookat field: 'eye' | 'center' | 'up'.

Returns Handle this

A p5.Vector mutated in place: the sphere sits at the handle's value
A camera lookat field: drag a second camera's eye, its frustum follows

sync()

Move the handle back onto its bound target after your code changed the target between drags, for example when the camera moved or a keyframe was edited (see the sync example). Does nothing when the handle is unbound. Chainable.

Returns Handle this

The bound vector moves under script control between grabs; sync() keeps the handle on it

draw([opts])

Draw the handle in the scene: the dot, the aim line from the anchor, the surface it moves on and an optional ring, in the current stroke and fill colours. Pick the parts with the HANDLE, AIM, LOCUS and RING bits and set size for the dot radius in pixels (see the bits and colours example). Chainable; needs a WEBGL canvas.

ParameterTypeDefaultDescription
opts optional { bits?: number, size?: number, marker?: null }

Returns Handle this

Bits and colours: locus and ring from the ambient stroke, the dot from the fill

scalar()

Read the handle's one-number value: the signed distance along an AXIS rail, or the angle in radians of a DIAL, which keeps counting past a full turn (see the DIAL example). NaN for the other constraints.

Returns number

A DIAL's accumulated angle turns the box; keep dragging past a full turn

azEl([out2])

Read a SPHERE handle's direction as azimuth and elevation angles in a two-element array (see the readout example). Pass out2 to write into an array you already have instead of getting a fresh one.

ParameterTypeDefaultDescription
out2 optional number[]

Returns number[] [az, el]

Azimuth and elevation of a SPHERE handle, read out

grabbed()

True while the handle is held, from the press that grabs it to the release (see the magenta example).

Returns boolean

The dot turns magenta while held

hovered()

True while the pointer is over the handle, and while the handle is held. A lone handle needs hover: true to track this (see the hover example); a handle on a PointerRouter gets it for free.

Returns boolean

hover: true on a lone handle: the dot lights up under the pointer

anchor(v)

Move the handle's reference point: the sphere centre, the plane point, the axis anchor, the dial centre, or the dragged point of a VIEW handle. The handle's own point follows, so the dot and its hit area stay together (see the orbiting object example). Chainable.

ParameterTypeDefaultDescription
v p5.Vector|number[]

Returns Handle this

The anchor follows an orbiting object; the ring rides along

dispose()

Detach the handle from the canvas: it lets go of any pointer it holds and stops reacting to the mouse or touch (see the any-key example).

Any key disposes the handle: its listeners go and the orbit is unconditional

PointerRouter

add(h)

Put a handle under the router, so presses on it are decided by the router together with the other members instead of by the handle alone (see the Z rail example). Chainable.

ParameterTypeDefaultDescription
h Handle

Returns PointerRouter this

Any key toggles the Z rail in and out of the router; out, it self-picks and updates on its own

remove(h)

Take a handle out of the router, so it decides its own presses again (see the Z rail example). Chainable.

ParameterTypeDefaultDescription
h Handle

Returns PointerRouter this

Any key toggles the Z rail in and out of the router; out, it self-picks and updates on its own

update()

Resolve the pending presses across the routed handles, so that only the nearest one grabs where they overlap, refresh hover and update every member. Call it first in draw() in place of the members' own updates; it returns true while any member is grabbed, so the cluster example uses it to decide whether orbitControl() runs.

Returns boolean true if any member is grabbed.

One gate for the cluster: three rails share an anchor, the nearest proxy wins the press

hovered()

The routed handle under the pointer right now, or null when there is none (see the shared hover example).

Returns Handle|null

Shared hover: the member under the pointer draws in yellow

dispose()

Shut the router down: every member decides its own presses again, updated in a plain loop (see the any-key example).

Any key disposes the router: the members self-pick again, updated in a plain loop