/* Double-sided magnetic board for TRIUMPH! (15 mm figures) All dimensions are millimetres. The default exposed playing area is 960 x 640 mm: 24 x 16 standard 40 mm TRIUMPH! base widths. Construction represented here: - a 24 gauge cold-rolled carbon-steel core; - 30 mm wide wooden frames on both faces; - 30 mm total finished board depth, with the steel centred; - 10 mm square blind corner holes in the top frame only; - full-length short-end pieces, with long-edge pieces between them. The steel extends below the whole frame. This preserves a regulation-size exposed playing area and gives the blind holes a metal stop. */ // ---------- User parameters ---------- playing_length = 960; // long dimension of exposed gaming area playing_width = 640; // short dimension of exposed gaming area frame_width = 30; // wooden border width in plan steel_thickness = 0.607; // nominal 24 ga carbon steel (0.0239 in) finished_depth = 60 + steel_thickness; // total board thickness, including steel peg_hole_size = 10; // square blind hole width peg_hole_inset = 10; // from each outside edge to the hole edge // "assembled", "exploded", "steel", "top_frame", "bottom_frame", // "short_end_piece", or "long_edge_piece" display = "assembled"; exploded_gap = 25; // Rendering colors only wood_color = [0.58, 0.34, 0.16]; steel_color = [0.50, 0.54, 0.58]; // ---------- Derived dimensions ---------- board_length = playing_length + 2 * frame_width; board_width = playing_width + 2 * frame_width; wood_face_depth = (finished_depth - steel_thickness) / 2; steel_bottom = -steel_thickness / 2; steel_top = steel_thickness / 2; bottom_wood_bottom = -finished_depth / 2; top_wood_bottom = steel_top; epsilon = 0.02; assert(playing_length > 0 && playing_width > 0, "The playing-area dimensions must be positive."); assert(frame_width > 0 && finished_depth > steel_thickness, "The frame must be wider than zero and deeper than the steel."); assert(peg_hole_size > 0 && 2 * peg_hole_inset + peg_hole_size <= frame_width, "Corner holes must fit completely inside the frame width."); // ---------- Components ---------- module steel_core(z = steel_bottom) { color(steel_color) translate([0, 0, z]) cube([board_length, board_width, steel_thickness]); } // Full-side pieces at the two short ends of the rectangular board. module short_end_rails(height) { cube([frame_width, board_width, height]); translate([board_length - frame_width, 0, 0]) cube([frame_width, board_width, height]); } // Long-edge pieces occupy the remaining distance between the end pieces. module long_edge_rails(height) { translate([frame_width, 0, 0]) cube([playing_length, frame_width, height]); translate([frame_width, board_width - frame_width, 0]) cube([playing_length, frame_width, height]); } module wood_frame_blank(height) { union() { short_end_rails(height); long_edge_rails(height); } } module corner_holes(height) { hole_xy = [ [peg_hole_inset, peg_hole_inset], [board_length - peg_hole_inset - peg_hole_size, peg_hole_inset], [peg_hole_inset, board_width - peg_hole_inset - peg_hole_size], [board_length - peg_hole_inset - peg_hole_size, board_width - peg_hole_inset - peg_hole_size] ]; for (p = hole_xy) translate([p[0], p[1], -epsilon]) cube([peg_hole_size, peg_hole_size, height + 2 * epsilon]); } module wood_frame(height, drilled = false) { color(wood_color) difference() { wood_frame_blank(height); if (drilled) corner_holes(height); } } module assembled_board(explode = 0) { // Sheet remains centred; the two frames move apart in exploded mode. steel_core(); translate([0, 0, top_wood_bottom + explode]) wood_frame(wood_face_depth, drilled = true); translate([0, 0, bottom_wood_bottom - explode]) wood_frame(wood_face_depth, drilled = false); } // ---------- Display / export selector ---------- if (display == "assembled") { assembled_board(); } else if (display == "exploded") { assembled_board(exploded_gap); } else if (display == "steel") { translate([0, 0, -steel_bottom]) steel_core(); } else if (display == "top_frame") { wood_frame(wood_face_depth, drilled = true); } else if (display == "bottom_frame") { wood_frame(wood_face_depth, drilled = false); } else if (display == "short_end_piece") { // One representative full-side rail, placed at the origin for export. color(wood_color) cube([frame_width, board_width, wood_face_depth]); } else if (display == "long_edge_piece") { // One representative infill rail, placed at the origin for export. color(wood_color) cube([playing_length, frame_width, wood_face_depth]); } else { assert(false, str("Unknown display mode: ", display)); }