Initial code dump.

This commit is contained in:
Revar Desmera 2017-08-29 17:00:16 -07:00
parent 381fb65b2e
commit b3f19b1407
15 changed files with 3935 additions and 0 deletions

118
acme_screw.scad Normal file
View File

@ -0,0 +1,118 @@
//////////////////////////////////////////////////////////////////////
// Square-threaded (ACME) Screw Rods and Nuts
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
include <math.scad>
// Constructs an acme threaded screw rod. This method makes much
// smoother threads than the naive linear_extrude method.
module acme_threaded_rod(
d=10.5,
l=100,
pitch=3.175,
thread_depth=1,
thread_angle=14.5
) {
astep = 360/segs(d/2);
asteps = ceil(360/astep);
threads = ceil(l/pitch)+2;
pa_delta = min(pitch/4.1,(thread_depth+0.05)*tan(thread_angle)/2);
poly_points = [
for (
thread = [0 : threads-1],
astep = [0 : asteps-1],
i = [0 : 3]
) let (
r = max(0, d/2 - ((i==1||i==2)? 0 : (thread_depth+0.05))),
a = astep / asteps,
rx = r * cos(360 * a),
ry = r * sin(360 * a),
tz = (thread + a - threads/2 + (i<2? -0.25 : 0.25)) * pitch + (i%2==0? -pa_delta : pa_delta)
) [rx, ry, tz]
];
point_count = len(poly_points);
poly_faces = concat(
[
for (
thread = [0 : threads-1],
astep = [0 : asteps-1],
j = [0 : 3],
i = [0 : 1]
) let(
p0 = (thread*asteps + astep)*4 + j,
p1 = p0 + 4,
p2 = (thread*asteps + astep)*4 + ((j+1)%4),
p3 = p2 + 4,
tri = (i==0? [p0, p3, p1] : [p0, p2, p3])
)
if (p0 < point_count-4) tri
],
[
[0, 3, 2],
[0, 2, 1],
[point_count-4, point_count-3, point_count-2],
[point_count-4, point_count-2, point_count-1]
]
);
intersection() {
union() {
polyhedron(points=poly_points, faces=poly_faces, convexity=10);
cylinder(h=(threads+0.5)*pitch, d=d-2*thread_depth, center=true, $fn=asteps);
}
cube([d+1, d+1, l], center=true);
}
}
//!acme_threaded_rod(d=3/8*25.4, l=20, pitch=1/8*25.4, thread_depth=1.3, thread_angle=29, $fn=32);
//!acme_threaded_rod(d=60, l=16, pitch=8, thread_depth=3, thread_angle=45, $fa=2, $fs=2);
module acme_threaded_nut(
od=17.4,
id=10.5,
h=10,
pitch=3.175,
thread_depth=1,
slop=printer_slop
) {
difference() {
cylinder(r=od/2/cos(30), h=h, center=true, $fn=6);
zspread(slop, n=slop>0?2:1) {
acme_threaded_rod(d=id+2*slop, l=h+1, pitch=pitch, thread_depth=thread_depth);
}
}
}
!acme_threaded_nut(od=17.4, id=10.5, h=10, pitch=3.175, thread_depth=1, slop=printer_slop);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

230
beziers.scad Normal file
View File

@ -0,0 +1,230 @@
//////////////////////////////////////////////////////////////////////
// Bezier functions and modules.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <math.scad>
include <paths.scad>
// Formulae to calculate points on a cubic bezier curve.
function bez_B0(curve,u) = curve[0]*pow((1-u),3);
function bez_B1(curve,u) = curve[1]*(3*u*pow((1-u),2));
function bez_B2(curve,u) = curve[2]*(3*pow(u,2)*(1-u));
function bez_B3(curve,u) = curve[3]*pow(u,3);
function bez_point(curve,u) = bez_B0(curve,u) + bez_B1(curve,u) + bez_B2(curve,u) + bez_B3(curve,u);
function bezier_polyline(bezier, splinesteps=16) = concat(
[
for (
b = [0 : 3 : len(bezier)-4],
l = [0 : splinesteps-1]
) let (
crv = [bezier[b+0], bezier[b+1], bezier[b+2], bezier[b+3]],
u = l / splinesteps
) bez_point(crv, u)
],
[bez_point([bezier[len(bezier)-4], bezier[len(bezier)-3], bezier[len(bezier)-2], bezier[len(bezier)-1]], 1.0)]
);
// Takes a closed 2D bezier path, and creates a 2D polygon from it.
module bezier_polygon(bezier, splinesteps=16) {
polypoints=bezier_polyline(bezier, splinesteps);
polygon(points=slice(polypoints, 0, -1));
}
// Generate bezier curve to fillet 2 line segments between 3 points.
// Returns two path points with surrounding cubic bezier control points.
function fillet3pts(p0, p1, p2, r) = let(
v0 = normalize(p0-p1),
v1 = normalize(p2-p1),
a = vector3d_angle(v0,v1),
mr = min(distance(p0,p1), distance(p2,p1))*0.9,
tr = min(r/tan(a/2), mr),
tp0 = p1+v0*tr,
tp1 = p1+v1*tr,
w=-2.7e-5*a*a + 8.5e-3*a - 3e-3,
nw=max(0, w),
cp0 = tp0+nw*(p1-tp0),
cp1 = tp1+nw*(p1-tp1)
) [tp0, tp0, cp0, cp1, tp1, tp1];
// Takes a 3D polyline path and fillets it into a 3d cubic bezier path.
function fillet_path(pts, fillet) = concat(
[pts[0], pts[0]],
(len(pts) < 3)? [] : [
for (
p = [1 : len(pts)-2],
pt = fillet3pts(pts[p-1], pts[p], pts[p+1], fillet)
) pt
],
[pts[len(pts)-1], pts[len(pts)-1]]
);
// Takes a closed 2D bezier and rotates it around the X axis, forming a solid.
// bezier = array of points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into.
// Example:
// path = [
// [ 0, 10], [ 50, 0], [ 50, 40],
// [ 95, 40], [100, 40], [100, 45],
// [ 95, 45], [ 66, 45], [ 0, 20],
// [ 0, 12], [ 0, 12], [ 0, 10],
// [ 0, 10]
// ];
// revolve_bezier(path, splinesteps=32, $fn=180);
module revolve_bezier(bezier, splinesteps=16) {
yrot(90) rotate_extrude(convexity=10) {
xrot(180) zrot(-90) bezier_polygon(bezier, splinesteps);
}
}
// Takes a bezier path and closes it to the X axis.
function bezier_close_to_axis(bezier) =
let(bezend = len(bezier)-1)
concat(
[ [bezier[0][0], 0], [bezier[0][0], 0], bezier[0] ],
bezier,
[ bezier[bezend], [bezier[bezend][0], 0], [bezier[bezend][0], 0] ]
);
// Takes a bezier curve and closes it with a matching path that is
// lowered by a given amount towards the X axis.
function bezier_offset(inset, bezier) =
let(backbez = reverse([ for (pt = bezier) [pt[0], pt[1]-inset] ]))
concat(
bezier,
[bezier[len(bezier)-1]],
[backbez[0]],
backbez,
[backbez[len(backbez)-1]],
[bezier[0]],
[bezier[0]]
);
// Takes a 2D bezier and rotates it around the X axis, forming a solid.
// bezier = array of points for the bezier path to rotate.
// splinesteps = number of segments to divide each bezier segment into.
// Example:
// path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
// revolve_bezier_solid_to_axis(path, splinesteps=32, $fn=72);
module revolve_bezier_solid_to_axis(bezier, splinesteps=16) {
revolve_bezier(bezier=bezier_close_to_axis(bezier), splinesteps=splinesteps);
}
// Takes a 2D bezier and rotates it around the X axis, into a hollow shell.
// bezier = array of points for the bezier path to rotate.
// offset = the thickness of the created shell.
// splinesteps = number of segments to divide each bezier segment into.
// Example:
// path = [ [0, 10], [33, 10], [66, 40], [100, 40] ];
// revolve_bezier_offset_shell(path, offset=1, splinesteps=32, $fn=72);
module revolve_bezier_offset_shell(bezier, offset=1, splinesteps=16) {
revolve_bezier(bezier=bezier_offset(offset, bezier), splinesteps=splinesteps);
}
// Extrudes 2D children along a bezier path.
// bezier = array of points for the bezier path to extrude along.
// splinesteps = number of segments to divide each bezier segment into.
// Example:
// path = [ [0, 0, 0], [33, 33, 33], [66, -33, -33], [100, 0, 0] ];
// extrude_2d_shapes_along_bezier(path, splinesteps=32)
// circle(r=10, center=true);
module extrude_2d_shapes_along_bezier(bezier, splinesteps=16) {
pointslist = slice(bezier_polyline(bezier, splinesteps), 0, -1);
ptcount = len(pointslist);
for (i = [0 : ptcount-2]) {
pt1 = pointslist[i];
pt2 = pointslist[i+1];
pt0 = i==0? pt1 : pointslist[i-1];
pt3 = (i>=ptcount-2)? pt2 : pointslist[i+2];
dist = distance(pt1,pt2);
v1 = pt2-pt1;
v0 = (i==0)? v1 : (pt1-pt0);
v2 = (i==ptcount-2)? v1 : (pt3-pt2);
az1 = atan2(v1[1], v1[0]);
alt1 = (len(pt1)<3)? 0 : atan2(v1[2], hypot(v1[1], v1[0]));
az0 = atan2(v0[1], v0[0]);
alt0 = (len(pt0)<3)? 0 : atan2(v0[2], hypot(v0[1], v0[0]));
az2 = atan2(v2[1], v2[0]);
alt2 = (len(pt2)<3)? 0 : atan2(v2[2], hypot(v2[1], v2[0]));
translate(pt1) {
difference() {
rotate([0, 90-alt1, az1]) {
translate([0, 0, -1]) {
linear_extrude(height=dist*3, convexity=10) {
children();
}
}
}
rotate([0, 90-(alt0+alt1)/2, (az0+az1)/2]) {
translate([0, 0, -dist-0.05]) {
cube(size=[99,99,dist*2], center=true);
}
}
rotate([0, 90-(alt1+alt2)/2, (az1+az2)/2]) {
translate([0, 0, dist+dist]) {
cube(size=[99,99,dist*2], center=true);
}
}
}
}
}
}
// Takes a closed 2D bezier path, centered on the XY plane, and
// extrudes it perpendicularly along a 3D bezier path, forming a solid.
// bezier = Array of points of a bezier path, to be extruded.
// path = Array of points of a bezier path, to extrude along.
// pathsteps = number of steps to divide each path segment into.
// bezsteps = number of steps to divide each bezier segment into.
// Example:
// bez = [ [-15, 0], [25, -15], [-5, 10], [0, 10], [5, 10], [10, 5], [15, 0], [10, -5], [5, -10], [0, -10], [-5, -10], [-10, -5], [-15, 0] ];
// path = [ [0, 0, 0], [33, 33, 33], [66, -33, -33], [100, 0, 0] ];
// extrude_bezier_along_bezier(bez, path, pathsteps=64, bezsteps=32);
module extrude_bezier_along_bezier(bezier, path, pathsteps=16, bezsteps=16) {
bez_points = simplify2d_path(bezier_polyline(bezier, bezsteps));
path_points = simplify3d_path(path3d(bezier_polyline(path, pathsteps)));
extrude_2dpath_along_3dpath(bez_points, path_points);
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

182
involute_gears.scad Normal file
View File

@ -0,0 +1,182 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// Public Domain Parametric Involute Spur Gear (and involute helical gear and involute rack)
// version 1.1
// by Leemon Baird, 2011, Leemon@Leemon.com
// Corrected and tweaked by Revar Desmera, 2017, revarbat@gmail.com
//http://www.thingiverse.com/thing:5505
//
// This file is public domain. Use it for any purpose, including commercial
// applications. Attribution would be nice, but is not required. There is
// no warranty of any kind, including its correctness, usefulness, or safety.
//
// This is parameterized involute spur (or helical) gear. It is much simpler and less powerful than
// others on Thingiverse. But it is public domain. I implemented it from scratch from the
// descriptions and equations on Wikipedia and the web, using Mathematica for calculations and testing,
// and I now release it into the public domain.
//
// http://en.wikipedia.org/wiki/Involute_gear
// http://en.wikipedia.org/wiki/Gear
// http://en.wikipedia.org/wiki/List_of_gear_nomenclature
// http://gtrebaol.free.fr/doc/catia/spur_gear.html
// http://www.cs.cmu.edu/~rapidproto/mechanisms/chpt7.html
//
// The module gear() gives an involute spur gear, with reasonable defaults for all the parameters.
// Normally, you should just choose the first 4 parameters, and let the rest be default values.
// The module gear() gives a gear in the XY plane, centered on the origin, with one tooth centered on
// the positive Y axis. The various functions below it take the same parameters, and return various
// measurements for the gear. The most important is pitch_radius, which tells how far apart to space
// gears that are meshing, and adendum_radius, which gives the size of the region filled by the gear.
// A gear has a "pitch circle", which is an invisible circle that cuts through the middle of each
// tooth (though not the exact center). In order for two gears to mesh, their pitch circles should
// just touch. So the distance between their centers should be pitch_radius() for one, plus pitch_radius()
// for the other, which gives the radii of their pitch circles.
//
// In order for two gears to mesh, they must have the same mm_per_tooth and pressure_angle parameters.
// mm_per_tooth gives the number of millimeters of arc around the pitch circle covered by one tooth and one
// space between teeth. The pitch angle controls how flat or bulged the sides of the teeth are. Common
// values include 14.5 degrees and 20 degrees, and occasionally 25. Though I've seen 28 recommended for
// plastic gears. Larger numbers bulge out more, giving stronger teeth, so 28 degrees is the default here.
//
// The ratio of number_of_teeth for two meshing gears gives how many times one will make a full
// revolution when the the other makes one full revolution. If the two numbers are coprime (i.e.
// are not both divisible by the same number greater than 1), then every tooth on one gear
// will meet every tooth on the other, for more even wear. So coprime numbers of teeth are good.
//
// The module rack() gives a rack, which is a bar with teeth. A rack can mesh with any
// gear that has the same mm_per_tooth and pressure_angle.
//
// Some terminology:
// The outline of a gear is a smooth circle (the "pitch circle") which has mountains and valleys
// added so it is toothed. So there is an inner circle (the "root circle") that touches the
// base of all the teeth, an outer circle that touches the tips of all the teeth,
// and the invisible pitch circle in between them. There is also a "base circle", which can be smaller than
// all three of the others, which controls the shape of the teeth. The side of each tooth lies on the path
// that the end of a string would follow if it were wrapped tightly around the base circle, then slowly unwound.
// That shape is an "involute", which gives this type of gear its name.
//
//////////////////////////////////////////////////////////////////////////////////////////////
pi = 3.141592653589793236;
//An involute spur gear, with reasonable defaults for all the parameters.
//Normally, you should just choose the first 4 parameters, and let the rest be default values.
//Meshing gears must match in mm_per_tooth, pressure_angle, and twist,
//and be separated by the sum of their pitch radii, which can be found with pitch_radius().
module gear (
mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 11, //total number of teeth around the entire perimeter
thickness = 6, //thickness of gear in mm
hole_diameter = 3, //diameter of the hole in the center, in mm
twist = 0, //teeth rotate this many degrees from bottom of gear to top. 360 makes the gear a screw with each thread going around once
teeth_to_hide = 0, //number of teeth to delete to make this only a fraction of a circle
pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees.
clearance = 0.0, //gap between top of a tooth on one gear and bottom of valley on a meshing gear (in millimeters)
backlash = 0.0 //gap between two meshing teeth, in the direction along the circumference of the pitch circle
) {
p = mm_per_tooth * number_of_teeth / pi / 2; //radius of pitch circle
c = p + mm_per_tooth / pi - clearance; //radius of outer circle
b = p*cos(pressure_angle); //radius of base circle
r = p-(c-p)-clearance; //radius of root circle
t = mm_per_tooth/2-backlash/2; //tooth thickness at pitch circle
k = -iang(b, p) - t/2/p/pi*180; //angle to where involute meets base circle on each side of tooth
difference() {
linear_extrude(height = thickness, center = true, convexity = 10, twist = twist, slices = ceil(abs(twist)/5)+1)
for (i = [0:number_of_teeth-teeth_to_hide-1] )
rotate([0,0,i*360/number_of_teeth])
polygon(
points=[
polar(hole_diameter/10, -181/number_of_teeth),
polar(r, -181/number_of_teeth),
polar(r, r<b ? k : -180/number_of_teeth),
q7(0/5,r,b,c,k, 1),q7(1/5,r,b,c,k, 1),q7(2/5,r,b,c,k, 1),q7(3/5,r,b,c,k, 1),q7(4/5,r,b,c,k, 1),q7(5/5,r,b,c,k, 1),
q7(5/5,r,b,c,k,-1),q7(4/5,r,b,c,k,-1),q7(3/5,r,b,c,k,-1),q7(2/5,r,b,c,k,-1),q7(1/5,r,b,c,k,-1),q7(0/5,r,b,c,k,-1),
polar(r, r<b ? -k : 180/number_of_teeth),
polar(r, 181/number_of_teeth),
polar(hole_diameter/10, 181/number_of_teeth),
],
paths=[[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]]
);
cylinder(h=2*thickness+1, r=hole_diameter/2, center=true);
}
};
//these 4 functions are used by gear
function polar(r,theta) = r*[sin(theta), cos(theta)]; //convert polar to cartesian coordinates
function iang(r1,r2) = sqrt((r2/r1)*(r2/r1) - 1)/pi*180 - acos(r1/r2); //unwind a string this many degrees to go from radius r1 to radius r2
function q7(f,r,b,r2,t,s) = q6(b,s,t,(1-f)*max(b,r)+f*r2); //radius a fraction f up the curved side of the tooth
function q6(b,s,t,d) = polar(d,s*(iang(b,d)+t)); //point at radius d on the involute curve
//a rack, which is a straight line with teeth (the same as a segment from a giant gear with a huge number of teeth).
//The "pitch circle" is a line along the X axis.
module rack (
mm_per_tooth = 3, //this is the "circular pitch", the circumference of the pitch circle divided by the number of teeth
number_of_teeth = 11, //total number of teeth along the rack
thickness = 6, //thickness of rack in mm (affects each tooth)
height = 120, //height of rack in mm, from tooth top to back of rack.
pressure_angle = 28, //Controls how straight or bulged the tooth sides are. In degrees.
backlash = 0.0 //gap between two meshing teeth, in the direction along the circumference of the pitch circle
) {
a = adendum(mm_per_tooth);
d = dedendum(mm_per_tooth);
xa = a * sin(pressure_angle);
xd = d * sin(pressure_angle);
linear_extrude(height = thickness, center = true, convexity = 10)
for (i = [0:number_of_teeth-1] )
translate([i*mm_per_tooth,0,0])
polygon(
points=[
[-1/2 * mm_per_tooth - 0.01, a-height],
[-1/2 * mm_per_tooth, -d],
[-1/4 * mm_per_tooth + backlash - xd, -d],
[-1/4 * mm_per_tooth + backlash + xa, a],
[ 1/4 * mm_per_tooth - backlash - xa, a],
[ 1/4 * mm_per_tooth - backlash + xd, -d],
[ 1/2 * mm_per_tooth, -d],
[ 1/2 * mm_per_tooth + 0.01, a-height],
],
paths=[[0,1,2,3,4,5,6,7]]
);
};
//These 5 functions let the user find the derived dimensions of the gear.
//A gear fits within a circle of radius outer_radius, and two gears should have
//their centers separated by the sum of their pictch_radius.
function circular_pitch (mm_per_tooth=3) = mm_per_tooth; //tooth density expressed as "circular pitch" in millimeters
function diametral_pitch (mm_per_tooth=3) = pi / mm_per_tooth; //tooth density expressed as "diametral pitch" in teeth per millimeter
function adendum (mm_per_tooth=3) = module_value(mm_per_tooth);
function dedendum (mm_per_tooth=3) = 1.25 * module_value(mm_per_tooth);
function module_value (mm_per_tooth=3) = mm_per_tooth / pi; //tooth density expressed as "module" or "modulus" in millimeters
function pitch_radius (mm_per_tooth=3,number_of_teeth=11) = mm_per_tooth * number_of_teeth / pi / 2;
function outer_radius (mm_per_tooth=3,number_of_teeth=11,clearance=0.1) //The gear fits entirely within a cylinder of this radius.
= mm_per_tooth*(1+number_of_teeth/2)/pi - clearance;
//////////////////////////////////////////////////////////////////////////////////////////////
//example gear train.
//Try it with OpenSCAD View/Animate command with 20 steps and 24 FPS.
//The gears will continue to be rotated to mesh correctly if you change the number of teeth.
n1 = 11; //red gear number of teeth
n2 = 20; //green gear
n3 = 5; //blue gear
n4 = 20; //orange gear
n5 = 8; //gray rack
mm_per_tooth = 9; //all meshing gears need the same mm_per_tooth (and the same pressure_angle)
thickness = 6;
hole = 3;
height = 12;
d1 =pitch_radius(mm_per_tooth,n1);
d12=pitch_radius(mm_per_tooth,n1) + pitch_radius(mm_per_tooth,n2);
d13=pitch_radius(mm_per_tooth,n1) + pitch_radius(mm_per_tooth,n3);
d14=pitch_radius(mm_per_tooth,n1) + pitch_radius(mm_per_tooth,n4);
translate([ 0, 0, 0]) rotate([0,0, $t*360/n1]) color([1.00,0.75,0.75]) gear(mm_per_tooth,n1,thickness,hole);
translate([ 0, d12, 0]) rotate([0,0,-($t+n2/2-0*n1+1/2)*360/n2]) color([0.75,1.00,0.75]) gear(mm_per_tooth,n2,thickness,hole,0);
translate([ d13, 0, 0]) rotate([0,0,-($t-n3/4+n1/4+1/2)*360/n3]) color([0.75,0.75,1.00]) gear(mm_per_tooth,n3,thickness,hole);
translate([ d13, 0, 0]) rotate([0,0,-($t-n3/4+n1/4+1/2)*360/n3]) color([0.75,0.75,1.00]) gear(mm_per_tooth,n3,thickness,hole);
translate([-d14, 0, 0]) rotate([0,0,-($t-n4/4-n1/4+1/2-floor(n4/4)-3)*360/n4]) color([1.00,0.75,0.50]) gear(mm_per_tooth,n4,thickness,hole,0,n4-3);
translate([(-floor(n5/2)-floor(n1/2)+$t+n1/2-1/2)*9, -d1+0.0, 0]) rotate([0,0,0]) color([0.75,0.75,0.75]) rack(mm_per_tooth,n5,thickness,height);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

211
joiners.scad Normal file
View File

@ -0,0 +1,211 @@
//////////////////////////////////////////////////////////////////////
// Snap-together joiners
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
module half_joiner_clear(h=20, w=10, a=30, clearance=0)
{
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
difference() {
// Diamonds.
scale([w+clearance, dmnd_width/2, dmnd_height/2]) {
xrot(45) cube(size=[1,sqrt(2),sqrt(2)], center=true);
}
// Blunt point of tab.
grid_of(ya=[-(guide_width/2+2), (guide_width/2+2)]) {
cube(size=[(w+clearance)*1.05, 4, h*0.99], center=true);
}
}
}
//half_joiner_clear();
module half_joiner(h=20, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
{
dmnd_height = h*1.0;
dmnd_width = dmnd_height*tan(a);
guide_size = w/3;
guide_width = 2*(dmnd_height/2-guide_size)*tan(a);
difference() {
union() {
// Make base.
difference() {
// Solid backing base.
translate([0,-l/2,0])
cube(size=[w, l, h], center=true);
// Clear diamond for tab
grid_of(xa=[-(w*2/3), (w*2/3)]) {
half_joiner_clear(h=h+0.01, w=w, clearance=slop*2, a=a);
}
}
difference() {
// Make tab
scale([w/3-slop*2, dmnd_width/2, dmnd_height/2]) xrot(45)
cube(size=[1,sqrt(2),sqrt(2)], center=true);
// Blunt point of tab.
translate([0,guide_width/2+2,0])
cube(size=[w*0.99,4,guide_size*2], center=true);
}
// Guide ridges.
if (guides == true) {
xspread(w/3-slop*2) {
// Guide ridge.
fwd(0.05/2) {
scale([0.75, 1, 2]) yrot(45)
cube(size=[guide_size/sqrt(2), guide_width+0.05, guide_size/sqrt(2)], center=true);
}
// Snap ridge.
scale([0.25, 0.5, 1]) zrot(45)
cube(size=[guide_size/sqrt(2), guide_size/sqrt(2), dmnd_width], center=true);
}
}
}
// Make screwholes, if needed.
if (screwsize != undef) {
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
}
}
}
//half_joiner(screwsize=3);
module half_joiner2(h=20, w=10, l=10, a=30, screwsize=undef, guides=true)
{
difference() {
union () {
translate([0,-l/2,0])
cube(size=[w, l, h], center=true);
half_joiner_clear(h=h, w=w, a=a);
}
// Subtract mated half_joiner.
zrot(180) half_joiner(h=h+0.05, w=w+0.05, l=l+0.05, a=a, screwsize=undef, guides=guides, slop=0.0);
// Make screwholes, if needed.
if (screwsize != undef) {
yrot(90) cylinder(r=screwsize*1.1/2, h=w+1, center=true, $fn=12);
}
}
}
//half_joiner2(screwsize=3);
module joiner(h=40, w=10, l=10, a=30, screwsize=undef, guides=true, slop=printer_slop)
{
union() {
translate([0,0,h/4])
half_joiner(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides, slop=slop);
translate([0,0,-h/4])
half_joiner2(h=h/2, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
}
}
//joiner(screwsize=3);
module joiner_clear(h=40, w=10, a=30, clearance=0)
{
grid_of(za=[-h/4,h/4]) {
half_joiner_clear(h=h/2.0, w=w, a=a, clearance=clearance);
}
}
//joiner_clear();
module joiner_pair(spacing=100, h=40, w=10, l=10, a=30, screwsize=undef, guides=true)
{
yrot_copies([0,180]) {
translate([spacing/2, 0, 0]) {
joiner(h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
}
}
}
//joiner_pair(spacing=100, h=40, w=10, l=10, a=30, screwsize=3, guides=true);
module joiner_pair_clear(spacing=100, h=40, w=10, a=30, clearance=0)
{
yrot_copies([0,180]) {
translate([spacing/2, 0, 0]) {
joiner_clear(h=h, w=w, a=a, clearance=clearance);
}
}
}
//joiner_pair_clear(spacing=100, h=40, w=10, a=30);
module joiner_quad(xspacing=100, yspacing=50, h=40, w=10, l=10, a=30, screwsize=undef, guides=true)
{
zrot_copies([0,180]) {
translate([0, yspacing/2, 0]) {
joiner_pair(spacing=xspacing, h=h, w=w, l=l, a=a, screwsize=screwsize, guides=guides);
}
}
}
//joiner_quad(xspacing=100, yspacing=50, h=40, w=10, l=10, a=30, screwsize=3, guides=true);
module joiner_quad_clear(xspacing=100, yspacing=50, h=40, w=10, a=30, clearance=0)
{
zrot_copies([0,180]) {
translate([0, yspacing/2, 0]) {
joiner_pair_clear(spacing=xspacing, h=h, w=w, a=a, clearance=clearance);
}
}
}
//joiner_quad_clear(xspacing=100, yspacing=50, h=40, w=10, a=30);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

128
linear_bearings.scad Normal file
View File

@ -0,0 +1,128 @@
//////////////////////////////////////////////////////////////////////
// Linear Bearings.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <shapes.scad>
include <metric_screws.scad>
function get_lmXuu_bearing_diam(size) = lookup(size, [
[ 4.0, 8.0],
[ 5.0, 10.0],
[ 6.0, 12.0],
[ 8.0, 15.0],
[ 10.0, 19.0],
[ 12.0, 21.0],
[ 13.0, 23.0],
[ 16.0, 28.0],
[ 20.0, 32.0],
[ 25.0, 40.0],
[ 30.0, 45.0],
[ 35.0, 52.0],
[ 40.0, 60.0],
[ 50.0, 80.0],
[ 60.0, 90.0],
[ 80.0, 120.0],
[100.0, 150.0]
]);
function get_lmXuu_bearing_length(size) = lookup(size, [
[ 4.0, 12.0],
[ 5.0, 15.0],
[ 6.0, 19.0],
[ 8.0, 24.0],
[ 10.0, 29.0],
[ 12.0, 30.0],
[ 13.0, 32.0],
[ 16.0, 37.0],
[ 20.0, 42.0],
[ 25.0, 59.0],
[ 30.0, 64.0],
[ 35.0, 70.0],
[ 40.0, 80.0],
[ 50.0, 100.0],
[ 60.0, 110.0],
[ 80.0, 140.0],
[100.0, 175.0]
]);
// Creates a model of a clamp to hold a given linear bearing cartridge.
// d = Diameter of linear bearing. (Default: 15)
// l = Length of linear bearing. (Default: 24)
// tab = Clamp tab height. (Default: 7)
// tabwall = Clamp Tab thickness. (Default: 5)
// wall = Wall thickness of clamp housing. (Default: 3)
// gap = Gap in clamp. (Default: 5)
// screwsize = Size of screw to use to tighten clamp. (Default: 3)
module linear_bearing_housing(d=15,l=24,tab=7,gap=5,wall=3,tabwall=5,screwsize=3)
{
od = d+2*wall;
ogap = gap+2*tabwall;
tabh = tab/2+od/2*sqrt(2)-ogap/2;
translate([0,0,od/2]) difference() {
union() {
rotate([0,0,90])
teardrop(r=od/2,h=l);
translate([0,0,tabh])
cube(size=[l,ogap,tab+0.05], center=true);
translate([0,0,-od/4])
cube(size=[l,od,od/2], center=true);
}
rotate([0,0,90])
teardrop(r=d/2,h=l+0.05);
translate([0,0,(d*sqrt(2)+tab)/2])
cube(size=[l+0.05,gap,d+tab], center=true);
translate([0,0,tabh]) {
translate([0,-ogap/2+2-0.05,0])
rotate([90,0,0])
screw(screwsize=screwsize*1.06, screwlen=ogap, headsize=screwsize*2, headlen=10);
translate([0,ogap/2+0.05,0])
rotate([90,0,0])
metric_nut(size=screwsize,hole=false);
}
}
}
module lmXuu_housing(size=8,tab=7,gap=5,wall=3,tabwall=5,screwsize=3)
{
d = get_lmXuu_bearing_diam(size);
l = get_lmXuu_bearing_length(size);
linear_bearing_housing(d=d,l=l,tab=tab,gap=gap,wall=wall,tabwall=tabwall,screwsize=screwsize);
}
lmXuu_housing(size=8);
//lmXuu_housing(size=10);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

364
masks.scad Normal file
View File

@ -0,0 +1,364 @@
//////////////////////////////////////////////////////////////////////
// Masking shapes.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
module angle_half_pie_mask(
ang=45, h=1,
r=undef, r1=undef, r2=undef,
d=1.0, d1=undef, d2=undef,
) {
r = (r != undef)? r : (d/2);
r1 = (r1 != undef)? r1 : ((d1 != undef)? (d1/2) : r);
r2 = (r2 != undef)? r2 : ((d2 != undef)? (d2/2) : r);
rm = max(r1,r2);
difference() {
cylinder(h=h, r1=r1, r2=r2, center=true);
translate([0, -rm/2, 0])
cube(size=[rm*2+1, rm, h+1], center=true);
zrot(ang) {
translate([0, rm/2, 0]) {
cube(size=[rm*2.1, rm, h+1], center=true);
}
}
}
}
// Creates a pie wedge shape that can be used to mask other shapes.
// You must specify either r or d, or their r1/r2, d1/d2 variants.
// ang = angle of wedge in degrees.
// h = height of wedge.
// r = Radius of circle wedge is created from. (optional)
// r1 = Bottom radius of cone that wedge is created from. (optional)
// r2 = Upper radius of cone that wedge is created from. (optional)
// d = Diameter of circle wedge is created from. (optional)
// d1 = Bottom diameter of cone that wedge is created from. (optional)
// d2 = Upper diameter of cone that wedge is created from. (optional)
// Example:
// angle_pie_mask(ang=30, d=100, h=20);
module angle_pie_mask(
ang=45, h=1,
r=undef, r1=undef, r2=undef,
d=1.0, d1=undef, d2=undef,
) {
a1 = min(ang, 180.0);
a2 = max(0.0, ang-180.0);
r = (r != undef)? r : (d/2);
r1 = (r1 != undef)? r1 : ((d1 != undef)? (d1/2) : r);
r2 = (r2 != undef)? r2 : ((d2 != undef)? (d2/2) : r);
union() {
angle_half_pie_mask(h=h, r1=r1, r2=r2, ang=a1);
if (a2 > 0.0) {
zrot(180) angle_half_pie_mask(h=h, r1=r1, r2=r2, ang=a2);
}
}
}
// Creates a shape that can be used to chamfer a 90 degree edge along the Z axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// Example:
// chamfer_mask_z(l=10.0, chamfer=2.0);
module chamfer_mask_z(l=1.0, chamfer=1.0) {
zrot(45) cube(size=[chamfer*sqrt(2.0), chamfer*sqrt(2.0), l], center=true);
}
// Creates a shape that can be used to chamfer a 90 degree edge along the Y axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// Example:
// chamfer_mask_y(l=10.0, chamfer=2.0);
module chamfer_mask_y(l=1.0, chamfer=1.0) {xrot(90) chamfer_mask(h=l, r=chamfer);}
// Creates a shape that can be used to chamfer a 90 degree edge along the X axis.
// Difference it from the object to be chamfered. The center of the mask
// object should align exactly with the edge to be chamfered.
// l = Height of mask
// chamfer = size of chamfer
// Example:
// chamfer_mask_x(l=10.0, chamfer=2.0);
module chamfer_mask_x(l=1.0, chamfer=1.0) {yrot(90) chamfer_mask(h=l, r=chamfer);}
// Chamfers the edges of a cuboid region containing the given children.
// chamfer = inset of the chamfer from the edge. (Default: 1)
// size = The size of the rectangular cuboid we want to chamfer.
// edges = which edges do we want to chamfer.
// [
// [Y+Z+, Y-Z+, Y-Z-, Y+Z-],
// [X+Z+, X-Z+, X-Z-, X+Z-],
// [X+Y+, X-Y+, X-Y-, X+Y-]
// ]
// Example:
// chamfer(chamfer=2, size=[10,40,90], edges=[[0,0,0,0], [1,1,0,0], [0,0,0,0]]) {
// cube(size=[10,40,90], center=true);
// }
module chamfer(chamfer=1, size=[1,1,1], edges=[[0,0,0,0], [1,1,0,0], [0,0,0,0]])
{
eps = 0.1;
x = size[0];
y = size[1];
z = size[2];
lx = x + eps;
ly = y + eps;
lz = z + eps;
difference() {
union() {
children();
}
union() {
if (edges[0][0] != 0)
up(z/2) back(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][1] != 0)
up(z/2) fwd(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][2] != 0)
down(z/2) back(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[0][3] != 0)
down(z/2) fwd(y/2) chamfer_mask_x(l=lx, chamfer=chamfer);
if (edges[1][0] != 0)
up(z/2) right(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][1] != 0)
up(z/2) left(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][2] != 0)
down(z/2) right(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[1][3] != 0)
down(z/2) left(x/2) chamfer_mask_y(l=ly, chamfer=chamfer);
if (edges[2][0] != 0)
back(y/2) right(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][1] != 0)
back(y/2) left(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][2] != 0)
fwd(y/2) right(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
if (edges[2][3] != 0)
fwd(y/2) left(x/2) chamfer_mask_z(l=lz, chamfer=chamfer);
}
}
}
// Creates a shape that can be used to fillet a vertical 90 degree edge.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the edge to be filletted.
// h = height of vertical mask.
// r = radius of the fillet.
// center = If true, vertically center mask.
// Example:
// difference() {
// cube(size=100, center=false);
// up(50) fillet_mask(h=100.1, r=10.0);
// }
module fillet_mask(h=1.0, r=1.0, center=true)
{
n = ceil(segs(r)/4)*4;
linear_extrude(height=h, convexity=4, center=center) {
polygon(
points=concat(
[for (a = [ 0:360/n: 90]) [r*cos(a)-r, r*sin(a)-r]],
[for (a = [270:360/n:360]) [r*cos(a)-r, r*sin(a)+r]],
[for (a = [180:360/n:270]) [r*cos(a)+r, r*sin(a)+r]],
[for (a = [ 90:360/n:180]) [r*cos(a)+r, r*sin(a)-r]]
)
);
}
}
// Creates a vertical mask that can be used to fillet the edge where two
// face meet, at any arbitrary angle. Difference it from the object to
// be filletted. The center of the mask should align exactly with the
// edge to be filletted.
// h = height of vertical mask.
// r = radius of the fillet.
// ang = angle that the planes meet at.
// center = If true, vertically center mask.
// Example:
// fillet_planes_joint_mask(h=50.0, r=10.0, ang=120, $fn=32);
module fillet_planes_joint_mask(h=1.0, r=1.0, ang=90, center=true)
{
sweep = 180-ang;
n = ceil(segs(r)*sweep/360);
x = r*sin(90-(ang/2))/sin(ang/2);
linear_extrude(height=h, convexity=4, center=center) {
polygon(
points=concat(
[for (i = [0:n]) let (a=90+ang+i*sweep/n) [r*cos(a)+x, r*sin(a)+r]],
[for (i = [0:n]) let (a=90+i*sweep/n) [r*cos(a)+x, r*sin(a)-r]],
[
[min(-1, r*cos(270-ang)+x-1), r*sin(270-ang)-r],
[min(-1, r*cos(90+ang)+x-1), r*sin(90+ang)+r],
]
)
);
}
}
// Creates a shape that can be used to fillet the corner of an angle.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the point of the corner to be filletted.
// fillet = radius of the fillet.
// ang = angle between planes that you need to fillet the corner of.
// Example:
// fillet_edge_joint_mask(fillet=100, ang=90);
module fillet_edge_joint_mask(fillet=1.0, ang=90)
{
dy = fillet * tan(ang/2);
th = max(dy, fillet*2);
difference() {
down(dy) {
up(th/2) {
forward(fillet) {
cube(size=[fillet*2, fillet*4, th], center=true);
}
}
}
down(dy) {
forward(fillet) {
grid_of(count=2, spacing=fillet*2) {
sphere(r=fillet);
}
xrot(ang) {
up(fillet*2) {
cube(size=[fillet*8, fillet*8, fillet*4], center=true);
}
}
}
}
}
}
// Creates a shape that you can use to round 90 degree corners on a fillet.
// Difference it from the object to be filletted. The center of the mask
// object should align exactly with the corner to be filletted.
// r = radius of corner fillet.
// Example:
// $fa=1; $fs=1;
// difference() {
// cube(size=[6,10,16], center=true);
// translate([0, 5, 8]) yrot(90) fillet_mask(h=7, r=3);
// translate([3, 0, 8]) xrot(90) fillet_mask(h=11, r=3);
// translate([3, 5, 0]) fillet_mask(h=17, r=3);
// translate([3, 5, 8]) corner_fillet_mask(r=3);
// }
module corner_fillet_mask(r=1.0)
{
difference() {
cube(size=r*2, center=true);
grid_of(count=[2,2,2], spacing=r*2-0.05) {
sphere(r=r, center=true);
}
}
}
//!corner_fillet_mask(r=10.0);
// Create a mask that can be used to round the end of a cylinder.
// Difference it from the cylinder to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// cylinder to be filletted.
// r = radius of cylinder to fillet. (Default: 1.0)
// fillet = radius of the edge filleting. (Default: 0.25)
// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
// Example:
// $fa=2; $fs=2;
// difference() {
// cylinder(r=50, h=100, center=true);
// translate([0, 0, 50])
// fillet_cylinder_mask(r=50, fillet=10, xtilt=30, ytilt=30);
// }
module fillet_cylinder_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
{
dhx = 2*r*sin(xtilt);
dhy = 2*r*sin(ytilt);
dh = hypot(dhy, dhx);
down(dh/2) {
skew_xz(zang=xtilt) {
skew_yz(zang=ytilt) {
down(fillet) {
difference() {
up((dh+2*fillet)/2) {
cube(size=[r*2+10, r*2+10, dh+2*fillet], center=true);
}
torus(or=r, ir=r-2*fillet);
cylinder(r=r-fillet, h=2*fillet, center=true);
}
}
}
}
}
}
// Create a mask that can be used to round the edge of a circular hole.
// Difference it from the hole to be filletted. The center of the
// mask object should align exactly with the center of the end of the
// hole to be filletted.
// r = radius of hole to fillet. (Default: 1.0)
// fillet = radius of the edge filleting. (Default: 0.25)
// xtilt = angle of tilt of end of cylinder in the X direction. (Default: 0)
// ytilt = angle of tilt of end of cylinder in the Y direction. (Default: 0)
// Example:
// $fa=2; $fs=2;
// difference() {
// cube([150,150,100], center=true);
// cylinder(r=50, h=100.1, center=true);
// up(50) fillet_hole_mask(r=50, fillet=10, xtilt=0, ytilt=0);
// }
module fillet_hole_mask(r=1.0, fillet=0.25, xtilt=0, ytilt=0)
{
skew_xz(zang=xtilt) {
skew_yz(zang=ytilt) {
difference() {
cylinder(r=r+fillet, h=2*fillet, center=true);
down(fillet) torus(ir=r, or=r+2*fillet);
}
}
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

192
math.scad Normal file
View File

@ -0,0 +1,192 @@
//////////////////////////////////////////////////////////////////////
// Math helper functions.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function Cpi() = 3.141592653589793236;
// Quantize a value x to an integer multiple of y, rounding to the nearest multiple.
function quant(x,y) = floor(x/y+0.5)*y;
// Quantize a value x to an integer multiple of y, rounding down to the previous multiple.
function quantdn(x,y) = floor(x/y)*y;
// Quantize a value x to an integer multiple of y, rounding up to the next multiple.
function quantup(x,y) = ceil(x/y)*y;
// Calculate OpenSCAD standard number of segments in a circle based on $fn, $fa, and $fs.
// r = radius of circle to get the number of segments for.
function segs(r) = $fn>0?($fn>3?$fn:3):(ceil(max(min(360.0/$fa,abs(r)*2*Cpi()/$fs),5)));
// Calculate hypotenuse length of 2D triangle.
function hypot(x,y) = sqrt(x*x+y*y);
// Calculate hypotenuse length of 3D triangle.
function hypot3(x,y,z) = sqrt(x*x+y*y+z*z);
// Returns all but the first item of a given array.
function cdr(list) = len(list)>1?[for (i=[1:len(list)-1]) list[i]]:[];
// Reverses a list/array.
function reverse(list) = [ for (i = [len(list)-1 : -1 : 0]) list[i] ];
// Returns the sum of the square of each element of a vector.
function sum_of_squares(v,n=0) = (n>=len(v))? 0 : ((v[n]*v[n]) + sum_of_squares(v,n+1));
// Returns a 3D vector/point from a 2D or 3D vector.
function point3d(p) = [p[0], p[1], ((len(p) < 3)? 0 : p[2])];
// Returns an array of 3D vectors/points from a 2D or 3D vector array.
function path3d(points) = [for (point = points) point3d(point)];
// Returns the distance between a pair of 2D or 3D points.
function distance(p1, p2) = let(d = point3d(p2) - point3d(p1)) hypot3(d[0], d[1], d[2]);
// Create an identity matrix, for a given number of axes.
function ident(n) = [for (i = [0:n-1]) [for (j = [0:n-1]) (i==j)?1:0]];
// Create an identity matrix, for 3 axes.
ident3 = ident(3);
ident4 = ident(4);
// Takes a 3x3 matrix and returns its 4x4 equivalent.
function mat3_to_mat4(m) = concat(
[for (r = [0:2])
concat(
[for (c = [0:2]) m[r][c]],
[0]
)
],
[[0, 0, 0, 1]]
);
// Returns the 3x3 matrix to perform a rotation of a vector around the X axis.
// ang = number of degrees to rotate.
function matrix3_xrot(ang) = [
[1, 0, 0],
[0, cos(ang), -sin(ang)],
[0, sin(ang), cos(ang)]
];
// Returns the 4x4 matrix to perform a rotation of a vector around the X axis.
// ang = number of degrees to rotate.
function matrix4_xrot(ang) = mat3_to_mat4(matrix3_xrot(ang));
// Returns the 3x3 matrix to perform a rotation of a vector around the Y axis.
// ang = number of degrees to rotate.
function matrix3_yrot(ang) = [
[ cos(ang), 0, sin(ang)],
[ 0, 1, 0],
[-sin(ang), 0, cos(ang)],
];
// Returns the 4x4 matrix to perform a rotation of a vector around the Y axis.
// ang = number of degrees to rotate.
function matrix4_yrot(ang) = mat3_to_mat4(matrix3_yrot(ang));
// Returns the 3x3 matrix to perform a rotation of a vector around the Z axis.
// ang = number of degrees to rotate.
function matrix3_zrot(ang) = [
[cos(ang), -sin(ang), 0],
[sin(ang), cos(ang), 0],
[ 0, 0, 1]
];
// Returns the 4x4 matrix to perform a rotation of a vector around the Z axis.
// ang = number of degrees to rotate.
function matrix4_zrot(ang) = mat3_to_mat4(matrix3_zrot(ang));
// Returns the 3x3 matrix to perform a rotation of a vector around an axis.
// u = axis vector to rotate around.
// ang = number of degrees to rotate.
function matrix3_rot_by_axis(u, ang) = let(
c = cos(ang), c2 = 1-c, s = sin(ang)
) [
[u[0]*u[0]*c2+c, u[0]*u[1]*c2-u[2]*s, u[0]*u[2]*c2+u[1]*s],
[u[1]*u[0]*c2+u[2]*s, u[1]*u[1]*c2+c, u[1]*u[2]*c2-u[0]*s],
[u[2]*u[0]*c2-u[1]*s, u[2]*u[1]*c2+u[0]*s, u[2]*u[2]*c2+c ]
];
// Returns the 4x4 matrix to perform a rotation of a vector around an axis.
// u = axis vector to rotate around.
// ang = number of degrees to rotate.
function matrix4_rot_by_axis(u, ang) = mat3_to_mat4(matrix3_rot_by_axis(u, ang));
// Gives the sum of a series of sines, at a given angle.
// a = angle to get the value for.
// sines = array of [amplitude, frequency] pairs, where the frequency is the
// number of times the cycle repeats around the circle.
function sum_of_sines(a,sines) = len(sines)==0? 0 :
len(sines)==1?sines[0][0]*sin(a*sines[0][1]+(len(sines[0])>2?sines[0][2]:0)):
sum_of_sines(a,[sines[0]])+sum_of_sines(a,cdr(sines));
// Returns unit length normalized version of vector v.
function normalize(v) = v/norm(v);
// Returns angle in degrees between two 2D vectors.
function vector2d_angle(v1,v2) = atan2(v1[1],v1[0]) - atan2(v2[1],v2[0]);
// Returns angle in degrees between two 3D vectors.
function vector3d_angle(v1,v2) = acos((v1*v2)/(norm(v1)*norm(v2)));
// Returns a slice of an array. An index of 0 is the array start, -1 is array end
function slice(arr,st,end) = let(
s=st<0?(len(arr)+st):st,
e=end<0?(len(arr)+end+1):end
) [for (i=[s:e-1]) if (e>s) arr[i]];
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

123
metric_screws.scad Normal file
View File

@ -0,0 +1,123 @@
//////////////////////////////////////////////////////////////////////
// Screws, Bolts, and Nuts.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
function get_metric_bolt_head_size(size) = lookup(size, [
[ 4.0, 7.0],
[ 5.0, 8.0],
[ 6.0, 10.0],
[ 7.0, 11.0],
[ 8.0, 13.0],
[10.0, 16.0],
[12.0, 18.0],
[14.0, 21.0],
[16.0, 24.0],
[18.0, 27.0],
[20.0, 30.0]
]);
function get_metric_nut_size(size) = lookup(size, [
[ 2.0, 4.0],
[ 2.5, 5.0],
[ 3.0, 5.5],
[ 4.0, 7.0],
[ 5.0, 8.0],
[ 6.0, 10.0],
[ 7.0, 11.0],
[ 8.0, 13.0],
[10.0, 17.0],
[12.0, 19.0],
[14.0, 22.0],
[16.0, 24.0],
[18.0, 27.0],
[20.0, 30.0],
]);
function get_metric_nut_thickness(size) = lookup(size, [
[ 2.0, 1.6],
[ 2.5, 2.0],
[ 3.0, 2.4],
[ 4.0, 3.2],
[ 5.0, 4.0],
[ 6.0, 5.0],
[ 7.0, 5.5],
[ 8.0, 6.5],
[10.0, 8.0],
[12.0, 10.0],
[14.0, 11.0],
[16.0, 13.0],
[18.0, 15.0],
[20.0, 16.0]
]);
// Makes a simple threadless screw, useful for making screwholes.
// screwsize = diameter of threaded part of screw.
// screwlen = length of threaded part of screw.
// headsize = diameter of the screw head.
// headlen = length of the screw head.
// Example:
// screw(screwsize=3,screwlen=10,headsize=6,headlen=3);
module screw(screwsize=3,screwlen=10,headsize=6,headlen=3,$fn=undef)
{
$fn = ($fn==undef)?max(8,floor(180/asin(2/screwsize)/2)*2):$fn;
translate([0,0,-(screwlen)/2])
cylinder(r=screwsize/2, h=screwlen+0.05, center=true, $fn=$fn);
translate([0,0,(headlen)/2])
cylinder(r=headsize/2, h=headlen, center=true, $fn=$fn*2);
}
// Makes an unthreaded model of a standard nut for a standard metric screw.
// size = standard metric screw size in mm. (Default: 3)
// hole = include an unthreaded hole in the nut. (Default: true)
// Example:
// metric_nut(size=8, hole=true);
// metric_nut(size=3, hole=false);
module metric_nut(size=3, hole=true, $fn=undef, center=false)
{
$fn = ($fn==undef)?max(8,floor(180/asin(2/size)/2)*2):$fn;
radius = get_metric_nut_size(size)/2/cos(30);
thick = get_metric_nut_thickness(size);
offset = (center == true)? 0 : thick/2;
translate([0,0,offset]) difference() {
cylinder(r=radius, h=thick, center=true, $fn=6);
if (hole == true)
cylinder(r=size/2, h=thick+0.5, center=true, $fn=$fn);
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

314
nema_steppers.scad Normal file
View File

@ -0,0 +1,314 @@
//////////////////////////////////////////////////////////////////////
// Masks and models for NEMA stepper motors.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
include <shapes.scad>
include <math.scad>
function nema_motor_width(size) = lookup(size, [
[11.0, 28.2],
[14.0, 35.2],
[17.0, 42.3],
[23.0, 57.0],
[34.0, 86.0],
]);
function nema_motor_plinth_height(size) = lookup(size, [
[11.0, 1.5],
[14.0, 2.0],
[17.0, 2.0],
[23.0, 1.6],
[34.0, 2.03],
]);
function nema_motor_plinth_diam(size) = lookup(size, [
[11.0, 22.0],
[14.0, 22.0],
[17.0, 22.0],
[23.0, 38.1],
[34.0, 73.0],
]);
function nema_motor_screw_spacing(size) = lookup(size, [
[11.0, 23.11],
[14.0, 26.0],
[17.0, 30.99],
[23.0, 47.14],
[34.0, 69.6],
]);
function nema_motor_screw_size(size) = lookup(size, [
[11.0, 2.6],
[14.0, 3.0],
[17.0, 3.0],
[23.0, 5.1],
[34.0, 5.5],
]);
function nema_motor_screw_depth(size) = lookup(size, [
[11.0, 3.0],
[14.0, 4.5],
[17.0, 4.5],
[23.0, 4.8],
[34.0, 9.0],
]);
module nema11_stepper(h=24, shaft=5, shaft_len=20)
{
size = 11;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
}
xspread(screw_spacing)
yspread(screw_spacing)
down(scred_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
//!nema11_stepper();
module nema14_stepper(h=24, shaft=5, shaft_len=24)
{
size = 14;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
}
xspread(screw_spacing)
yspread(screw_spacing)
down(screw_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
//!nema14_stepper();
module nema17_stepper(h=34, shaft=5, shaft_len=20)
{
size = 17;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
difference() {
color([0.4, 0.4, 0.4]) {
down(h/2) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
}
xspread(screw_spacing)
yspread(screw_spacing)
down(screw_depth/2-0.05)
cylinder(r=screw_size/2, h=screw_depth, center=true, $fn=max(12,segs(screw_size/2)));
}
color([0.4, 0.4, 0.4])
up(plinth_height/2)
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color([0.9, 0.9, 0.9]) {
down(h-motor_width/12) {
fwd(motor_width/2+motor_width/24/2-0.1) {
difference() {
cube(size=[motor_width/8, motor_width/24, motor_width/8], center=true);
xrot(90) {
cylinder(d=motor_width/8-2, h=motor_width/6, center=true, $fn=12);
}
}
}
}
}
color("silver") {
difference() {
cylinder(h=shaft_len, r=shaft/2, $fn=max(12,segs(shaft/2)));
up(shaft_len/2+1) {
right(shaft_len/2+shaft/2-0.5) {
cube(shaft_len, center=true);
}
}
}
}
}
//!nema17_stepper();
module nema23_stepper(h=50, shaft=6.35, shaft_len=25)
{
size = 23;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
difference() {
union() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(12,segs(shaft/2)));
}
xspread(screw_spacing) {
yspread(screw_spacing) {
down(screw_depth/2)
cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth+h/2)
cube(size=[screw_inset, screw_inset, h], center=true);
}
}
}
}
//!nema23_stepper();
module nema34_stepper(h=75, shaft=12.7, shaft_len=32)
{
size = 34;
motor_width = nema_motor_width(size);
plinth_height = nema_motor_plinth_height(size);
plinth_diam = nema_motor_plinth_diam(size);
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size);
screw_depth = nema_motor_screw_depth(size);
screw_inset = motor_width - screw_spacing + 1;
difference() {
union() {
color([0.4, 0.4, 0.4]) {
translate([0, 0, -h/2]) {
rrect(size=[motor_width, motor_width, h], r=2, center=true);
}
}
color([0.4, 0.4, 0.4])
translate([0, 0, plinth_height/2])
cylinder(h=plinth_height, r=plinth_diam/2, center=true);
color("silver")
translate([0, 0, shaft_len/2])
cylinder(h=shaft_len, r=shaft/2, center=true, $fn=max(24,segs(shaft/2)));
}
xspread(screw_spacing) {
yspread(screw_spacing) {
down(screw_depth/2)
cylinder(r=screw_size/2, h=screw_depth+2, center=true, $fn=max(12,segs(screw_size/2)));
down(screw_depth+h/2)
cube(size=[screw_inset, screw_inset, h], center=true);
}
}
}
}
//!nema34_stepper();
module nema17_mount_holes(depth=5, l=5, slop=printer_slop)
{
size = 17;
plinth_diam = nema_motor_plinth_diam(size)+slop;
screw_spacing = nema_motor_screw_spacing(size);
screw_size = nema_motor_screw_size(size)+slop;
union() {
xspread(screw_spacing) {
yspread(screw_spacing) {
if (l>0) {
union() {
yspread(l) cylinder(h=depth, d=screw_size, center=true, $fn=max(8,segs(screw_size/2)));
cube([screw_size, l, depth], center=true);
}
} else {
cylinder(h=depth, d=screw_size, center=true, $fn=max(8,segs(screw_size/2)));
}
}
}
}
if (l>0) {
union () {
yspread(l) cylinder(h=depth, d=plinth_diam, center=true);
cube([plinth_diam, l, depth], center=true);
}
} else {
cylinder(h=depth, d=plinth_diam, center=true);
}
}
//!nema17_mount_holes(depth=5, l=5);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

224
paths.scad Normal file
View File

@ -0,0 +1,224 @@
//////////////////////////////////////////////////////////////////////
// 2D and Bezier Stuff.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <math.scad>
include <quaternions.scad>
// Creates a 2D polygon circle, modulated by one or more superimposed
// sine waves.
// r = radius of the base circle.
// sines = array of [amplitude, frequency] pairs, where the frequency is the
// number of times the cycle repeats around the circle.
// Example:
// modulated_circle(r=40, sines=[[3, 11], [1, 31]], $fn=6);
module modulated_circle(r=40, sines=[10])
{
freqs = len(sines)>0? [for (i=sines) i[1]] : [5];
points = [
for (a = [0 : (360/segs(r)/max(freqs)) : 360])
let(nr=r+sum_of_sines(a,sines)) [nr*cos(a), nr*sin(a)]
];
polygon(points);
}
// Similar to linear_extrude(), except the result is a hollow shell.
// wall = thickness of shell wall.
// height = height of extrusion.
// twist = degrees of twist, from bottom to top.
// slices = how many slices to use when making extrusion.
// Example:
// extrude_2d_hollow(wall=2, height=100, twist=90, slices=50)
// circle(r=40, center=true, $fn=6);
module extrude_2d_hollow(wall=2, height=50, twist=90, slices=60)
{
linear_extrude(height=height, twist=twist, slices=slices) {
difference() {
children();
offset(r=-wall) {
children();
}
}
}
}
// Takes a 2D polyline and removes uneccessary collinear points.
function simplify2d_path(path) = concat(
[path[0]],
[
for (
i = [1:len(path)-2]
) let (
v1 = path[i] - path[i-1],
v2 = path[i+1] - path[i-1]
) if (abs(cross(v1,v2)) > 1e-6) path[i]
],
[path[len(path)-1]]
);
// Takes a 3D polyline and removes uneccessary collinear points.
function simplify3d_path(path) = concat(
[path[0]],
[
for (
i = [1:len(path)-2]
) let (
v1 = path[i] - path[i-1],
v2 = path[i+1] - path[i-1]
) if (vector3d_angle(v1,v2) > 1e-6) path[i]
],
[path[len(path)-1]]
);
// Takes a closed 2D polyline path, centered on the XY plane, and
// extrudes it along a 3D spiral path of a given radius, height and twist.
// polyline = Array of points of a polyline path, to be extruded.
// h = height of the spiral to extrude along.
// r = radius of the spiral to extrude along.
// twist = number of degrees of rotation to spiral up along height.
// Example:
// poly = [[-10,0], [-3,-5], [3,-5], [10,0], [0,-30]];
// extrude_2dpath_along_spiral(poly, h=200, r=50, twist=1000, $fn=36);
module extrude_2dpath_along_spiral(polyline, h, r, twist=360) {
pline_count = len(polyline);
steps = ceil(segs(r)*(twist/360));
poly_points = [
for (
p = [0:steps]
) let (
a = twist * (p/steps),
dx = r*cos(a),
dy = r*sin(a),
dz = h * (p/steps),
cp = [dx, dy, dz],
rotx = matrix3_xrot(90),
rotz = matrix3_zrot(a),
rotm = rotz * rotx
) for (
b = [0:pline_count-1]
) rotm*point3d(polyline[b])+cp
];
poly_faces = concat(
[[for (b = [0:pline_count-1]) b]],
[
for (
p = [0:steps-1],
b = [0:pline_count-1],
i = [0:1]
) let (
b2 = (b == pline_count-1)? 0 : b+1,
p0 = p * pline_count + b,
p1 = p * pline_count + b2,
p2 = (p+1) * pline_count + b2,
p3 = (p+1) * pline_count + b,
pt = (i==0)? [p0, p2, p1] : [p0, p3, p2]
) pt
],
[[for (b = [pline_count-1:-1:0]) b+(steps)*pline_count]]
);
polyhedron(points=poly_points, faces=poly_faces, convexity=10);
}
function points_along_path3d(
polyline, // The 2D polyline to drag along the 3D path.
path, // The 3D polyline path to follow.
q=Q_Ident(), // Used in recursion
n=0 // Used in recursion
) = let(
end = len(path)-1,
v1 = (n == 0)? [0, 0, 1] : normalize(path[n]-path[n-1]),
v2 = (n == end)? normalize(path[n]-path[n-1]) : normalize(path[n+1]-path[n]),
crs = cross(v1, v2),
axis = norm(crs) <= 0.001? [0, 0, 1] : crs,
ang = vector3d_angle(v1, v2),
hang = ang * (n==0? 1.0 : 0.5),
hrot = Quat(axis, hang),
arot = Quat(axis, ang),
roth = Q_Mul(hrot, q),
rotm = Q_Mul(arot, q)
) concat(
[for (i = [0:len(polyline)-1]) Q_Rot_Vector(point3d(polyline[i]),roth) + path[n]],
(n == end)? [] : points_along_path3d(polyline, path, rotm, n+1)
);
// Takes a closed 2D polyline path, centered on the XY plane, and
// extrudes it perpendicularly along a 3D polyline path, forming a solid.
// polyline = Array of points of a polyline path, to be extruded.
// path = Array of points of a polyline path, to extrude along.
// convexity = max number of surfaces any single ray can pass through.
// Example:
// shape = [ [-15, 0], [0, 0], [-5, 10], [0, 10], [5, 10], [10, 5], [15, 0], [10, -5], [5, -10], [0, -10], [-5, -10], [-10, -5], [-15, 0] ];
// path = [ [0, 0, 0], [100, 33, 33], [200, -33, -33], [300, 0, 0] ];
// extrude_2dpath_along_3dpath(shape, path, tilt=false);
module extrude_2dpath_along_3dpath(polyline, path, convexity=10) {
pline_count = len(polyline);
path_count = len(path);
poly_points = points_along_path3d(polyline, path);
poly_faces = concat(
[[for (b = [0:pline_count-1]) b]],
[
for (
p = [0:path_count-2],
b = [0:pline_count-1],
i = [0:1]
) let (
b2 = (b == pline_count-1)? 0 : b+1,
p0 = p * pline_count + b,
p1 = p * pline_count + b2,
p2 = (p+1) * pline_count + b2,
p3 = (p+1) * pline_count + b,
pt = (i==0)? [p0, p2, p1] : [p0, p3, p2]
) pt
],
[[for (b = [pline_count-1:-1:0]) b+(path_count-1)*pline_count]]
);
polyhedron(points=poly_points, faces=poly_faces, convexity=convexity);
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

92
quaternions.scad Normal file
View File

@ -0,0 +1,92 @@
///////////////////////////////////////////
// Quaternions
///////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Quaternions are stored internally as a 4-value vector:
// [X, Y, Z, W] = W + Xi + Yj + Zk
function _Quat(a,s,w) = [a[0]*s, a[1]*s, a[2]*s, w];
function Quat(ax, ang) = _Quat(ax/norm(ax), sin(ang/2), cos(ang/2));
function Q_Ident() = [0, 0, 0, 1];
function Q_Add_S(q, s) = [q[0], q[1], q[2], q[3]+s];
function Q_Sub_S(q, s) = [q[0], q[1], q[2], q[3]-s];
function Q_Mul_S(q, s) = [q[0]*s, q[1]*s, q[2]*s, q[3]*s];
function Q_Div_S(q, s) = [q[0]/s, q[1]/s, q[2]/s, q[3]/s];
function Q_Add(a, b) = [a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3]];
function Q_Sub(a, b) = [a[0]-b[0], a[1]-b[1], a[2]-b[2], a[3]-b[3]];
function Q_Mul(a, b) = [
a[3]*b[0] + a[0]*b[3] + a[1]*b[2] - a[2]*b[1],
a[3]*b[1] - a[0]*b[2] + a[1]*b[3] + a[2]*b[0],
a[3]*b[2] + a[0]*b[1] - a[1]*b[0] + a[2]*b[3],
a[3]*b[3] - a[0]*b[0] - a[1]*b[1] - a[2]*b[2],
];
function Q_Dot(a, b) = a[0]*b[0] + a[1]*b[1] + a[2]*b[2] + a[3]*b[3];
function Q_Neg(q) = [-q[0], -q[1], -q[2], -q[3]];
function Q_Conj(q) = [-q[0], -q[1], -q[2], q[3]];
function Q_Norm(q) = sqrt(q[0]*q[0] + q[1]*q[1] + q[2]*q[2] + q[3]*q[3]);
function Q_Normalize(q) = q/Q_Norm(q);
function Q_Dist(q1, q2) = Q_Norm(Q_Sub(q1-q2));
// Returns the 3x3 rotation matrix for the given normalized quaternion q.
function Q_Matrix3(q) = [
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3]],
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3]],
[ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1]]
];
// Returns the 4x4 rotation matrix for the given normalized quaternion q.
function Q_Matrix4(q) = [
[1-2*q[1]*q[1]-2*q[2]*q[2], 2*q[0]*q[1]-2*q[2]*q[3], 2*q[0]*q[2]+2*q[1]*q[3], 0],
[ 2*q[0]*q[1]+2*q[2]*q[3], 1-2*q[0]*q[0]-2*q[2]*q[2], 2*q[1]*q[2]-2*q[0]*q[3], 0],
[ 2*q[0]*q[2]-2*q[1]*q[3], 2*q[1]*q[2]+2*q[0]*q[3], 1-2*q[0]*q[0]-2*q[1]*q[1], 0],
[ 0, 0, 0, 1]
];
// Returns the vector v after rotating it by the quaternion q.
function Q_Rot_Vector(v,q) = Q_Mul(Q_Mul(q,concat(v,0)),Q_Conj(q));
// Rotates all children by the given quaternion q.
module Qrot(q) {
multmatrix(Q_Matrix4(q)) {
children();
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

905
shapes.scad Normal file
View File

@ -0,0 +1,905 @@
//////////////////////////////////////////////////////////////////////
// Compound Shapes.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <transforms.scad>
// For when you MUST pass a child to a module, but you want it to be nothing.
module nil() difference() {cube(0.1, center=true); cube(0.2, center=true);}
// Makes a cube that is centered in X and Y axes, and has its bottom aligned with Z=0.
module upcube(size=[1,1,1]) {up(size[2]/2) cube(size, center=true);}
// Makes a cube with chamfered edges.
// size = size of cube [X,Y,Z]. (Default: [1,1,1])
// chamfer = chamfer inset along axis. (Default: 0.25)
// Example:
// chamfcube(size=[10,30,50], chamfer=1, chamfaxes=[1,1,1], chamfcorners=true);
module chamfcube(
size=[1,1,1],
chamfer=0.25,
chamfaxes=[1,1,1],
chamfcorners=false
) {
ch_width = sqrt(2)*chamfer;
ch_offset = 1;
difference() {
cube(size=size, center=true);
for (xs = [-1,1]) {
for (ys = [-1,1]) {
if (chamfaxes[0] == 1) {
translate([0,xs*size[1]/2,ys*size[2]/2]) {
rotate(a=[45,0,0]) cube(size=[size[0]+0.1,ch_width,ch_width], center=true);
}
}
if (chamfaxes[1] == 1) {
translate([xs*size[0]/2,0,ys*size[2]/2]) {
rotate(a=[0,45,0]) cube(size=[ch_width,size[1]+0.1,ch_width], center=true);
}
}
if (chamfaxes[2] == 1) {
translate([xs*size[0]/2,ys*size[1]/2],0) {
rotate(a=[0,0,45]) cube(size=[ch_width,ch_width,size[2]+0.1], center=true);
}
}
if (chamfcorners) {
for (zs = [-1,1]) {
translate([xs*size[0]/2,ys*size[1]/2,zs*size[2]/2]) {
scale([chamfer,chamfer,chamfer]) {
polyhedron(
points=[
[0,-1,-1], [0,-1,1], [0,1,1], [0,1,-1],
[-1,0,-1], [-1,0,1], [1,0,1], [1,0,-1],
[-1,-1,0], [-1,1,0], [1,1,0], [1,-1,0]
],
faces=[
[ 8, 4, 9, 5],
[ 9, 3, 10, 2],
[10, 7, 11, 6],
[11, 0, 8, 1],
[ 0, 7, 3, 4],
[ 1, 5, 2, 6],
[ 1, 8, 5],
[ 5, 9, 2],
[ 2, 10, 6],
[ 6, 11, 1],
[ 0, 4, 8],
[ 4, 3, 9],
[ 3, 7, 10],
[ 7, 0, 11],
]
);
}
}
}
}
}
}
}
}
// Makes a cube with rounded (filletted) vertical edges.
// size = size of cube [X,Y,Z]. (Default: [1,1,1])
// r = radius of edge/corner rounding. (Default: 0.25)
// Examples:
// rrect(size=[9,4,1], r=1, center=true);
// rrect(size=[5,7,3], r=1, $fn=24);
module rrect(size=[1,1,1], r=0.25, center=false)
{
w = size[0];
l = size[1];
h = size[2];
up(center? 0 : h/2) {
linear_extrude(height=h, convexity=2, center=true) {
left(w/2-r) {
back(l/2-r) circle(r=r, center=true);
fwd(l/2-r) circle(r=r, center=true);
}
right(w/2-r) {
back(l/2-r) circle(r=r, center=true);
fwd(l/2-r) circle(r=r, center=true);
}
square(size=[w, l-r*2], center=true);
square(size=[w-r*2, l], center=true);
}
}
}
// Makes a cube with rounded (filletted) edges and corners.
// size = size of cube [X,Y,Z]. (Default: [1,1,1])
// r = radius of edge/corner rounding. (Default: 0.25)
// Examples:
// rcube(size=[9,4,1], r=0.333, center=true, $fn=24);
// rcube(size=[5,7,3], r=1);
module rcube(size=[1,1,1], r=0.25, center=false)
{
$fn = ($fn==undef)?max(18,floor(180/asin(1/r)/2)*2):$fn;
xoff=abs(size[0])/2-r;
yoff=abs(size[1])/2-r;
zoff=abs(size[2])/2-r;
offset = center?[0,0,0]:size/2;
translate(offset) {
union() {
grid_of([-xoff,xoff],[-yoff,yoff],[-zoff,zoff])
sphere(r=r,center=true,$fn=$fn);
grid_of(xa=[-xoff,xoff],ya=[-yoff,yoff])
cylinder(r=r,h=zoff*2,center=true,$fn=$fn);
grid_of(xa=[-xoff,xoff],za=[-zoff,zoff])
rotate([90,0,0])
cylinder(r=r,h=yoff*2,center=true,$fn=$fn);
grid_of(ya=[-yoff,yoff],za=[-zoff,zoff])
rotate([90,0,0])
rotate([0,90,0])
cylinder(r=r,h=xoff*2,center=true,$fn=$fn);
cube(size=[xoff*2,yoff*2,size[2]], center=true);
cube(size=[xoff*2,size[1],zoff*2], center=true);
cube(size=[size[0],yoff*2,zoff*2], center=true);
}
}
}
// Creates a cylinder with chamferred edges.
// h = height of cylinder. (Default: 1.0)
// r = radius of cylinder. (Default: 1.0)
// d = diameter of cylinder. (use instead of r)
// chamfer = radial inset of the edge chamfer. (Default: 0.25)
// chamfedge = length of the chamfer edge. (Use instead of chamfer)
// center = boolean. If true, cylinder is centered. (Default: false)
// top = boolean. If true, chamfer the top edges. (Default: True)
// bottom = boolean. If true, chamfer the bottom edges. (Default: True)
// Example:
// chamferred_cylinder(h=50, r=20, chamfer=5, angle=45, bottom=false, center=true);
// chamferred_cylinder(h=50, r=20, chamfedge=10, angle=30, center=true);
module chamferred_cylinder(h=1, r=1, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
{
chamf = (chamfedge == undef)? chamfer * sqrt(2) : chamfedge;
x = (chamfedge == undef)? chamfer : (chamfedge * sin(angle));
y = (chamfedge == undef)? chamfer*sin(90-angle)/sin(angle) : (chamfedge * sin(90-angle));
rad = d == undef? r : d / 2.0;
up(center? 0 : h/2) {
rotate_extrude(angle=360, convexity=2) {
polygon(
points=[
[0, h/2],
[rad-x*(top?1:0), h/2],
[rad, h/2-y*(top?1:0)],
[rad, -h/2+y*(bottom?1:0)],
[rad-x*(bottom?1:0), -h/2],
[0, -h/2],
[0, h/2],
]
);
}
}
}
module chamf_cyl(h=1, r=1, d=undef, chamfer=0.25, chamfedge=undef, angle=45, center=false, top=true, bottom=true)
chamferred_cylinder(h=h, r=d, d=d, chamfer=chamfer, chamfedge=chamfedge, angle=angle, center=center, top=top, bottom=bottom);
//!chamf_cyl(h=20, d=20, chamfedge=10, angle=30, center=true, $fn=36);
// Creates a cylinder with filletted (rounded) ends.
// h = height of cylinder. (Default: 1.0)
// r = radius of cylinder. (Default: 1.0)
// d = diameter of cylinder. (Use instead of r)
// fillet = radius of the edge filleting. (Default: 0.25)
// center = boolean. If true, cylinder is centered. (Default: false)
// Example:
// rcylinder(h=50, r=20, fillet=5, center=true, $fa=1, $fs=1);
module rcylinder(h=1, r=1, d=undef, fillet=0.25, center=false)
{
d = (d == undef)? r * 2.0 : d;
up(center? 0 : h/2) {
rotate_extrude(angle=360, convexity=2) {
left(d/2-fillet) {
back(h/2-fillet) circle(r=fillet, center=true);
fwd(h/2-fillet) circle(r=fillet, center=true);
}
left(d/2/2) square(size=[d/2, h-fillet*2], center=true);
left((d/2-fillet)/2) square(size=[d/2-fillet, h], center=true);
}
}
}
module filleted_cylinder(h=1, r=1, d=undef, fillet=0.25, center=false)
rcylinder(h=h, r=r, d=d, fillet=fillet, center=center);
// Creates a pyramidal prism with a given number of sides.
// n = number of pyramid sides.
// h = height of the pyramid.
// l = length of one side of the pyramid. (optional)
// r = radius of the base of the pyramid. (optional)
// d = diameter of the base of the pyramid. (optional)
// circum = base circumscribes the circle of the given radius or diam.
// Example:
// pyramid(h=3, d=4, n=6, circum=true);
module pyramid(n=4, h=1, l=1, r=undef, d=undef, circum=false)
{
cm = circum? 1/cos(180/n) : 1.0;
radius = (r!=undef)? r*cm : ((d!=undef)? d*cm/2 : (l/(2*sin(180/n))));
zrot(180/n) cylinder(r1=radius, r2=0, h=h, $fn=n, center=false);
}
// Creates a vertical prism with a given number of sides.
// n = number of sides.
// h = height of the prism.
// l = length of one side of the prism. (optional)
// r = radius of the prism. (optional)
// d = diameter of the prism. (optional)
// circum = prism circumscribes the circle of the given radius or diam.
// Example:
// prism(n=6, h=3, d=4, circum=true);
module prism(n=3, h=1, l=1, r=undef, d=undef, circum=false, center=false)
{
cm = circum? 1/cos(180/n) : 1.0;
radius = (r!=undef)? r*cm : ((d!=undef)? d*cm/2 : (l/(2*sin(180/n))));
zrot(180/n) cylinder(r=radius, h=h, center=center, $fn=n);
}
// Creates a right triangle, with the hypotenuse on the right (X+) side.
// size = [width, thickness, height]
// center = true if triangle will be centered.
// Examples:
// right_triangle([4, 1, 6], center=true);
// right_triangle([4, 1, 9]);
module right_triangle(size=[1, 1, 1], center=false)
{
w = size[0];
thick = size[1];
h = size[2];
translate(center? [-w/2, -thick/2, -h/2] : [0, 0, 0]) {
polyhedron(
points=[
[0, 0, 0],
[0, 0, h],
[w, 0, 0],
[0, thick, 0],
[0, thick, h],
[w, thick, 0]
],
faces=[
[0, 1, 2],
[0, 2, 5, 3],
[0, 3, 4, 1],
[1, 4, 5, 2],
[3, 5, 4]
],
convexity=2
);
}
}
// Creates a trapezoidal prism.
// size1 = [width, length] of the bottom of the prism.
// size2 = [width, length] of the top of the prism.
// h = Height of the prism.
// center = vertically center the prism.
// Example:
// trapezoid(size1=[1,4], size2=[4,1], h=4, center=false);
// trapezoid(size1=[2,6], size2=[4,0], h=4, center=false);
module trapezoid(size1=[1,1], size2=[1,1], h=1, center=false)
{
s1 = [max(size1[0], 0.001), max(size1[1], 0.001)];
s2 = [max(size2[0], 0.001), max(size2[1], 0.001)];
up(center? 0 : h/2) {
polyhedron(
points=[
[+s2[0]/2, +s2[1]/2, +h/2],
[+s2[0]/2, -s2[1]/2, +h/2],
[-s2[0]/2, -s2[1]/2, +h/2],
[-s2[0]/2, +s2[1]/2, +h/2],
[+s1[0]/2, +s1[1]/2, -h/2],
[+s1[0]/2, -s1[1]/2, -h/2],
[-s1[0]/2, -s1[1]/2, -h/2],
[-s1[0]/2, +s1[1]/2, -h/2],
],
faces=[
[0, 1, 2, 3],
[0, 4, 5, 1],
[1, 5, 6, 2],
[2, 6, 7, 3],
[3, 7, 4, 0],
[4, 7, 6, 5],
],
convexity=2
);
}
}
// Created a sphere with a conical hat, to make a 3D teardrop.
// r = radius of spherical portion of the bottom. (Default: 1)
// d = diameter of spherical portion of bottom. (Use instead of r)
// h = height above sphere center to truncate teardrop shape. (Default: 1)
// maxang = angle of cone on top from vertical.
// Example:
// onion(h=15, r=10, maxang=30);
module onion(h=1, r=1, d=undef, maxang=45)
{
rr = (d!=undef)? (d/2.0) : r;
xx = rr*cos(maxang);
yy = rr*sin(maxang);
tipy = xx*sin(90-maxang)/sin(maxang) + yy;
rotate_extrude(angle=360, convexity=4) {
difference() {
union() {
circle(r=rr, center=true);
polygon(
points=[
[0, 0],
[0, tipy],
[xx, yy],
[rr, 0]
]
);
}
back(tipy/2+h) square(size=[rr*2, tipy], center=true);
left(rr) square(size=rr*2, center=true);
}
}
}
// Makes a hollow tube with the given outer size and wall thickness.
// h = height of tube. (Default: 1)
// r = Outer radius of tube. (Default: 1)
// r1 = Outer radius of bottom of tube. (Default: value of r)
// r2 = Outer radius of top of tube. (Default: value of r)
// wall = horizontal thickness of tube wall. (Default 0.5)
// Example:
// tube(h=3, r=4, wall=1, center=true);
// tube(h=6, r=4, wall=2, $fn=6);
// tube(h=3, r1=5, r2=7, wall=2, center=true);
module tube(h=1, r=1, r1=undef, r2=undef, wall=0.5, center=false)
{
r1 = (r1==undef)? r : r1;
r2 = (r2==undef)? r : r2;
difference() {
cylinder(h=h, r1=r1, r2=r2, center=center);
down(0.25) cylinder(h=h+1, r1=r1-wall, r2=r2-wall, center=center);
}
}
// Creates a torus with a given outer radius and inner radius.
// or = outer radius of the torus.
// ir = inside radius of the torus.
// Example:
// torus(or=30, ir=20, $fa=1, $fs=1);
module torus(or=1, ir=0.5)
{
rotate_extrude(convexity = 4)
translate([(or-ir)/2+ir, 0, 0])
circle(r = (or-ir)/2);
}
// Makes a linear slot with rounded ends, appropriate for bolts to slide along.
// p1 = center of starting circle of slot. (Default: [0,0,0])
// p2 = center of ending circle of slot. (Default: [1,0,0])
// h = height of slot shape. (default: 1.0)
// r = radius of slot circle. (default: 0.5)
// r1 = bottom radius of slot cone. (use instead of r)
// r2 = top radius of slot cone. (use instead of r)
// d = diameter of slot circle. (default: 1.0)
// d1 = bottom diameter of slot cone. (use instead of d)
// d2 = top diameter of slot cone. (use instead of d)
module slot(
p1=[0,0,0], p2=[1,0,0], h=1.0,
r=undef, r1=undef, r2=undef,
d=1.0, d1=undef, d2=undef
) {
r = (r != undef)? r : (d/2);
r1 = (r1 != undef)? r1 : ((d1 != undef)? (d1/2) : r);
r2 = (r2 != undef)? r2 : ((d2 != undef)? (d2/2) : r);
delta = p2 - p1;
theta = atan2(delta[1], delta[0]);
xydist = sqrt(pow(delta[0],2) + pow(delta[1],2));
phi = atan2(delta[2], xydist);
dist = sqrt(pow(delta[2],2) + xydist*xydist);
$fn = quantup(segs(max(r1,r2)),4);
translate(p1) {
zrot(theta) {
yrot(phi) {
cylinder(h=h, r1=r1, r2=r2, center=true);
right(dist/2) trapezoid([dist, r1*2], [dist, r2*2], h=h, center=true);
right(dist) cylinder(h=h, r1=r1, r2=r2, center=true);
}
}
}
}
// Makes an arced slot, appropriate for bolts to slide along.
// cp = centerpoint of slot arc. (default: [0, 0, 0])
// h = height of slot arc shape. (default: 1.0)
// r = radius of slot arc. (default: 0.5)
// d = diameter of slot arc. (default: 1.0)
// sr = radius of slot channel. (default: 0.5)
// sd = diameter of slot channel. (default: 0.5)
// sr1 = bottom radius of slot channel cone. (use instead of sr)
// sr2 = top radius of slot channel cone. (use instead of sr)
// sd1 = bottom diameter of slot channel cone. (use instead of sd)
// sd2 = top diameter of slot channel cone. (use instead of sd)
// sa = starting angle. (Default: 0.0)
// ea = ending angle. (Default: 90.0)
// Examples:
// arced_slot(d=100, h=15, sd=10, sa=60, ea=280);
// arced_slot(r=100, h=10, sd1=30, sd2=10, sa=45, ea=180, $fa=5, $fs=2);
module arced_slot(
cp=[0,0,0],
r=undef, d=1.0, h=1.0,
sr=undef, sr1=undef, sr2=undef,
sd=1.0, sd1=undef, sd2=undef,
sa=0, ea=90
) {
r = (r != undef)? r : (d/2);
sr = (sr != undef)? sr : (sd/2);
sr1 = (sr1 != undef)? sr1 : ((sd1 != undef)? (sd1/2) : sr);
sr2 = (sr2 != undef)? sr2 : ((sd2 != undef)? (sd2/2) : sr);
da = ea - sa;
zrot(sa) {
translate([r, 0, 0]) cylinder(h=h, r1=sr1, r2=sr2, center=true);
difference() {
angle_pie_mask(h=h, r1=(r+sr1), r2=(r+sr2), ang=da);
cylinder(h=h+0.05, r1=(r-sr1), r2=(r-sr2), center=true);
}
zrot(da) {
translate([r, 0, 0]) cylinder(h=h, r1=sr1, r2=sr2, center=true);
}
}
}
// Makes a teardrop shape in the XZ plane. Useful for 3D printable holes.
// r = radius of circular part of teardrop. (Default: 1)
// h = thickness of teardrop. (Default: 1)
// Example:
// teardrop(r=3, h=2, ang=30);
module teardrop(r=1, h=1, ang=45, $fn=undef)
{
$fn = ($fn==undef)?max(12,floor(180/asin(1/r)/2)*2):$fn;
xrot(90) union() {
translate([0, r*sin(ang), 0]) {
scale([1, 1/tan(ang), 1]) {
difference() {
zrot(45) {
cube(size=[2*r*cos(ang)/sqrt(2), 2*r*cos(ang)/sqrt(2), h], center=true);
}
translate([0, -r/2, 0]) {
cube(size=[2*r, r, h+1], center=true);
}
}
}
}
cylinder(h=h, r=r, center=true);
}
}
// Makes a rectangular strut with the top side narrowing in a triangle.
// The shape created may be likened to an extruded home plate from baseball.
// This is useful for constructing parts that minimize the need to support
// overhangs.
// w = Width (thickness) of the strut.
// l = Length of the strut.
// wall = height of rectangular portion of the strut.
// ang = angle that the trianglar side will converge at.
// Example:
// narrowing_strut(w=10, l=100, wall=5, ang=30);
module narrowing_strut(w=10, l=100, wall=5, ang=30)
{
tipy = wall + (w/2)*sin(90-ang)/sin(ang);
xrot(90) linear_extrude(height=l, center=true, steps=2) {
polygon(
points=[
[-w/2, 0],
[-w/2, wall],
[0, tipy],
[w/2, wall],
[w/2, 0]
]
);
}
}
// Makes a rectangular wall which thins to a smaller width in the center,
// with angled supports to prevent critical overhangs.
// h = height of wall.
// l = length of wall.
// thick = thickness of wall.
// ang = maximum overhang angle of diagonal brace.
// strut = the width of the diagonal brace.
// wall = the thickness of the thinned portion of the wall.
// Example:
// thinning_wall(h=50, l=100, thick=4, ang=30, strut=5, wall=2);
module thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2)
{
l1 = (l[0] == undef)? l : l[0];
l2 = (l[1] == undef)? l : l[1];
trap_ang = atan2((l2-l1)/2, h);
corr1 = 1 + sin(trap_ang);
corr2 = 1 - sin(trap_ang);
z1 = h/2;
z2 = max(0.1, z1 - strut);
z3 = max(0.05, z2 - (thick-wall)/2*sin(90-ang)/sin(ang));
x1 = l2/2;
x2 = max(0.1, x1 - strut*corr1);
x3 = max(0.05, x2 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr1);
x4 = l1/2;
x5 = max(0.1, x4 - strut*corr2);
x6 = max(0.05, x5 - (thick-wall)/2*sin(90-ang)/sin(ang)*corr2);
y1 = thick/2;
y2 = y1 - min(z2-z3, x2-x3) * sin(ang);
zrot(90) {
polyhedron(
points=[
[-x4, -y1, -z1],
[ x4, -y1, -z1],
[ x1, -y1, z1],
[-x1, -y1, z1],
[-x5, -y1, -z2],
[ x5, -y1, -z2],
[ x2, -y1, z2],
[-x2, -y1, z2],
[-x6, -y2, -z3],
[ x6, -y2, -z3],
[ x3, -y2, z3],
[-x3, -y2, z3],
[-x4, y1, -z1],
[ x4, y1, -z1],
[ x1, y1, z1],
[-x1, y1, z1],
[-x5, y1, -z2],
[ x5, y1, -z2],
[ x2, y1, z2],
[-x2, y1, z2],
[-x6, y2, -z3],
[ x6, y2, -z3],
[ x3, y2, z3],
[-x3, y2, z3],
],
faces=[
[ 4, 5, 1],
[ 5, 6, 2],
[ 6, 7, 3],
[ 7, 4, 0],
[ 4, 1, 0],
[ 5, 2, 1],
[ 6, 3, 2],
[ 7, 0, 3],
[ 8, 9, 5],
[ 9, 10, 6],
[10, 11, 7],
[11, 8, 4],
[ 8, 5, 4],
[ 9, 6, 5],
[10, 7, 6],
[11, 4, 7],
[11, 10, 9],
[20, 21, 22],
[11, 9, 8],
[20, 22, 23],
[16, 17, 21],
[17, 18, 22],
[18, 19, 23],
[19, 16, 20],
[16, 21, 20],
[17, 22, 21],
[18, 23, 22],
[19, 20, 23],
[12, 13, 17],
[13, 14, 18],
[14, 15, 19],
[15, 12, 16],
[12, 17, 16],
[13, 18, 17],
[14, 19, 18],
[15, 16, 19],
[ 0, 1, 13],
[ 1, 2, 14],
[ 2, 3, 15],
[ 3, 0, 12],
[ 0, 13, 12],
[ 1, 14, 13],
[ 2, 15, 14],
[ 3, 12, 15],
],
convexity=2
);
}
}
//!thinning_wall(h=50, l=[100, 80], thick=4, ang=30, strut=5, wall=2);
module braced_thinning_wall(h=50, l=100, thick=5, ang=30, strut=5, wall=2)
{
dang = atan((h-2*strut)/(l-2*strut));
dlen = (h-2*strut)/sin(dang);
union() {
xrot_copies([0, 180]) {
down(h/2) narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
fwd(l/2) xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
intersection() {
cube(size=[thick, l, h], center=true);
xrot_copies([-dang,dang]) {
zspread(strut/2) {
scale([1,1,1.5]) yrot(45) {
cube(size=[thick/sqrt(2), dlen, thick/sqrt(2)], center=true);
}
}
cube(size=[thick, dlen, strut/2], center=true);
}
}
}
cube(size=[wall, l-0.1, h-0.1], center=true);
}
}
// Makes a triangular wall with thick edges, which thins to a smaller width in
// the center, with angled supports to prevent critical overhangs.
// h = height of wall.
// l = length of wall.
// thick = thickness of wall.
// ang = maximum overhang angle of diagonal brace.
// strut = the width of the diagonal brace.
// wall = the thickness of the thinned portion of the wall.
// diagonly = boolean, which denotes only the diagonal brace should be thick.
// Example:
// thinning_triangle(h=50, l=100, thick=4, ang=30, strut=5, wall=2, diagonly=true);
module thinning_triangle(h=50, l=100, thick=5, ang=30, strut=5, wall=3, diagonly=false)
{
dang = atan(h/l);
dlen = h/sin(dang);
difference() {
union() {
if (!diagonly) {
translate([0, 0, -h/2])
narrowing_strut(w=thick, l=l, wall=strut, ang=ang);
translate([0, -l/2, 0])
xrot(-90) narrowing_strut(w=thick, l=h-0.1, wall=strut, ang=ang);
}
intersection() {
cube(size=[thick, l, h], center=true);
xrot(-dang) yrot(180) {
narrowing_strut(w=thick, l=dlen*1.2, wall=strut, ang=ang);
}
}
cube(size=[wall, l-0.1, h-0.1], center=true);
}
xrot(-dang) {
translate([0, 0, h/2]) {
cube(size=[thick+0.1, l*2, h], center=true);
}
}
}
}
// Makes a triangular wall which thins to a smaller width in the center,
// with angled supports to prevent critical overhangs. Basically an alias
// of thinning_triangle(), with diagonly=true.
// h = height of wall.
// l = length of wall.
// thick = thickness of wall.
// ang = maximum overhang angle of diagonal brace.
// strut = the width of the diagonal brace.
// wall = the thickness of the thinned portion of the wall.
// Example:
// thinning_brace(h=50, l=100, thick=4, ang=30, strut=5, wall=2);
module thinning_brace(h=50, l=100, thick=5, ang=30, strut=5, wall=3)
{
thinning_triangle(h=h, l=l, thick=thick, ang=ang, strut=strut, wall=wall, diagonly=true);
}
// Makes an open rectangular strut with X-shaped cross-bracing, designed with 3D printing in mind.
// h = Z size of strut.
// w = X size of strut.
// l = Y size of strut.
// thick = thickness of strut walls.
// maxang = maximum overhang angle of cross-braces.
// max_bridge = maximum bridging distance between cross-braces.
// strut = the width of the cross-braces.
// Example:
// sparse_strut3d(h=40, w=40, l=120, thick=4, maxang=30, strut=5, max_bridge=20);
module sparse_strut3d(h=50, l=100, w=50, thick=3, maxang=40, strut=3, max_bridge = 20)
{
xoff = w - thick;
yoff = l - thick;
zoff = h - thick;
xreps = ceil(xoff/yoff);
yreps = ceil(yoff/xoff);
xstep = xoff / xreps;
ystep = yoff / yreps;
cross_ang = atan2(xstep, ystep);
cross_len = hypot(xstep, ystep);
union() {
if(xreps>1) {
yspread(yoff) {
xspread(xstep, n=xreps-1) {
cube(size=[thick, thick, h], center=true);
}
}
}
if(yreps>1) {
xspread(xoff) {
yspread(ystep, n=yreps-1) {
cube(size=[thick, thick, h], center=true);
}
}
}
xspread(xoff) sparse_strut(h=h, l=l, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
yspread(yoff) zrot(90) sparse_strut(h=h, l=w, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
for(xs = [0:xreps-1]) {
for(ys = [0:yreps-1]) {
translate([(xs+0.5)*xstep-xoff/2, (ys+0.5)*ystep-yoff/2, 0]) {
zrot( cross_ang) sparse_strut(h=h, l=cross_len, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
zrot(-cross_ang) sparse_strut(h=h, l=cross_len, thick=thick, maxang=maxang, strut=strut, max_bridge=max_bridge);
}
}
}
}
}
//!sparse_strut3d(h=40, w=40, l=120, thick=3, strut=3);
// Makes an open rectangular strut with X-shaped cross-bracing, designed with 3D printing in mind.
// h = height of strut wall.
// l = length of strut wall.
// thick = thickness of strut wall.
// maxang = maximum overhang angle of cross-braces.
// max_bridge = maximum bridging distance between cross-braces.
// strut = the width of the cross-braces.
// Example:
// sparse_strut(h=40, l=120, thick=4, maxang=30, strut=5, max_bridge=20);
module sparse_strut(h=50, l=100, thick=4, maxang=30, strut=5, max_bridge = 20)
{
zoff = h/2 - strut/2;
yoff = l/2 - strut/2;
maxhyp = 1.5 * (max_bridge+strut)/2 / sin(maxang);
maxz = 2 * maxhyp * cos(maxang);
zreps = ceil(2*zoff/maxz);
zstep = 2*zoff / zreps;
hyp = zstep/2 / cos(maxang);
maxy = min(2 * hyp * sin(maxang), max_bridge+strut);
yreps = ceil(2*yoff/maxy);
ystep = 2*yoff / yreps;
ang = atan(ystep/zstep);
len = zstep / cos(ang);
union() {
zspread(zoff*2)
cube(size=[thick, l, strut], center=true);
yspread(yoff*2)
cube(size=[thick, strut, h], center=true);
grid_of(ya=[-yoff+ystep/2:ystep:yoff], za=[-zoff+zstep/2:zstep:zoff]) {
xrot( ang) cube(size=[thick, strut, len], center=true);
xrot(-ang) cube(size=[thick, strut, len], center=true);
}
}
}
// Makes a corrugated wall which relieves contraction stress while still
// providing support strength. Designed with 3D printing in mind.
// h = height of strut wall.
// l = length of strut wall.
// thick = thickness of strut wall.
// strut = the width of the cross-braces.
// wall = thickness of corrugations.
// Example:
// corrugated_wall(h=50, l=100, thick=4, strut=5, wall=2);
module corrugated_wall(h=50, l=100, thick=5, strut=5, wall=2)
{
innerlen = l - strut*2;
inner_height = h - wall*2;
spacing = thick*sqrt(3);
corr_count = floor(innerlen/spacing/2)*2;
yspread(l-strut) {
cube(size=[thick, strut, h], center=true);
}
zspread(h-wall) {
cube(size=[thick, l, wall], center=true);
}
difference() {
for (ypos = [-innerlen/2:spacing:innerlen/2]) {
translate([0, ypos, 0]) {
translate([0, spacing/4, 0])
zrot(-45) cube(size=[wall, thick*sqrt(2), inner_height], center=true);
translate([0, spacing*3/4, 0])
zrot(45) cube(size=[wall, thick*sqrt(2), inner_height], center=true);
}
}
xspread(2*thick) {
cube(size=[thick, l, h], center=true);
}
yspread(2*l) {
cube(size=[thick*2, l, h], center=true);
}
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

223
sliders.scad Normal file
View File

@ -0,0 +1,223 @@
//////////////////////////////////////////////////////////////////////
// Sliders and Rails.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
use <transforms.scad>
module slider(l=30, w=10, h=10, base=10, wall=5, ang=30, slop=printer_slop)
{
full_width = w + 2*wall;
full_height = h + base;
difference() {
// Overall slider shell
up(full_height/2) cube([w+2*wall, l, full_height], center=true);
up(base-slop) {
// Clear slider gap
up((h+5)/2) {
cube([w+slop, l+1, h+5], center=true);
}
// Horiz edge bevel
yspread(l) {
scale([1, 1, tan(30)]) {
xrot(45) cube([w+slop, 2*sqrt(2), 2*sqrt(2)], center=true);
}
}
}
// Back top bevel
up(full_height) {
xspread(full_width) {
yrot(45) {
cube([wall/2*sqrt(2), l+1, wall/2*sqrt(2)], center=true);
}
}
}
}
up(base) {
up(h/2) {
xflip_copy() {
left((w+slop)/2) {
difference() {
// Rails
right_half() {
scale([tan(ang), 1, 1]) {
yrot(45) cube([h*sin(45), l, h*sin(45)], center=true);
}
}
// Rail bevels
yflip_copy() {
right(sqrt(2)*h/2) {
fwd(l/2) {
zrot(45) cube(h, center=true);
}
}
}
}
}
}
}
}
}
//slider(l=30, base=10, wall=4, slop=0.2);
module rail(l=30, w=10, h=10, chamfer=1.0, ang=30)
{
attack_ang = 30;
attack_len = 2;
fudge = 1.177;
chamf = sqrt(2) * chamfer;
cosa = cos(ang*fudge);
sina = sin(ang*fudge);
z1 = h/2;
z2 = z1 - chamf * cosa;
z3 = z1 - attack_len * sin(attack_ang);
z4 = 0;
x1 = w/2;
x2 = x1 - chamf * sina;
x3 = x1 - chamf;
x4 = x1 - attack_len * sin(attack_ang);
x5 = x2 - attack_len * sin(attack_ang);
x6 = x1 - z1 * sina;
x7 = x4 - z1 * sina;
y1 = l/2;
y2 = y1 - attack_len * cos(attack_ang);
polyhedron(
convexity=4,
points=[
[-x5, -y1, z3],
[ x5, -y1, z3],
[ x7, -y1, z4],
[ x4, -y1, -z1-0.05],
[-x4, -y1, -z1-0.05],
[-x7, -y1, z4],
[-x3, -y2, z1],
[ x3, -y2, z1],
[ x2, -y2, z2],
[ x6, -y2, z4],
[ x1, -y2, -z1-0.05],
[-x1, -y2, -z1-0.05],
[-x6, -y2, z4],
[-x2, -y2, z2],
[ x5, y1, z3],
[-x5, y1, z3],
[-x7, y1, z4],
[-x4, y1, -z1-0.05],
[ x4, y1, -z1-0.05],
[ x7, y1, z4],
[ x3, y2, z1],
[-x3, y2, z1],
[-x2, y2, z2],
[-x6, y2, z4],
[-x1, y2, -z1-0.05],
[ x1, y2, -z1-0.05],
[ x6, y2, z4],
[ x2, y2, z2],
],
faces=[
[0, 1, 2],
[0, 2, 5],
[2, 3, 4],
[2, 4, 5],
[0, 13, 6],
[0, 6, 7],
[0, 7, 1],
[1, 7, 8],
[1, 8, 9],
[1, 9, 2],
[2, 9, 10],
[2, 10, 3],
[3, 10, 11],
[3, 11, 4],
[4, 11, 12],
[4, 12, 5],
[5, 12, 13],
[5, 13, 0],
[14, 15, 16],
[14, 16, 19],
[16, 17, 18],
[16, 18, 19],
[14, 27, 20],
[14, 20, 21],
[14, 21, 15],
[15, 21, 22],
[15, 22, 23],
[15, 23, 16],
[16, 23, 24],
[16, 24, 17],
[17, 24, 25],
[17, 25, 18],
[18, 25, 26],
[18, 26, 19],
[19, 26, 27],
[19, 27, 14],
[6, 21, 20],
[6, 20, 7],
[7, 20, 27],
[7, 27, 8],
[8, 27, 26],
[8, 26, 9],
[9, 26, 25],
[9, 25, 10],
[10, 25, 24],
[10, 24, 11],
[11, 24, 23],
[11, 23, 12],
[12, 23, 22],
[12, 22, 13],
[13, 22, 21],
[13, 21, 6],
]
);
}
//!rail(l=30, w=10, h=10);
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

523
transforms.scad Normal file
View File

@ -0,0 +1,523 @@
//////////////////////////////////////////////////////////////////////
// Transformations, distributors, duplicators, and manipulators.
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
printer_slop = 0.20; // mm
//////////////////////////////////////////////////////////////////////
// Transformations.
//////////////////////////////////////////////////////////////////////
// Moves/translates children.
// x = X axis translation.
// y = Y axis translation.
// z = Z axis translation.
// Example:
// move([10,20,30]) sphere(r=1);
// move(y=10) sphere(r=1);
// move(x=10, z=20) sphere(r=1);
module move(a=[0,0,0], x=0, y=0, z=0) {
translate(a) translate([x,y,z]) children();
}
// Moves/translates children the given amount along the X axis.
// Example:
// xmove(10) sphere(r=1);
module xmove(x=0) { translate([x,0,0]) children(); }
// Moves/translates children the given amount along the Y axis.
// Example:
// ymove(10) sphere(r=1);
module ymove(y=0) { translate([0,y,0]) children(); }
// Moves/translates children the given amount along the Z axis.
// Example:
// zmove(10) sphere(r=1);
module zmove(z=0) { translate([0,0,z]) children(); }
// Moves children left by the given amount in the -X direction.
// Example:
// left(10) sphere(r=1);
module left(x=0) { translate([-x,0,0]) children(); }
// Moves children right by the given amount in the +X direction.
// Example:
// right(10) sphere(r=1);
module right(x=0) { translate([x,0,0]) children(); }
// Moves children forward by x amount in the -Y direction.
// Example:
// forward(10) sphere(r=1);
module forward(y=0) { translate([0,-y,0]) children(); }
module fwd(y=0) { translate([0,-y,0]) children(); }
// Moves children back by the given amount in the +Y direction.
// Example:
// back(10) sphere(r=1);
module back(y=0) { translate([0,y,0]) children(); }
// Moves children down by the given amount in the -Z direction.
// Example:
// down(10) sphere(r=1);
module down(z=0) { translate([0,0,-z]) children(); }
// Moves children up by the given amount in the +Z direction.
// Example:
// up(10) sphere(r=1);
module up(z=0) { translate([0,0,z]) children(); }
// Rotates children around the Z axis by the given number of degrees.
// Example:
// xrot(90) cylinder(h=10, r=2, center=true);
module xrot(a=0) { rotate([a, 0, 0]) children(); }
// Rotates children around the Y axis by the given number of degrees.
// Example:
// yrot(90) cylinder(h=10, r=2, center=true);
module yrot(a=0) { rotate([0, a, 0]) children(); }
// Rotates children around the Z axis by the given number of degrees.
// Example:
// zrot(90) cube(size=[9,1,4], center=true);
module zrot(a=0) { rotate([0, 0, a]) children(); }
// Scales children by the given factor in the X axis.
// Example:
// xscale(3) sphere(r=100, center=true);
module xscale(x) {scale([x,1,1]) children();}
// Scales children by the given factor in the Y axis.
// Example:
// yscale(3) sphere(r=100, center=true);
module yscale(y) {scale([1,y,1]) children();}
// Scales children by the given factor in the Z axis.
// Example:
// zscale(3) sphere(r=100, center=true);
module zscale(z) {scale([1,1,z]) children();}
// Mirrors the children along the X axis, kind of like xscale(-1)
module xflip() mirror([1,0,0]) children();
// Mirrors the children along the Y axis, kind of like yscale(-1)
module yflip() mirror([0,1,0]) children();
// Mirrors the children along the Z axis, kind of like zscale(-1)
module zflip() mirror([0,0,1]) children();
// Skews children on the X-Y plane, keeping constant in Z.
// xang = skew angle towards the X direction.
// yang = skew angle towards the Y direction.
// Examples:
// skew_xy(xang=15) cube(size=10);
// skew_xy(xang=15, yang=30) cube(size=10);
module skew_xy(xang=0, yang=0)
{
multmatrix(m = [
[1, 0, tan(xang), 0],
[0, 1, tan(yang), 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]) {
children();
}
}
module zskew(xa=0,ya=0) skew_xy(xang=xa,yang=ya) children();
// Skews children on the Y-Z plane, keeping constant in X.
// yang = skew angle towards the Y direction.
// zang = skew angle towards the Z direction.
// Examples:
// skew_yz(yang=15) cube(size=10);
// skew_yz(yang=15, zang=30) cube(size=10);
module skew_yz(yang=0, zang=0)
{
multmatrix(m = [
[1, 0, 0, 0],
[tan(yang), 1, 0, 0],
[tan(zang), 0, 1, 0],
[0, 0, 0, 1]
]) {
children();
}
}
module xskew(ya=0,za=0) skew_yz(yang=ya,zang=za) children();
// Skews children on the X-Z plane, keeping constant in Y.
// xang = skew angle towards the X direction.
// zang = skew angle towards the Z direction.
// Examples:
// skew_xz(xang=15) cube(size=10);
// skew_xz(xang=15, zang=30) cube(size=10);
module skew_xz(xang=0, zang=0)
{
multmatrix(m = [
[1, tan(xang), 0, 0],
[0, 1, 0, 0],
[0, tan(zang), 1, 0],
[0, 0, 0, 1]
]) {
children();
}
}
module yskew(xa=0,za=0) skew_xz(xang=xa,zang=za) children();
//////////////////////////////////////////////////////////////////////
// Mutators.
//////////////////////////////////////////////////////////////////////
// Performs hull operations between consecutive pairs of children,
// then unions all of the hull results.
module chain_hull() {
union() {
if ($children == 1) {
children();
} else if ($children > 1) {
for (i =[1:$children-1]) {
hull() {
children(i-1);
children(i);
}
}
}
}
}
//////////////////////////////////////////////////////////////////////
// Duplicators and Distributers.
//////////////////////////////////////////////////////////////////////
// Makes a copy of the children, mirrored across the given axes.
// v = The normal vector of the plane to mirror across.
// Example:
// mirror_copy([1,-1,0]) yrot(30) cylinder(h=10, r=1, center=true);
module mirror_copy(v=[0,0,1])
{
union() {
children();
mirror(v) children();
}
}
module xflip_copy() {children(); mirror([1,0,0]) children();}
module yflip_copy() {children(); mirror([0,1,0]) children();}
module zflip_copy() {children(); mirror([0,0,1]) children();}
// Given a number of euller angles, rotates copies of the given children to each of those angles.
// Example:
// rot_copies(rots=[[0,0,0],[45,0,0],[0,45,120],[90,-45,270]])
// translate([6,0,0]) cube(size=[9,1,4], center=true);
module rot_copies(rots=[[0,0,0]])
{
for (rot = rots) rotate(rot) children();
}
// Given an array of angles, rotates copies of the children to each of those angles around the X axis.
// rots = Optional array of angles, in degrees, to make copies at.
// count = Optional number of evenly distributed copies, rotated around a circle.
// offset = Angle offset in degrees, for use with count.
// Example:
// xrot_copies(rots=[0,15,30,60,120,240]) translate([0,6,0]) cube(size=[4,9,1], center=true);
// xrot_copies(count=6, offset=15) translate([0,6,0]) cube(size=[4,9,1], center=true);
module xrot_copies(rots=[0], offset=0, count=undef)
{
if (count != undef) {
for (i = [0 : count-1]) {
a = (i / count) * 360.0;
rotate([a+offset, 0, 0]) {
children();
}
}
} else {
for (a = rots) {
rotate([a+offset, 0, 0]) {
children();
}
}
}
}
// Given an array of angles, rotates copies of the children to each of those angles around the Y axis.
// rots = Optional array of angles, in degrees, to make copies at.
// count = Optional number of evenly distributed copies, rotated around a circle.
// offset = Angle offset in degrees, for use with count.
// Example:
// yrot_copies(rots=[0,15,30,60,120,240]) translate([6,0,0]) cube(size=[9,4,1], center=true);
// yrot_copies(count=6, offset=15) translate([6,0,0]) cube(size=[9,4,1], center=true);
module yrot_copies(rots=[0], offset=0, count=undef)
{
if (count != undef) {
for (i = [0 : count-1]) {
a = (i / count) * 360.0;
rotate([0, a+offset, 0]) {
children();
}
}
} else {
for (a = rots) {
rotate([0, a+offset, 0]) {
children();
}
}
}
}
// Given an array of angles, rotates copies of the children to each of those angles around the Z axis.
// rots = Optional array of angles, in degrees, to make copies at.
// count = Optional number of evenly distributed copies, rotated around a circle.
// offset = Angle offset in degrees for first copy.
// Example:
// zrot_copies(rots=[0,15,30,60,120,240]) translate([6,0,0]) cube(size=[9,1,4], center=true);
// zrot_copies(count=6, offset=15) translate([6,0,0]) cube(size=[9,1,4], center=true);
module zrot_copies(rots=[0], offset=0, count=undef)
{
if (count != undef) {
for (i = [0 : count-1]) {
a = (i / count) * 360.0;
rotate([0, 0, a+offset]) {
children();
}
}
} else {
for (a = rots) {
rotate([0, 0, a+offset]) {
children();
}
}
}
}
// Makes copies of the given children at each of the given offsets.
// offsets = array of XYZ offset vectors. Default [[0,0,0]]
// Example:
// translate_copies([[-5,-5,0], [5,-5,0], [0,-5,7], [0,5,0]])
// sphere(r=3,center=true);
module translate_copies(offsets=[[0,0,0]])
{
for (off = offsets) translate(off) children();
}
module place_copies(a=[[0,0,0]]) {translate_copies(a) children();}
// Evenly distributes n duplicate children along an XYZ line.
// p1 = starting point of line. (Default: [0,0,0])
// p2 = ending point of line. (Default: [10,0,0])
// n = number of copies to distribute along the line. (Default: 2)
// Examples:
// line_of(p1=[0,0,0], p2=[-10,15,20], n=5) cube(size=[3,1,1],center=true);
//
module line_of(p1=[0,0,0], p2=[10,0,0], n=2)
{
delta = (p2 - p1) / (n-1);
for (i = [0:n-1]) translate(p1+delta*i) children();
}
module spread(p1,p2,n=3) {line_of(p1,p2,n) children();}
// Evenly distributes n duplicate children around an ovoid arc on the XY plane.
// n = number of copies to distribute around the circle. (Default: 6)
// r = radius of circle (Default: 1)
// rx = radius of ellipse on X axis. Used instead of r.
// ry = radius of ellipse on Y axis. Used instead of r.
// d = diameter of circle. (Default: 2)
// dx = diameter of ellipse on X axis. Used instead of d.
// dy = diameter of ellipse on Y axis. Used instead of d.
// rot = whether to rotate the copied children. (Default: false)
// sa = starting angle. (Default: 0.0)
// ea = ending angle. Will distribute copies CCW from sa to ea. (Default: 360.0)
// Examples:
// arc_of(d=8,n=5)
// cube(size=[3,1,1],center=true);
// arc_of(r=10,n=12,rot=true)
// cube(size=[3,1,1],center=true);
// arc_of(rx=15,ry=10,n=12,rot=true)
// cube(size=[3,1,1],center=true);
// arc_of(r=10,n=5,rot=true,sa=30.0,ea=150.0)
// cube(size=[3,1,1],center=true);
//
module arc_of(
n=6,
r=1, rx=undef, ry=undef,
d=undef, dx=undef, dy=undef,
sa=0.0, ea=360.0,
rot=false
) {
r = (d == undef)?r:(d/2.0);
rx = (dx == undef)?rx:(dx/2.0);
ry = (dy == undef)?rx:(dy/2.0);
rx = (rx == undef)?r:rx;
ry = (ry == undef)?r:ry;
sa = ((sa % 360.0) + 360.0) % 360.0; // make 0 < ang < 360
ea = ((ea % 360.0) + 360.0) % 360.0; // make 0 < ang < 360
n = (abs(ea-sa)<0.01)?(n+1):n;
delt = (((ea<=sa)?360.0:0)+ea-sa)/(n-1);
for (i = [0:n-1]) {
ang = sa + (i * delt);
translate([cos(ang)*rx, sin(ang)*ry, 0]) {
zrot(rot? atan2(sin(ang)*ry,cos(ang)*rx) : 0) {
children();
}
}
}
}
module xring(n=2,r=0,rot=true) {if (n>0) for (i=[0:n-1]) {a=i*360/n; xrot(a) back(r) xrot(rot?0:-a) children();}}
module yring(n=2,r=0,rot=true) {if (n>0) for (i=[0:n-1]) {a=i*360/n; yrot(a) right(r) yrot(rot?0:-a) children();}}
module zring(n=2,r=0,rot=true) {if (n>0) for (i=[0:n-1]) {a=i*360/n; zrot(a) right(r) zrot(rot?0:-a) children();}}
// Spreads out n copies of the given children along the X axis.
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// Examples:
// xspread(25) sphere(1);
// xspread(25,3) sphere(1)
// xspread(25, n=3) sphere(1)
// xspread(spacing=20, n=4) sphere(1)
module xspread(spacing=1,n=2) for (i=[0:n-1]) right((i-(n-1)/2.0)*spacing) children();
// Spreads out n copies of the given children along the Y axis.
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// Examples:
// yspread(25) sphere(1);
// yspread(25,3) sphere(1)
// yspread(25, n=3) sphere(1)
// yspread(spacing=20, n=4) sphere(1)
module yspread(spacing=1,n=2) for (i=[0:n-1]) back((i-(n-1)/2.0)*spacing) children();
// Spreads out n copies of the given children along the Z axis.
// spacing = spacing between copies. (Default: 1.0)
// n = Number of copies to spread out. (Default: 2)
// Examples:
// zspread(25) sphere(1);
// zspread(25,3) sphere(1)
// zspread(25, n=3) sphere(1)
// zspread(spacing=20, n=4) sphere(1)
module zspread(spacing=1,n=2) for (i=[0:n-1]) up((i-(n-1)/2.0)*spacing) children();
// Makes a 3D grid of duplicate children.
// xa = array or range of X-axis values to offset by. (Default: [0])
// ya = array or range of Y-axis values to offset by. (Default: [0])
// za = array or range of Z-axis values to offset by. (Default: [0])
// count = Optional number of copies to have per axis. (Default: none)
// spacing = spacing of copies per axis. Use with count. (Default: 0)
// Examples:
// grid_of(xa=[0,2,3,5],ya=[3:5],za=[-4:2:6]) sphere(r=0.5,center=true);
// grid_of(ya=[-6:3:6],za=[4,7]) sphere(r=1,center=true);
// grid_of(count=3, spacing=10) sphere(r=1,center=true);
// grid_of(count=[3, 1, 2], spacing=10) sphere(r=1,center=true);
// grid_of(count=[3, 4], spacing=[10, 8]) sphere(r=1,center=true);
// grid_of(count=[3, 4, 2], spacing=[10, 8, 5]) sphere(r=1,center=true, $fn=24);
module grid_of(xa=[0], ya=[0], za=[0], count=[], spacing=[])
{
count = (len(count) == undef)? [count,1,1] :
((len(count) == 1)? [count[0], 1, 1] :
((len(count) == 2)? [count[0], count[1], 1] :
((len(count) == 3)? count : undef)));
spacing = (len(spacing) == undef)? [spacing,spacing,spacing] :
((len(spacing) == 1)? [spacing[0], 0, 0] :
((len(spacing) == 2)? [spacing[0], spacing[1], 0] :
((len(spacing) == 3)? spacing : undef)));
if (count != undef && spacing != undef) {
for (x = [-(count[0]-1)/2 : (count[0]-1)/2 + 0.1]) {
for (y = [-(count[1]-1)/2 : (count[1]-1)/2 + 0.1]) {
for (z = [-(count[2]-1)/2 : (count[2]-1)/2 + 0.1]) {
translate([x*spacing[0], y*spacing[1], z*spacing[2]]) {
children();
}
}
}
}
} else {
for (xoff = xa) {
for (yoff = ya) {
for (zoff = za) {
translate([xoff,yoff,zoff]) {
children();
}
}
}
}
}
}
module top_half (s=100) difference() {children(); down(s/2) cube(s, center=true);}
module bottom_half(s=100) difference() {children(); up(s/2) cube(s, center=true);}
module left_half (s=100) difference() {children(); right(s/2) cube(s, center=true);}
module right_half (s=100) difference() {children(); left(s/2) cube(s, center=true);}
module front_half (s=100) difference() {children(); back(s/2) cube(s, center=true);}
module back_half (s=100) difference() {children(); fwd(s/2) cube(s, center=true);}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap

106
wiring.scad Normal file
View File

@ -0,0 +1,106 @@
//////////////////////////////////////////////////////////////////////
// Rendering for wiring bundles
//////////////////////////////////////////////////////////////////////
/*
BSD 2-Clause License
Copyright (c) 2017, Revar Desmera
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
include <math.scad>
include <paths.scad>
include <beziers.scad>
// Returns an array of 1 or 6 points that form a ring, based on wire diam and ring level.
// Level 0 returns a single point at 0,0. All greater levels return 6 points.
function hex_offset_ring(wirediam, lev=0) =
(lev == 0)? [[0,0]] : [
for (
sideang = [0:60:359.999],
sidewire = [1:lev]
) [
lev*wirediam*cos(sideang)+sidewire*wirediam*cos(sideang+120),
lev*wirediam*sin(sideang)+sidewire*wirediam*sin(sideang+120)
]
];
// Returns an array of 2D centerpoints for each of a bundle of wires of given diameter.
// The lev and arr variables are used for internal recursion.
function hex_offsets(wires, wirediam, lev=0, arr=[]) =
(len(arr) >= wires)? arr :
hex_offsets(
wires=wires,
wirediam=wirediam,
lev=lev+1,
arr=concat(arr, hex_offset_ring(wirediam, lev=lev))
);
// Returns a 3D object representing a bundle of wires that follow a given path,
// with the corners filleted to a given radius. There are 17 base wire colors.
// If you have more than 17 wires, colors will get re-used.
// Arguments:
// path: The 3D polyline path that the wire bundle should follow.
// wires: The number of wires in the wiring bundle.
// wirediam: The diameter of each wire in the bundle.
// fillet: The radius that the path corners will be filleted to.
// wirenum: The first wire's offset into the color table.
// bezsteps: The corner fillets in the path will be converted into this number of segments.
// Usage:
// wiring([[50,0,-50], [50,50,-50], [0,50,-50], [0,0,-50], [0,0,0]], fillet=10, wires=13);
module wiring(path, wires, wirediam=2, fillet=10, wirenum=0, bezsteps=12) {
vect = path[1]-path[0];
theta = atan2(vect[1], vect[0]);
xydist = hypot(vect[1], vect[0]);
phi = atan2(vect[2],xydist);
colors = [
[0.2, 0.2, 0.2], [1.0, 0.2, 0.2], [0.0, 0.8, 0.0], [1.0, 1.0, 0.2],
[0.3, 0.3, 1.0], [1.0, 1.0, 1.0], [0.7, 0.5, 0.0], [0.5, 0.5, 0.5],
[0.2, 0.9, 0.9], [0.8, 0.0, 0.8], [0.0, 0.6, 0.6], [1.0, 0.7, 0.7],
[1.0, 0.5, 1.0], [0.5, 0.6, 0.0], [1.0, 0.7, 0.0], [0.7, 1.0, 0.5],
[0.6, 0.6, 1.0],
];
offsets = hex_offsets(wires, wirediam);
bezpath = fillet_path(path, fillet);
poly = simplify3d_path(path3d(bezier_polyline(bezpath, bezsteps)));
n = max(segs(wirediam), 8);
r = wirediam/2;
for (i = [0:wires-1]) {
extpath = [for (a = [0:(360.0/n):360]) [r*cos(a), r*sin(a)] + offsets[i]];
roty = matrix3_yrot(90-phi);
rotz = matrix3_zrot(theta);
color(colors[(i+wirenum)%len(colors)]) {
extrude_2dpath_along_3dpath(extpath, poly);
}
}
}
// vim: noexpandtab tabstop=4 shiftwidth=4 softtabstop=4 nowrap