How a design is described¶
A woodwright design says what a woodworker would say about a piece: which boards it is made of, and how they are joined. It doesn't say where each board is in space or how big each tenon is: those follow from the joints. This page goes through the ideas one by one; the reference has every option.
Stock¶
Stock is the material parts are cut from: a thickness in millimetres and
a species, for the cut list.
oak = ww.Stock.board(thickness=20, species="oak")
dowel_stock = ww.Stock.rod(diameter=10, species="beech") # round stock
Parts and their sides¶
A Board is a named piece of board stock with a length (along the grain)
and a width. Picture it lying flat on the bench in front of you, its length
from left to right, its reference face up and its reference edge towards
you. Its six sides are named from there:
| Side | Where | Also |
|---|---|---|
top_face |
facing up | ref_face |
bottom_face |
facing down | |
front_edge |
towards you | ref_edge |
back_edge |
away from you | |
left_end |
to the left | |
right_end |
to the right |
Joints and cuts refer to these sides, e.g. rail.left_end or
stile.back_edge. Where two sides meet is an arris: board.arris(board.top_face,
board.front_edge), the reference arris.
A Rod is a part of round stock: a dowel, a peg, a rung, a round leg. It
has the same six side names, thought of as the sides of the square box
around it; only its ends are real, the others are directions (e.g. for a
hole across it or a flat on it).
A board can be mirrored (mirror=True): the mirror image of the same
board, e.g. the right side of a case against the left one, both with their
reference faces outside and their reference edges at the front. Its ends
swap names, so the same joints on the same named sides make the mirror
image. A board can also be laminated (plies=2): glued up from equal
boards, through its thickness or across its width; the glue lines show in
the drawings and the cut list.
Design dimensions¶
The lengths you give are design dimensions: what you'd measure in the finished piece, between the joints. A rail is 300 mm from shoulder to shoulder; its tenons are added to its cut length. A panel's size is the opening of its frame; the grooves add to it. The cut list gives the cut sizes.
The assembly¶
An Assembly holds the parts and the joints between them. The first part
added is the root: root_location says which way it stands, by the
directions two of its sides face. Every other part is placed by joints,
directly or through other parts.
leg_stock = ww.Stock.board(thickness=60, species="oak")
table = ww.Assembly("table", root_location=ww.orient(right_end=ww.UP, back_edge=ww.RIGHT))
leg = table.add(ww.Board("leg", leg_stock, length=720, width=60))
Directions are as seen by someone in front of the piece: RIGHT, LEFT,
BACK (away from the viewer), FRONT, UP, DOWN. Here the leg stands
up (its right end up) with its back edge to the right.
Joints¶
A joint relates a side of one part to a side of another. It does three things:
- places the second part against the first (the side of one against the side of the other, two other sides flush);
- sizes itself, filling in what you didn't give from rules of thumb (a tenon about a third of the stock thick, rounded to a chisel width; see Rules) and lengthening parts where needed;
- cuts both parts: the tenon and the mortise, a groove, a dado, the tails and pins.
apron = table.add(ww.Board("apron", oak, length=500, width=100))
table.join(
ww.MortiseTenon(
tenon=apron.left_end,
mortise=leg.back_edge,
position=ww.flush_with(leg.right_end),
shoulder=(10, 15),
)
)
print(table.resolve().cut_list())
leg 720 x 60 x 60 mm oak
- mortise 75 x 8 mm, 42 deep, in back_edge
apron 540 x 100 x 20 mm oak
- tenon 75 x 8 mm, 40 long, on left_end
Every joint has its own sides with their own names (tenon=, mortise=;
dado=, end=; groove=, edge= ...), and most have a position.
Positions¶
By default, the parts meet with their reference faces flush (across the
joint) and centered on each other (along it). Along the joint, position
sets that otherwise:
ww.flush_with(leg.right_end): flush with the end of the leg, e.g. an apron level with the top of the leg;ww.offset(100, leg.right_end): 100 mm in from it;ww.centered(): centered (the default for most joints).
Across it, across sets the second part back from the flush faces
(across=ww.offset(5)), or centers it in the thickness
(across=ww.centered()), and flush chooses which sides are flush
instead of the reference faces.
Placing joints and the others¶
Joints that put one part against another place it. Some don't: a Screw
or a Bore through two parts already placed, a Butterfly key across two
boards. They cut the parts where the placing joints put them. If a part
isn't reached by placing joints from the root, resolving says which.
A loop, like the four corners of a frame, is checked: the last joint must agree with where the others put the parts, or resolving says by how much it is off.
Separate pieces¶
Some joints have pieces of their own: the loose tenon of LooseTenon, the
keys of KeyedMitre, the dowels of Dowels. They are parts you make and
add to the assembly like the others, and give to the joint, which places
and cuts them.
Cuts¶
The cuts joints make come by themselves. Cuts that don't belong to a joint are added to a part: chamfers, holes, tapers, end chamfers, mitres.
shelf = ww.Board("shelf", oak, length=500, width=200)
shelf.cut(ww.Chamfer(shelf.arris(shelf.top_face, shelf.front_edge), size=3))
shelf.cut(ww.Hole(shelf.top_face, 8, depth=12, at={shelf.left_end: 50}))
Resolving¶
assembly.resolve() works out the design: it fills in the joints'
defaults, sizes the blanks, places every part and collects their cuts. It
returns a ResolvedModel and leaves the design unchanged, so you can
change a parameter and resolve again.
From the resolved model:
model.cut_list(): the cut list, identical parts once;model.parts: each part with its size, cuts, location and 3D shape;model.compound(): the whole assembly as a build123d shape;export_model(model, path): all the files (see Outputs).
Identical parts¶
Parts that are the same piece, perhaps turned over (end for end, face down), are found and grouped: "2 pieces: stile_left, stile_right" in the cut list, one drawing for both. Mirror images are grouped too, marked as such. You don't have to declare them; name the parts as they are in the piece.