Files
2026-07-22 19:38:36 +08:00

8.2 KiB

Primitive and Profile Operation Serialization

This guide covers replayable primitive/profile operations in the canonical operation graph.

All examples assume:

import json
import simplecadapi as scad

with scad.GraphSession() as session:
    ...

payload = json.loads(scad.export_model_json(session))

In exported JSON, each operation appears in payload["graph"]["nodes"] as:

{
  "node_id": "node_xxxxxxxx",
  "op": "make_line_redge",
  "params": {...},
  "inputs": [],
  "output_count": 1,
  "tags": [...],
  "display": {...},
  "param_exprs": {...},
  "context": {...}
}

display, tags, context, semantic_delta, and topo_delta are useful metadata. Replay primarily depends on op, params, and inputs.

Point

Source:

p = scad.make_point_rvertex(1.0, 2.0, 3.0)

Serialized node:

{
  "op": "make_point_rvertex",
  "params": {"x": 1.0, "y": 2.0, "z": 3.0},
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_point_rvertex(x, y, z) and returns a Vertex.

Line edge

Source:

edge = scad.make_line_redge((0, 0, 0), (5, 0, 0))

Serialized node:

{
  "op": "make_line_redge",
  "params": {"start": [0.0, 0.0, 0.0], "end": [5.0, 0.0, 0.0]},
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_line_redge(start, end) and returns an Edge.

Segment aliases

make_segment_redge(start, end) is an alias of make_line_redge(...) and records the same make_line_redge node.

make_segment_rwire(start, end) lowers to:

  1. make_line_redge
  2. make_wire_from_edges_rwire

There is no canonical make_segment_wire node in model JSON.

Circle edge, wire, and face

Source edge:

edge = scad.make_circle_redge((0, 0, 0), 2.0, normal=(0, 0, 1))

Serialized node:

{
  "op": "make_circle_redge",
  "params": {
    "center": [0.0, 0.0, 0.0],
    "radius": 2.0,
    "normal": [0.0, 0.0, 1.0]
  },
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_circle_redge(center, radius, normal).

Source wire:

wire = scad.make_circle_rwire((0, 0, 0), 2.0)

Lowered serialized graph:

make_circle_redge -> make_wire_from_edges_rwire

Source face:

face = scad.make_circle_rface((0, 0, 0), 2.0)

Lowered serialized graph:

make_circle_redge -> make_wire_from_edges_rwire -> make_face_from_wire_rface

There is no canonical make_circle_wire or make_circle_face node.

Three-point arc edge and wire

Source edge:

arc = scad.make_three_point_arc_redge(
    (0, 0, 0),
    (1, 1, 0),
    (2, 0, 0),
)

Serialized node:

{
  "op": "make_three_point_arc_redge",
  "params": {
    "start": [0.0, 0.0, 0.0],
    "middle": [1.0, 1.0, 0.0],
    "end": [2.0, 0.0, 0.0]
  },
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_three_point_arc_redge(start, middle, end).

make_three_point_arc_rwire(...) lowers to:

make_three_point_arc_redge -> make_wire_from_edges_rwire

Angle arc edge and wire

Source edge:

arc = scad.make_angle_arc_redge(
    center=(0, 0, 0),
    radius=1.0,
    start_angle=0.0,
    end_angle=1.57,
    normal=(0, 0, 1),
)

Serialized node:

{
  "op": "make_angle_arc_redge",
  "params": {
    "center": [0.0, 0.0, 0.0],
    "radius": 1.0,
    "start_angle": 0.0,
    "end_angle": 1.57,
    "normal": [0.0, 0.0, 1.0]
  },
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_angle_arc_redge(center, radius, start_angle, end_angle, normal).

make_angle_arc_rwire(...) lowers to:

make_angle_arc_redge -> make_wire_from_edges_rwire

Spline edge and wire

Source edge:

fit = scad.fit_cubic_bspline_control_points(
    [(0, 0, 0), (1, 1, 0), (2, 0, 0)],
    tolerance=0.01,
)
spline = scad.make_spline_redge(
    control_points=fit.control_points,
    knots=fit.unique_knots,
    multiplicities=fit.multiplicities,
)

Serialized node:

{
  "op": "make_spline_redge",
  "params": {
    "control_points": [[0.0, 0.0, 0.0], [0.6, 1.0, 0.0], [1.4, 1.0, 0.0], [2.0, 0.0, 0.0]],
    "degree": 3,
    "knots": [0.0, 1.0],
    "multiplicities": [4, 4],
    "weights": null,
    "periodic": false
  },
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_spline_redge(control_points=..., degree=..., knots=..., multiplicities=..., weights=..., periodic=...).

make_spline_rwire(control_points=..., ...) lowers to:

make_spline_redge -> make_wire_from_edges_rwire

make_spline_redge now stores an exact B-spline definition. It does not accept sampled/interpolated curve points directly; use fit_cubic_bspline_control_points(...) first when human/LLM-authored code starts from samples.

Helix edge and wire

Source edge:

helix = scad.make_helix_redge(
    pitch=0.7,
    height=2.2,
    radius=0.9,
    center=(0, 0, 0),
    dir=(0, 0, 1),
)

Serialized node:

{
  "op": "make_helix_redge",
  "params": {
    "pitch": 0.7,
    "height": 2.2,
    "radius": 0.9,
    "center": [0.0, 0.0, 0.0],
    "dir": [0.0, 0.0, 1.0]
  },
  "inputs": [],
  "output_count": 1
}

Replay effect: calls make_helix_redge(pitch, height, radius, center, dir).

make_helix_rwire(...) lowers to:

make_helix_redge -> make_wire_from_edges_rwire

Wire from edges

Source:

a = scad.make_line_redge((0, 0, 0), (1, 0, 0))
b = scad.make_line_redge((1, 0, 0), (1, 1, 0))
wire = scad.make_wire_from_edges_rwire([a, b])

Serialized node:

{
  "op": "make_wire_from_edges_rwire",
  "params": {"edge_count": 2},
  "inputs": ["node_for_a", "node_for_b"],
  "output_count": 1
}

Replay effect:

  1. Replay each input edge node.
  2. Collect input edge outputs in input order.
  3. Call make_wire_from_edges_rwire(edges).

The actual edge geometry is not duplicated inside this node; it is recovered through inputs.

Face from wire

Source:

face = scad.make_face_from_wire_rface(wire, normal=(0, 0, 1))

Serialized node:

{
  "op": "make_face_from_wire_rface",
  "params": {"normal": [0.0, 0.0, 1.0]},
  "inputs": ["node_for_wire"],
  "output_count": 1
}

Replay effect:

  1. Replay the input wire node.
  2. Call make_face_from_wire_rface(wire, normal=...).

Rectangle wire and face lowering

Source:

wire = scad.make_rectangle_rwire(4.0, 2.0, center=(0, 0, 0))
face = scad.make_rectangle_rface(4.0, 2.0, center=(0, 0, 0))

Lowered serialized graph:

make_rectangle_rwire:
  make_line_redge x4 -> make_wire_from_edges_rwire

make_rectangle_rface:
  make_line_redge x4 -> make_wire_from_edges_rwire -> make_face_from_wire_rface

There is no canonical make_rectangle_wire or make_rectangle_face node.

Polyline wire lowering

Source:

wire = scad.make_polyline_rwire(
    [(0, 0, 0), (1, 0, 0), (1, 1, 0)],
    closed=False,
)

Lowered serialized graph:

make_line_redge x(number_of_segments) -> make_wire_from_edges_rwire

If closed=True, one additional closing line edge is emitted.

There is no canonical make_polyline_wire node.

Box, cylinder, sphere, and cone lowering

These user-facing primitive solids are intentionally lowered to canonical profile/feature operations.

Box

Source:

box = scad.make_box_rsolid(4.0, 2.0, 1.0)

Lowered serialized graph:

make_line_redge x4
  -> make_wire_from_edges_rwire
  -> make_face_from_wire_rface
  -> make_extrude_rsolid

Replay effect: rebuilds the rectangular face, then extrudes it.

There is no canonical make_box node.

Cylinder

Source:

cyl = scad.make_cylinder_rsolid(1.0, 3.0)

Lowered serialized graph:

make_circle_redge
  -> make_wire_from_edges_rwire
  -> make_face_from_wire_rface
  -> make_extrude_rsolid

There is no canonical make_cylinder node.

Sphere

Source:

sphere = scad.make_sphere_rsolid(1.5, center=(0, 0, 0))

Lowered serialized graph:

profile edges/wire/face -> make_revolve_rsolid

There is no canonical make_sphere node.

Cone / truncated cone

Source:

cone = scad.make_cone_rsolid(1.2, 2.0, top_radius=0.4)

Lowered serialized graph:

profile edges/wire/face -> make_revolve_rsolid

There is no canonical make_cone node.