Using the Box type to define your panels

Sheet stock is typically used for making boxes, cabinets and drawers, all of which have a rectangular crosssection on each axis.

A Box is first specified by its outside dimensions. The Unitful package (and optionally UnitfulUS) are used so we can specify units for the box dimensions.

using PanelCutting, Unitful, UnitfulUS

my_box = Box(0.3u"m", 0.2u"m", 0.1u"m")
Box(0.3 m, 0.2 m, 0.1 m, DataStructures.DefaultDict{Any, Any, Unitful.Quantity{Float64, 𝐋, Unitful.FreeUnits{(inch,), 𝐋, nothing}}}(), DataStructures.DefaultDict{Any, Any, Nothing}(), DataStructures.DefaultDict{Any, Any, Bool}(), DataStructures.DefaultDict{Any, Any, GDLong}(), DataStructures.DefaultDict{Any, Any, FingerJoint}())

The Box has six faces which are identified by singleton types. The faces come in three opposite pairs: Top and Bottom, Left and Right, and Front and Back.

Once a box is defined, one can specify whether each face is open, or the thickness of the panel for that face. One can also specify what material should be used.

my_box.open[Top()] = true

do_faces(my_box) do face
    # All faces are 5mm baltic birch:
    my_box.thickness[face] = (1/4)u"inch"
    my_box.material[face] = "Baltic Birch"
end

For each face of a box, one can also specify the grain direction (also represented by singleton types), one of GDLong(the default), GDShort or GDEither.

my_box.grain_direction[Bottom()] = GDEither()
GDEither()

A Box also has edges. An edge is identified by a pair of faces. Edge(Front(), Bottom()) specifies the edge where the bottom and front panels of the box meet.

One can specify the joint type to be used for each edge. The joint type affects this sizes of the panels which meet at that edge. For a butt joint, one face is shortened by the thickness of the other. For a finger joint, both panels would be at full length. For a dado/tongue and grrove joint, one panel is shortened by the thickness of the other but lengthened by the tongue length.

let
    # Use finger joints between each pair of sides:
    sides = [Left(), Back(), Right(), Front()]
    for i in 0:(length(sides) - 1)
        e = Edge(sides[i + 1],
                 sides[1 + (i + 1) % length(sides)])
        my_box.joint_types[e] = FingerJoint()
    end
    # Use a dado joint for each edge of the bottom:
    for n in neighbors(Bottom())
        edge = Edge(Bottom(), n)
        my_box.joint_types[edge] = DadoJoint(Bottom(), 2u"mm")
    end
end

Once the box is fully specified, we can ask about the panels it requires, its WantedPanels:

WantedPanels(my_box)
6-element Vector{AbstractWantedPanel}:
 WantedPanel(1, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Back")
 WantedPanel(2, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom")
 FlippedPanel(3, WantedPanel(2, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom"))
 WantedPanel(4, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Front")
 WantedPanel(5, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Left")
 WantedPanel(6, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Right")

To make our box, we first need sheet stock, which comes from a Supplier.

BOULTER_PLYWOOD = Supplier(
    name = "Boulter Plywood",
    kerf = (1/8)u"inch",
    cost_per_cut = money(1.50),
    available_stock = [
        AvailablePanel(;
            label = "1/4 5 × 5 Baltic Birch",
            material = "Baltic Birch",
            thickness = (1/4)u"inch",
            length = 5u"ft",
            width = 5u"ft",
            cost = 84.00   # I've not been able to get UnitfulCurrency to work
        ),
        AvailablePanel(;
            label = "1/4 30 × 60 Baltic Birch",
            material = "Baltic Birch",
            thickness = (1/4)u"inch",
            length = 60u"inch",
            width = 30u"inch",
            cost = 51.00
        ),
        AvailablePanel(;
            label = "1/4 30 × 30 Baltic Birch",
            material = "Baltic Birch",
            thickness = (1/4)u"inch",
            length = 30u"inch",
            width = 30u"inch",
            cost = 36.00)
      ])
Supplier("Boulter Plywood", 1.5, 0.125 inch, AvailablePanel[AvailablePanel(7, "1/4 5 × 5 Baltic Birch", 5 ft, 5 ft, 0.25 inch, "Baltic Birch", 84.0), AvailablePanel(8, "1/4 30 × 60 Baltic Birch", 30 inch, 60 inch, 0.25 inch, "Baltic Birch", 51.0), AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)])

Now we can figure out where to make the cuts. We can multiply our list of WantedPanels by an integer if we want to make several boxes.

searcher = Searcher(BOULTER_PLYWOOD, 2 * WantedPanels(my_box))
search(searcher)
searcher.cheapest.finished
(FinishedPanel(18849, WantedPanel(16, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Back 1/2"), Panel(18847, 0.2 m, 0.1 m, Panel(18840, 0.2 m, 0.34929999999999994 m, Panel(18833, 0.2 m, 0.45247499999999996 m, Panel(18819, 0.2 m, 0.55565 m, Panel(18816, 0.2 m, 0.658825 m, Panel(18739, 0.26435 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 13.04703363057249), 0.2 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 11.139683071176409), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.20635 m, 10.711873558299658), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.309525 m, 10.00147968377146), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.4127 m, 8.941613295217829), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.4127 m, 3.0167174561842782)), FinishedPanel(18841, WantedPanel(17, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Back 2/2"), Panel(18839, 0.2 m, 0.1 m, Panel(18833, 0.2 m, 0.45247499999999996 m, Panel(18819, 0.2 m, 0.55565 m, Panel(18816, 0.2 m, 0.658825 m, Panel(18739, 0.26435 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 13.04703363057249), 0.2 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 11.139683071176409), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.20635 m, 10.711873558299658), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.309525 m, 10.00147968377146), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.309525 m, 2.5598663885536306)), FinishedPanel(18834, WantedPanel(22, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Front 1/2"), Panel(18832, 0.2 m, 0.1 m, Panel(18819, 0.2 m, 0.55565 m, Panel(18816, 0.2 m, 0.658825 m, Panel(18739, 0.26435 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 13.04703363057249), 0.2 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 11.139683071176409), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.20635 m, 10.711873558299658), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.20635 m, 2.2103938745281977)), FinishedPanel(18820, WantedPanel(23, 0.2 m, 0.1 m, 0.25 inch, "Baltic Birch", "Front 2/2"), Panel(18818, 0.2 m, 0.1 m, Panel(18816, 0.2 m, 0.658825 m, Panel(18739, 0.26435 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 13.04703363057249), 0.2 m, LengthAxis(), 0.49765000000000004 m, 0.103175 m, 11.139683071176409), 0.1 m, WidthAxis(), 0.49765000000000004 m, 0.103175 m, 1.9278095128767496)), FinishedPanel(18812, WantedPanel(18, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom 1/2"), Panel(18810, 0.29129999999999995 m, 0.19130000000000003 m, Panel(18796, 0.29129999999999995 m, 0.2579999999999999 m, Panel(18793, 0.29129999999999995 m, 0.45247499999999996 m, Panel(64, 0.3 m, 0.45247499999999996 m, Panel(57, 0.3 m, 0.55565 m, Panel(34, 0.3 m, 0.658825 m, Panel(31, 0.3 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0 inch, 0 inch, 14.825552663657628), 0.1 m, WidthAxis(), 0.0 m, 0.103175 m, 14.17412741229432), 0.1 m, WidthAxis(), 0.0 m, 0.20635 m, 13.283503235935846), 0.1 m, WidthAxis(), 0.0 m, 0.309525 m, 12.107634963898949), 0.29129999999999995 m, LengthAxis(), 0.0 m, 0.309525 m, 13.354347056291632), 0.19130000000000003 m, WidthAxis(), 0.0 m, 0.504 m, 8.529760829119164), 0.19130000000000003 m, WidthAxis(), 0.0 m, 0.504 m, 7.529454514315695)), FinishedPanel(18797, WantedPanel(19, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom 2/2"), Panel(18795, 0.29129999999999995 m, 0.19130000000000003 m, Panel(18793, 0.29129999999999995 m, 0.45247499999999996 m, Panel(64, 0.3 m, 0.45247499999999996 m, Panel(57, 0.3 m, 0.55565 m, Panel(34, 0.3 m, 0.658825 m, Panel(31, 0.3 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0 inch, 0 inch, 14.825552663657628), 0.1 m, WidthAxis(), 0.0 m, 0.103175 m, 14.17412741229432), 0.1 m, WidthAxis(), 0.0 m, 0.20635 m, 13.283503235935846), 0.1 m, WidthAxis(), 0.0 m, 0.309525 m, 12.107634963898949), 0.29129999999999995 m, LengthAxis(), 0.0 m, 0.309525 m, 13.354347056291632), 0.19130000000000003 m, WidthAxis(), 0.0 m, 0.309525 m, 6.3245862271724675)), FinishedPanel(18778, FlippedPanel(20, WantedPanel(11, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom")), Panel(18776, 0.19130000000000003 m, 0.29129999999999995 m, Panel(18741, 0.19130000000000003 m, 0.36435000000000006 m, Panel(18738, 0.19130000000000003 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.303175 m, 0.103175 m, 9.441639998216449), 0.29129999999999995 m, WidthAxis(), 0.303175 m, 0.39764999999999995 m, 6.080357711202872), 0.29129999999999995 m, WidthAxis(), 0.303175 m, 0.39764999999999995 m, 6.113817958810537)), FinishedPanel(18742, FlippedPanel(21, WantedPanel(11, 0.29129999999999995 m, 0.19130000000000003 m, 0.25 inch, "Baltic Birch", "Bottom")), Panel(18740, 0.19130000000000003 m, 0.29129999999999995 m, Panel(18738, 0.19130000000000003 m, 0.658825 m, Panel(90, 0.45882500000000004 m, 0.658825 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.103175 m, 20.988673628788938), 0.19130000000000003 m, LengthAxis(), 0.303175 m, 0.103175 m, 9.441639998216449), 0.29129999999999995 m, WidthAxis(), 0.303175 m, 0.103175 m, 4.861282287013575)), FinishedPanel(93, WantedPanel(24, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Left 1/2"), Panel(91, 0.3 m, 0.1 m, Panel(89, 0.45882500000000004 m, 0.1 m, Panel(32, 0.45882500000000004 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 22.674447336342375), 0.1 m, WidthAxis(), 0.303175 m, 0.0 m, 3.1857737075534382), 0.3 m, LengthAxis(), 0.303175 m, 0.0 m, 3.085113820401692)), FinishedPanel(65, WantedPanel(25, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Left 2/2"), Panel(63, 0.3 m, 0.1 m, Panel(57, 0.3 m, 0.55565 m, Panel(34, 0.3 m, 0.658825 m, Panel(31, 0.3 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0 inch, 0 inch, 14.825552663657628), 0.1 m, WidthAxis(), 0.0 m, 0.103175 m, 14.17412741229432), 0.1 m, WidthAxis(), 0.0 m, 0.20635 m, 13.283503235935846), 0.1 m, WidthAxis(), 0.0 m, 0.20635 m, 2.6758682720368974)), FinishedPanel(58, WantedPanel(26, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Right 1/2"), Panel(56, 0.3 m, 0.1 m, Panel(34, 0.3 m, 0.658825 m, Panel(31, 0.3 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0 inch, 0 inch, 14.825552663657628), 0.1 m, WidthAxis(), 0.0 m, 0.103175 m, 14.17412741229432), 0.1 m, WidthAxis(), 0.0 m, 0.103175 m, 2.390624176358472)), FinishedPanel(35, WantedPanel(27, 0.3 m, 0.1 m, 0.25 inch, "Baltic Birch", "Right 2/2"), Panel(33, 0.3 m, 0.1 m, Panel(31, 0.3 m, 0.762 m, BoughtPanel(30, AvailablePanel(9, "1/4 30 × 30 Baltic Birch", 30 inch, 30 inch, 0.25 inch, "Baltic Birch", 36.0)), 0.3 m, LengthAxis(), 0 inch, 0 inch, 14.825552663657628), 0.1 m, WidthAxis(), 0 inch, 0 inch, 2.1514252513633085)))

We would like a clearer description though:

report(searcher;
       includeCutDiagram=true,
       includeCutGraph=false,
       filename="box_example_panel_cut_report.html")
"box_example_panel_cut_report.html"

View the result.

Box Faces

PanelCutting.FaceType
Face

Face is the abstract supertype for the tokens that identify each face of a Box.

Subtypes are Top, Bottom, Left, Right, Front and Back, which are all singleton types.

source

Edges

Between each pair of adjacent faces is an Edge. For each edge, the type of joinery to be used at that edge can be specified.

Grain Direction

For each face of a box, one can specify the grain direction.

PanelCutting.GDEitherType
GDEither

Use GDEither() if you don't care whether the grain runs parallel to the long or short edges of the panel.

source
PanelCutting.GDLongType
GDLong

Use GDLong() to indicate that the grain of the face should run parallel to its longer edges.

source
PanelCutting.GDShortType
GDShort

Use GDShort() to indicate that the grain of the face should run parallel to its shorter edges.

source
PanelCutting.GrainDirectionType
GrainDirection

GrainDirection is the abstract type for the choices of how the grain direction of the stock is oriented with respect to the shape of the panel.

source

Joint Type

PanelCutting.ButtJointType
ButtJoint(shortened)

If the joint type specified for a pair of Faces is a ButtJoint, then the face specified by shortened is shortened by the thickness of the other face.

source
PanelCutting.DadoJointType
DadoJoint(tongued::Face, tongue_length::LengthType)

If the joint type specified for a pair of faces is a DadoJoint, then the face specified by tongued is shortened by the thickness of the other face and then lengthened by tongue_length.

source
PanelCutting.FingerJointType
FingerJoint

If the joint type specified for a pair of faces is FingerJoint then the size of neither panel needs to be adjusted for that joint.

If you intend to make blind finger or dovetail joints, use DadoJoint.

If you want a miter joint, use FingerJoint.

source
PanelCutting.BoxType
Box(length::LengthType, width::LengthType, height::LengthType)

Define a box of the spcified dimensions.

source
PanelCutting.do_facesFunction
do_faces(::Function, ::Box)

Apply the function to each face of the Box that is not open/missing.

source
PanelCutting.neighborsFunction

neighbors(face1::Face, face2::Face)::Boll

Return true if face1 is a neighbor of face2.

source
neighbors(face::Face)

Return the Faces that are adjacent to the specified Face.

source