352 lines
No EOL
13 KiB
OpenSCAD
352 lines
No EOL
13 KiB
OpenSCAD
/**
|
|
* 'Roubo' bench
|
|
*
|
|
* Woodworking bench constructed primarily from softwood and without
|
|
* metal fastenings, traditional in parts of east Asia. The internally
|
|
* threaded blocks for the two vices are clearly turned from close-
|
|
* grained hardwood; I suspect the moving block for the end vice, and
|
|
* the treenails and locking wedges, should also be hardwood.
|
|
*
|
|
* All dimensions in millimetres.
|
|
*
|
|
* If reasonable sizes for top planks include 150mm (which is
|
|
* what we have) then the maximum possible diameter of a vice
|
|
* screw is 25mm; which means we may as well use commercial
|
|
* metal threaded rod. A wooden screw that small would be
|
|
* finicky to make and pretty fragile.
|
|
*
|
|
* if you can get 200x75mm for the top planks then a turned wooden screw
|
|
* may become more practical, but I have not parameterised the screw
|
|
* diameter
|
|
*
|
|
* This file is partially constructed from analysis of
|
|
* [this video](https://www.youtube.com/watch?v=XhAEbklxJME).
|
|
*
|
|
* Key points:
|
|
* 1:55-3:08 leg top joint detail
|
|
* 3:10-7:50 top plank end tenon detail
|
|
* 7:50-9:20 cutting end vice slot (I don't think the cutting of the
|
|
* internal slot for the end vice moving block is ever shown)
|
|
* 9:30-10:14 dowels to assemble top planks
|
|
* 10:24-13:25 detail of end vice assembly
|
|
* 10:24-13-09 top end cap assembly
|
|
* 13:30-14:20 cutting end tenons of leg cross brace
|
|
* 14:20-16:40; 19:30-20:00 front vice assembly
|
|
* 16:42-17:08 assembling leg cross brace
|
|
* 17:20-18:05 leg longitudinal brace
|
|
* 18:20-18:55 assembling leg assembly to top assembly
|
|
*
|
|
* This model is parameterised and is resizeable within limits, but if
|
|
* you vary any dimenstion by more than about 25% you're probably going
|
|
* to need to do some rework.
|
|
*
|
|
* Colours are for clarity only.
|
|
*
|
|
* Errata/todo:
|
|
* 1. locking wedges and treenails are generally not present in this
|
|
* drawing;
|
|
* 2. mortices and holes for wedges and treenails are not all present;
|
|
* 3. two of the tapered mortices for the longitudinal braces are the
|
|
* wrong way round;
|
|
* 5. while all tenons are modelled, not all mortices are; where a tenon
|
|
* intersects an apparently solid piece, assume there is a mortice!
|
|
*/
|
|
|
|
/**
|
|
* mortice for cross brace. This is a half-dovetail, locked with a cuboid
|
|
* locking block. There are no shoulders visible on the braces in the video.
|
|
*/
|
|
module cross_brace_mortice(section = 100) {
|
|
translate([section+1, 0, 0])
|
|
rotate([90,0,180])
|
|
linear_extrude(height=section/3)
|
|
polygon(points=[[0, 0],
|
|
[section + 2, section*0.25],
|
|
[section + 2, section *1.25],
|
|
[0, section *1.25]]);
|
|
}
|
|
|
|
/**
|
|
* mortice to receive the end of the longitudinal brace. Contra the cross
|
|
* brace, the longitudinal braces in the video are just plain planks without
|
|
* any shaping, and are locked by wedges. I cannot see any reason for this
|
|
* difference, unless it be to demonstrate two different styles of joint;
|
|
* but there may be one.
|
|
*
|
|
* NOTE: this module produces an object centred on the origin, which is the
|
|
* right way to do things, but most of the other modules in this file do
|
|
* not.
|
|
*/
|
|
module long_brace_mortice(section = 100, n = 0) {
|
|
// correct orientation
|
|
rotate([90,180,90+(180*n)])
|
|
// centre on origin
|
|
translate([0-((section +2)/2), 0-(section*0.625), 0-section/6])
|
|
// add thickness
|
|
linear_extrude(height=section/3)
|
|
// define shape
|
|
polygon(points=[[-1, 0],
|
|
[section + 1, section*0.25],
|
|
[section + 1, section *1.25],
|
|
[-1, section *1.25]]);
|
|
}
|
|
|
|
/**
|
|
* the cut removed from the top of the leg. Obviously, the cut in
|
|
* the bench top to receive the leg must be the inverse of this.
|
|
*/
|
|
module leg_top_cut(section = 100) {
|
|
translate([-1, -1, 0])
|
|
cube([(section/10)+1, section+2, section+1]);
|
|
translate([(section/10)+(section/4), -1, 0])
|
|
cube( [(section/5)+1, section+2, section+1]);
|
|
translate([section+1, 0, 0])
|
|
rotate([0,270,0])
|
|
linear_extrude( height = (section/5)+1)
|
|
polygon(points = [ [-1,-1], [(section/3),(section/2)], [-1, section+1], [section+2, section+1], [section+2, -1]]);
|
|
}
|
|
|
|
/**
|
|
* leg with all the cuts all legs have; additional cuts are needed on the
|
|
* vice leg.
|
|
*/
|
|
module leg(height = 950, section = 100, top_thickness = 75, n=0) {
|
|
color("red")
|
|
difference() {
|
|
cube( [section, section, height]);
|
|
{
|
|
translate([0, 0, height - top_thickness])
|
|
leg_top_cut(section);
|
|
translate([0, section/3, (height-section)/2])
|
|
cross_brace_mortice(section);
|
|
translate([section/2, 0, height/4])
|
|
long_brace_mortice( section, n);
|
|
}
|
|
};
|
|
};
|
|
|
|
module legs( height = 950, section = 100, bench_width =1500, bench_depth = 600, top_thickness=75) {
|
|
translate( [section-1, (bench_width/5)+ (section/2)])
|
|
rotate([0,0,180])
|
|
leg( height+1, section, top_thickness, 1);
|
|
translate( [section-1, (4 * (bench_width/5))+ (section/2)])
|
|
rotate([0,0,180])
|
|
leg( height+1, section, top_thickness, 2);
|
|
|
|
translate( [bench_depth+1-section, (bench_width/5)- (section/2), 0])
|
|
leg( height+1, section, top_thickness, 3);
|
|
translate( [bench_depth+1-section, (4 * (bench_width/5))- (section/2), 0])
|
|
leg( height+1, section, top_thickness, 4);
|
|
}
|
|
|
|
module top_plank_end_tenon(plank_width= 150, top_thickness=75) {
|
|
translate([ plank_width/8, 3 * top_thickness/4, top_thickness/4])
|
|
cube([ 3*(plank_width/4), top_thickness, top_thickness/2 ]);
|
|
translate([ (7 * plank_width/8) - top_thickness/2, -1, top_thickness/4])
|
|
cube([plank_width/4, top_thickness + 2, top_thickness/2]);
|
|
translate([ plank_width/8, -1, top_thickness/4])
|
|
cube([plank_width/4, top_thickness + 2, top_thickness/2]);
|
|
}
|
|
|
|
module top_plank_end_cut(plank_width=150, top_thickness=75) {
|
|
difference(){
|
|
translate([-1, -1, -1])
|
|
cube([plank_width + 2, top_thickness + 2 , top_thickness + 2]);
|
|
top_plank_end_tenon(plank_width, top_thickness);
|
|
};
|
|
};
|
|
|
|
//top_plank_end_cut();
|
|
|
|
module top_plank(bench_width = 1500, plank_width= 150, top_thickness=75) {
|
|
color("yellow")
|
|
difference() {
|
|
cube([ plank_width, bench_width, top_thickness]);
|
|
{
|
|
top_plank_end_cut( plank_width, top_thickness);
|
|
translate([0, bench_width, top_thickness])
|
|
rotate( [180, 0, 0])
|
|
top_plank_end_cut( plank_width, top_thickness);
|
|
}
|
|
};
|
|
}
|
|
|
|
module top_end(nplanks=4, plank_gap=20, plank_width=150, section=75) {
|
|
depth = (nplanks*plank_width) + (plank_gap * (nplanks - 1));
|
|
|
|
color("green")
|
|
difference() {
|
|
cube([ depth, section, section]);
|
|
for ( plank = [1:nplanks]) {
|
|
translate([(plank -1) * (plank_width + plank_gap), 0, 0])
|
|
top_plank_end_tenon( plank_width, section);
|
|
};
|
|
};
|
|
}
|
|
|
|
module end_vice_cut( plank_width=150, width=300, top_thickness=75) {
|
|
translate([plank_width * 0.375, 0, -1])
|
|
cube([plank_width/4, width, top_thickness + 2]);
|
|
translate([plank_width * 0.3125, 0, top_thickness * 0.4])
|
|
cube([plank_width*0.375, width, top_thickness/5]);
|
|
}
|
|
|
|
/**
|
|
* If reasonable sizes for top planks include 150mm (which is
|
|
* what we have) then the maximum possible diameter of a vice
|
|
* screw is 25mm; which means we may as well use commercial
|
|
* metal threaded rod. A wooden screw that small would be
|
|
* finicky to make and pretty fragile.
|
|
*/
|
|
module vice_screw(depth=400) {
|
|
color( "indigo")
|
|
rotate([180,90,0]) {
|
|
cylinder(h=depth, r=12.5);
|
|
cylinder(h=depth/10, r=25);
|
|
}
|
|
color( "darkslateblue")
|
|
translate([0 - depth/20, 0, 0])
|
|
rotate([60, 0, 0]) {
|
|
translate([ 0, 0, depth/4])
|
|
sphere(6);
|
|
translate([0, 0, 0 - (depth/4)])
|
|
sphere( 6);
|
|
cylinder(h=depth/2, r=4, center=true);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* todo: the block doesn't get correctly centred in the slot if the
|
|
* plank_width isn't 150mm, so I've got geometry wrong.
|
|
*/
|
|
module end_vice( plank_width=150, width=300, top_thickness=75){
|
|
// the end vice block: this has exactly the section of the end
|
|
// vice cut, so we'll use the same model (but much smaller width)
|
|
color("purple")
|
|
translate([50 - (width/5), 0, 0])
|
|
end_vice_cut( plank_width, 50, top_thickness);
|
|
rotate([0, 0, 90])
|
|
translate([width *1.1, 0 - plank_width/2, top_thickness/2])
|
|
vice_screw(width);
|
|
}
|
|
|
|
module bench_top( nplanks=4, ndowels = 4, plank_gap=20, bench_width=1500, plank_width=150, top_thickness=75) {
|
|
for (plank=[0:nplanks - 1]) {
|
|
translate( [plank*(plank_width + plank_gap), 0, 0]) {
|
|
if (plank == nplanks - 1) {
|
|
difference() {
|
|
top_plank(bench_width, plank_width, top_thickness);
|
|
translate([0, bench_width*(5/6), 0])
|
|
end_vice_cut(plank_width, bench_width/6, top_thickness);
|
|
}
|
|
translate([0, bench_width*(5/6), 0])
|
|
end_vice(plank_width, bench_width/6, top_thickness);
|
|
} else {
|
|
top_plank(bench_width, plank_width, top_thickness);
|
|
for (dowel=[1:ndowels]) {
|
|
translate([plank_width-(plank_gap/2), dowel * (bench_width/(ndowels+1)), top_thickness/2])
|
|
rotate([0,90,0])
|
|
color("orange")
|
|
cylinder(h=50+plank_gap, r=(top_thickness/6), center=true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//module top_end(nplanks=4, plank_gap=20, plank_width=150, section=75) {
|
|
|
|
translate([0, 1, 0])
|
|
top_end( nplanks, plank_gap, plank_width, top_thickness);
|
|
translate([ 0, bench_width - 1, top_thickness])
|
|
rotate([ 180, 0, 0])
|
|
top_end( nplanks, plank_gap, plank_width, top_thickness);
|
|
|
|
}
|
|
|
|
/**
|
|
* cross brace, as shown on the video, has half-dovetail end tenons
|
|
* without shoulders. That's not how I would do it!
|
|
*/
|
|
module cross_brace(section = 100, depth =600) {
|
|
color("blue")
|
|
rotate([90,0,0])
|
|
linear_extrude(height=section/3)
|
|
polygon(points=[[0,0],
|
|
[section,section/4],
|
|
[section,0],
|
|
[depth - section, 0],
|
|
[depth - section, section/4],
|
|
[depth, 0],
|
|
[depth, section],
|
|
[0, section]]);
|
|
}
|
|
|
|
/**
|
|
* long brace is shown in the video as just an unshaped plank.
|
|
*/
|
|
module long_brace(section=100, bench_width=1500) {
|
|
color("blue")
|
|
cube([ section/3, 0.8 * bench_width, section]);
|
|
}
|
|
|
|
module undercarriage( height = 950, leg_section = 100, bench_width =1500, bench_depth = 600, top_thickness=75) {
|
|
legs( height, leg_section, bench_width, bench_depth, top_thickness);
|
|
|
|
translate([ leg_section/3, 0.1*bench_width, height/4-leg_section*0.625])
|
|
long_brace(leg_section, bench_width);
|
|
translate([ bench_depth-(2*(leg_section/3)), 0.1*bench_width, height/4-leg_section*0.625])
|
|
long_brace(leg_section, bench_width);
|
|
translate([0, bench_width/5 + leg_section/6, height/2 - leg_section/2])
|
|
cross_brace(leg_section,bench_depth);
|
|
translate([0, (4 * bench_width/5) + leg_section/6, height/2 - leg_section/2])
|
|
cross_brace(leg_section,bench_depth);
|
|
}
|
|
|
|
|
|
module front_vice( width=150, height=800, depth=400) {
|
|
// front jaw of vice:
|
|
color("purple")
|
|
difference() {
|
|
cube([width/3, width, height]);
|
|
translate([width/3 + 1, -1, height + 1])
|
|
rotate([180, 90, 270])
|
|
linear_extrude(height=width+2)
|
|
polygon([[0,0], [0,30], [width,0], [width,0]]);
|
|
}
|
|
// bottom link:
|
|
color("fuchsia")
|
|
translate([0-(depth*0.8), (width/2)-(width/12), width/2])
|
|
difference() {
|
|
cube([depth, width/6, 75]);
|
|
for( hole_pair=[1:5]) {
|
|
translate([25+(depth/10 * hole_pair), 50, 25])
|
|
rotate([90,0,0])
|
|
cylinder(width/2, r=12.5);
|
|
translate([(depth/10 * hole_pair), 50, 50])
|
|
rotate([90,0,0])
|
|
cylinder(width/2, r=12.5);
|
|
}
|
|
};
|
|
translate([(depth*0.22), width/2, height * 0.75])
|
|
vice_screw( depth);
|
|
}
|
|
|
|
|
|
module bench( height=950, width=1500, nplanks=4, ndowels=4, plank_width=150, plank_gap=20, top_thickness=75, leg_section=100) {
|
|
bench_depth=((plank_width*nplanks)+(plank_gap*(nplanks -1)));
|
|
|
|
undercarriage(height, leg_section, width, bench_depth, top_thickness);
|
|
translate([0, 0, height-top_thickness])
|
|
bench_top(nplanks, ndowels, plank_gap, width, plank_width,top_thickness);
|
|
|
|
translate([ bench_depth + plank_gap,(width/5)-(plank_width/2), height/4])
|
|
front_vice(plank_width, height*0.75, bench_depth*0.666);
|
|
}
|
|
|
|
//translate([0, 0, 875])
|
|
// bench_top();
|
|
|
|
bench();
|
|
translate([1000, 0, 0])
|
|
bench(width=1700, nplanks=3, plank_width = 200);
|
|
|
|
//legs(); |