Skip to content

Cuts

cuts

Cuts: material removed from a part, built in the part's frame.

Most cuts remove an axis-aligned box from a board (BoxCut): grooves, dados, rabbets, mortises. They are described by ranges along the three axes of the part frame, measured on the design box; an infinite bound means the cut is open on that side (it runs out of the board), so it also goes through any allowance there. The cut list names such cuts by their shape, whichever class made them: a Groove at the very edge of a board is listed as a rabbet.

Round cuts (holes, end chamfers and tapers of rods) are built from cylinders and cones. Cuts that need the planar sides of a board (grooves, dados, rabbets, mortises, tenons, chamfers along an arris) refuse rods; a rod can be mitred at an end, tapered flat along a long side, flattened (Flat) or cut by a Pocket.

Allowance dataclass

Allowance(side: SideRef, amount: float)

Extension of a part's blank beyond its design box at one side.

Cut

Base class of cuts. Most cuts are made into one side of a part.

removal

removal() -> Part

The removed volume in the part frame.

allowances

allowances() -> list[Allowance]

Extensions of the blank the cut needs, e.g. for a tenon.

offsets

offsets() -> tuple[Offset, ...] | None

Where the cut lies on its side, if its description says so (a hole): per axis of the side, the side measured from and the distance, or None where it is centred. Such a cut describes itself with other positions by describe(at=...), so that cuts differing only in them are listed once ("24 x hole ø5").

BoxCut

Bases: Cut

A cut removing an axis-aligned box, given by ranges in the part frame.

Some can be stopped short of sides of the part (stopped, by default not): they are then described as the cut running through, stopped.

unstopped_ranges

unstopped_ranges() -> tuple[Range, Range, Range]

The ranges as if the cut weren't stopped.

Pocket dataclass

Pocket(
    side: SideRef,
    box: tuple[Range, Range, Range],
    kind: str | None = None,
    stopped: dict[SideRef, float] | None = None,
)

Bases: BoxCut

A box cut given directly by its ranges, e.g. a socket derived by a joint.

With stopped, it stops that far short of each of the given sides of the part (see Groove).

Groove dataclass

Groove(
    side: SideRef,
    width: float,
    depth: float,
    distance: float | None = None,
    ref: SideRef | None = None,
    stopped: dict[SideRef, float] | None = None,
)

Bases: BoxCut

A groove along the grain in a face or an edge, from end to end.

It lies distance from ref, an adjacent side along the grain (by default the reference edge for faces and the reference face for edges), or in the middle when distance is None.

With stopped, a stopped groove: it stops that far short of each of the given ends, e.g. {board.right_end: 20}, so it doesn't show there (as cut with a router, its end left square).

Dado dataclass

Dado(
    side: SideRef,
    width: float,
    depth: float,
    distance: float | None = None,
    ref: SideRef | None = None,
    stopped: dict[SideRef, float] | None = None,
)

Bases: BoxCut

A dado across the grain in a face or an edge, from side to side.

It lies distance from ref, an end (by default the left end), or in the middle when distance is None.

With stopped, a stopped dado: it stops that far short of each of the given sides it runs to (edges, for a dado in a face), e.g. {board.front_edge: 15}, so it doesn't show at the front of a case.

Rabbet dataclass

Rabbet(
    side: SideRef,
    at: SideRef,
    width: float,
    depth: float,
    stopped: dict[SideRef, float] | None = None,
)

Bases: BoxCut

A rabbet into side, open to the adjacent side at, along their arris.

width is measured on side from at, depth into side. With at an end it is an end rabbet. With stopped, a stopped rabbet: it stops that far short of each of the given sides at the ends of the arris, e.g. a rabbet for the back of a case stopped short of the top end.

EndRabbet dataclass

EndRabbet(
    side: SideRef,
    at: SideRef,
    width: float,
    depth: float,
    stopped: dict[SideRef, float] | None = None,
)

Bases: Rabbet

A rabbet at an end of a board, across the grain.

SlotMortise dataclass

SlotMortise(
    side: SideRef,
    width: float,
    depth: float,
    distance: float | None = None,
    ref: SideRef | None = None,
)

Bases: BoxCut

A slot into an end, open at the end and on two opposite sides.

The slot lies distance from ref (by default the reference face, so the slot goes through the width), or in the middle when distance is None.

Mortise dataclass

Mortise(
    side: SideRef,
    start: float,
    length: float,
    offset: float,
    width: float,
    depth: float,
)

Bases: BoxCut

A rectangular pocket into a side, in the side's plane coordinates.

On a mirrored board it is mirrored: start is from its (named) left end.

Tenon dataclass

Tenon(
    side: SideRef,
    length: float,
    thickness: float,
    width: float | None = None,
    width_offset: float | None = None,
    thickness_offset: float | None = None,
    haunch: SideRef | None = None,
    haunch_length: float | None = None,
    mitre: SideRef | None = None,
    mitre_angle: float = 45.0,
    mitre_depth: float = 0.0,
    mitred: tuple[SideRef, ...] = (),
    chamfer: float = 0.0,
    corner_chamfer: float = 0.0,
    count: int = 1,
    gap: float = 0.0,
    thickness_count: int = 1,
    thickness_gap: float = 0.0,
)

Bases: Cut

Shoulders and cheeks cut beyond a side (usually an end), leaving a tenon.

The side is the shoulder plane; the material beyond it comes from an allowance the tenon asks for. Across the thickness the tenon lies thickness_offset below the top face; along the side (across the width for an end, along the length for an edge) width_offset from the front edge or the left end. Either is centered when None. Without width the tenon spans the whole side, with cheeks but no shoulders.

With haunch, an edge (or an end) next to the side, the tenon has a haunch: a stub haunch_length long and as thick as the tenon, filling the shoulder towards that side, e.g. to fill a groove in the mortised part.

With mitre, a face of the part, the end of the tenon is mitred at mitre_angle across its thickness: the cheek towards that face is shorter, the other one keeps the full length. Two tenons meeting inside a leg from adjacent sides are mitred like this so they don't clash.

With mitre_depth, the shoulders are mitred: the part reaches that far beyond the side with its whole cross-section, into a notch in the mortised part, and the tenon starts there, length long. At each of mitred, edges next to the side, the reach is mitred at 45 degrees back to the side, so the parts meet on a short mitre at that edge. A tenon wider than the space between the mitres reaches under them: there the mitre is cut only down to its cheeks, the tenon starts at the side.

With chamfer, the four arrises around the end of the tenon are chamfered at 45 degrees that wide, e.g. on a through tenon standing proud of the mortised part. With corner_chamfer, only the two arrises at its edges (across its width) are chamfered at 45 degrees, that far each way, e.g. big chamfers taking the sharp corners off a long tenon standing proud of a workbench's leg.

With count, the tenon is split into that many tenons side by side along the side, gap apart, e.g. twin tenons on a wide rail; width and width_offset are then of all of them together. Between them the part is cut back to the shoulder. With thickness_count, there are that many tenons one beside the other across the thickness, thickness_gap apart, e.g. on a thick rail: thickness is then of each of them, thickness_offset to the top one. The two combine into a grid. The haunch is then as thick as each tenon, beside each of them; a mitre runs across all of them.

total_thickness property

total_thickness: float

Of all the tenons across the thickness together, with the gaps.

single_width property

single_width: float

The width of each of multiple tenons (of the tenon, if there's one).

removes_material property

removes_material: bool

False for a tenon as thick and wide as the part, e.g. a thin rail in a slot.

tenon_box

tenon_box() -> tuple[Range, Range, Range]

Ranges of the tenon itself in the part frame.

layers

layers() -> list[Range]

The z ranges of the tenons across the thickness, from the top one.

tenon_boxes

tenon_boxes() -> list[tuple[Range, Range, Range]]

Ranges of each of the tenons in the part frame: side by side along the side, for each across the thickness, from the top one.

haunch_boxes

haunch_boxes() -> list[tuple[Range, Range, Range]]

Ranges of the haunch in the part frame, from the tenon to the haunch side; one beside each tenon across the thickness.

boxes

boxes() -> list[tuple[Range, Range, Range]]

The tenons and the haunch, if any.

tenon

tenon() -> Part

The tenons themselves (with the haunch), in the part frame.

reach_solid

reach_solid() -> Part

What the part reaches beyond the side before the tenon (with mitred shoulders), in the part frame, open behind the side and at the edges that aren't mitred, so that it doesn't leave slivers.

cheeks

cheeks() -> int

How many cheeks are cut: 0, 1 or 2.

WedgedTenon dataclass

WedgedTenon(
    side: SideRef,
    length: float,
    thickness: float,
    width: float | None = None,
    width_offset: float | None = None,
    thickness_offset: float | None = None,
    haunch: SideRef | None = None,
    haunch_length: float | None = None,
    mitre: SideRef | None = None,
    mitre_angle: float = 45.0,
    mitre_depth: float = 0.0,
    mitred: tuple[SideRef, ...] = (),
    chamfer: float = 0.0,
    corner_chamfer: float = 0.0,
    count: int = 1,
    gap: float = 0.0,
    thickness_count: int = 1,
    thickness_gap: float = 0.0,
    kerfs: int = 2,
    kerf: float = 2.0,
    kerf_inset: float | None = None,
    kerf_depth: float = 0.0,
    flare: float = 0.0,
    proud: float = 0.0,
)

Bases: Tenon

A through tenon with saw kerfs across its thickness, spread by wedges.

Each tenon (see count and thickness_count) has one kerf in its middle or two, kerf_inset in from its edges (by default a fifth of its width), kerf wide and kerf_depth deep from its end. It is modelled as glued up: the wedges driven into the kerfs have spread the tenon, so each end of it leans out by flare where it leaves the mortised part, proud before its end; the mortise is flared to match. The kerfs are the wedges' shapes, straight at the stationary side of the kerf, sloped by flare at the spread side (at both with one kerf). The cut list gives the tenon as cut, straight with straight kerfs.

flare_solid

flare_solid() -> Part

What the tenon spreads into beyond its straight cheeks (and the mortise is flared by).

wedge_locations

wedge_locations() -> list[Location]

Of the wedges in the kerfs, in the part frame, in the order of the kerfs.

A wedge is a board with its thick end at its left end, at the end of the tenon, its front edge at the stationary side of the kerf (for one kerf, at the side towards the tenon's front edge or left end) and as thick as the tenon. With two kerfs it is tapered on its back edge, with one on both edges. With tenons across the thickness, the kerfs of the top one come first.

Tongue dataclass

Tongue(
    side: SideRef,
    length: float,
    thickness: float,
    width: float | None = None,
    width_offset: float | None = None,
    thickness_offset: float | None = None,
    haunch: SideRef | None = None,
    haunch_length: float | None = None,
    mitre: SideRef | None = None,
    mitre_angle: float = 45.0,
    mitre_depth: float = 0.0,
    mitred: tuple[SideRef, ...] = (),
    chamfer: float = 0.0,
    corner_chamfer: float = 0.0,
    count: int = 1,
    gap: float = 0.0,
    thickness_count: int = 1,
    thickness_gap: float = 0.0,
)

Bases: Tenon

A tongue along an edge (or across an end): a tenon over the whole side, without shoulders.

With a cheek only on one face (thickness_offset 0, or the tongue at the bottom face) it is listed as the rabbet that leaves it, e.g. the edges of a drawer bottom thinned to fit their grooves.

DovetailTongue dataclass

DovetailTongue(
    side: SideRef,
    length: float,
    thickness: float,
    slope: float = 7.0,
    thickness_offset: float | None = None,
    half: bool = False,
)

Bases: Cut

A dovetail-shaped tongue beyond a side, for a sliding dovetail.

The tongue runs along the whole side. Beyond an end or an edge it is thickness thick at the shoulder, thickness_offset below the top face (centered when None), and its cheeks flare outwards by 1:slope; with half only the bottom one. Beyond a face it runs along the length, thickness wide at the shoulder, thickness_offset from the front edge, its cheeks flaring across the width (with half only the front one): e.g. a batten lying flat whose whole width goes into a wide, shallow socket, its edges bevelled. Like a tenon, it asks for an allowance of its length.

across property

across: int

The axis the tongue's cheeks flare along: the thickness, or the width beyond a face.

solid

solid(back: float = 0.0, beyond: float = 0.0) -> Part

The tongue in the part frame, its cheeks extended back behind the shoulder and beyond its end (e.g. for the socket's clearance).

DovetailLap dataclass

DovetailLap(
    side: SideRef,
    length: float,
    thickness: float,
    width: float | None = None,
    width_offset: float | None = None,
    thickness_offset: float | None = None,
    haunch: SideRef | None = None,
    haunch_length: float | None = None,
    mitre: SideRef | None = None,
    mitre_angle: float = 45.0,
    mitre_depth: float = 0.0,
    mitred: tuple[SideRef, ...] = (),
    chamfer: float = 0.0,
    corner_chamfer: float = 0.0,
    count: int = 1,
    gap: float = 0.0,
    thickness_count: int = 1,
    thickness_gap: float = 0.0,
    slope: float = 7.0,
    sloped: tuple[SideRef, ...] | None = None,
    cut_back: dict[SideRef, float] | None = None,
)

Bases: Tenon

A lap over the whole width of an end, dovetailed across the face: a tail narrower at the shoulder than at its end, for a dovetailed half-lap.

The lap is a Tenon with one cheek (see thickness_offset). Its edges flare outwards by 1:slope towards its end, where it is as wide as the part; sloped are the edges of the part that slope (by default both, one for a half dovetail, e.g. at a corner). Beside the tail, the part is cut back to the shoulder through its whole thickness. With cut_back, the tail is narrower by that much at the given edges, e.g. where the socket stops short of the end of the other part: the part is cut back to there over the lap, and a sloped edge slopes from there.

edges property

edges: tuple[SideRef, ...]

The sloped edges.

flare property

flare: float

How far each sloped edge leans over the length of the lap.

end_width property

end_width: float

The width of the tail at its end: the part's, less any cut back.

shoulder_width property

shoulder_width: float

The width of the tail at the shoulder.

tail

tail() -> Part

The tail's outline across the face, through the part (in its frame).

Exact beyond the side, up to the lap's end; it reaches back behind the shoulder, on beyond the end and out at the straight edges, so that a socket cut by it runs out of the other part there.

Chamfer dataclass

Chamfer(
    arris: ArrisRef,
    size: float,
    size_b: float | None = None,
    run_out: bool = False,
)

Bases: Cut

A chamfer along an arris, size wide on its first side, size_b on the second.

With size_b None the chamfer is at 45 degrees. With run_out, it runs on at the same slope past the second side, over any allowance there: e.g. the bevel of a raised panel, planed down to the groove's width at the edge of the design box (the mouth of the groove) and on into it.

Mitre dataclass

Mitre(
    side: SideRef,
    inside: SideRef,
    angle: float = 45.0,
    face: SideRef | None = None,
    depth: float | None = None,
)

Bases: Cut

An end cut at angle from square, shortening the inside side.

The opposite side keeps the full design length (the long point), so the design length is the outside length, as for a picture frame. With inside an edge the mitre is across the face (picture frame), with inside a face it is across the edge (carcase corner).

An edge can be mitred too, along the grain, narrowing the inside face: the opposite face keeps the full design width, e.g. for the boards of a hollow leg meeting at its corners.

With face and depth, a mitre across the face is cut only depth deep below that face, e.g. the front half of a mitred half-lap.

shortening property

shortening: float

How much shorter (or narrower) the inside side is than the design size.

Taper dataclass

Taper(
    side: SideRef,
    end: SideRef,
    amount: float,
    length: float | None = None,
)

Bases: Cut

A face or an edge planed at a slope, so that the part is amount thinner (or narrower) at end than at the start of the taper.

The taper starts length from end (by default at the other end).

ShapeCut dataclass

ShapeCut(
    target: Part, shape: Part, name: str = "custom cut"
)

Bases: Cut

An arbitrary build123d solid removed from a part, given in its frame.

Hole dataclass

Hole(
    side: SideRef,
    diameter: float,
    depth: float | None = None,
    at: dict[SideRef, float] | None = None,
    point: float | None = None,
    bottom_diameter: float | None = None,
    counterbore: float | None = None,
    counterbore_depth: float = 0.0,
    countersink: float | None = None,
    countersink_angle: float = 90.0,
    kind: str | None = None,
    note: str | None = None,
)

Bases: Cut

A round hole drilled into a side along its normal, diameter wide.

at gives the distance of the hole's center from sides of the part next to side, e.g. {board.left_end: 20, board.front_edge: 30}; across an axis not given it is centered (so into a rod's end, on its axis). From a long side of a rod it is a cross hole, centered across the rod by default.

Without depth it goes through the part. A blind hole is depth deep with a flat bottom (Forstner, auger), or with point, the angle of a drill's tip (118 degrees for a twist drill), a conical point below that.

With bottom_diameter, it is a tapered (reamed) hole, that wide at its bottom (at the far side, if through), e.g. for the tapered tenon of a Windsor chair's leg, or flared for a wedged round tenon.

With counterbore (a diameter) and counterbore_depth, a wider, shallow hole over it, for a screw head or a plug; with countersink (the diameter at the side), a cone at countersink_angle for a screw head.

kind names it in the cut list instead of "hole" (e.g. "pilot hole"), note is added at the end (e.g. "for a 4 x 30 screw").

length property

length: float

How deep the hole is: to its bottom, or through the part.

entry_diameter property

entry_diameter: float

The widest the hole is at the side.

center

center() -> Vector

The hole's center on the side, in the part frame.

describe

describe(at: str | None = None) -> str

at, if given, says where it is, instead of its own offsets.

RoundTenon dataclass

RoundTenon(
    side: SideRef,
    diameter: float,
    length: float,
    tip_diameter: float | None = None,
    chamfer: float = 0.0,
)

Bases: Cut

A round tenon beyond an end, diameter wide and length long: on the axis of a rod (turned, or cut with a tenon cutter, leaving a shoulder), or in the middle of the end of a board (a square leg with a round tenon).

As with Tenon, the end is the shoulder and the tenon comes from an allowance it asks for. With tip_diameter, it tapers to that at its end, e.g. a Windsor chair's leg in a reamed hole; with chamfer, its end is chamfered at 45 degrees that wide, so that it enters its hole. A tenon as wide as a rod (without a taper or a chamfer) cuts nothing: the whole rod goes in.

tip property

tip: float

The diameter at the end (before any chamfer).

removes_material property

removes_material: bool

False for a whole rod going into a hole.

center

center() -> Vector

The center of the tenon at the shoulder, in the part frame.

diameter_at

diameter_at(distance: float) -> float

At a distance beyond the shoulder, as if it went on tapering.

profile

profile() -> list[tuple[float, float]]

The outline as (from the axis, beyond the shoulder), from the axis at the shoulder to the axis at the end.

solid

solid() -> Part

The tenon in the part frame.

WedgedRoundTenon dataclass

WedgedRoundTenon(
    side: SideRef,
    diameter: float,
    length: float,
    tip_diameter: float | None = None,
    chamfer: float = 0.0,
    kerf: float = 2.0,
    kerf_depth: float = 0.0,
    flare: float = 0.0,
    proud: float = 0.0,
    spread: int = 1,
)

Bases: RoundTenon

A through round tenon with a saw kerf across its end, for a wedge.

The kerf is kerf wide and kerf_depth deep from the end, across the axis spread of the part frame (1 or 2), along which the wedge spreads the tenon. It is modelled simply, the wood's give ignored: the tenon stays round, in a straight hole, and the kerf is the wedge's shape, opened by flare at either side where the tenon leaves the mortised part, proud before its end. The cut list gives the tenon as cut, with a straight kerf.

bottom property

bottom: float

Of the kerf, beyond the shoulder.

kerf_solid

kerf_solid() -> Part

The kerf, as the wedge's shape, through the tenon.

wedge_location

wedge_location(thickness: float) -> Location

Of the wedge in the kerf, in the part frame: a board thickness thick, its thick left end at the end of the tenon, its front edge at the side of the kerf towards lower coordinates along spread, centered across the tenon.

EndChamfer dataclass

EndChamfer(
    side: SideRef, size: float, length: float | None = None
)

Bases: Cut

A chamfer around an end: size wide on the end, length (by default as much, at 45 degrees) along the part.

On a board, the four arrises around the end are chamfered, e.g. at the foot of a leg so that it doesn't splinter. On a rod, the round arris: a cone, e.g. at the ends of a dowel, so that it enters its hole.

RoundTaper dataclass

RoundTaper(
    end: SideRef,
    diameter: float,
    length: float | None = None,
)

Bases: Cut

A rod turned (or shaved) to a cone towards end, diameter there.

The taper starts length from end (by default at the other end). With diameter 0, it comes to a point, e.g. a peg or a stake.

Flat dataclass

Flat(
    side: SideRef,
    depth: float,
    stopped: dict[SideRef, float] | None = None,
)

Bases: BoxCut

A flat planed (or ripped) along a rod, depth into it from one of its long sides, e.g. where it lies on a board; a half dowel at half its diameter.

With stopped, it stops that far short of each of the given ends.

width property

width: float

Of the flat face: a chord of the rod's section.

side_box

side_box(
    side: SideRef,
    u: float,
    v: float,
    w: float,
    du: float,
    dv: float,
    dw: float,
) -> Part

A box in the side's plane coordinates, from corner (u, v, w) with sizes (du, dv, dw).

w is along the outward normal: negative into the part, positive beyond it.

describe_offsets

describe_offsets(offsets: tuple[Offset, ...]) -> str

Where a cut is: '20 from b.left_end, centered'.