From 024dd8246ba8b04e9ef00ff42442929ab813fecc Mon Sep 17 00:00:00 2001 From: Craig Ringer Date: Mon, 21 Oct 2019 16:01:33 +0800 Subject: [PATCH] Initial pegboard module --- README.md | 163 ++++++++++++++++++++++++++++++++++ pegboard.scad | 209 ++++++++++++++++++++++++++++++++++++++++++++ pegboard_tests.png | Bin 0 -> 57137 bytes pegboard_tests.scad | 199 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 571 insertions(+) create mode 100644 README.md create mode 100644 pegboard.scad create mode 100644 pegboard_tests.png create mode 100644 pegboard_tests.scad diff --git a/README.md b/README.md new file mode 100644 index 0000000..467a630 --- /dev/null +++ b/README.md @@ -0,0 +1,163 @@ +OpenSCAD library to generate various parametric pegboard-like shapes, i.e. +perforated rectangles. + +![Image of the test suite output](pegboard_tests.png) + +See [Thingiverse](https://www.thingiverse.com/thing:3926282) for previews, +examples, etc. Customiser enabled, but really, it's not at all hard to just use +OpenSCAD for the job. + +## Basic Usage + +Download `pegboard.scad` and put it in the same directory as your project, then: + +``` +use + +board_dimensions = [30, 30, 3]; // 30mm x 30mm rectangle, 3mm thick +hole_diameter = 3; // 3mm holes +hole_pitch = 6; // hole centers 6mm apart + +pegboard(board_dimensions, hole_diameter, hole_pitch); +``` + +(It's better to put it on the OpenSCAD library path rather than putting it in +your project directory, but that'll get you started). + +## Modules: + +* **`pegboard`**: produces a rectangular board with peg holes. + + Holes may optionally be less than full depth, and peg pattern may optionally + be a square grid instead of the hexagonally packed default arrangement. + Pitch, peg diameter and edge margins with no pegs are all customisable. + + `pegboard(dims, hole_diameter, hole_pitch)` + `pegboard(dims, hole_diameter, hole_pitch, hexpattern, margins, center, hole_depth)` + +* **`peg_grid`**: produces a rectangular arrangement of cylindrical pegs, i.e. a + negative pegboard. Used to subtract from other objects. You can clip it etc + too. + + `peg_grid(dims, peg_diameter, peg_pitch)` + `peg_grid(dims, peg_diameter, peg_pitch, hexpattern, margins, center)` + +## Parameters + +All parameters may be passed as positional parameters or by name. + +All dimensional units are millimeters. + +* **`dims` (3-vector of +number, required)**: + + Vector `[x,y,z]` of dimensions of the pegboard, or in the case of the peg + grid, the outer bounds within which the grid should be laid out. Positive + number. + + For `peg_grid` the z-dimension is the peg length; for `pegboard` it's the + board thickness and the holes default to fully punching through both sides of + the board. Internally some slop is added when subtracting the pegs to ensure + there are no floating point artifacts here. + +* **`hole_diameter` / `peg_diameter` (+number, required)**: + + Diameter of the pegs/holes. + + Remember printer tolerances, print some tests for fit before you do a big + print run, or print with extra wall thickness and be prepared to do some + filing/drilling. Consider whether you're going for a friction fit or free + movement too. + + TODO: support non-cylindrical pegs/holes: basic shapes, cylinders with + friction grip knobs, and `children()` for arbitrary holes. + +* **`hole_pitch` / `peg_pitch` (+number, required)**: + + Center-to-center spacing of the pegs/holes. The solid space between holes + (the bridge size) will be `hole_pitch` - `hole_diameter`. If set the same as + `hole_diameter`, pegs/holes will touch. If less than `hole_diameter` the + pegs/holes will intersect each other. See examples in test suite. + + A sensible initial choice can be `hole_diameter * 2`, which makes the spaces + between pegs (bridges between holes) the same size as the pegs themselves. + +* **`hexpattern` (boolean, optional)**: + + Arrange the pegs/holes in a tighter packed hexagonal pattern where every second + row's pegs are horizontally offset by `hole_pitch/2` from the pegs on the + adjacent rows. This yields a stronger shape because the pattern is now + tesselated triangles instead of squares. also gives more options for + positioning things in the holes. + + hexpattern breaks y-symmetry if an even number of rows are produced. + + Default: `true` + +* **`margins` (number or 2-vector of number, optional):** + + Margins around the edges of the board where no holes should be placed. Can be + a 2-vector `[x,y]` or a scalar. Zero margins makes the hole edges touch the + edge of the square defined by `dims` so it's the default for `peg_grid`, + while `peg_board` defaults to a hole radius worth of margin. + + If `center=true` and the hole pitch/diameter doesn't cause the holes to fully + pack the available space, actual effective will be slightly larger because + the hole grid will get centered. + + Default: `peg_diameter/2` for `pegboard`, `0` for `peg_grid`. + +* **`center` (boolean, optional):** + + Arrange the holes / peg grid within the supplied x/y `dims` bounds so that + any extra space not filled by the peg grid is equal on both sides, i.e. the + peg grid is centered. This enables you to safely mirror the peg grid across + the y axis (flip horizontally) and have the holes in the mirrored pieces line + up. It'll be vertically symmetrical too unless it's a hex pattern grid with an + even number of rows. + + Default: `true` + +## Examples + +See the basic test suite in `pegboard_test.scad` for examples. + +beam_margins = hole_pitch/2; + +See `pegboard.scad`, its tests and its examples for all the things you can do with it. + +## License and derivatives + +This library is BSD licensed. The license specifies what you *can* do with it, +and nothing in the following restricts the permissions granted by the license. + +I have a few requests that I would *appreciate* you following, though the +license does not require you to do so: + +1. **Please do not create derivatives of this library on Thingiverse or + elsewhere without linking to the original**. I would prefer that you submit + changes to me for inclusion in the main library instead, if you can make + them work without breaking the existing tests. +2. Credit me as original author if you publish derivatives. Feel free to credit + me for the bugs too ;) . + +## Testing + +To test changes you make to the library, enable the run_tests parameter in the +file. It'll produce a set of tests. There are a few assertions in there for +basic functionality, but OpenSCAD doesn't make it easy to write useful tests +since it lacks the ability to introspect objects usefully. So you have to +eyeball it and compare the described expected results to what you see on the +test pattern. A sample test result `.stl` and `.png` is included. + +## TODO + +At some stage I may be motivated to: + +* Support non-rectangular shapes +* Support non-cylindrical holes +* Use OpenSCAD's `children()` to let you define your own arbitrary hole modules + +... none of which is hard, so if you want it, ask me for advice and I'll point you in the right direction. + +Code contributions appreciated. + diff --git a/pegboard.scad b/pegboard.scad new file mode 100644 index 0000000..ab7b68e --- /dev/null +++ b/pegboard.scad @@ -0,0 +1,209 @@ +/* + * pegboard.scad - library for generating pegboard and grids of pegs/holes + * + * Copyright 2019 Craig Ringer + * + * BSD Licensed + */ + +// if negative, print +negative = false; + +// Length (mm) of board section (x axis) +p_board_length = 120; +// Width (mm) of board section (y axis) +p_board_width = 21; +// Depth (mm) of board section (z axis) +p_board_thickness = 3; + +// Diameter of holes in board +p_hole_diameter = 4.0; +// Hole pitch (distance between centers of holes) +p_hole_pitch = 5.0; +// Should holes be in a packed triangular environment (true) or a regular grid (false) +p_hole_hexpattern = true; + +// Margins around the edges of the board where no holes should be placed. Can be a 2-array [x,y] or a scalar. Actual margins will be slightly larger because the hole grid will get centered. +p_beam_margins = p_hole_pitch/2; + +/* + * Example usage: + * + * pegboard([board_length, board_width, board_thickness], + * peg_diameter, peg_pitch, hole_hexpattern, + * beam_margins); + * + */ + + /* [Hidden] */ + +module pegboard(dims, hole_diameter, hole_pitch, hexpattern = true, margins = undef, center = true, hole_depth = undef) +{ + t_length = dims[0]; // x + t_height = dims[1]; // y + t_thickness = dims[2]; // z + + hole_depth = + hole_depth == undef ? t_thickness: hole_depth; + + // margins can be an xy array or a scalar, or unset. Negative + // margins are allowed. + margin_x = margins == undef ? hole_diameter/2 : + margins[0] == undef ? margins : margins[0]; + margin_y = margins[1] == undef ? margin_x : margins[1]; + + punchthrough_slop = 0.1; + + // Make sure the pegs punch fully out the top, and the bottom if + // we're going through both sides. + peg_zoff = hole_depth >= t_thickness ? -punchthrough_slop : 0; + peg_length = hole_depth + punchthrough_slop - peg_zoff; + + difference() { + cube([t_length, t_height, t_thickness]); + + + // Defend against floating point issues with holes + // that fully punch out of the object by pushing + // them right through. Extra height will be added + // to the hole being punched to compensate. + translate([0,0,peg_zoff]) + peg_grid([t_length,t_height,peg_length], hole_diameter, hole_pitch, hexpattern, [margin_x, margin_y], center) + + { + // Hack to allow tests etc + assert($strip_ncols != undef); + assert($strip_nrows != undef); + let ($strip_ncols = $strip_ncols, + $strip_nrows = $strip_nrows, + $pegboard_running_tests = true) + children(); + + } + } +} + +module peg_grid(dims, peg_diameter, peg_pitch, hexpattern=true, margins=0, center = true) +{ + t_length = dims[0]; // x + t_height = dims[1]; // y + peg_length = dims[2]; // z + + // margins can be an xy array or a scalar, or unset. Negative + // margins are allowed. + margin_x = margins == undef ? 0 : margins[0] == undef ? margins : margins[0]; + margin_y = margins[1] == undef ? margin_x : margins[1]; + + assert(t_length > 0); + assert(t_height > 0); + assert(peg_length > 0); + assert(peg_diameter > 0); + assert(peg_pitch >= 0); + assert(margin_x != undef); + assert(margin_y != undef); + + /* + * distance between centers of two adjacent holes in the same row. + */ + xpitch = peg_pitch; + /* + * Y gap between lines drawn through centers of holes in two + * adjacent rows. If hexpatterned then there are equilateral + * triangles between centers of each row; the pitch is the + * hypotenuse so the y-gap is the long side of the triangle + * formed between the y-axis and the line between the centers. + */ + ypitch = xpitch * (hexpattern ? sin(60) : 1); + nrows = 1 + max(0, floor( + (t_height - margin_y * 2 - peg_diameter)/ypitch)); + ncols = 1 + max(0, floor( + (t_length - margin_x * 2 - peg_diameter)/peg_pitch)); + + assert(nrows != undef && nrows >= 0); + assert(ncols != undef && ncols >= 0); + assert((xpitch > 0 && ncols > 0) || ncols == 0); + assert((ypitch > 0 && nrows > 0) || nrows == 0); + + /* + * Compute bridge size for reporting use. Bridge min + * size should not vary based on hexpattern since we x-offset + * to compensate for the closer rows. + */ + bridge_size = (peg_pitch - peg_diameter); + + /* + * Work out how much space is left once we pack in the hole grid + * area and subtract the margins. If we pad the margins by this + * leftover/unused space the grid will be centered. + */ + unused_x = t_length - margin_x * 2 - xpitch * (ncols-1) - peg_diameter; + unused_y = t_height - margin_y * 2 - ypitch * (nrows-1) - peg_diameter; + adjusted_margin_x = margin_x + (center ? unused_x / 2 : 0); + adjusted_margin_y = margin_y + (center ? unused_y / 2 : 0); + + echo(str("pegboard or peg_grid: ", t_length, "x", t_height, " margins ", adjusted_margin_x, "x", adjusted_margin_y, " rows ", nrows, " cols ", ncols, , " holes diameter ", peg_diameter, (hexpattern ? " hexpatterned" : " straight"), " at pitch ", peg_pitch, " (bridges ", bridge_size, ")")); + + translate([adjusted_margin_x + peg_diameter/2, + adjusted_margin_y + peg_diameter/2, 0]) + union() { + for (r = [0 : 1 : nrows - 1]) + { + for (i = [0 : 1 : ncols - 1]) + { + /* + * For hexpatterned holes, t_ncols is computed to fit + * the non-offset row within the margins. The last + * hole of the offset row won't fit and must be + * omitted. + */ + if (!(hexpattern && r % 2 == 1 && i == ncols - 1)) + { + // Handle row hexpattern + translate( + hexpattern ? + /* + * Rows are hexpatterned so the y-offset is + * sin(45deg) of the pitch and every second + * row is inset by half the pitch. + */ + [(r % 2)*xpitch/2 + i*xpitch, r * ypitch, 0] + : + // Not hexpatterned, regular grid + [i*xpitch, r * ypitch, 0] + ) + cylinder(d=peg_diameter, + h=peg_length); + } + } + } + }; + + + /* + * OpenSCAD doesn't let us assign values to outer scopes. Nor does + * it let us pass functions as objects. So to allow assertions etc, + * permit definition of child objects here but discard the product. + * + * There must be a better way to do this; what we really want to + * do is pass a function to evaluate, or export some values to + * the outer scope. + */ + let ($strip_ncols = ncols, + $strip_nrows = nrows, + $pegboard_running_tests = true) + children(); +} + + + +if (negative) { + peg_grid([p_board_length, p_board_width, p_board_thickness], + p_hole_diameter, p_hole_pitch, p_hole_hexpattern, + p_beam_margins); +} +else +{ + pegboard([p_board_length, p_board_width, p_board_thickness], + p_hole_diameter, p_hole_pitch, p_hole_hexpattern, + p_beam_margins); +} \ No newline at end of file diff --git a/pegboard_tests.png b/pegboard_tests.png new file mode 100644 index 0000000000000000000000000000000000000000..549a7eb91700d9ab609c2b2f4b771f8ab3511a80 GIT binary patch literal 57137 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 Hafkl}v{gjC literal 0 HcmV?d00001 diff --git a/pegboard_tests.scad b/pegboard_tests.scad new file mode 100644 index 0000000..ba8dfac --- /dev/null +++ b/pegboard_tests.scad @@ -0,0 +1,199 @@ +use + +module test_case(test_number, test_name, xoff, yoff) +{ + echo(str("--> test ", test_number, ": ", test_name)); + + translate([xoff, yoff, 0]) + let($test = test_number, $test_name = test_name) + children(); + + echo(str("<-- test ", test_number, ": ", test_name)); +} + +module assert_expect(varname, actual, expected) +{ + assert(expected == actual, str(varname, ": expected ", expected, ", got ", actual)); +} + +// Hint: use F12 "thrown together" view for tests +module pegboard_test() +{ + // Coarse draft for tests; we don't need quality and we're going + // to have a LOT of holes. + $fn = 10; + + // Make sure we have proper is_undef support + assert(is_undef($undefined)); + + //=== BASIC FORMS === + + test_case(1, "default margin, hexpattern", 0, 0) + { + pegboard([20, 20, 3], 1, 2, hexpattern=true) + { + assert_expect("$test", $test, 1); // Can see $test + assert($strip_ncols != undef); // Can see $strip_ncols + assert_expect("$strip_ncols", $strip_ncols, 10); + assert_expect("$strip_nrows", $strip_nrows, 11); + } + }; + + test_case(1, "scalar margin, hexpattern", 25, 0) + { + pegboard([20, 20, 3], 1, 2, hexpattern=true, margins=4) { + assert_expect("$strip_ncols", $strip_ncols, 6); + assert_expect("$strip_nrows", $strip_nrows, 7); + } + } + + test_case(1, "vector margin, hexpattern", 50, 0) { + pegboard([20, 20, 3], 1, 2, true, [2, 4]) { + assert_expect("$strip_ncols", $strip_ncols, 8); + assert_expect("$strip_nrows", $strip_nrows, 7); + } + } + + test_case(2, "default margin, grid", 0, 25) { + pegboard([20, 20, 3], 1, 2, false) { + assert_expect("$strip_ncols", $strip_ncols, 10); + assert_expect("$strip_nrows", $strip_nrows, 10); + } + } + + test_case(3, "scalar margin, grid", 25, 25) { + pegboard([20, 20, 3], 1, 2, false, 4) { + assert_expect("$strip_ncols", $strip_ncols, 6); + assert_expect("$strip_nrows", $strip_nrows, 6); + } + } + + test_case(4, "vector margin, grid", 50, 25) { + pegboard([20, 20, 3], 1, 2, false, [2, 4]) + { + assert_expect("$strip_ncols", $strip_ncols, 8); + assert_expect("$strip_nrows", $strip_nrows, 6); + } + } + + // === SMALL FORMS === + + // Should produce 1 row, 1 col, as diameter is 1, margin total is 1, + // height is 2 + test_case(5, "small 1x1", 0, 50) { + pegboard([2, 2, 3], 1, 2, false, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 1); + assert_expect("$strip_nrows", $strip_nrows, 1); + } + } + + // With 1 hole, hexpattern doesn't matter so same result + test_case(6, "small 1x1", 10, 50) { + pegboard([2, 2, 3], 1, 2, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 1); + assert_expect("$strip_nrows", $strip_nrows, 1); + } + } + + // This should have 4 holes + test_case(7, "small 2x2", 20, 50) { + pegboard([4, 4, 3], 1, 2, false, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 2); + assert_expect("$strip_nrows", $strip_nrows, 2); + }; + } + + // This should produce 3 holes. The bottom row isn't + // affected by hexpattern. + test_case(8, "small 3 holes triangle arrangement", 30, 50) { + pegboard([4, 4, 3], 1, 2, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 2); + assert_expect("$strip_nrows", $strip_nrows, 2); + }; + } + + // === Shallow holes ==== + + // Holes in this one only go half way through + test_case(9, "shallow", 40, 50) { + pegboard([2, 2, 3], 1, 2, false, 0.5, 1.5); + } + + // Centered vs uncentered. Uncentered should have holes + // cutting through edges. Also tests negative margins. + test_case(10, "centered", 0, 60) { + pegboard([8,8,5], 2, 4, false, 0, center=true); + } + + test_case(11, "uncentered", 10, 60) { + pegboard([8,8,5], 2, 4, false, 0, center=false); + } + + // Negative margins: Holes will cut through edges + // to form semicircular cutouts + one hole in center. + test_case(12, "negative margin, holes intersect edges", 20, 60) { + pegboard([8,8,5], 2, 4, false, -2); + } + + // === TIGHT PACKING === + + // No hexpattern. Holes should touch, bridge ~= 0 + test_case(13, "pitch=diam, grid, holes touch each other", 30, 60) { + pegboard([7, 7, 3], 2, 2, false, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 3); + assert_expect("$strip_nrows", $strip_nrows, 3); + } + } + + // With hexpattern the holes should touch too, but not + // overlap. + test_case(14, "pitch=diam, hex, holes touch each other", 40, 60) { + pegboard([7, 7, 3], 2, 2, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 3); + assert_expect("$strip_nrows", $strip_nrows, 3); + } + } + + // Hole bigger than object will force 1 hole + // which will cut out edges + test_case(15, "hole bigger than object, 1 hole cuts out edges", 50, 60) { + pegboard([7, 7, 3], 8, 1, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 1); + assert_expect("$strip_nrows", $strip_nrows, 1); + } + } + + // Holes bigger than pitch will overlap holes. + test_case(16, "hole bigger than pitch, holes overlap each other", 60, 60) { + pegboard([20, 7, 3], 6, 5, true, 0.5); + } + + + // === LONG THIN FORMS === + test_case(17, "2x30 hex/stagger", 0, 70) { + pegboard([60, 4, 3], 1, 2, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 30); + assert_expect("$strip_nrows", $strip_nrows, 2); + } + } + + test_case(18, "2x30 grid", 0, 75) { + pegboard([60, 4, 3], 1, 2, false, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 30); + assert_expect("$strip_nrows", $strip_nrows, 2); + } + } + + // === NEGATIVES === + + // Pegs shouldn't be longer than needed when making + // negative forms. + test_case(19, "negative form pegs", 0, 80) { + peg_grid([60, 4, 3], 1, 2, true, 0.5) { + assert_expect("$strip_ncols", $strip_ncols, 30); + assert_expect("$strip_nrows", $strip_nrows, 2); + } + } +} + +pegboard_test();