Skip to content

ossia: deduce a port's unit from the arity of its value - #200

Merged
jcelerier merged 1 commit into
mainfrom
ossia-port-unit-arity
Sep 5, 2026
Merged

jcelerier merged 1 commit into
mainfrom
ossia-port-unit-arity

Conversation

@jcelerier

Copy link
Copy Markdown
Member

The concepts behind the ossia binding's unit deduction nest: a value with
x, y, z also has x, y, and one with r, g, b, a also has r, g, b. There
was a single xy_parameter overload and a single rgb_parameter one, so every
three- and four-component port was declared to be in position.cart2D, and every
colour in color.rgba.

Each overload now excludes the wider ones, which is how the TouchDesigner binding
already chooses between appendXY / appendXYZ / appendXYZW:

value unit
.x .y cartesian_2d_u — unchanged
.x .y .z cartesian_3d_u
.x .y .z .w no unit, val_type::VEC4F
.r .g .b rgb_u
.r .g .b .a rgba_u — unchanged

xyzw deliberately gets no unit: the only four-component unit in ossia is
orientation.axis, and claiming axis-angle for a generic vec4 control would be
exactly the kind of wrong inference this fixes. The arity is what the conversion
machinery needs there.

Affected in score: TinyObj's Position / Rotation / Scale, Point Tracker 3D's
Anchor, and in the addons SolvePnP's object points. Nothing in tree declares a
three-component colour today, so the rgb_u branch is new ground.

halp_meta(unit, "...")

That declaration went through ossia::parse_dataspace, which knows only the eight
dataspace names — so "midipitch", "distance.mm", "color.rgb" and everything
else a process might actually want to name resolved to an empty unit. It now goes
through parse_pretty_unit, which accepts all of those and answers with a concrete
unit rather than a dataspace with nothing picked inside it. A name that does not
parse leaves the port alone instead of wiping the value type deduced just above.

The only in-tree user is score's Pitch To Value (unit, "midipitch"), whose outlet
consequently gains a real unit (time.midinote) where it previously had none.

The two duplicated blocks in setup_inlets / setup_outlets are folded into one
setup_value_port::setup_port<Field>(), which is also what the test drives.

Testing

Covered by a new test on the score side (Avnd_port_unit_Test, 16 assertions),
since that is where the binding is instantiated: arity for xy / xyz / xyzw and
rgb / rgba, a declared unit resolving, a declared unit winning over the shape of
the value, and a bare dataspace name giving its neutral unit. Reverting the
overload constraints fails 4 of its assertions; reverting the parse_pretty_unit
change fails 3 more.

A full score build is clean — every avnd process in the tree recompiles against
the new overloads.

Ordering

Part of a three-repo change. This one stands alone; ossia/libossia and ossia/score
carry the rest, and the score PR needs all three.

🤖 Generated with Claude Code

https://claude.ai/code/session_0197hSihNtB1bPw9WDUV3SR2

xy_value, xyz_value and xyzw_value nest - a value with x, y and z also has
x and y - so the single xy_parameter overload matched all three and every
three- or four-component port was declared to be in position.cart2D. That
hit TinyObj's Position / Rotation / Scale, Point Tracker 3D's anchor and
SolvePnP's object points, among others; nothing acted on the unit at the
time, so it went unnoticed.

Each overload now excludes the wider ones, the way the TouchDesigner
binding picks between appendXY / appendXYZ / appendXYZW: xy stays
cart2D, xyz becomes cart3D, and xyzw gets its arity but no unit - the only
four-component unit in ossia is orientation.axis, which would claim a
meaning a plain xyzw control does not have.

Same nesting for colours: rgba_value satisfies rgb_value, so a
three-component colour was declared rgba.

halp_meta(unit, "...") went through parse_dataspace, which knows only the
eight dataspace names, so "midipitch", "distance.mm", "color.rgb" and
everything else a process might actually want to name resolved to an empty
unit. parse_pretty_unit accepts all of them, and answers with a concrete
unit rather than a dataspace with nothing picked inside it. A name that
does not parse now leaves the port alone rather than wiping the value type
deduced just above it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0197hSihNtB1bPw9WDUV3SR2
@jcelerier
jcelerier force-pushed the ossia-port-unit-arity branch from 1fcfe25 to ccd5dc3 Compare September 5, 2026 10:07
@jcelerier
jcelerier merged commit e829aca into main Sep 5, 2026
5 of 23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant