-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 024dd82
Showing
4 changed files
with
571 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
OpenSCAD library to generate various parametric pegboard-like shapes, i.e. | ||
perforated rectangles. | ||
|
||
 | ||
|
||
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 <pegboard.scad> | ||
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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
/* | ||
* pegboard.scad - library for generating pegboard and grids of pegs/holes | ||
* | ||
* Copyright 2019 Craig Ringer <[email protected]> | ||
* | ||
* 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); | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.