Annotations & preview
This recipe annotates a machined bracket with dimensions and scene overlays, then renders a PNG that shows every label and primitive in one shot. It also shows how to ray-cast a pixel back to a world-space surface point and use it as a dimension anchor.
Server note: all annotation tools (add_dimension, add_scene_primitive, show_bounding_box, auto_dimension, list_annotations, remove_scene_annotation) and pick_surface_point are Swift only — they require occtmcp-server. render_preview runs on both servers.
Annotations are written to <output_dir>/annotations.json and overlaid automatically whenever render_preview is called (controlled by options.renderAnnotations, default true).
1. Add a linear dimension between two faces
Use select_topology to mint stable selectionIds for the bottom and top planar faces of the bracket, then pass them as anchors.from / anchors.to to add_dimension.
// tool call arguments — select_topology (bottom face)
{
"bodyId": "bracket",
"entity": "face",
"filter": { "surfaceType": "plane" },
"limit": 2
}
// example result
[
{ "id": "face[0]", "surfaceType": "plane", "centroid": [0.0, 0.0, 0.0] },
{ "id": "face[1]", "surfaceType": "plane", "centroid": [0.0, 0.0, 40.0] }
]
select_topology returns topology IDs (face[N]); prefix the body to form a full selectionId:
// tool call arguments — add_dimension
{
"kind": "linear",
"id": "dim-height",
"label": "H",
"anchors": {
"from": "sel:bracket#face[0]",
"to": "sel:bracket#face[1]"
}
}
// example result
{
"id": "dim-height",
"kind": "linear",
"label": "H",
"computedValue": 40.0
}
2. Drop a trihedron at the origin
Place a coordinate trihedron using add_scene_primitive to visually anchor the world origin in the render.
// tool call arguments
{
"kind": "trihedron",
"id": "origin-axes",
"params": {
"origin": [0, 0, 0],
"axisLength": 20
}
}
// example result
{ "id": "origin-axes", "kind": "trihedron" }
3. Overlay the bounding box
show_bounding_box registers a boundingBox primitive and returns the extents inline — no separate compute_metrics call needed.
// tool call arguments
{ "bodyId": "bracket", "primitiveId": "bbox-bracket" }
// example result
{
"primitiveId": "bbox-bracket",
"min": [ -25.0, -15.0, 0.0],
"max": [ 25.0, 15.0, 40.0],
"extent": [ 50.0, 30.0, 40.0],
"center": [ 0.0, 0.0, 20.0]
}
4. Auto-dimension all holes
auto_dimension runs AAG hole detection and adds a radial (or diameter) annotation to each hole’s circular rim edge in one call.
// tool call arguments
{ "bodyId": "bracket", "showDiameter": true }
// example result
{
"dimensions": [
{ "dimensionId": "auto-dim-0", "selectionId": "sel:bracket#edge[4]" },
{ "dimensionId": "auto-dim-1", "selectionId": "sel:bracket#edge[9]" }
]
}
5. Render to PNG
render_preview performs a headless Metal render. All annotations in annotations.json are composited automatically.
// tool call arguments
{
"outputPath": "<output_dir>/preview.png",
"options": {
"camera": "iso",
"displayMode": "shadedWithEdges",
"background": "light",
"width": 1200,
"height": 900
}
}
// example result
{
"outputPath": "<output_dir>/preview.png",
"width": 1200,
"height": 900
}
🖱️ Drag to orbit · scroll to zoom · auto-rotating. The static render shows until the 3D model loads. The overlays (bounding box, trihedron) appear in the PNG poster; the interactive model shows the body geometry. (Model exported via export_scene → glTF.)
6. Pick a surface point and dimension to it
pick_surface_point casts a ray through a pixel in the image produced in step 5 and returns the nearest world-space surface point as a selectionId — directly usable as a dimension anchor.
Pass the same options used in render_preview so the pixel maps to the same camera ray.
// tool call arguments
{
"screenX": 650,
"screenY": 430,
"options": { "camera": "iso", "width": 1200, "height": 900 }
}
// example result
{
"point": [12.4, 0.0, 8.7],
"bodyId": "bracket",
"selectionId": "sel:bracket#surfacePoint[0]"
}
Use the returned selectionId as an anchor in a follow-on add_dimension call — for example to measure from a face centroid to an arbitrary point on a fillet surface:
// tool call arguments
{
"kind": "linear",
"id": "dim-fillet-offset",
"anchors": {
"from": "sel:bracket#face[0]",
"to": "sel:bracket#surfacePoint[0]"
}
}
// example result
{
"id": "dim-fillet-offset",
"kind": "linear",
"computedValue": 8.7
}
7. Inspect and clean up
list_annotations reads the full sidecar:
// tool call arguments
{}
// example result
{
"dimensions": [
{ "id": "dim-height", "kind": "linear", "label": "H", "computedValue": 40.0 },
{ "id": "auto-dim-0", "kind": "radial", "computedValue": 4.0 },
{ "id": "auto-dim-1", "kind": "radial", "computedValue": 4.0 },
{ "id": "dim-fillet-offset","kind": "linear", "computedValue": 8.7 }
],
"primitives": [
{ "id": "origin-axes", "kind": "trihedron" },
{ "id": "bbox-bracket", "kind": "boundingBox" }
]
}
Remove an annotation you no longer need with remove_scene_annotation:
// tool call arguments
{ "id": "bbox-bracket" }
// example result
{ "found": true }
What next?
- Carry
selectionIds through a body mutation without re-picking → Selection & remap (Swift only) - Produce a formal ISO multi-view DXF drawing from the same body → Meshing & drawings
- Verify wall thickness around the bracket holes → Measurement & verification