/** * '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();