diff options
| author | Dustin Walker <dwalker@dwalker.xyz> | 2026-09-07 21:55:22 -0400 |
|---|---|---|
| committer | Dustin Walker <dwalker@dwalker.xyz> | 2026-09-07 21:55:22 -0400 |
| commit | 1e232ebc071e198590aa9003fb273c61462969a5 (patch) | |
| tree | 90a489291d4520ac788d1b55dd96c26e585a108b /triumphboard/triumph_board.scad | |
initial commit
Diffstat (limited to 'triumphboard/triumph_board.scad')
| -rw-r--r-- | triumphboard/triumph_board.scad | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/triumphboard/triumph_board.scad b/triumphboard/triumph_board.scad new file mode 100644 index 0000000..11f5c5a --- /dev/null +++ b/triumphboard/triumph_board.scad @@ -0,0 +1,142 @@ +/* + 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)); +} |
