Migrating from client/client (v1) to client/api (v2)
The client/api package is the new public API for drawing to the motion-tools visualizer. It replaces the legacy client/client package, which will be removed in a future release. This guide walks through the code changes you need to move from v1 to v2 without changing what your visualizations render.
Prerequisites
Section titled “Prerequisites”Both v1 and v2 assume you run the visualizer locally with make up. No extra server-lifecycle code is required in your program.
Quick Reference
Section titled “Quick Reference”v1 (client/client) | v2 (client/api) | Change type |
|---|---|---|
DrawGeometry | DrawGeometry | Signature changed |
DrawGeometries | DrawGeometriesInFrame | Renamed + signature changed |
DrawFrameSystem | DrawFrameSystem | Signature changed |
DrawFrames | DrawFrames | Signature changed |
DrawPoints | DrawPoints | Signature changed |
DrawLine | DrawLine | Signature changed |
DrawPoses | DrawPosesAsArrows | Renamed + signature changed |
DrawPointCloud | DrawPointCloud | Signature changed |
DrawNurbs | DrawNurbs | Signature changed |
DrawGLTF | DrawGLTF | Signature changed |
DrawRobot | DrawRobot | Signature changed |
DrawWorldState | DrawWorldState | Signature changed |
SetCameraPose | SetCamera | Renamed + signature changed |
| — | ResetCamera | New |
RemoveAllSpatialObjects | RemoveAll | Renamed + returns count |
RemoveSpatialObjects | — | Removed |
| — | RemoveTransforms | New |
| — | RemoveDrawings | New |
| — | CreateRelationship | New |
| — | DeleteRelationship | New |
Record / StopRecord / Replay | Record / StopRecord / Replay | Same signatures, new recording format |
SetURL | — | Removed |
DefaultColorMap | — | Removed (see color section) |
colorutil.NamedColorToRGB | — | Removed (use draw.ColorFromName) |
shapes.Nurbs | — | Removed (fields inlined) |
Import path
Section titled “Import path”Cross-cutting changes
Section titled “Cross-cutting changes”Four changes apply to nearly every function, so it’s worth understanding them before the per-function migrations below.
1. Options structs replace positional arguments
Section titled “1. Options structs replace positional arguments”Every draw function now takes a single DrawXxxOptions struct instead of positional arguments. This makes calls self-documenting and lets new fields be added without breaking existing callers.
2. Unified color system (draw.Color)
Section titled “2. Unified color system (draw.Color)”v1 used a mix of color representations depending on the function: string names ("red"), *[3]uint8 RGB tuples, and [][3]uint8 per-element arrays. v2 standardizes on a single draw.Color type everywhere. The convenience helpers cover the common cases:
See draw Package: Color API at the bottom of this guide for the full reference, including alpha, hex, HSV, chooser, and default colors.
3. Return values: UUIDs
Section titled “3. Return values: UUIDs”v1 draw functions returned only error. v2 functions return a UUID (or slice of UUIDs for functions that draw multiple entities) identifying what they just drew. Pass the same value back in as the ID field on a later call to update that entity in place.
Functions that draw multiple entities (for example DrawFrameSystem, DrawGeometriesInFrame, DrawRobot) return [][]byte.
4. Optional entity fields (ID, Name, Parent, Attrs)
Section titled “4. Optional entity fields (ID, Name, Parent, Attrs)”Every options struct accepts these optional fields (see client/api/attrs.go):
ID— stable identifier for in-place updates via subsequent calls.Name— human-readable label shown in the treeview.Parent— parent frame name; defaults to"world"when empty.Attrs— pointer struct withShowAxesHelper *boolandInvisible *booltoggles.
Removed helpers
Section titled “Removed helpers”The following v1 helpers have no v2 equivalent and can be deleted from your code:
client.SetURL— transport is no longer user-configurable.client.DefaultColorMap— see draw Package: Color API for how to rebuild the same palette or swap indraw.ChromaticColorChooser.colorutil.NamedColorToRGB— usedraw.ColorFromNameinstead.shapes.Nurbs— passControlPoints,Knots,Weights, andDegreedirectly toDrawNurbs.client.RemoveSpatialObjects(names)— remove-by-name is not supported; see Removing objects below.
Per-function migration
Section titled “Per-function migration”DrawGeometry
Section titled “DrawGeometry”- Reuse
IDon subsequent calls to update the geometry in place. - Omit
Colorto use the default.
DrawGeometries → DrawGeometriesInFrame (renamed)
Section titled “DrawGeometries → DrawGeometriesInFrame (renamed)”- The length of
Colorsdetermines behavior:0(omitted) defaults to red,1color for all geometries,len(geometries)for per-geometry, any other length for a cycling palette. - Rendering parity: v1 silently downscaled any
pointcloud.PointCloudgeometry tominDistance=25 mmand forced a red override color. v2 does not do this automatically. To preserve v1 rendering of mixed geometry/point-cloud bundles, setDownscalingThreshold: 25on the options, and/or callapi.DrawPointCloudseparately withColors: []draw.Color{draw.ColorFromRGB(200, 0, 0)}.
DrawFrameSystem
Section titled “DrawFrameSystem”- Optional
Colors map[string]draw.Colorsets per-frame colors. Frames not in the map inherit their parent’s color, falling back to magenta if no ancestor has a color.
DrawFrames
Section titled “DrawFrames”- v2 produces one transform per frame geometry (named
frameName:geoLabel, or justframeNamewhen the geometry has no label). Frames with no geometry render as an axes helper. - Optional
Colors map[string]draw.Colorcolors frames by name. Frames not in the map default todraw.DefaultFrameColor.
DrawPoints
Section titled “DrawPoints”points []spatialmath.PosebecomesPositions []r3.Vector. Replacepose.Point()calls with the vector directly.colors [][3]uint8and the fallbackcolor *[3]uint8collapse into a singleColors []draw.Color. The length decides the semantics:0= default,1= single color for all points,len(Positions)= per-point, anything else = cycling palette.- New optional
PointSize float32(mm). - New optional
ChunkSize intandOnProgress func(draw.ChunkProgress)fields let you stream very large point sets in chunks; leave unset (the default) to send everything in one call.
DrawLine
Section titled “DrawLine”points []spatialmath.PosebecomesPositions []r3.Vector.color/dotColorscalars becomeColors/DotColorsslices whose length (0/1/len(Positions)/ other) selects default / single / per-vertex / palette behavior.- When
DotColorsis omitted butColorsis set, dots inherit theColorsvalues. To use the default dot color while customizing the line, leave both unset or setDotColorsexplicitly. - New optional
LineWidthandDotSizein millimeters.
DrawPoses → DrawPosesAsArrows (renamed)
Section titled “DrawPoses → DrawPosesAsArrows (renamed)”- Breaking change: the
arrowHeadAtPose boolparameter is gone. v2 always renders with the arrow tip at the pose (equivalent to v1’sarrowHeadAtPose=true). If your v1 code used the default (false, tail at pose, tip along the orientation vector), flip each pose’s orientation before callingDrawPosesAsArrowsto preserve the previous look. - Color semantics are length-driven:
0=draw.DefaultArrowColor,1= single,len(Poses)= per-arrow, other = palette.
DrawPointCloud
Section titled “DrawPointCloud”overrideColor *[3]uint8becomesColors []draw.Color. Length0keeps the cloud’s own color data;1overrides every point;pc.Size()is per-point; other = cycling palette.- New optional
DownscalingThreshold float64(mm). This replaces the implicitminDistance=25downscaling that v1 performed insideDrawGeometriesfor anypointcloud.PointCloudgeometry. - New optional
ChunkSize intandOnProgress func(draw.ChunkProgress)fields let you stream very large point clouds in chunks; leave unset (the default) to send everything in one call.
DrawNurbs
Section titled “DrawNurbs”- The
shapes.Nurbswrapper is no longer needed. - New optional
LineWidth float32(mm).
DrawGLTF
Section titled “DrawGLTF”Scale r3.Vectoris optional. Omit it to render at the model’s native size (equivalent tor3.Vector{X: 1, Y: 1, Z: 1}). If you do set it, all three components must be non-zero — a partial-zero value like{1, 0, 1}returns an error.
DrawRobot
Section titled “DrawRobot”- The default color palette in v2 matches v1’s
DefaultColorMapexactly, so the render is unchanged. - Optional
Colors []draw.Coloroverrides the palette. OptionalIDprefix names each drawn element.
DrawWorldState
Section titled “DrawWorldState”- Rendering parity: when
Colorsis empty, v1 cycled throughDefaultColorMap(the Matplotlib “Set1” palette) while v2 cycles throughdraw.ChromaticColorChooser(SVG named colors with perceptible hue). To keep the v1 look, pass the v1 palette explicitly:
SetCameraPose → SetCamera (renamed)
Section titled “SetCameraPose → SetCamera (renamed)”PositionandLookAtare in millimeters in both versions.
ResetCamera (new)
Section titled “ResetCamera (new)”No v1 equivalent. Resets the camera to the default pose.
CreateRelationship / DeleteRelationship (new)
Section titled “CreateRelationship / DeleteRelationship (new)”No v1 equivalent. Relationships are directed links between two entities (identified by their UUIDs from a Draw* call) that the visualizer can use to keep their state in sync — for example, a "HoverLink" that ties a label to the geometry it annotates.
Typeis a free-form string identifying the relationship kind.IndexMappingis an optional filtrex expression; leave empty to use the server default of"index".
Removing objects
Section titled “Removing objects”- All three v2 functions return
int32, the count of removed entities. RemoveTransformsclears geometries, frames, frame systems, and point clouds.RemoveDrawingsclears lines, points, arrows, NURBS, and GLTF models.RemoveSpatialObjects(names)has no direct replacement — remove-by-name is not supported. UseRemoveTransforms/RemoveDrawingsto clear a category, or prefer the v2IDpattern: keep your entity IDs stable and update them in place via subsequentDraw*calls rather than remove-then-redraw.
Recording and replay
Section titled “Recording and replay”The Record, StopRecord, and Replay signatures are unchanged:
Breaking change: the recording file format is now protobuf-framed Connect-RPC requests rather than v1’s HTTP payloads. v1 recordings will not replay under v2 and vice versa. Re-record any replay files you still need.
End-to-end example
Section titled “End-to-end example”A fully-runnable v2 script lives at docs/examples/basic/main.go. With the visualizer up, run it with:
It clears the scene, sets the camera, draws a box, a square on the ground, a ring of arrows, and two reference frames, then animates the box in place by re-drawing it with the same ID.
draw package: Color API
Section titled “draw package: Color API”All color inputs in the v2 client API use the draw.Color type from draw/color.go. If you use the draw package directly (for example, for snapshots), the same type and its constructors apply.
Creating colors
Section titled “Creating colors”The convenience helpers cover the most common cases:
For advanced use cases, the functional options constructor is available:
Migrating common v1 color inputs
Section titled “Migrating common v1 color inputs”Modifying colors
Section titled “Modifying colors”Each setter returns a new Color, so it’s safe to chain:
Default colors
Section titled “Default colors”The draw package exports default colors for each primitive type (see draw/color.go):
When you omit Color / Colors on a Draw* call, these defaults apply.
Color choosers (replacement for DefaultColorMap)
Section titled “Color choosers (replacement for DefaultColorMap)”v1 exposed client.DefaultColorMap, a []string of 9 hex values cycled through by a private helper. v2 replaces this with a reusable draw.ColorChooser type (see draw/color_chooser.go).
To get the same 9-color Matplotlib “Set1” palette v1 used, build it from hex strings:
Two preset choosers are also available:
draw.ChromaticColorChooser— SVG named colors with perceptible hue. This is the v2 default palette forDrawWorldState.draw.AchromaticColorChooser— near-greyscale SVG names (gray,silver,ivory, etc.).