New Project

Paths & Splines#

Spline segments, named compound paths, and derived circle paths

Names: path cut_path spline circle line arc polyline

When to use#

A shaped edge or a pierced cut-out that is not a straight line or a round: a cloud lift on an apron, a serpentine rail, a vase-shaped splat, a scalloped edge, a freeform window. Use spline for a smooth curve through points you choose, line/arc/polyline for the straight and circular parts, and chain them in one cut_path (a compound path). Name a compound path once with path <Name> { ... } when several cut_paths reuse it. Use circle for a plain round hole of any size (a hole is simpler for a drilled one).

Syntax#

cut_path depth= [face=] [keep="outside"|"inside"] [path=Name] { <segment> ... }
path Name <kind> <props>            // one named segment
path Name { <segment> ... }         // a named compound path
line   from_x= from_y= to_x= to_y=
arc    center_x= center_y= radius= start_angle= end_angle=
polyline { point x= y= ; ... }
spline [closed=#true] [tolerance=<mm> | samples=<n>] { point x= y= ; ... }
circle center_x= center_y= radius=
  • spline passes through every point. tolerance= (default 0.01 length units) is the largest gap between the cut edge and the true curve; samples= instead fixes the chords per span (whole number, at most 1024). Give one, not both. The curve is flattened to chords once, the same way every time, so the result is deterministic and native/wasm identical.
  • An open spline closes along its chord, like a lone arc; in a compound path it chains end to start (each segment's end must meet the next one's start). closed=#true is a whole loop and cannot chain. A circle is a whole loop too.
  • A cut_path removes the closed region (keep="outside", the default); keep="inside" keeps only the region, making it the part's new footprint.

Example#

// A cloud lift cut into the lower edge of an apron: straight sides, a spline between them.
project name="Cloud lift" units=mm schema_version=1 {
    materials { material Oak type=hardwood }

    component Apron {
        stock Oak 190.0 22.0 90.0

        // Named once as a compound path, then cut.
        path Cloud {
            line from_x=30.0 from_y=-10.0 to_x=30.0 to_y=0.0
            spline {
                point x=30.0 y=0.0
                point x=75.0 y=34.0
                point x=115.0 y=34.0
                point x=160.0 y=0.0
            }
            line from_x=160.0 from_y=0.0 to_x=160.0 to_y=-10.0
        }
        cut_path depth=0.0 face=neg_y path=Cloud

        // A closed spline: a pierced, smooth-edged window, finer than the default.
        cut_path depth=0.0 face=neg_y {
            spline closed=#true tolerance=0.05 {
                point x=60.0 y=45.0
                point x=80.0 y=65.0
                point x=100.0 y=45.0
                point x=80.0 y=25.0
            }
        }

        // A round hole by centre and radius.
        cut_path depth=0.0 face=neg_y { circle center_x=20.0 center_y=65.0 radius=8.0 }
    }

    assembly Rail {
        part Apron instance=Apron { pos x=0.0 y=0.0 z=0.0 }
    }
}

Pitfalls and diagnostics#

  • The region must have an enclosed area and not cross itself. A wave that dips below its own chord is reported as a skip ("the closed path crosses itself"); lift the end points or close the loop yourself with a line.
  • Cut corners must lie outside the stock so the cut breaks cleanly through the edge: the cloud lift's low corners are at y=-10, below the true edge at y=0.
  • Consecutive spline points that coincide are refused. A curve of closed=#true needs at least three points, an open one at least two.
  • A spline is drawn through the points you give, so overshoot comes from the points, not the maths: space them evenly, and add a point where the curve bends sharply.
  • closed=#true combined with other segments, tolerance= together with samples=, a non-whole samples=, and a tolerance so fine that a span would need more than 1024 chords are all refused with the cause named.
  • spline and circle are path segments, not features: written outside a cut_path/path they are reported as such.
  • kdl-check passing says only that the file is well formed; it cannot see whether a path closes or is sensible. Run ww check.
  • Not built: a bent or curved part (RFC-0040), G02/G03 toolpath arcs, features placed along a path.

See also#

  • docs/KDL_AUTHORING.md §7, cut_path
  • docs/adr/0032-spline-and-compound-paths.md, docs/adr/0016-path-geometry.md
  • docs/rfc/0120-curved-path-geometry.md
  • docs/agent-reference/nodes.md (cut_path), tasks.md ("cut a curved outline")

Names#

Every name this page declares, how the example library uses it, and the shortest real use.

path#

feature, word-valued property; node in 9 of 116 examples (15 uses); property in 9 of 116 examples (19 uses).

Shortest real use, from jacobean_joint_stool.kdl:

path ArchLong {
    line from_x="span_w * 0.10" from_y=-5.0 to_x="span_w * 0.10" to_y=0.0
    spline {
        point x="span_w * 0.10" y=0.0
        point x="span_w * 0.30" y="arch_rise * 0.80"
        point x="span_w * 0.50" y=arch_rise
        point x="span_w * 0.70" y="arch_rise * 0.80"
        point x="span_w * 0.90" y=0.0
    }
    line from_x="span_w * 0.90" from_y=0.0 to_x="span_w * 0.90" to_y=-5.0
}

cut_path#

feature; node in 50 of 116 examples (239 uses).

Shortest real use, from ming_waisted_corner_leg_table.kdl:

cut_path depth=0.0 face=neg_y path=Hoof

spline#

node in 26 of 116 examples (110 uses); value in 28 of 116 examples (42 uses).

Shortest real use, from adirondack_plank_chair.kdl:

spline { point x=0.0 y=60.0; point x="arm_w * 0.12" y=18.0; point x="arm_w * 0.5" y=0.0; point x="arm_w * 0.88" y=18.0; point x=arm_w y=60.0 }

circle#

node in 40 of 116 examples (933 uses).

Shortest real use, from windsor_round_stool.kdl:

circle r=rf

line#

node in 33 of 116 examples (256 uses).

Shortest real use, from postmodern_flat_silhouette_chair.kdl:

line from_x=w from_y=0.0 to_x=w to_y=seat_h

arc#

node in 19 of 116 examples (65 uses).

Shortest real use, from windsor_fan_back_side_chair.kdl:

arc center_x=rf center_y=cy radius=rf start_angle=0.0 end_angle=180.0

polyline#

node in 21 of 116 examples (91 uses).

Shortest real use, from incised_walnut_washstand.kdl:

polyline { point x="9.0 + (0.0000)" y="9.0 + (1.0000)"; point x="face_w - 9.0 + (0.0000)" y="9.0 + (1.0000)"; point x="face_w - 9.0 - (0.0000)" y="9.0 - (1.0000)"; point x="9.0 - (0.0000)" y="9.0 - (1.0000)" }

Settings

Region

A region picks sensible defaults; change any of these separately.

Units

In inches, type lengths the way you say them: 34 1/2, 3/4", 1'-2" or 12.7mm. A length that can't be read is flagged, never rounded to something else.

Paper
Date order
Clock