mirror of
https://github.com/revarbat/BOSL2.git
synced 2025-01-16 13:50:23 +01:00
Initial code dump.
This commit is contained in:
parent
381fb65b2e
commit
b3f19b1407
118
acme_screw.scad
Normal file
118
acme_screw.scad
Normal 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
230
beziers.scad
Normal 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
182
involute_gears.scad
Normal 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
211
joiners.scad
Normal 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
128
linear_bearings.scad
Normal 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
364
masks.scad
Normal 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
192
math.scad
Normal 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
123
metric_screws.scad
Normal 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
314
nema_steppers.scad
Normal 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
224
paths.scad
Normal 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
92
quaternions.scad
Normal 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
905
shapes.scad
Normal 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
223
sliders.scad
Normal 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
523
transforms.scad
Normal 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
106
wiring.scad
Normal 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
|
Loading…
x
Reference in New Issue
Block a user