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.
allowances
¶
allowances() -> list[Allowance]
Extensions of the blank the cut needs, e.g. for a tenon.
offsets
¶
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
¶
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
¶
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
¶
Of all the tenons across the thickness together, with the gaps.
single_width
property
¶
The width of each of multiple tenons (of the tenon, if there's one).
removes_material
property
¶
False for a tenon as thick and wide as the part, e.g. a thin rail in a slot.
tenon_boxes
¶
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
¶
Ranges of the haunch in the part frame, from the tenon to the
haunch side; one beside each tenon across the thickness.
reach_solid
¶
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.
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
¶
What the tenon spreads into beyond its straight cheeks (and the mortise is flared by).
wedge_locations
¶
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.
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.
end_width
property
¶
The width of the tail at its end: the part's, less any cut back.
tail
¶
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
¶
How much shorter (or narrower) the inside side is than the design size.
Taper
dataclass
¶
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).
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").
describe
¶
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.
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.
wedge_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
¶
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.
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
¶
Where a cut is: '20 from b.left_end, centered'.