Joints¶
joints
¶
Joints: relations between two parts that place them and cut both.
Most joints put one part (or a tenon on it) into a socket cut into the other
(Housing): the socket is derived from where the inserted part lands, so
the two fit by construction, and the cut list names it by its shape (dado,
rabbet, groove, mortise, ...).
Allowance
dataclass
¶
Allowance(side: SideRef, amount: float)
Extension of a part's blank beyond its design box at one side.
Joint
¶
Base class of joints between two parts.
placement() gives the location of parts()[1] in the frame of
parts()[0]. Cuts are produced once the placement is known, so that
joints defined in a shared region can map geometry between the parts.
A joint that doesn't place its parts (e.g. a screw through two parts
placed by other joints) gets the placement they ended up in.
check_parts
¶
Fail early if the joint can't join its parts, e.g. a rod into a dado.
Butt
dataclass
¶
Butt(
a: SideRef,
b: SideRef,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
angle: float = 0.0,
)
Bases: Joint
Side b glued against side a, without any cuts.
flush are the sides kept facing the same way (by default the reference
faces, or the reference edges for a face); see mate for position and
across. With angle, part b is turned by that many degrees in the
plane of side a (see mate), e.g. a diagonal brace on a face.
Housing
¶
Bases: Joint
A side of part b goes into a socket cut into a side of part a.
Part b reaches beyond its design box into part a (an allowance), either
with its whole cross-section or with a tenon. The socket is cut where it
lands, with clearance at the bottom if it doesn't go through.
With through, the socket runs out of part a at both ends of the mating
arris, e.g. a groove along the whole edge. With stopped, it stops that
far short of each of the given sides of part a, e.g. a dado stopped short
of the front edge so that it doesn't show; where part b would reach into
the part left standing there, it is notched to clear it (e.g. the front
corner of a shelf in a stopped dado). Subclasses give the sides, the
depth and the tenon, and are dataclasses with the fields position,
across, flush and clearance (and through and stopped, if they
apply).
sides
¶
The side the socket is cut into and the side going into it.
tenon_cut
¶
tenon_cut() -> Tenon | None
The tenon on part b, or None when its whole cross-section goes in.
notches
¶
notches(
placement: Location,
inserted: list[tuple[Range, Range, Range]],
) -> list[Cut]
Notches in part b where it reaches past the stops of the socket, so
it clears the part of a left standing there; inserted are the boxes
of b (in its frame) going into the socket.
socket_box
¶
socket_box(
placement: Location,
inserted: tuple[Range, Range, Range],
clearance: float | None = None,
) -> tuple[Range, Range, Range]
Ranges of the socket in part a, for a box of part b given in its frame.
DadoJoint
dataclass
¶
DadoJoint(
dado: SideRef,
end: SideRef,
depth: float | None = None,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
)
Bases: Housing
An end (or an edge) of part b housed in a dado in a face (or an edge) of part a.
Part b's design length stops at the face of part a; the dado depth is
added to it. By default the reference edges are flush and b is centered
along a; give position e.g. as offset(100, ref=a.left_end). With
stopped, e.g. {a.front_edge: 15}, a stopped dado: it stops short of
the front edge, and the front corner of part b is notched to the stop.
RabbetJoint
dataclass
¶
RabbetJoint(
rabbet: SideRef,
end: SideRef,
at: SideRef,
depth: float | None = None,
lap: float | None = None,
through: bool = False,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
)
Bases: Housing
An end of part b in a rabbet cut into side rabbet of part a, at its side at.
E.g. the top of a carcase in rabbets at the top ends of the sides. The
reference face of part b is flush with at; along the arris b is
centered (or as given by position). With lap, part b gets a rabbet
too, keeping a lip lap thick at its reference face: the two rabbets
interlock. With through, the rabbet runs the whole length of part a,
e.g. for a box bottom, into the rabbets at the box's corners.
SlidingDovetail
dataclass
¶
SlidingDovetail(
socket: SideRef,
end: SideRef,
depth: float | None = None,
thickness: float | None = None,
slope: float = 7.0,
half: bool = False,
cut_tongue: bool = True,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
)
Bases: Housing
An end of part b with a dovetail tongue, slid into a dovetail socket across part a.
Like a dado joint (see DadoJoint for the placement), but the socket is
narrower at the face than at its bottom, so the parts can only slide
together along it. thickness is the tongue's thickness at the shoulder;
with half only one cheek is angled.
With end a face of part b, part b lies flat against part a and its
whole width goes into a wide, shallow socket, its edges bevelled by
1:slope (thickness is then the width at the shoulder, by default
so that the tongue is as wide as part b at its end): e.g. a batten
across the underside of a glued-up panel.
Without cut_tongue, the tongue is cut by another sliding dovetail and
this one only places part b and cuts the socket into part a: e.g. a
batten whose tongue runs across several boards glued edge to edge, each
of them housing it.
GrooveJoint
dataclass
¶
GrooveJoint(
groove: SideRef,
edge: SideRef,
depth: float | None = None,
through: bool = True,
position: Position = CENTERED,
across: Position = CENTERED,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
)
Bases: Housing
An edge of part b (e.g. a panel) in a groove in a side of part a (e.g. a rail).
By default part b is centered across the thickness of part a and the groove runs through the whole length of part a, as when cut on a table saw.
TongueAndGroove
dataclass
¶
TongueAndGroove(
tongue: SideRef,
groove: SideRef,
tongue_thickness: float | None = None,
tongue_length: float | None = None,
tongue_offset: float | None = None,
cut_tongue: bool = True,
through: bool = True,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
)
Bases: Housing
A tongue along an edge (or across an end) of part b in a groove in a side of part a.
The boards' design widths are their visible faces; the tongue is added.
It is centered across the thickness unless tongue_offset (from the
reference face) is given: with 0 it is flush with the reference face,
as on a drawer bottom rabbeted on its underside.
Without cut_tongue, part b goes in whole and only the groove is cut,
as wide as the tongue would be: e.g. a raised panel whose bevel, planed
down to the groove's width at its mouth, runs on into it (see Chamfer
with run_out).
MortiseTenon
dataclass
¶
MortiseTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] = 5.0,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
)
Bases: Housing
A tenon on an end of one part, in a mortise in a side of the other.
The reference faces of both parts are flush (see across to change
that). The tenon has a shoulder on both edges, or a pair of them (at
the front edge, at the back edge), and is centered across the
thickness unless tenon_offset (from the reference face) is given.
The mortise is cut where the tenon goes, with some clearance at its
bottom; a tenon as long as the mortised part is deep goes through.
With haunch, an edge of the tenoned part (e.g. the outer edge of a
rail), the tenon gets a haunch towards it, haunch_length long (by
default as deep as a panel groove), and the mortised part a haunch
mortise. It fills a groove running out of the mortised part, or the
shoulder of a wide rail (e.g. a table apron) set down from the end of
the mortised part, so its mortise doesn't run out of it.
With mitre, a face of the tenoned part, the end of the tenon is mitred
at 45 degrees, shorter at the cheek towards that face: for two tenons
meeting inside a leg. The mortise stays rectangular.
With tenon_chamfer, the arrises around the end of the tenon are
chamfered, e.g. on a through tenon a bit longer than the mortised part
is deep, so that it stands proud of it. With tenon_corner_chamfer, the
corners at its edges get big chamfers, that far each way (see Tenon).
With tenons, there are that many tenons side by side between the
shoulders, e.g. twin tenons on a wide rail, tenon_gap apart (by
default as wide as the tenons), each in its own mortise. The other
options apply to each of them (the haunch to the outer one).
With thickness_tenons, there are that many tenons one beside the
other across the thickness, e.g. on a thick, square rail,
thickness_gap apart (by default as thick as the tenons). Each is
tenon_thickness thick, by default about as thick as the gaps and the
cheek shoulders; tenon_offset is then to the nearest of them. With
tenons too, they make a grid, each in its own mortise. A haunch is
beside each of them across the thickness; a mitre runs across all.
WedgedMortiseTenon
dataclass
¶
WedgedMortiseTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] = 5.0,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
wedges: tuple[Board, ...] = (),
kerf: float = 2.0,
kerf_inset: float | None = None,
)
Bases: MortiseTenon
A through mortise and tenon, the tenon spread by wedges driven into saw kerfs in its end, into a mortise flared towards the outside.
The wedges are separate parts, one per kerf: one or two per tenon (see
tenons and thickness_tenons), in the order of the kerfs along the
tenon, the top tenons' first. They are given as
made: as thick as the tenon (which by default they set), as long as the
kerfs are deep, and as wide at their heads as the kerf plus the
spread of the tenon (twice the spread for one kerf in the middle). The
joint tapers them to fit. Two kerfs lie kerf_inset in from the edges of
the tenon (by default a fifth of its width), so the outer parts of the
tenon bend out into the flare at each end of the mortise; one kerf
spreads both halves of the tenon. The mortise is flared over the depth
of the kerfs (less what the tenon stands proud) and straight below.
The tenon goes through, by default flush with the far side of the
mortised part; it may stand proud of it, then the wedges are as long. The
model is of the joint glued up, the tenon spread (see WedgedTenon).
TuskTenon
dataclass
¶
TuskTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] = 5.0,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
wedge: Board | None = None,
wedge_head: SideRef | None = None,
wedge_slope: float = 8.0,
protrusion: float | None = None,
draw: float = 3.0,
)
Bases: MortiseTenon
A tusk tenon: a long through tenon held by a wedge (a tusk, a key) through a mortise in its end, a knock-down joint, e.g. for the stretchers of a workbench or a trestle table.
The tenon goes through the mortised part (the leg) and stands
protrusion proud of its outer side (by default two and a half times
the wedge's width). The wedge is a separate part, given as made: a
straight blank as long, wide (at its head) and thick as the wedge; the
joint tapers its back edge by 1:wedge_slope towards its right end, the
tip. It goes through a mortise in the protruding part of the tenon, its
straight front edge bearing on the outer side of the leg, its tapered
edge on the outer end of the mortise, which is sloped to match. The
mortise starts draw inside the outer side of the leg, so the wedge
bears on the leg, not on the inner end of the mortise: driven in, it
pulls the tenon out of the leg and the shoulders tight against it. The
wedge is centered on the tenon both ways, so its head and its tip stick
out on either side. The model is of the joint assembled, the wedge
driven home.
wedge_head is the side of the mortised part the head of the wedge
points to (by default its right end). With an end, the wedge is
parallel to the stile (the leg's length): with the usual placement it
goes through the tenon's width, from one edge to the other, e.g. upright
through the tenon of a horizontal stretcher in a standing leg, and is as
thick as a third or so of the tenon. With a face (or an edge), the wedge
is perpendicular to the stile: it goes through the tenon's thickness,
from one cheek to the other, e.g. lying level through an upright tenon,
and may be thicker, its thickness across the tenon's width.
DowelTuskTenon
dataclass
¶
DowelTuskTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] = 5.0,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
tusks: tuple[Rod, ...] = (),
bearings: tuple[Rod, ...] = (),
tusk_head: SideRef | None = None,
tusk_slope: float = 12.0,
flat_length: float | None = None,
protrusion: float | None = None,
)
Bases: MortiseTenon
A tusk tenon held by dowels instead of a wedge (as in Rex Krueger's
workbench): a long through tenon, and in the part of it standing proud of
the mortised part, a straight hole for each of the tusks, dowels with a
tapered flat driven through, bearing on a half dowel.
The tenon goes through the mortised part (the leg) and stands
protrusion proud of its outer side (by default the tusk's diameter and
a half beyond the hole). The tusks are rods, given as made; the joint
planes a flat on each over flat_length from its tip (its right end; by
default half its length), from nothing to 1:tusk_slope of that at the
tip, its flat towards the leg. Their heads point to tusk_head, a side
of the mortised part (by default its front edge: the tusks perpendicular
to the stile, through the tenon's thickness; an end makes them parallel
to it, through its width). Across the tenon, they lie as tenons would
in a rail: the tenon divided into twice as many parts and one, the tusks
in the middle of every other one, e.g. of two tusks in the second and
the fourth fifth.
The bearings, one per tusk or one for all of them, are half dowels
(rods as made; the joint rips them in half) glued flat side down on the
outer side of the leg, across the tusks, on the tips' side of the
tenon, next to it. The tusks' flats bear on their round tops, which
stay tangent to the flat however the tusk lies as it is driven in. A
tusk is driven home when it bears on the half dowel at the middle of its
flat; its hole is bored parallel to the leg, its inner edge below the
top of the half dowel by about half the flat's depth there: driving the
tusk on pulls the tenon out of the leg and its shoulders tight against
it. Knocked out, the tusks free the tenon. The model is of the joint
assembled.
MitredShoulderTenon
dataclass
¶
MitredShoulderTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] | None = None,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
mitre_depth: float | None = None,
mitred: tuple[SideRef, ...] | None = None,
)
Bases: MortiseTenon
A mortise and tenon whose shoulders are partly mitred, e.g. in a frame of square-ish stock.
The tenoned part reaches mitre_depth into the mortised part (by default
a third of its width) with its whole cross-section, into a notch, and
the tenon starts there. At each of mitred, edges of the tenoned part
(by default both of them, for a T-joint), the notch is mitred at 45
degrees back to the side of the mortised part: the parts meet on a short
mitre there and along the mortised part in between. For an L-shaped
corner, mitre only the inner edge; the tenon may then go through, as long
as the material behind the notch. The design length of the tenoned part
is to the side of the mortised part, as for a plain tenon.
The tenon may reach under the mitres, which makes it wider and the joint sturdier: its shoulders are by default half as deep as the mitres at the mitred edges, as deep as them at the others (e.g. to keep material at the end of the stile at a corner). Under a mitre the tenon starts at the side of the mortised part, elsewhere at the bottom of the notch; its length is measured from there. The mortise is cut from the side of the mortised part, through the notch and the mitres.
LooseTenon
dataclass
¶
LooseTenon(
a: SideRef,
b: SideRef,
tenon: Board,
depth: float | None = None,
tenon_position: Position = CENTERED,
tenon_offset: float | None = None,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 2.0,
)
Bases: Joint
Two parts joined by a loose (floating) tenon glued into a mortise in each.
Side b of part b (an end, or an edge) butts against side a of part a,
placed as by Butt. The loose tenon tenon is a separate part, e.g. a
strip planed as thick as the mortises are wide: it crosses the joint
line along its length (and grain), reaching depth into part a (by
default half its length) and the rest into part b. Its reference face
faces the same way as b's; on side b it is centered, or placed by
tenon_position (along the arris with b's reference face) and
tenon_offset (below b's reference face). Each mortise is clearance
deeper than the tenon reaches, unless it goes through.
tenon_location
¶
Of the loose tenon in the frame of part b, its left end inside b.
LooseTongue
dataclass
¶
LooseTongue(
a: SideRef,
b: SideRef,
tongue: Board,
depth: float | None = None,
through: bool = True,
tongue_position: Position = CENTERED,
tongue_offset: float | None = None,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 1.0,
)
Bases: Joint
Two parts edge to edge, joined by a loose tongue (a spline) glued into a groove in each.
Side b of part b (an edge, or an end) butts against side a of part
a, placed as by Butt. The loose tongue tongue is a separate part, a
strip planed as thick as the grooves are wide, its length along the
joint and so its grain too: simple to make, if weaker across the joint
than a strip of plywood or of short grain. Its width crosses the joint
line, reaching depth into part a (by default half its width) and the
rest into part b. Its reference face faces the same way as b's; along
side b it is centered, or placed by tongue_position (along the
arris with b's reference face), across it by tongue_offset (below b's
reference face). Each groove is clearance deeper than the tongue
reaches, for the glue.
With through (the default), the grooves run the whole length of the
parts along the joint, as cut on a table saw, showing at their ends.
Without, they are stopped grooves as long as the tongue, as cut with a
router, e.g. hidden by a tongue shorter than the boards.
tongue_location
¶
Of the loose tongue in the frame of part b, its front edge inside b.
Bridle
dataclass
¶
Bridle(
tenon: SideRef,
slot: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_offset: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
)
Bases: Housing
A tenon over the whole width of an end of part b, through a slot in part a.
For an L-shaped corner the slot is at an end of part a, e.g. with
position=flush_with(a.left_end); it then is a slot mortise. For a
T-shaped bridle, with the slot in part b, see TeeBridle. The tenon
is centered across the thickness unless tenon_offset is given.
TeeBridle
dataclass
¶
TeeBridle(
slot: SideRef,
edge: SideRef,
position: Position = CENTERED,
slot_thickness: float | None = None,
slot_offset: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
)
Bases: Joint
A T-shaped bridle joint: a slot in the end of part b straddles part a, which is cut down to a tongue by a dado across each of its faces.
Part b comes to part a at its side edge and reaches across it, its
end flush with a's opposite side: its design length is overall. The
slot is slot_thickness wide (by default a third of part a, as is
usual for bridles), centered across b's thickness unless slot_offset
(from b's reference face) is given. The reference faces are flush (see
across to change that); along part a, b is centered (or as given by
position).
HalfLap
dataclass
¶
HalfLap(
lap: SideRef,
socket: SideRef,
position: Position = CENTERED,
lap_thickness: float | None = None,
over: bool = False,
proud: float = 0.0,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
)
Bases: Housing
An end of part b lapped over (or under) part a, both cut to half thickness.
Part b gets a lap as long as part a is wide, part a a dado (or an end
rabbet at a corner) where it lands. With over, part b keeps the half at
its reference face and lies over part a; by default it lies under it, so
the reference face of part a stays whole.
With proud, part b stands that far out of part a beyond its flush side
(a face), so it is let in that much less deep: e.g. the square rail of a
workbench's leg, lapped to half its thickness, let in a quarter of the
leg's thickness (half its lap) and standing out of the leg by the other
quarter.
DovetailHalfLap
dataclass
¶
DovetailHalfLap(
lap: SideRef,
socket: SideRef,
position: Position = CENTERED,
lap_thickness: float | None = None,
over: bool = False,
proud: float = 0.0,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 0.0,
stopped: dict[SideRef, float] | None = None,
slope: float = 7.0,
sloped: tuple[SideRef, ...] | None = None,
)
Bases: HalfLap
A half lap whose lap is dovetailed across the face, in a dovetail-shaped socket, so that part b can't be pulled out of part a along its length.
Placed and cut to half thickness as HalfLap. The lap is a tail as wide
as part b at its end, its edges sloping in by 1:slope to the shoulder,
where part b is cut back beside it; part a gets a socket of the tail's
shape. sloped are the edges of part b that slope: by default both,
except one flush with an end of part a, i.e. at an L-shaped corner it is
a half dovetail sloped at the inner edge.
With proud (see HalfLap), e.g. a rail lapped to half its thickness
and let in only half of that, the tail is shaped through the whole lap,
and the socket is as deep as the lap is let in. With stopped, e.g.
{leg.right_end: 30}, the socket stops that far short of that side of
part a, so that a leg keeps a solid end above it: the tail is narrower
there, part b cut back over the lap to the stop, a sloped edge sloping
from there.
BoxJoint
dataclass
¶
BoxJoint(
a: SideRef,
b: SideRef,
fingers: int | None = None,
finger_width: float | None = None,
flush: tuple[SideRef, SideRef] | None = None,
)
Bases: _CornerJoint
A box (finger) joint: both ends cut into square fingers that interlock.
The fingers alternate across the width, part a's at its reference edge;
each finger is as long as the other board is thick, so the boards reach
through each other, as with through dovetails: part a's reference face
is outside, flush are the sides kept flush (by default the reference
edges). The fingers are all equally wide: fingers of them in all, or
else the odd number of them coming closest to finger_width (by default
the thickness of part a), so that part a has fingers at both edges and
the width is used up without a narrow finger left over.
ThroughDovetail
dataclass
¶
ThroughDovetail(
tails: SideRef,
pins: SideRef,
tail_count: int = 4,
slope: float = 7.0,
pin_width: float = 8.0,
half_pin_width: float = 10.0,
flush: tuple[SideRef, SideRef] | None = None,
)
Bases: _CornerJoint
Through dovetails at a corner of two boards.
The tails are cut on an end of one board, the pins on an end of the
other (any ends, so a board may have pins or tails at both ends). Both
boards reach through each other, so each gets an allowance of the
other's thickness. The pin board's reference face is outside; flush
are the sides kept flush along the corner (by default the reference
edges). The pin board may be narrower than the tail board, e.g. the back
of a drawer sitting on its bottom; the tails are laid out across it. The
geometry is built once in the frame of the pin board and split.
MitredCornerDovetail
dataclass
¶
MitredCornerDovetail(
tails: SideRef,
pins: SideRef,
tail_count: int = 4,
slope: float = 7.0,
pin_width: float = 8.0,
half_pin_width: float = 10.0,
flush: tuple[SideRef, SideRef] | None = None,
mitred: tuple[SideRef, ...] | None = None,
mitre_width: float = 10.0,
)
Bases: ThroughDovetail
Through dovetails mitred at the edges of the corner, e.g. to hide a groove or a rabbet running along the inside of a box.
At each of the pin board's edges in mitred (by default both), a strip
mitre_width wide across the width is mitred through the whole
thickness instead of dovetailed, so the edge of the corner shows a mitre
and a groove or a rabbet within the strip runs through both boards
unseen. The dovetails are laid out across the rest of the width, from a
half pin next to each mitre. The boards must be equally wide.
HalfBlindDovetail
dataclass
¶
HalfBlindDovetail(
tails: SideRef,
pins: SideRef,
tail_count: int = 4,
slope: float = 7.0,
pin_width: float = 8.0,
half_pin_width: float = 10.0,
flush: tuple[SideRef, SideRef] | None = None,
lap: float | None = None,
)
Bases: ThroughDovetail
Half-blind dovetails, e.g. at the front of a drawer.
Like through dovetails, but the tails stop short of the pin board's
outside face, so it keeps a lap that hides them (by default a quarter
of its thickness). The tails are as long as the sockets are deep.
BlindMitredDovetail
dataclass
¶
BlindMitredDovetail(
tails: SideRef,
pins: SideRef,
tail_count: int = 4,
slope: float = 7.0,
pin_width: float = 8.0,
half_pin_width: float = 10.0,
flush: tuple[SideRef, SideRef] | None = None,
lap: float | None = None,
mitre_width: float = 10.0,
)
Bases: ThroughDovetail
Blind mitred (secret mitre) dovetails, e.g. at the corners of a fine carcase: from outside the corner looks like a plain mitre.
Both boards keep a lip at their outside face, mitred where they meet: the
pin board one lap thick (by default a quarter of its thickness), the
tail board one in proportion to its thickness, so that both lips meet the
mitre at their inside corner. Behind the lips the tails and the pins
interlock hidden, the tails as long as the pin board's sockets are deep
and the pins stopping at the tail board's lip. At both edges a strip
mitre_width wide is mitred through the whole thickness, so the edges
show a mitre too; the dovetails are laid out between these strips, from a
half pin next to each. The boards must be equally wide.
tail_lip
¶
The thickness of the tail board's lip, covering the ends of the pins.
MitreCorner
dataclass
¶
MitreCorner(
a: SideRef,
b: SideRef,
a_inside: SideRef,
b_inside: SideRef,
flush: tuple[SideRef, SideRef] | None = None,
)
Bases: Joint
Two ends meeting at a right-angled corner, both mitred.
a_inside and b_inside are the sides inside the corner: edges for a
picture frame (mitre across the face), faces for a carcase (mitre across
the edge). Design lengths are outside lengths, to the long points. The
mitres meet on the diagonal of the corner: at 45 degrees for parts of
equal width (or thickness, for a carcase).
Two edges can meet the same way, mitred along the grain, the faces inside the corner: e.g. the boards of a hollow leg, a long L-shaped corner. Design widths are then outside widths; the left ends are flush.
MitredHalfLap
dataclass
¶
MitredHalfLap(
a: SideRef,
b: SideRef,
a_inside: SideRef,
b_inside: SideRef,
flush: tuple[SideRef, SideRef] | None = None,
face: SideRef | None = None,
depth: float | None = None,
)
Bases: MitreCorner
A corner half lap mitred on the show face, as on a frame: the front half of both parts is mitred, the back half lapped.
Placed as MitreCorner (ends to the outside of the corner), with edges
inside the corner. On face of part a (by default its reference face)
and the face of part b flush with it, both parts are mitred depth deep
(by default half their thickness), so the face shows a mitre; behind it
part a keeps the whole corner, a lap, and part b is cut away there by an
end rabbet, so the back shows a plain half lap. The mitre hides the end
grain on the face, the lap gives the glue far more long-grain area than
a plain mitre.
MitredFaceTenon
dataclass
¶
MitredFaceTenon(
a: SideRef,
b: SideRef,
a_inside: SideRef,
b_inside: SideRef,
flush: tuple[SideRef, SideRef] | None = None,
face: SideRef | None = None,
depth: float | None = None,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
tenon_offset: float | None = None,
shoulder: float | tuple[float, float] | None = None,
clearance: float = 2.0,
)
Bases: MitredHalfLap
A frame corner mitred on the show face, with a hidden mortise and tenon behind the mitre (a mitred face tenon, as on a frame with a moulded or chamfered front).
Placed as MitreCorner (ends to the outside of the corner, design
lengths to the outside), with edges inside the corner. On face of part
a (by default its reference face) and the face of part b flush with it,
both parts are mitred depth deep (by default a third of their
thickness), so the face shows a mitre. Behind it, part a (the stile)
runs on square to the corner and part b (the rail) butts square against
its inside edge, as in a plain mortise and tenon: b is cut back to that
shoulder by an end rabbet, but for a tenon, which goes into a mortise in
a's inside edge. The tenon lies in the layer behind the mitre, by
default centered across it, tenon_thickness thick (by default a third
or so of the layer) and tenon_offset below face if given. Across
part b's width it keeps a shoulder at both edges (or a pair of them,
at the front edge and at the back edge), by default a quarter of its
width, so the mortise stays closed both towards the end of part a and
at its inside edge. It is tenon_length long (by default two thirds of
part a's width), so it doesn't show at the outer edge, and the mortise
clearance deeper.
tenon_box
¶
Ranges of the tenon in the frame of part b.
KeyedMitre
dataclass
¶
KeyedMitre(
a: SideRef,
b: SideRef,
a_inside: SideRef,
b_inside: SideRef,
flush: tuple[SideRef, SideRef] | None = None,
keys: tuple[Board, ...] = (),
depth: float | tuple[float, ...] | None = None,
offsets: tuple[float, ...] | None = None,
angle: float | tuple[float, ...] = 0.0,
dovetail: float | None = None,
)
Bases: MitreCorner
A mitre corner with keys (slip feathers) glued into kerfs across its outside corner.
A key is a small piece slid into an open notch running across the arris
where the boards meet (unlike a spline, SplinedMitre, hidden in the
mitre): a straight key goes in from the outside, a dovetail key only
along its grain, across the corner.
Placed and mitred as MitreCorner, in either orientation: a picture
frame (edges inside the corner) or a carcase (faces inside). Each kerf
is a slab square to the corner's arris, sawn from the outside corner in
along the mitre line, depth deep (by default a third of the mitre
line), on a cradle on the table saw. The keys are thin boards as made,
as thick as the kerf, their grain across the mitre line, glued in proud
and trimmed flush with both outside faces: the model shows them trimmed,
as triangles. They are spaced evenly along the corner, or centered at
offsets from part a's left end, front edge or top face (whichever the
corner runs from); depth and angle may be given per key.
With angle, the kerf is turned that many degrees about the mitre line
(the cradle set askew), so that a key shows as a slanted line on each
face, a chevron round the corner. With dovetail (the slope of its
sides, in degrees), the kerf is cut with a dovetail bit: as wide as the
key is thick at its bottom, narrower towards the corner, and the key is
a dovetail strip.
SplinedMitre
dataclass
¶
SplinedMitre(
a: SideRef,
b: SideRef,
a_inside: SideRef,
b_inside: SideRef,
flush: tuple[SideRef, SideRef] | None = None,
splines: tuple[Board, ...] = (),
along: Literal["corner", "mitre"] | None = None,
depth: float | None = None,
offsets: tuple[float, ...] | None = None,
spline_offset: float | None = None,
clearance: float = 1.0,
)
Bases: MitreCorner
A mitre corner with loose splines across the mitre, glued into grooves cut square into both mitre faces.
A spline is a small loose tenon (or tongue) in mortises or grooves in
both parts, usually hidden; unlike a key (KeyedMitre), slid into an
open notch across the corner's arris.
Placed and mitred as MitreCorner, in either orientation: a picture
frame (edges inside the corner) or a carcase or a shelf (faces inside).
The splines are thin boards, as thick as the grooves are wide, their
grain along their length (as LooseTongue). Their width crosses the
mitre, reaching depth into part a (by default half the width) and the
rest into part b; each groove is clearance deeper.
along gives the direction of their length, by default depending on
how the boards meet:
- "corner" (the default for faces inside the corner, a carcase or a shelf): parallel to the corner's arris, e.g. one spline through the whole length of a box corner, or several shorter ones in a row in stopped grooves on a wide board;
- "mitre" (the default for edges inside, a picture frame): along the mitre line, from the outside corner towards the inside, e.g. a spline hidden in a frame's mitre.
Along the corner, the splines are spaced with equal gaps, or centered at
offsets from part a's left end, front edge or top face (whichever the
corner runs from). Across the mitre line, they are centered on it, or at
spline_offset from the outside corner along it. A groove reaching the
end of the mitre runs out there (through); grooves breaking out of the
outside of a part are refused.
MitreSplice
dataclass
¶
Bases: Joint
Two parts end to end, lengthened by a mitre across both ends.
short is the side of part a that the mitre shortens: an edge for a
mitre across the face, a face for a mitre across the edge. The reference
faces and edges of both parts are flush. Design lengths are to the long
points, so the overall length is shorter by the overlap of the mitres.
CrossLap
dataclass
¶
CrossLap(
a: SideRef,
b: SideRef,
position: Position = CENTERED,
overhang: float | None = None,
depth: float | None = None,
)
Bases: Joint
Two parts crossing each other, each with a dado where the other one crosses.
Part b crosses part a at its edge a, with its end b first, and
reaches overhang beyond a's other edge (by default the parts cross in
their middles). The reference faces are flush; part a is cut depth
deep from its reference face (by default half its thickness), part b
from the other face, so b lies over a at the crossing.
Button
dataclass
¶
Button(
mortise: SideRef,
tongue: SideRef,
level: SideRef,
position: Position = CENTERED,
thickness: float = 6.0,
depth: float = 4.0,
gap: float = 2.0,
pull: float = 2.0,
clearance: float = 2.0,
play: float = 1.0,
)
Bases: Joint
A wooden button holding a top down: a block screwed to the underside of the top, with a tongue at its end in a short mortise in the inside of an apron.
The button's end tongue faces side mortise of the apron, gap away
from it, and its top face is level with the apron's side level (its
top edge, where the top lies); along the apron it is placed by
position. The tongue is at the bottom of the button, thickness
thick, and reaches depth into the apron. The mortise is pull higher
than the tongue, so screwing the button pulls the top down onto the
apron, clearance deeper, so the top can move towards the apron, and
longer by play at either end, so it can move along it.
RoundMortiseTenon
dataclass
¶
RoundMortiseTenon(
tenon: SideRef,
mortise: SideRef,
diameter: float | None = None,
length: float | None = None,
tip_diameter: float | None = None,
chamfer: float = 0.0,
clearance: float = 2.0,
position: Position = CENTERED,
across: Position = CENTERED,
flush: tuple[SideRef, SideRef] | None = None,
gap: float = 0.0,
)
Bases: Joint
A round tenon on an end of part b (a rod, or square stock) in a hole drilled into a side of part a, e.g. a rung into a leg or a stile.
The hole is drilled where the tenon lands, so they fit by construction,
clearance deeper than the tenon reaches; a tenon as long as part a is
deep goes through. By default the parts are centered on each other both
ways (see mate for position, across and flush).
diameter is of the tenon, by default the whole rod (a rod going into
a hole with its whole cross-section, without a shoulder), or about 0.6
of square stock. length is by default two thirds of the material
behind the hole's side. With tip_diameter, the tenon and the hole are
tapered (reamed); with chamfer, the tenon's end is chamfered.
The hole's side may be a long side of a rod, e.g. a stretcher into a round leg; the tenon's shoulder then meets the rod along a line.
With gap, the shoulder stands that far off part a, e.g. below a
batten between them, through which the tenon passes (see Bore); the
tenon's length includes it.
Bore
dataclass
¶
Bases: Joint
A hole bored through part a for a round tenon (or a whole rod) on
the end tenon of part b, passing through it, both placed by other
joints: e.g. a stool's leg through a batten under the seat, tenoned
into the seat (RoundMortiseTenon with a gap). The hole is diameter
wide, on the tenon's axis.
WedgedRoundMortiseTenon
dataclass
¶
WedgedRoundMortiseTenon(
tenon: SideRef,
mortise: SideRef,
diameter: float | None = None,
length: float | None = None,
tip_diameter: float | None = None,
chamfer: float = 0.0,
clearance: float = 2.0,
position: Position = CENTERED,
across: Position = CENTERED,
flush: tuple[SideRef, SideRef] | None = None,
gap: float = 0.0,
wedge: Board | None = None,
kerf: float = 2.0,
spread: SideRef | None = None,
)
Bases: RoundMortiseTenon
A round tenon through a hole, held by a wedge in a saw kerf across its end, e.g. a stool's leg through its seat.
The wedge is a separate part, given as made: as long as the kerf is
deep, at its head as wide as the kerf plus what it opens the kerf
by, and as thick as fits within the tenon (its corners inside the
circle, so a bit less than the tenon is wide); the joint tapers both its
edges to fit. The kerf runs across the grain of part a, so that the
wedge spreads the tenon along it and doesn't split it: towards
spread, a side of part a (by default its left end).
The tenon goes through, by default flush with the far side of part a;
it may stand proud of it, then the wedge is as long. The model ignores
the wood's give: the tenon and the hole stay straight, the kerf opened
to the wedge's shape (see WedgedRoundTenon). The small gaps left
between the wedge's ends and the hole fill with glue.
Dowels
dataclass
¶
Dowels(
a: SideRef,
b: SideRef,
dowels: tuple[Rod, ...],
depth: float | None = None,
offsets: tuple[float, ...] | None = None,
dowel_offset: float | None = None,
position: Position = CENTERED,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
clearance: float = 1.0,
)
Bases: Joint
Side b of part b (an end or an edge) butted against side a of
part a, as by Butt, and joined by dowels glued into holes in both.
The dowels are separate parts (rods), all of the same size, reaching
depth into part a (by default half their length) and the rest into
part b. Along side b they are spaced evenly, or at offsets from its
front edge (on an end) or its left end (on an edge); across it they lie
in the middle of its thickness, or dowel_offset below its reference
face. Each hole is clearance deeper than the dowel reaches, for glue.
PeggedMortiseTenon
dataclass
¶
PeggedMortiseTenon(
tenon: SideRef,
mortise: SideRef,
position: Position = CENTERED,
tenon_thickness: float | None = None,
tenon_length: float | None = None,
shoulder: float | tuple[float, float] = 5.0,
clearance: float = 2.0,
tenon_offset: float | None = None,
haunch: SideRef | None = None,
haunch_length: float | None = None,
mitre: SideRef | None = None,
tenon_chamfer: float = 0.0,
tenon_corner_chamfer: float = 0.0,
tenons: int = 1,
tenon_gap: float | None = None,
thickness_tenons: int = 1,
thickness_gap: float | None = None,
across: Position = FLUSH,
flush: tuple[SideRef, SideRef] | None = None,
peg: Rod | None = None,
peg_distance: float | None = None,
drawbore: float = 0.0,
)
Bases: MortiseTenon
A mortise and tenon held by a peg (a rod) through the mortised part
and the tenon, across its cheeks: pinned, or drawbored.
The peg goes through the mortised part, centered on its thickness there
(so a longer peg stands proud of both sides, to be trimmed), its axis
peg_distance from the mortised side (by default about a third of the
tenon's length), in the middle of the tenon's width.
With drawbore, the hole in the tenon is drilled that much closer to
the shoulder, so the peg driven in pulls the shoulders tight. As for
wedged tenons, the model shows the joint assembled: the holes and the
peg straight; the offset is noted in the cut list.
Screw
dataclass
¶
Screw(
head: SideRef,
into: Part,
diameter: float = 4.0,
length: float = 30.0,
at: dict[SideRef, float] | None = None,
head_diameter: float | None = None,
counterbore_depth: float | None = None,
clearance_diameter: float | None = None,
pilot_diameter: float | None = None,
)
Bases: Joint
A screw driven through part b from its side head into part a, both
placed by other joints, e.g. a batten screwed to the back of a door.
Part b gets a clearance hole, countersunk for the screw's head (by
default twice as wide as the screw), or with counterbore_depth
counterbored as wide, e.g. for a plug; part a a pilot hole, as deep as
the screw reaches into it. The hole is at at on side head (see
Hole). The screw itself isn't modelled; the cut list names it.
Butterfly
dataclass
¶
Butterfly(
face: SideRef,
key: Board,
at: dict[SideRef, float] | None = None,
angle: float = 0.0,
waist: float | None = None,
other: SideRef | None = None,
)
Bases: Joint
A butterfly key (a bow tie, two dovetails tail to tail) inlaid into a shallow mortise in a face, across a crack in a board or across the joint of two boards, glued and flush with the face.
The key is a board as made: as long as the bow tie, as wide as its
ends and as thick as the mortise is deep, its grain along its length
(across the crack); the joint shapes it to its waist (by default a
third of its width). On face it is centered, or at at distances of
its center from sides of the part next to the face (as for Hole), its
length across the grain, or turned angle degrees from that.
With other, the face of a second board flush with face (e.g. two
slabs butted edge to edge), the mortise is cut into both. The joint
doesn't place the boards (Joint.places), only the key. The crack
itself isn't modelled: the board stays whole.