Box (Finger) Joints
Cutting box (finger) joints vertically in boards at the front of the machine
Starting with the box design template from the previous chapter: https://www.blockscad3d.com/community/projects/1012246 it is a simple matter of adding cuts for joinery along the corners of the box. Box joints are notable since they can be implemented along all edges of a box and with a bit of a relief can be cut flat on the machine simplifying fixturing and making manufacture more efficient, but the traditional technique of box oints at the box corner with the top and bottom in rabbeted grooves is simpler to calculate, and is discussed at: https://community.carbide3d.com/t/cnc-finger-joint-box/8880 and will be worked out in detail below. For further details, see: https://en.wikipedia.org/wiki/Box_joint
Note that while they may be colloquially termed Finger Joints, that term should properly be limited to the angled joints used for joining two pieces of wood (usually along their length). https://en.wikipedia.org/wiki/Finger_joint
Necessary additional variables are:
  • Endmill Diameter
  • Thickness of top/bottom grooves (or possibly rabbets for reducing board thickness along edges to fit into grooves)
  • (optional) a number to influence how many iterations of the joint features will be cut
  • Some sort of option for the lid ― options include a sliding lid, a rabbet along the edge for the lid to fit onto, or a through cut after glue-up and a suitable hinge
If box joints are cut in sheet goods with the boards flat, it is necessarily more complex
The interplay of the geometry of the board being flat and the cutter being round requires certain options and accommodations in the module for cutting the joints. It is necessary to specify:
  • Endmill Diameter
  • Whether the cut should be adjusted for endmill diameter
  • Whether the cut should be relieved for endmill diameter
  • If the cut should be adjusted at its beginning or end for the thickness of the material
Setting up a box design for this we duplicate the project from the previous chapter as: https://www.blockscad3d.com/community/projects/1052045 and update it with the desired dimensions:
  • 10 in wide
  • 8 in deep
  • 3.5 in high
  • 0.25" thick stock
  • 0.21653543" (5.5mm) thick top and bottom
  • 0.375" part spacing
As OpenSCAD code, this has the file beginning as:
1
//!OpenSCAD
2
3
Height = 3.5;
4
Width = 10;
5
Depth = 8;
6
Stock_Thickness = 0.25;
7
Top_Bottom_Thickness = 0.21653543;
8
Part_Spacing = 0.375;
9
//(millimeters or inches)
10
Units = 1; //[1:millimeters, 25.4:inches]
11
Preview_3D = true;
12
Joinery_Cut = false;
13
Generate_DXF = false;
14
Endmill_Diameter = 0.125;
Copied!
With all of the variables set up, it is necessary to scale for units:
1
h = Height * Units;
2
w = Width * Units;
3
d = Depth * Units;
4
st = Stock_Thickness * Units;
5
tbt = Top_Bottom_Thickness * Units;
6
ps = Part_Spacing * Units;
7
cd = Endmill_Diameter * Units;
8
fjcount = (ceil(Height / Stock_Thickness) / 2) * 2 + 1;
9
fjsize = h / fjcount;
Copied!
as shown below:
BlockSCAD: Box joints: Vertical: Variables
Add code to cut grooves for the lid and for the box joints themselves --- this will want a module for modeling the endmill:
1
module em() {
2
cylinder(r1=(cd / 2), r2=(cd / 2), h=(cd + st), center=false);
3
}
Copied!
which is then used in a module which makes a cut as a pocket:
1
module cut(cbx, cby, cex, cey, abx, aby, aex, aey, sd, ed) {
2
translate([0, 0, (sd - ed)]){
3
hull(){
4
translate([cbx, cby, 0]){
5
em();
6
}
7
translate([cex, cey, 0]){
8
em();
9
}
10
translate([abx, aby, 0]){
11
em();
12
}
13
translate([aex, aey, 0]){
14
em();
15
}
16
}
17
}
18
}
Copied!
BlockSCAD: Box joints: Vertical: Features
A legacy of the prototype box file used is that there is a module for modeling a board:
1
module board(ht, wd, dpth) {
2
cube([wd, dpth, ht], center=false);
3
}
Copied!
At some point in the future that may be used as a control point for modifying how the file is previewed or rendered.
That module is then used for additional modules for each sort of part needed. Quite simple for the top and bottom:
1
module topbottom() {
2
board(tbt, w - st, d - st);
3
}
Copied!
But requiring various calculations for the grooves and box joints for the sides:
1
module sides() {
2
difference() {
3
board(st, h, d);
4
5
cut(st / 2 + cd / 2, st / 2, st / 2 + cd / 2, d - st / 2, (st / 2 + tbt) - cd / 2, st / 2, (st / 2 + tbt) - cd / 2, d - st / 2, st, st / 2);
6
translate([(h - st * 2), 0, 0]){
7
cut(st / 2 + cd / 2, st / 2, st / 2 + cd / 2, d - st / 2, (st / 2 + tbt) - cd / 2, st / 2, (st / 2 + tbt) - cd / 2, d - st / 2, st, st / 2);
8
}
9
if (Generate_DXF == false) {
10
union(){
11
translate([0, (-cd), (-st)]){
12
for (i = [1 : abs(2) : fjcount]) {
13
translate([(i * fjsize), 0, 0]){
14
cube([fjsize, (cd + st), (st * 3)], center=false);
15
}
16
}
17
18
}
19
translate([0, (d - st), (-st)]){
20
for (i = [1 : abs(2) : fjcount]) {
21
translate([(i * fjsize), 0, 0]){
22
cube([fjsize, (cd + st), (st * 3)], center=false);
23
}
24
}
25
26
}
27
}
28
}
29
30
}
31
}
Copied!
and front/back:
1
module frontback() {
2
difference() {
3
board(st, w, h);
4
5
cut(st / 2, st / 2 + cd / 2, w - st / 2, st / 2 + cd / 2, st / 2, (st / 2 + tbt) - cd / 2, w - st / 2, (st / 2 + tbt) - cd / 2, st, st / 2);
6
translate([0, (h - st * 2), 0]){
7
cut(st / 2, st / 2 + cd / 2, w - st / 2, st / 2 + cd / 2, st / 2, (st / 2 + tbt) - cd / 2, w - st / 2, (st / 2 + tbt) - cd / 2, st, st / 2);
8
}
9
if (Generate_DXF == false) {
10
union(){
11
translate([(-cd), 0, (-st)]){
12
for (i = [0 : abs(2) : fjcount - 1]) {
13
translate([0, (i * fjsize), 0]){
14
cube([(cd + st), fjsize, (st * 3)], center=false);
15
}
16
}
17
18
}
19
translate([(w - st), 0, (-st)]){
20
for (i = [0 : abs(2) : fjcount - 1]) {
21
translate([0, (i * fjsize), 0]){
22
cube([(cd + st), fjsize, (st * 3)], center=false);
23
}
24
}
25
26
}
27
}
28
}
29
30
}
31
}
Copied!
In particular note the halving and then doubling of the number of box joints before adding 1 so as to ensure that an odd number is used for one set.
As well as laying out the parts for cutting:
BlockSCAD: Box joints: Vertical: DXF
The final aspect is cutting the joints ― the boards need to be offset from each other:
BlockSCAD: Box joints: Vertical: Boards
and cut in at least pairs (cutting all four at once will halve the number of operations):
BlockSCAD: Box joints: Vertical: Cuts
Export to OpenSCAD from BlockSCAD and then customize the file in OpenSCAD to allow exporting SVGs:
OpenSCAD: Box joints: Vertical: Parts
OpenSCAD: Box joints: Vertical: Cuts
The final code is using the checkboxes to control how the parts are rendered as discussed above:
1
if (Preview_3D == false) {
2
projection(cut = true)
3
{
4
union(){
5
translate([0, (h + ps), -st*0.9]){
6
sides();
7
}
8
translate([(h + ps), 0, -st*0.9]){
9
frontback();
10
}
11
translate([((h + ps) + st / 2), ((h + ps) + st / 2), 0]){
12
topbottom();
13
}
14
translate([(h + ps), ((h + ps) + (d + ps)), -st*0.9]){
15
frontback();
16
}
17
translate([((h + ps) + (w + ps)), (h + ps), -st*0.9]){
18
sides();
19
}
20
}
21
}} else {
22
if (Joinery_Cut == false) {
23
union(){
24
translate([0, ps, (h + ps)]){
25
rotate([0, 90, 0]){
26
sides();
27
}
28
}
29
translate([0, 0, 0]){
30
mirror([0,1,0]){
31
translate([ps, 0, ps]){
32
rotate([90, 0, 0]){
33
frontback();
34
}
35
}
36
}
37
}
38
translate([(ps + st / 2), (ps + st / 2), (st / 2)]){
39
topbottom();
40
}
41
translate([(ps + st / 2), (ps + st / 2), ((h + ps * 2) - st * 1.5)]){
42
topbottom();
43
}
44
translate([((w + ps * 2) - 0), ps, (h + ps)]){
45
rotate([0, 90, 0]){
46
mirror([0,0,1]){
47
sides();
48
}
49
}
50
}
51
translate([ps, (d + ps * 2), ps]){
52
rotate([90, 0, 0]){
53
frontback();
54
}
55
}
56
}
57
} else {
58
if (Generate_DXF == true) {
59
projection(){
60
union(){
61
cube([(cd / 2), (cd / 2), (cd / 2)], center=false);
62
translate([fjsize, (-(cd / 2)), 0]){
63
for (j = [0 : abs(2) : fjcount]) {
64
translate([(j * fjsize), 0, 0]){
65
cube([fjsize, ((st * 4 + ps * 3) + cd), (st * 2)], center=false);
66
}
67
}
68
69
}
70
}
71
}} else {
72
difference() {
73
union(){
74
translate([0, st, (st - d)]){
75
rotate([90, 0, 0]){
76
union(){
77
sides();
78
translate([0, 0, (-(st + ps))]){
79
sides();
80
}
81
}
82
}
83
}
84
translate([fjsize, ((st + ps) * 2 + st), st]){
85
rotate([90, 90, 0]){
86
union(){
87
frontback();
88
translate([0, 0, (-(st + ps))]){
89
frontback();
90
}
91
}
92
}
93
}
94
}
95
96
translate([fjsize, (-(cd / 2)), 0]){
97
for (j = [0 : abs(2) : fjcount]) {
98
translate([(j * fjsize), 0, 0]){
99
cube([fjsize, ((st * 4 + ps * 3) + cd), (st * 2)], center=false);
100
}
101
}
102
103
}
104
}
105
}
106
}
107
}
108
Copied!
Export each view and import into Carbide Create files:
Carbide Create: Box joints: Vertical: Parts
Carbide Create: Box joints: Vertical: Cuts
Once imported, it will be helpful for the Cuts file to set the stock size to match the boards being used, and to draw in rectangles representing the boards --- this will help to make clear how they are to be positioned for cutting and how the toolpaths will interact. The 3D preview of the toolpaths shows the cuts which will be made which is accurate save for the excess stock shown at the upper left and lower right corners (areas not encompassed by the orange highlighted boards in the image above):
Carbide Create: Box joints: Vertical: Cut 3D Preview
A desirable improvement is to trim the right-most geometry so that it only cuts the rearmost boards:
Carbide Create: Box joints: Vertical: Cuts trimmed
For the Parts file, it is only necessary at first to cut one side, and a single instance of the front/back part --- the parts need to be positioned in-line and separated by at least the endmill diameter plus 10%. The top/bottom will be measured for from the actual box after it is cut and dry fit. A further consideration here is the matter of accessing the interior of the box --- we will draw in a rectangle which may be cut with a narrow endmill (or one may do the traditional cutting apart with a bandsaw or hand saw):
Carbide Create: Box joints: Vertical: Parts in-line
Since the parts will be cut from an S4S board which is the correct size, it will only be necessary to machine the grooves for the top/bottom, the groove with tabs separating the lid from the base of the box (import a copy of the box joint cuts and rotate 90 degrees to allow for placement), and pockets at the ends of each board to cut them to length. To eliminate the need for tabs and the attendant post-processing, work-holding will be two-stage:
  • normal clamps outside the cutting area as well as a sacrificial caul clamp across the gap between the parts
  • additional cauls added after cutting the grooves to secure the stock and ensure the parts do not move once cut free
To ensure the board is square to the machine the first operation will be to machine a pocket in which to register the board, then the board will be secured and the grooves cut, then the additional cauls put in place, and the last operation will be cutting the pockets which cut the part to length.
Carbide Create: Box joints: Vertical: Toolpaths
Verify the preview:
Carbide Create: Box joints: Vertical: Parts Preview
Then cut the parts:
Carbide Create: Box joints: Vertical: Cutting Parts
Then mount them in a fixture such as: https://cutrocket.com/p/5cb25f3380844/ and cut the joints
Carbide Create: Box joints: Vertical: Setup for Joints
Carbide Create: Box joints: Vertical: Clamping for Joints
Carbide Create: Box joints: Vertical: Measuring Joints
Then dry-fit to measure for cutting the top/bottom (or just cut the top/bottom based on the initial CAD measurement):
Carbide Create: Box joints: Vertical: Joints Test Fit
The cut the top and bottom and glue and clamp up:
Carbide Create: Box joints: Vertical: Joint Glue Up
Last cut apart, clean up edges, finish, and install hardware:
Carbide Create: Box joints: Vertical: Assembled and Glued
Carbide Create: Box joints: Vertical: Cut Apart
Carbide Create: Box joints: Vertical: Ready for finishing and hardware
Finished box with box joints and installed hardware
While it is possible to do traditional box joints on a CNC, the tedium of multiple setups can be avoided on a CNC, which will be explored in the next chapter.
The files for the above box are available at: https://community.carbide3d.com/t/cnc-finger-joint-box/8880/133
Last modified 5mo ago
Copy link