Partitions: planar cuts & interlocking splits

Pure-Python port of BOSL2’s partitions.scad – slice an object with a plane, or partition a large object into two interlocking pieces for printing. The cut operators are methods on shapes3d via the partitions mixin; the 2-D cut-path generators return paths objects and the mask builders return Bosl2Solids.

Planar half-cuts

left_half / right_half / front_half / back_half / top_half / bottom_half keep one side of an axis-aligned plane, and half_of(v, cp) cuts at any plane. Each intersects the solid with a half-space mask auto-sized from the object’s own bounding box, so BOSL2’s s= mask-size argument is optional:

cuboid([40, 30, 20]).left_half()          # keep the -X half
cuboid([40, 30, 20]).bottom_half(z=5)      # cut at Z=5, keep below
sphere(radius=20).half_of([0, 1, 1])            # cut on an arbitrary plane through the centre

Passing cut_path= (a 2-D partitions()) makes the cut face follow an interlocking profile instead of a flat plane; cut_angle spins that face about the normal and offset grows the mask.

Interlocking partitions

.partition() cuts a solid into two mating pieces along a joint (jigsaw, dovetail, hammerhead, …) and spreads them apart, returning [back_piece, front_piece]:

back, front = cuboid([60, 40, 20]).partition(spread=12, cutpath="dovetail")

partitions() builds the joint profile from a list of segment descriptors, and partitions() / partitions() give the raw masks if you want to cut manually. Every partition_path output is pinned to the real BOSL2 in tests/test_pybosl2_reorient.py.

Coverage of BOSL2 partitions.scad

BOSL2 function

Status

Notes

half_of

ported

half_of() – any plane; cut_path / cut_angle / offset supported. Auto-sizes the mask; the 2-D planar form is not ported (Bosl2Solid is 3-D).

left_half / right_half / front_half / back_half / top_half / bottom_half

ported

the six axis half-cuts, as methods.

partition

ported

partition() – returns the two interlocking pieces (spread/cutsize/cutpath/gap/spin/slop).

partition_mask / partition_cut_mask

ported

partitions() / partitions().

partition_path

ported

partitions() – the full segment grammar, including the xflip/yflip/addflip/wave/Nx/WxH/skew:/pinch: modifiers and the altpath redirect.

show_frameref

not ported

a preview-only frame-reference arrow (no geometry payload).

Examples

A box split into two dovetail-jointed pieces, spread apart:

from pybosl2 import shapes3d as s3

back, front = s3.cuboid([60, 40, 20]).partition(spread=14, cutpath="dovetail")
(back | front).show()
Loading 3-D preview…

⬇ Download STL mesh

A jigsaw cut face on one half of a long bar:

from pybosl2 import partition_path, shapes3d as s3

cut = partition_path([50, "jigsaw", 50], fn=20)
s3.cuboid([100, 40, 16]).back_half(cut_path=cut).show()
Loading 3-D preview…

⬇ Download STL mesh

API reference

Planar half-cuts and interlocking partitions (jigsaw/dovetail/… joints).

pybosl2.partitions.partition_path(pathdesc, repeat=1, y=None, altpath=None, seglen=25, segwidth=25, fn=None, fa=None, fs=None)[source]

Build a 2-D interlocking cut path from a list of segment descriptors (BOSL2 partition_path()).

Each item of pathdesc is a numeric length (a flat section), a 2-D path (used as-is), or a named section pattern – "flat", "sawtooth", "square", "triangle", "halfsine", "semicircle", "sinewave", "comb", "finger", "dovetail", "hammerhead", "jigsaw" – optionally suffixed with space-separated modifiers ("3x" repeat, "30x20" resize, "xflip"/"yflip"/"addflip"/"wave", "skew:15", "pinch:33" / "pinch:20deg"). Modifiers apply left to right.

Parameters:
pathdesc : Sequence[Any]

list of segment descriptors

repeat : int

repeat the whole pathdesc this many times (default 1)

y : float | None

if given, close the path at this Y (for a polygon); its sign orients the result

altpath : Path2D | None

optional base path the pattern is redirected along

seglen : float

default length for named sections (default 25)

segwidth : float

default width for named sections (default 25)

fn : int | None

number of fragments for circle resolution.

fa : float | None

minimum fragment angle for circle resolution.

fs : float | None

minimum fragment size for circle resolution.

Returns:

A Path2D (closed when y is given).

Return type:

Path2D

Examples

A wall profile mixing jigsaw and hammerhead joints, stroked into a divider:

from pybosl2.partitions import partition_path

wall = partition_path([40, "jigsaw", 10, "jigsaw yflip", 40], fn=24)
wall.stroke(width=3).linear_extrude(height=30).show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.partitions.partition_mask(length=100, w=100, height=100, cutsize=10, cutpath='jigsaw', gap=0, cutpath_centered=True, inverse=False, slop=0.0, fn=None, fa=None, fs=None)[source]

Return a mask to remove half of an object, leaving an interlocking edge (BOSL2 partition_mask()).

Intersect it with (or subtract it from) a solid to keep the half within w of the cut plane. Pair a plain mask with an inverse=True one to split a part into two mating pieces.

Parameters:
length : float

length of the cut axis

w : float

width of the kept part, back from the cut plane

height : float

height of the part

cutsize : float | Sequence[float]

cut-pattern width (scalar, or [length, width])

cutpath : str | Path2D

named cut pattern or an explicit 2-D path

gap : float

empty gaps between pattern iterations

cutpath_centered : bool

keep the pattern centered (default True)

inverse : bool

build the mating (inverted) mask

slop : float

shrink the mask by this much for a printer-fit clearance

fn : int | None

number of fragments for circle resolution.

fa : float | None

minimum fragment angle for circle resolution.

fs : float | None

minimum fragment size for circle resolution.

Return type:

Bosl2Solid

Examples

A jigsaw-pattern mask for a 100×100×10 mm part:

from pybosl2.solid import cuboid
from pybosl2.partitions import partition_mask

(cuboid([100, 100, 10]) & partition_mask(w=50, height=10, cutpath="jigsaw", slop=0.15)).show()
Loading 3-D preview…

⬇ Download STL mesh

pybosl2.partitions.partition_cut_mask(length=100, height=100, cutsize=10, cutpath='jigsaw', gap=0, cutpath_centered=True, slop=0.1, fn=None, fa=None, fs=None)[source]

Return a thin mask to cut an object into two mating pieces (BOSL2 partition_cut_mask()).

Subtract it from a solid to split it along the cut path with a slop-wide kerf.

Examples

Split a 100×100×10 mm plate with a jigsaw cut:

from pybosl2.partitions import partition_cut_mask

mask = partition_cut_mask(height=10, cutsize=8, slop=0.3)
mask.show()
Loading 3-D preview…

⬇ Download STL mesh

Parameters:
length : float

height : float

cutsize : float | Sequence[float]

cutpath : str | Path2D

gap : float

cutpath_centered : bool

slop : float

fn : int | None

fa : float | None

fs : float | None

Return type:

Bosl2Solid

class pybosl2.partitions.Partitionable[source]

Bases: ABC

Mixin adding the partitions.scad planar cuts and the partition() split as methods.

Inherited by Bosl2Solid. A half-cut intersects the solid with a half-space mask whose size defaults to the object’s own bounding box (so BOSL2’s s= argument is optional). cut_path= follows a 2-D partition_path() to make an interlocking cut face instead of a flat plane.

half_of(v=Anchor.TOP, center=None, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the half of this solid on the side the normal v points to (BOSL2 half_of()).

center is a point on the cut plane, or a scalar distance to shift the plane along v. s (the mask size) defaults to twice the object’s bounding-box reach, so it rarely needs setting. cut_path follows a 2-D partition_path() for an interlocking cut face; cut_angle spins that face about v; offset grows the mask.

Examples

Cut a cube in half along a jigsaw pattern:

from pybosl2.solid import cuboid
from pybosl2.partitions import partition_path
from pybosl2 import UP

path = partition_path(["finger", 20, "finger"], seglen=25)
cuboid([60, 60, 20]).half_of(v=UP, cut_path=path).show()
Loading 3-D preview…

⬇ Download STL mesh

Parameters:
v : Any

center : bool | list[float] | None

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

left_half(x=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the left (-X) half, cut at X=x (BOSL2 left_half()).

Parameters:
x : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

right_half(x=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the right (+X) half, cut at X=x (BOSL2 right_half()).

Parameters:
x : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

front_half(y=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the front (-Y) half, cut at Y=y (BOSL2 front_half()).

Parameters:
y : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

back_half(y=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the back (+Y) half, cut at Y=y (BOSL2 back_half()).

Parameters:
y : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

bottom_half(z=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the bottom (-Z) half, cut at Z=z (BOSL2 bottom_half()).

Parameters:
z : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

top_half(z=0, s=None, cut_path=None, cut_angle=0, offset=0)[source]

Keep the top (+Z) half, cut at Z=z (BOSL2 top_half()).

Parameters:
z : float

s : float | None

cut_path : Path2D | None

cut_angle : float

offset : float

Return type:

Self

partition(spread=10, cutsize=10, cutpath='jigsaw', gap=0, cutpath_centered=True, spin=0, slop=0.0, fn=None, fa=None, fs=None)[source]

Cut this solid into two interlocking pieces, spread apart (BOSL2 partition()).

Returns [back_piece, front_piece] – the two halves with matched joining edges, moved spread apart along the (spun) Y axis so they print separately and snap back together. The joint follows cutpath ("jigsaw", "dovetail", "hammerhead", …); spin rotates the cut direction; slop leaves a printer-fit clearance.

Examples

Split a block into two dovetailed halves:

from pybosl2.solid import cuboid

halves = cuboid([60, 60, 20]).partition(spread=15, cutpath="dovetail", slop=0.15)
halves[0].show()
Loading 3-D preview…

⬇ Download STL mesh

Parameters:
spread : float

cutsize : float | Sequence[float]

cutpath : str | Path2D

gap : float

cutpath_centered : bool

spin : float

slop : float

fn : int | None

fa : float | None

fs : float | None

Return type:

list[Self]