Created
February 9, 2020 22:12
-
-
Save jbrown123/1bf9c3d9404a4797b33c3d3d238f746e to your computer and use it in GitHub Desktop.
A library of openscad functions that I frequently use in my designs
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
/* | |
* MyLib.scad | |
* | |
* A bunch of openscad functions that I might need from time to time in various projects | |
* | |
* Usage: use <mylib.scad>; | |
* | |
* Version: 20200209 | |
* | |
* Copyright (c) 2011-2020 by James H. Brown | |
* License: Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported (CC BY-NC-SA 3.0) | |
* License text: https://creativecommons.org/licenses/by-nc-sa/3.0/ | |
* | |
* You are explicitly allowed to use this library to generate objects that you can then sell commercially. | |
* You may NOT sell the library itself nor any of it's individual components. | |
* | |
* Some components below are included from other authors. Their license information is included as well | |
* and applies to their code only. Any modifications I made follow Creative Commons licensing rules. | |
* | |
* Some of these functions require openscad 2019.05 or later. | |
*/ | |
// openscad cheat sheet: http://www.openscad.org/cheatsheet/ | |
// translate from inches to mm | |
function in2mm(in)=25.4*in; | |
// translate diameter to radius & radius to diameter | |
function d2r(d)=d/2; | |
function r2d(r)=r*2; | |
// torus - a doughnut or o-ring shape | |
// specify either od & id or cs + either od or id | |
// cs = cross section (diameter of the torus itself) | |
// center, if true, will center the torus in the Z plane | |
// angle defaults to 360 & specifies the number of degrees to sweep, | |
// starting at the positive X axis. The direction of the sweep follows | |
// the Right Hand Rule, hence a negative angle will sweep clockwise. | |
// spin defaults to 0 & specifies the degrees of rotation of a torus around Z | |
module torus(od=-1, id=-1, cs=0, center=false, angle=360, spin=0) | |
{ | |
if (od == -1 && id == -1) | |
echo("<font color='red'>both od & id are not set in torus()</font>"); | |
else | |
{ | |
od = (od == -1) ? id + (cs * 2) : od; | |
id = (id == -1) ? od - (cs * 2) : id; | |
or = od/2; | |
ir = id/2; | |
rotate([0,0,spin]) // spin around Z as requested | |
translate([0, 0, center==true ? 0 : (or-ir)/2]) | |
// This rotation is required because apparelty rotate_extrude doesn't | |
// start at the same place as the sides of a sphere with an odd $fn value | |
// Without this if you put a torus on top of a cylinder with an odd $fn, | |
// they will be misaligned by 1/2 a rotation | |
rotate([0,0,$fn % 2 == 1 ? 360/$fn/2 : 0]) | |
rotate_extrude(convexity=10, angle=angle) | |
translate([ir+(or-ir)/2, 0, 0]) | |
circle(r=(or-ir)/2); | |
} | |
} | |
// ring - a hollow cylinder | |
// either specify the od or odt & odb | |
// same with id (or idt, idb) | |
// xxT and xxB stand for 'top' and 'bottom' | |
// rounded, if true, makes the ends of the ring rounded over | |
// rounded = 1 rounds only the top, rounded = 2 rounds only the bottom, rounded = true / 3 rounds both | |
// the diam of the rounding is based on the od-id of each end | |
// angle defaults to 360 & specifies the number of degrees to sweep, | |
// starting at the positive X axis. The direction of the sweep follows | |
// the Right Hand Rule, hence a negative angle will sweep clockwise. | |
// spin defaults to 0 & specifies the degrees of rotation of a torus around Z | |
module ring(od=0, id=0, h=0, odt=-1, odb=-1, idt=-1, idb=-1, rounded=false, center=false, angle=360, spin=0) | |
{ | |
odt = odt == -1 ? od : odt; | |
odb = odb == -1 ? od : odb; | |
idt = idt == -1 ? id : idt; | |
idb = idb == -1 ? id : idb; | |
rounded = rounded == false ? 0 : rounded == true ? 3 : rounded; | |
intersection() | |
{ | |
// had to do the pie call and interesection here to fix issues with very low $fn values | |
if (angle != 360) | |
pie(radius=max(odb, odt)/2, angle=angle, height=h, spin=spin, center = center, $fn=max($fn, 30)); | |
translate([0,0, center==true ? -h/2 : 0]) | |
{ | |
offsetT = (rounded == 1 || rounded == 3) ? (odt-idt)/4 : 0; | |
offsetB = (rounded == 2 || rounded == 3) ? (odb-idb)/4 : 0; | |
rh = h - offsetT - offsetB; | |
translate([0,0,offsetB]) | |
difference() | |
{ | |
cylinder(d1=odb, d2=odt, h=rh); | |
cylinder(d1=idb, d2=idt, h=rh); | |
// clean up the preview render | |
// requires version 2019.05 | |
if ($preview) | |
{ | |
translate([0,0,-0.1]) | |
cylinder(d=idb, h=.2); | |
translate([0,0,rh-.1]) | |
cylinder(d=idt, h=.2); | |
} | |
} | |
// bottom torus | |
if (rounded == 2 || rounded == 3) | |
torus(od=odb, id=idb); | |
// top torus | |
if (rounded == 1 || rounded == 3) | |
translate([0,0,h-(odt-idt)/2]) | |
torus(od=odt, id=idt); | |
} | |
} | |
} | |
// make a clone of all children using the translate, rotate, and mirror transformations | |
// the parameters t, r, & m are the translate vector, rotate vector, and mirror vector | |
// the o parameter is the order to execute the three different transforms | |
// note that the order is the execution order which is the reverse of the order they appear in the code | |
// so 'trm' means first translate, then rotate, them mirror; in the code that would be mirror() rotate() translate() | |
// useful defaults are provided so that you can use any one or more of the transforms to create clones of an object or objects | |
// | |
// example: | |
// clone(m=[1,0,0]) cube([3,2,1]); // create 2 cubes mirrored across the X axis (a cube 6x2,1) | |
// clone(m=[1,0,0], t=[1,1,0]) cube([3,2,1]); // 2 cubes mirrored across X, the left one moved -1mm X and 1mm Y (default order is "trm" so translate first, then mirror) | |
// clone(m=[1,0,0], t=[1,1,0], o="mtr") cube([3,2,1]); // 2 cubes mirrored across X, the left one moved 1mmX and 1mm Y (mirror first, then translate) | |
// clone(t=[0,2,1]) cube([3,2,1]); // creates 2 cubes with the second shifted in Y & Z (stair steps?) | |
module clone(t=[0,0,0], r=[0,0,0], m=[0,0,0], o="trm") | |
{ | |
children(); | |
if (o == "trm") | |
mirror(m) | |
rotate(r) | |
translate(t) | |
children(); | |
else if (o == "tmr") | |
rotate(r) | |
mirror(m) | |
translate(t) | |
children(); | |
else if (o == "rtm") | |
mirror(m) | |
translate(t) | |
rotate(r) | |
children(); | |
else if (o == "mtr") | |
rotate(r) | |
translate(t) | |
mirror(m) | |
children(); | |
else if (o == "rmt") | |
translate(t) | |
mirror(m) | |
rotate(r) | |
children(); | |
else if (o == "mrt") | |
translate(t) | |
rotate(r) | |
mirror(m) | |
children(); | |
else | |
echo(str("<font color='red'><b>clone can't understand ", o, "</b></font>")); | |
} | |
// create a grid of all children | |
// cols = how many columns (x) | |
// rows = how many rows (y) | |
// xstep = the amount to offset each column from the previous element | |
// ystep = the amount to offset each row from the previous element | |
// num = the number of items to put in the grid; default is cols * rows | |
// | |
// specify either rows & cols, or num plus one of rows & cols; if you specify all 3 num acts as a limit or expansion | |
// | |
// examples: | |
// grid(cols=4, rows=3, xstep=10, ystep=12) cube([8,10,5]); | |
// grid(cols=4, rows=3, xstep=10, ystep=12, num=10) cube([8,10,5]); | |
// grid(cols=4, num=12, xstep=10, ystep=12) cube([8,10,5]); | |
module grid(cols=undef, rows=undef, xstep=1, ystep=1, num=undef) | |
{ | |
assert((cols != undef && rows != undef) || (num != undef && (cols != undef || rows != undef)), | |
"At least 2 of cols, rows, and num must be specified"); | |
assert((xstep > 0 && ystep > 0), "both xstep & ystep must be > 0"); | |
num = num == undef ? cols * rows : num; | |
cols = cols == undef ? ceil(num / rows) : cols; | |
rows = rows == undef ? ceil(num / cols) : rows; // not really needed | |
for (i=[0:1:num-1]) | |
{ | |
x = i % cols; | |
y = floor(i / cols); | |
translate([x*xstep,y*ystep,0]) | |
children(); | |
} | |
} | |
// calculate the height of a ring made by cutting | |
// a cylinder out of a sphere | |
// R is outer radius (sphere), r is inner (cylinder cut) radius | |
function ringHeight(R,r) = 2*sqrt((R*R) - (r*r)); | |
// rounded cube - cube with rounded corners and sides using hull() & spheres | |
// size= [x, y, z] | |
// center = true / false | |
// radius = radius of rounded corners (0 = standard cube) | |
// t = [x,y,z] translate parameters | |
// r = [x,y,z] rotate parameters | |
// results will be rotated, then translated | |
module cubeR( size, center=false, radius=1, t=[0,0,0], r=[0,0,0]) | |
{ | |
x = (len(size)==undef) ? size : size[0]; | |
y = (len(size)==undef) ? size : size[1]; | |
z = (len(size)==undef) ? size : size[2]; | |
if (radius > x/2 || radius > y/2 || radius > z/2) | |
echo("<font color='red'>radius too large in cubeR</font>"); | |
if (radius == 0) | |
{ | |
// just a regular cube; don't know why you are calling us | |
translate(t) | |
rotate(r) | |
cube([x,y,z], center=center); | |
} | |
else | |
{ | |
translate(t) | |
rotate(r) | |
translate(center ? [-x/2, -y/2, -z/2] : [0 , 0, 0]) | |
hull() | |
{ | |
translate( [radius, radius, radius] ) | |
sphere( radius ); | |
translate( [radius, y-radius, radius] ) | |
sphere( radius ); | |
translate( [radius, radius, z-radius] ) | |
sphere( radius ); | |
translate( [radius, y-radius, z-radius] ) | |
sphere( radius ); | |
translate( [x-radius, radius, radius] ) | |
sphere( radius ); | |
translate( [x-radius, y-radius, radius] ) | |
sphere( radius ); | |
translate( [x-radius, radius, z-radius] ) | |
sphere( radius ); | |
translate( [x-radius, y-radius, z-radius] ) | |
sphere( radius ); | |
} | |
} | |
} | |
/* | |
* I took the following code and modified it to center | |
* the generated grid in the space. | |
* I removed the module call to create a single honeycomb | |
* since it was just a single primitive call (circle). | |
* I converted it from 2D objects (circles) to 3D objects (cylinders). | |
* I changed the interface so the first parameter is the vector | |
* to create the honeycomb inside ([x, y, z]) | |
* I changed the previous 'dia' parameter to be 'width' instead | |
* meaning the distance from flat side to flat side. I think this | |
* is the way most people think of dimensions of a honeycomb | |
* I added an option to return basically the holes (hexes) insted of the | |
* honeycomb pattern. This is useful with difference() | |
* I added the ability to show the bounding box (ghosted using the % operator) | |
* | |
* Example: | |
* honeycomb(box=[15, 20, 4], width=4, wall=1, showBoundingBox=true); | |
*/ | |
/** | |
* Honeycomb library | |
* License: Creative Commons - Attribution | |
* Copyright: Gael Lafond 2017 | |
* URL: https://www.thingiverse.com/thing:2484395 | |
* | |
* Inspired from: | |
* https://www.thingiverse.com/thing:1763704 | |
*/ | |
module honeycomb(box, width, wall, inverted = false, showBoundingBox = false) | |
{ | |
// Diagram | |
// ______ ___ ___ | |
// / /\ | | | |
// / dia / \ | smallDia | | |
// / / \ _|_ | width | |
// \ / ____ | | |
// \ / / | | |
// ___ \______/ / _|_ | |
// wall | / | |
// _|_ ______ \ | |
// / \ \ | |
// / \ \ | |
// |---| | |
// projWall | |
// | |
dia = width * (2/sqrt(3)); | |
x = box[0]; | |
y = box[1]; | |
z = box[2]; | |
smallDia = dia * cos(30); | |
projWall = wall * cos(30); | |
yStep = smallDia + wall; | |
xStep = dia*3/2 + projWall*2; | |
xStart = xStep - ((x % xStep) / 2); | |
yStart = yStep - ((y % yStep) / 2); | |
if (inverted) | |
{ | |
intersection() | |
{ | |
cube(box); // constrain it to exactly what they asked for | |
// only return the hexs themselves, not the 'borders' | |
// Note, extra loops to ensure the whole surface is covered | |
for (yOffset = [-yStart:yStep:y+yStep], xOffset = [-xStart:xStep:x+xStep]) | |
{ | |
translate([xOffset, yOffset, -.1]) | |
cylinder(d=dia, h=z+.2, $fn=6); | |
translate([xOffset + dia*3/4 + projWall, yOffset + (smallDia+wall)/2, -.1]) | |
cylinder(d=dia, h=z+.2, $fn=6); | |
} | |
} | |
} | |
else | |
{ | |
difference() | |
{ | |
cube(box); | |
// Note, extra loops to ensure the whole surface is covered | |
for (yOffset = [-yStart:yStep:y+yStep], xOffset = [-xStart:xStep:x+xStep]) | |
{ | |
translate([xOffset, yOffset, -.1]) | |
cylinder(d=dia, h=z+.2, $fn=6); | |
translate([xOffset + dia*3/4 + projWall, yOffset + (smallDia+wall)/2, -.1]) | |
cylinder(d=dia, h=z+.2, $fn=6); | |
} | |
} | |
} | |
if (showBoundingBox) | |
%difference() | |
{ | |
cube(box); | |
translate([wall/2,wall/2,-.1]) | |
cube([box[0]-wall, box[1]-wall, box[2]+.2]); | |
} | |
} | |
/** | |
* pie.scad | |
* | |
* Use this module to generate a pie- or pizza- slice shape, which is particularly useful | |
* in combination with `difference()` and `intersection()` to render shapes that extend a | |
* certain number of degrees around or within a circle. | |
* | |
* This openSCAD library is part of the [dotscad](https://github.com/dotscad/dotscad) | |
* project. | |
* | |
* @copyright Chris Petersen, 2013 | |
* @license http://creativecommons.org/licenses/LGPL/2.1/ | |
* @license http://creativecommons.org/licenses/by-sa/3.0/ | |
* | |
* @see http://www.thingiverse.com/thing:109467 | |
* @source https://github.com/dotscad/dotscad/blob/master/pie.scad | |
* | |
* example: | |
* union() | |
* { | |
* pie(40, 45, 5); | |
* rotate([0,0,-5]) | |
* difference() | |
* { | |
* sphere(r=25); | |
* translate([0,0,-30]) | |
* pie(50, -45, 60, 15); | |
* } | |
* } | |
* | |
* @param float radius Radius of the pie | |
* @param float angle Angle (size) of the pie to slice (right hand rule, starting at positive X axis) | |
* @param float height Height (thickness) of the pie | |
* @param float spin Angle to spin the slice on the Z axis (right hand rule, starting at positive X axis) | |
* @param bool center If true, center the pie slice vertically | |
*/ | |
module pie(radius, angle, height, spin=0, center=false) | |
{ | |
// this portion copyright 2020 by James H. Brown | |
translate([0,0, center==true ? -height/2 : 0]) | |
rotate([0,0,spin]) | |
// This rotation is required because apparelty rotate_extrude doesn't | |
// start at the same place as the sides of a sphere with an odd $fn value | |
rotate([0,0,$fn % 2 == 1 ? 360/$fn/2 : 0]) | |
rotate_extrude(convexity=10, angle=angle) | |
square([radius, height]); | |
/* JHB: Previous to openscad v 2019.05 the code below was necessary. Now rotate extrude can do all this for us | |
// Negative angles shift direction of rotation | |
clockwise = (angle < 0) ? true : false; | |
// Support angles < 0 and > 360 | |
normalized_angle = abs((angle % 360 != 0) ? angle % 360 : angle % 360 + 360); | |
// Select rotation direction | |
rotation = clockwise ? [0, 180 - normalized_angle] : [180, normalized_angle]; | |
// Render | |
if (angle != 0) { | |
rotate([0,0,spin]) linear_extrude(height=height) | |
difference() { | |
circle(radius); | |
if (normalized_angle < 180) { | |
union() for(a = rotation) | |
rotate(a) translate([-radius, 0, 0]) square(radius * 2); | |
} | |
else if (normalized_angle != 360) { | |
intersection_for(a = rotation) | |
rotate(a) translate([-radius, 0, 0]) square(radius * 2); | |
} | |
} | |
} | |
*/ | |
} | |
/* | |
* roundclip | |
* od = outside diamaeter of the clip body | |
* id = inside diameter of the clip body | |
* cliparc = the amount of a circle the clip takes up | |
* height = the height of the clip in Z | |
* tiparc = the amount of a circle the tips of the clip take up (default 60) | |
* rounded = whether to round the top and bottom edges (default false) | |
* upright = rotate the finished clip so the opening is centered along the positive X axis | |
*/ | |
module roundclip(od, id, cliparc, height, tiparc=60, rounded=false, upright=true) | |
{ | |
cs = (od-id) / 2; // cross section of the ring | |
tipOD = od/2; | |
tipID = tipOD-cs*2; | |
tipOffset = od/2 + tipOD/2 - cs; | |
translate([0,upright ? height/2 : 0,0]) | |
rotate([upright ? 90 : 0,0,0]) | |
rotate([0,0,90-((cliparc-180) / 2)]) | |
{ | |
intersection() | |
{ | |
ring(od=od, id=id, h=height, rounded=rounded); | |
pie(od, cliparc, height, spin=0); | |
} | |
translate([tipOffset,0,0]) | |
intersection() | |
{ | |
ring(od=tipOD, id=tipID, h=height, rounded=rounded); | |
union() | |
{ | |
pie(tipOD, -tiparc, height, spin=-180+tiparc); | |
rotate([0,0,-180+tiparc]) | |
translate([tipOD/2-cs/2,0,0]) | |
cylinder(d=cs, h=height); | |
} | |
} | |
rotate([0,0,cliparc]) | |
translate([tipOffset,0,0]) | |
intersection() | |
{ | |
ring(od=tipOD, id=tipID, h=height, rounded=rounded); | |
union() | |
{ | |
pie(tipOD, tiparc, height, spin=180-tiparc); | |
rotate([0,0,180-tiparc]) | |
translate([tipOD/2-cs/2,0,0]) | |
cylinder(d=cs, h=height); | |
} | |
} | |
} | |
} | |
/* | |
Pegboard Specs | |
All pegboard has holes with 1-in. spacing, but there are two thicknesses and two hole sizes available. | |
‘Small hole’ pegboard is usually 1/8-in.-thick hardboard with 3/16-in.-diameter holes. The holes will accommodate only the smaller 1/8-in. pegs. This thickness is good for small projects and for hanging lighter weight stuff. But for heavy tools—and longevity—go with the thicker board. | |
‘Large hole’ pegboard is usually 1/4-in.-thick hardboard with 1/4-in.-diameter holes that will accept both 1/8-in. and 1/4-in. hooks. This is the type you need for workshops, garages and other heavy-use areas. Some pegboard shelves come with a melamine coating on one side. | |
box is a vector [x, y, z] of the size of pegboard you want | |
inverted, if true, creates the holes, rather than a square of pegboard | |
centered, if true, centers the pegboard holes in the box (x, & y); otherwise they start .5" from x & y | |
showBoundingBox, if true & inverted is true, will add a %-masked cube the size of box | |
*/ | |
module pegboard(box, inverted = false, centered=true, showBoundingBox=false) | |
{ | |
bx = box[0]; | |
by = box[1]; | |
bz = box[2]; | |
xStart = centered ? (bx % in2mm(1)) / 2 : in2mm(.5) ; | |
yStart = centered ? (by % in2mm(1)) / 2 : in2mm(.5) ; | |
if (inverted) | |
{ | |
intersection() | |
{ | |
cube(box); | |
for (x = [xStart:in2mm(1):bx+in2mm(1)], y=[yStart:in2mm(1):by+in2mm(1)]) | |
{ | |
translate([x, y, 0]) | |
cylinder(d=in2mm(.25), h=bz); | |
} | |
} | |
if (showBoundingBox) | |
%cube(box); | |
} | |
else | |
{ | |
difference() | |
{ | |
cube(box); | |
translate([0,0,-.1]) | |
pegboard([bx,by,bz+.2], inverted=true, centered=centered, showBoundingBox=false); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment