roubo/roubo.scad

337 lines
No EOL
12 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);
}
};
}
//top_plank();
module top_end(depth=600, section=75) {
color("green")
cube([ depth, section, section]);
// todo: add mortices
}
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);
}
}
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);
}
}
}
}
translate([0, 1, 0])
top_end((plank_width * nplanks)+(plank_gap * (nplanks - 1)), top_thickness);
translate([ 0, bench_width - (top_thickness +1), 0])
top_end((plank_width * nplanks)+(plank_gap * (nplanks - 1)), 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, width/2]);
for( hole_pair=[1:5]) {
translate([25+(depth/10 * hole_pair), width/3, width/6])
rotate([90,0,0])
cylinder(width/2, r=12.5);
translate([(depth/10 * hole_pair), width/3, width/3])
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(width=1700);