//use // Author: Juan Gonzalez-Gomez, GPL //use // Author: William A Adams, Public Domain //use // Author: Damian Axford, with elements by nophead, Public Domain fudge = 0.01; // result is u-v function subv(u,v) = [u[0]-v[0], u[1]-v[1], u[2]-v[2]]; function vec3_from_vec4(v) = [v[0], v[1], v[2]]; function vec4_from_vec3(v) = [v[0], v[1], v[2], 1]; module pipeOrientate(v1,v2) { // calc rotation for v1 v1axis = v1[0]==0 && v1[1] == 0 ? [0,1,0] : cross([0,0,1], v1); // condition accounts for v1 being aligned with z axis v1ang = anglev([0,0,1], v1); v1axisLen = mod(v1axis); // v2 as vec4 vec2 = vec4_from_vec3(v2); // make quat to reverse the final rotation qRev = quat(v1axis, v1ang); qRevMat = quat_to_mat4(qRev); // rotate v2 by qRev vec2Rev = v1axisLen>0 ? vec4_mult_mat4(vec2, qRevMat) : vec2; // look and x,y components of vec2Rev and calc rot about z theta = atan2(vec2Rev[1], vec2Rev[0]); // complete the two rotations rotate(a=v1ang, v=v1axis) rotate(a=theta<0 || theta>0?theta:0, v=[0,0,1]) children(0); } // The following module does the actual WRITE function and is repeated as many times as required to write all pipes: module pipeCurve(points,point,radii, od,id, segments,isLastSegment=false) { pre = points[point-1]; start = points[point]; mid = points[point+1]; end = points[point+2]; post = points[point+3]; preR = radii[point-1]; r = radii[point]; postR = radii[point+1]; dir1 = subv(mid,start); dir2 = subv(end,mid); l1 = mod(dir1); l2 = mod(dir2); ang = anglev(dir1,dir2); preDir = pre? subv(start,pre) : dir1; preAng = pre? anglev(preDir, dir1) : 0; preInset = pre? preR * tan(preAng/2) : 0; postDir = post? subv(post,end) : dir2; postAng = post? anglev(dir2, postDir) : 0; postInset = post? postR * tan(postAng/2) : 0; dir1u = unitv(dir1); inset = r * tan(ang/2); rStart = start + (l1-inset)*dir1u; // start of the tubes write command translate(start) orientate(dir1) translate([0,0,preInset]) tube(h=l1-preInset-inset,or=od/2, ir=id/2, center=false); // THE ACTUAL 'tube'WRITE ASSIGNMENT for the start of the pipe // somewhere over here, a memory function needs to be built to NOT make a wall when it collides with the previous walls, and vice versa! Then, the inner area of crossing pipes will remain empty, instead of blocked by the crossing pipe! This can be best done by running each pipe section with numbered naming and use all others as cutouts for any run... Check for how the Internl diameter is cutoff, this might be useful as example as to WHERE put the cutoffs.. OR- an easier approach could be to just cutout ALL internal diameters for all pipes everywhere.... //end of the tubes write command translate(mid) orientate(dir2) translate([0,0,inset]) tube(h=l2-postInset-inset,or=od/2, ir=id/2, center=false);// THE ACTUAL 'tube' WRITE ASSIGNMENT for the end of the pipe // curved section write command (repeated for each section) in between start and end // nb: torus slice always starts at x axis and goes counter clockwise around z translate(rStart) pipeOrientate(dir1,dir2) rotate([0,0,180]) // rotate to lie along x rotate([90,0,0]) // flip up //difference(){ translate([-r,0,0])torusSlice(r1=r, r2=od/2, r3=id/2, start_angle=0, end_angle=ang); //translate([-r,0,0])torusSlice_only_inner_pipes(r1=r, r2=od/2, r3=id/2, start_angle=0, end_angle=ang); //} // THE ACTUAL 'torusSlice' WRITE ASSIGNMENT where we must solve the problem that the pipes block each other upon multiple pipes when bending in each other's way //torusSlice(r1=r, r2=od/2, start_angle=0, end_angle=ang, convexity=10, r3=id/2 , //OR USE: torusSlice_only_inner_pipes FOR THE CUTOUTS OF INNER PIPES } // The following module does the actual WRITE function of the pipe's curved coutouts for each part and is repeated as many times as required to write all pipes: module pipeCurveCutout(points,point,radii, od,id, segments,isLastSegment=false) { pre = points[point-1]; start = points[point]; mid = points[point+1]; end = points[point+2]; post = points[point+3]; preR = radii[point-1]; r = radii[point]; postR = radii[point+1]; dir1 = subv(mid,start); dir2 = subv(end,mid); l1 = mod(dir1); l2 = mod(dir2); ang = anglev(dir1,dir2); preDir = pre? subv(start,pre) : dir1; preAng = pre? anglev(preDir, dir1) : 0; preInset = pre? preR * tan(preAng/2) : 0; postDir = post? subv(post,end) : dir2; postAng = post? anglev(dir2, postDir) : 0; postInset = post? postR * tan(postAng/2) : 0; dir1u = unitv(dir1); inset = r * tan(ang/2); rStart = start + (l1-inset)*dir1u; // start of the tubes write command translate(start) orientate(dir1) translate([0,0,preInset]) tube(h=l1-preInset-inset,or=id/2, ir=0, center=false); // THE ACTUAL 'tube'WRITE ASSIGNMENT for the start of the pipe // somewhere over here, a memory function needs to be built to NOT make a wall when it collides with the previous walls, and vice versa! Then, the inner area of crossing pipes will remain empty, instead of blocked by the crossing pipe! This can be best done by running each pipe section with numbered naming and use all others as cutouts for any run... Check for how the Internl diameter is cutoff, this might be useful as example as to WHERE put the cutoffs.. OR- an easier approach could be to just cutout ALL internal diameters for all pipes everywhere.... //end of the tubes write command translate(mid) orientate(dir2) translate([0,0,inset]) tube(h=l2-postInset-inset,or=id/2, ir=0, center=false);// THE ACTUAL 'tube' WRITE ASSIGNMENT for the end of the pipe // curved section write command (repeated for each section) in between start and end // nb: torus slice always starts at x axis and goes counter clockwise around z translate(rStart) pipeOrientate(dir1,dir2) rotate([0,0,180]) // rotate to lie along x rotate([90,0,0]) // flip up //difference(){ translate([-r,0,0])torusSlice_only_inner_pipes(r1=r, r2=od/2, r3=id/2, start_angle=0, end_angle=ang); //TEST for cutout //translate([0,0,od/2]) cube([1000,1000,od],center=true);// test for showing lower 1/2 part of all the pipes //translate([-r,0,0])torusSlice_only_inner_pipes(r1=r, r2=od/2, r3=id/2, start_angle=0, end_angle=ang); //} // THE ACTUAL 'torusSlice' WRITE ASSIGNMENT where we must solve the problem that the pipes block each other upon multiple pipes when bending in each other's way //torusSlice(r1=r, r2=od/2, start_angle=0, end_angle=ang, convexity=10, r3=id/2 , //OR USE: torusSlice_only_inner_pipes FOR THE CUTOUTS OF INNER PIPES } // The following module does the actual WRITE function for the pipes and is repeated as many times as required to write all pipes: module curvedPipe(points, segments, radii, od, id) { //difference(){ for (point = [0:segments-2]) pipeCurve(points,point,radii,od,id); //for (point = [0:segments-2]) //pipeCurveCutout(points,point,radii,od,id); //pipeCurve(points,point,radii,id,0); //translate([97,0,od/2-id/2])rotate([0,0,0]) cube([40,id,od],center=true);// test for showing 1/2 the pipes //TEST for cutout //translate([0,0,od/2]) cube([1000,1000,od],center=true);// test for showing lower 1/2 part of all the pipes //} } difference(){ curvedPipe_module(34,26,50); curvedPipe_module (26,0,50); //translate([0,0,6]) cube([1000,1000,8],center=true);// test for showing lower 1/2 part of all the pipes } module curvedPipe_module(odia,idia,angle){ //the (curved if required) pipes to be made: if (true) { curvedPipe([ [-30,0,0],// start point 1 coordinates x,y,z [30,0,0],// point 2 coordinates x,y,z [60,40,0],// point 3 coordinates x,y,z [110,40,0]],// point 4 coordinates x,y,z 3,// number of segments [angle,angle],// rev.angle between segments (0-1, 2-3, etc) odia,//outer diameter of tube idia);//inner diameter of tube curvedPipe([ [-30,0,0],// start point 1 coordinates x,y,z [30,0,0],// point 2 coordinates x,y,z [60,0,40],// point 3 coordinates x,y,z [110,0,40]],// point 4 coordinates x,y,z 3,// number of segments [angle,angle],// rev.angle between segments (0-1, 2-3, etc) odia,//outer diameter of tube idia);//inner diameter of tube curvedPipe([ [-30,0,0],// start point 1 coordinates x,y,z [30,0,0],// point 2 coordinates x,y,z [60,0,-40],// point 3 coordinates x,y,z [110,0,-40]],// point 4 coordinates x,y,z 3,// number of segments [angle,angle],// rev.angle between segments (0-1, 2-3, etc) odia,//outer diameter of tube idia);//inner diameter of tube curvedPipe([ [-30,0,0],// start point 1 coordinates x,y,z [30,0,0],// point 2 coordinates x,y,z [60,-40,0],// point 3 coordinates x,y,z [110,-40,0]],// point 4 coordinates x,y,z 3,// number of segments [angle,angle],// rev.angle between segments (0-1, 2-3, etc) odia,//outer diameter of tube idia);//inner diameter of tube //rotate([0,0,180]) curvedPipe([ [0,0,0], // [100,0,0], // [100,100,0], // [100,100,100], // [0,100,100], // [0,100,0], // [0,0,0], // [50,0,50] // ], // 7, // [70,30,30,6,50,30], // 10, // 8); }} //--------------------------------------------------------------- //-- Openscad vector library //-- This is a component of the obiscad opescad tools by Obijuan //-- (C) Juan Gonzalez-Gomez (Obijuan) //-- Sep-2012 //--------------------------------------------------------------- //-- Released under the GPL license //--------------------------------------------------------------- //-- Updated orientate function to correct singularity when v lies along vref //---------------------------------------- //-- FUNCTIONS FOR WORKING WITH VECTORS //---------------------------------------- //-- Calculate the module of a vector function mod(v) = (sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2])); //-- Calculate the cros product of two vectors function cross(u,v) = [ u[1]*v[2] - v[1]*u[2], -(u[0]*v[2] - v[0]*u[2]) , u[0]*v[1] - v[0]*u[1]]; //-- Calculate the dot product of two vectors function dot(u,v) = u[0]*v[0]+u[1]*v[1]+u[2]*v[2]; //-- Return the unit vector of a vector function unitv(v) = v/mod(v); //-- Return the angle between two vectores function anglev(u,v) = acos( dot(u,v) / (mod(u)*mod(v)) ); //---------------------------------------------------------- //-- Draw a point in the position given by the vector p //---------------------------------------------------------- module point(p) { translate(p) sphere(r=0.7,$fn=20); } //------------------------------------------------------------------ //-- Draw a vector poiting to the z axis //-- This is an auxiliary module for implementing the vector module //-- //-- Parameters: //-- l: total vector length (line + arrow) //-- l_arrow: Vector arrow length //-- mark: If true, a mark is draw in the vector head, for having //-- a visual reference of the rolling angle //------------------------------------------------------------------ module vectorz(l=10, l_arrow=4, mark=false) { //-- vector body length (not including the arrow) lb = l - l_arrow; //-- The vector is locatead at 0,0,0 translate([0,0,lb/2]) union() { //-- Draw the arrow translate([0,0,lb/2]) cylinder(r1=2/2, r2=0.2, h=l_arrow, $fn=20); //-- Draw the mark if (mark) { translate([0,0,lb/2+l_arrow/2]) translate([1,0,0]) cube([2,0.3,l_arrow*0.8],center=true); } //-- Draw the body cylinder(r=1/2, h=lb, center=true, $fn=20); } //-- Draw a sphere in the vector base sphere(r=1/2, $fn=20); } //----------------------------------------------------------------- //-- ORIENTATE OPERATOR //-- //-- Orientate an object to the direction given by the vector v //-- Parameters: //-- v : Target orientation //-- vref: Vector reference. It is the vector of the local frame //-- of the object that want to be poiting in the direction //-- of v //-- roll: Rotation of the object around the v axis //------------------------------------------------------------------- module orientate(v,vref=[0,0,1], roll=0) { //-- Calculate the rotation axis raxis = v[0]==vref[0] && v[1]==vref[1] ? [0,1,0] : cross(vref,v); //-- Calculate the angle between the vectors ang = anglev(vref,v); //-- Rotate the children! rotate(a=roll, v=v) rotate(a=ang, v=raxis) children(0); } module orientate2(v,vref=[0,0,1], roll=0) { //-- Calculate the rotation axis raxis = cross(vref,v); //-- Calculate the angle between the vectors ang = anglev(vref,v); //-- Rotate the children! rotate(a=roll, v=v) rotate(a=ang, v=raxis) children(0); } //--------------------------------------------------------------------------- //-- Draw a vector //-- //-- There are two modes of drawing the vector //-- * Mode 1: Given by a cartesian point(x,y,z). A vector from the origin //-- to the end (x,y,z) is drawn. The l parameter (length) must //-- be 0 (l=0) //-- * Mode 2: Give by direction and length //-- A vector of length l pointing to the direction given by //-- v is drawn //--------------------------------------------------------------------------- //-- Parameters: //-- v: Vector cartesian coordinates //-- l: total vector length (line + arrow) //-- l_arrow: Vector arrow length // mark: If true, a mark is draw in the vector head, for having //-- a visual reference of the rolling angle //--------------------------------------------------------------------------- module vector(v,l=0, l_arrow=4, mark=false) { //-- Get the vector length from the coordinates mod = mod(v); //-- The vector is very easy implemented by means of the orientate //-- operator: //-- orientate(v) vectorz(l=mod, l_arrow=l_arrow) //-- BUT... in OPENSCAD 2012.02.22 the recursion does not //-- not work, so that if the user use the orientate operator //-- on a vector, openscad will ignore it.. //-- The solution at the moment (I hope the openscad developers //-- implement the recursion in the near future...) //-- is to repite the orientate operation in this module //---- SAME CALCULATIONS THAN THE ORIENTATE OPERATOR! //-- Calculate the rotation axis vref = [0,0,1]; raxis = cross(vref,v); //-- Calculate the angle between the vectors ang = anglev(vref,v); //-- orientate the vector //-- Draw the vector. The vector length is given either //--- by the mod variable (when l=0) or by l (when l!=0) if (l==0) rotate(a=ang, v=raxis) vectorz(l=mod, l_arrow=l_arrow, mark=mark); else rotate(a=ang, v=raxis) vectorz(l=l, l_arrow=l_arrow, mark=mark); } //---------------------------------------------------- //-- Draw a Frame of reference //-- Parameters: //-- l: length of the Unit vectors //----------------------------------------------------- module frame(l=10, l_arrow=4) { //-- Z unit vector color("Blue") vector([0,0,l], l_arrow=l_arrow); //-- X unit vector color("Red") vector([l,0,0], l_arrow=l_arrow ); //-- Y unit vector color("Green") vector([0,l,0],l_arrow=l_arrow); //-- Origin color("Gray") sphere(r=1, $fn=20); } //-------------------------------------------------- //-- Modules for testings and examples //-- Testing that the vector library is working ok //-------------------------------------------------- //-- 22 vectors in total are drawn, poiting to different directions module Test_vectors1() { a = 20; k = 1; //-- Add a frame of reference (in the origin) frame(l=a); //-- Negative vectors, pointing towards the three axis: -x, -y, -z color("Red") vector([-a, 0, 0]); color("Green") vector([0, -a, 0]); color("Blue") vector([0, 0, -a]); //-- It is *not* has been implemented using a for loop on purpose //-- This way, individual vectors can be commented out or highlighted //-- vectors with positive z vector([a, a, a*k]); vector([0, a, a*k]); vector([-a, a, a*k]); vector([-a, 0, a*k]); vector([-a, -a, a*k]); vector([0, -a, a*k]); vector([a, -a, a*k]); vector([a, 0, a*k]); //-- Vectors with negative z vector([a, a, -a*k]); vector([0, a, -a*k]); vector([-a, a, -a*k]); vector([-a, 0, -a*k]); vector([-a, -a, -a*k]); vector([0, -a, -a*k]); vector([a, -a, -a*k]); vector([a, 0, -a*k]); } //--- Another test... module Test_vectors2() { //-- Add the vector into the vector table //-- This vectors are taken as directions //-- All the vectors will be drawn with the same length (l) vector_table = [ [1, 1, 1], [0, 1, 1], [-1, 1, 1], [-1, 0, 1], [-1, -1, 1], [0, -1, 1], [1, -1, 1], [1, 0, 1], [1, 1, -1], [0, 1, -1], [-1, 1, -1], [-1, 0, -1], [-1, -1, -1], [0, -1, -1], [1, -1, -1], [1, 0, -1], ]; //-- Vector length l=20; frame(l=10); //-- Draw all the vector given in the table //-- The vectors point to the direction given in the table //-- They all are drawn with a length equal to l for (v=vector_table) { //-- Vector given by direction and length vector(v,l=l); } } //-- Test the cross product and the angle //-- between vectors module Test_vector3() { //-- Start with 2 unit vectors v=unitv([1,1,1]); u=unitv([0,1,0]); //-- Draw the vector in different colors //-- Increase the length for drawing color("Red") vector(v*20); color("blue") vector(u*20); //-- Get the cross product w = cross(v,u); vector(w*20); //-- The cross product is NOT conmutative... //-- change the order of v and u w2 = cross(u,v); vector(w2*20); //-- w should be perpendicular to v and u //-- Calculate the angles between them: echo("U , V: ", anglev(u,v)); echo("W , U: ", anglev(w,u)); echo("W , V: ", anglev(w,v)); } //-- Test the orientate operator module Test_vector4() { o = [10,10,10]; v = [-10,10,10]; color("Red") vector(o); color("Blue") vector(v); //-- Orientate the vector o in the direction of v orientate(v,o) vector(o); //-- Inverse operation: orientate the v vector in the direction //-- of o orientate(o,v) vector(v); //-- Example of orientation of a cube orientate(o,vref=[10,-2,5],roll=0) cube([10,2,5],center=true); vector([10,-2,5]); } //===================================== // This is public Domain Code // Contributed by: William A Adams // May 2011 //===================================== /* A set of math routines for graphics calculations There are many sources to draw from to create the various math routines required to support a graphics library. The routines here were created from scratch, not borrowed from any particular library already in existance. One great source for inspiration is the book: Geometric Modeling Author: Michael E. Mortenson This book has many great explanations about the hows and whys of geometry as it applies to modeling. As this file may accumulate quite a lot of routines, you can either include it whole in your OpenScad files, or you can choose to copy/paste portions that are relevant to your particular situation. It is public domain code, simply to avoid any licensing issues. */ //======================================= // Constants //======================================= // a very small number Cepsilon = 0.00000001; // The golden mean Cphi = 1.61803399; // PI Cpi = 3.14159; Chalfpi = Cpi/2; Ctau = Cpi*2; //======================================= // // Point Routines // //======================================= // Create a point function Point2D_Create(u,v) = [u,v]; function Point3D_Create(u,v,w) = [u,v,w]; // Create a homogenized point from a vec3 function point3_from_vec3(vec) = [vec[0], vec[1], vec[2], 1]; function vec3_from_point3(pt) = [pt[0], pt[1], pt[2]]; function vec2_from_point3(pt) = [pt[0], pt[1]]; function vec2_from_vec3(pt) = [pt[0], pt[1]]; //======================================= // // Vector Routines // //======================================= // Basic vector routines function vec2_add(v1, v2) = [v1[0]+v2[0], v1[1]+v2[1]]; function vec3_add(v1, v2) = [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]]; function vec4_add(v1, v2) = [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2], v1[3]+v2[3]]; function vec2_mults(v, s) = [v[0]*s, v[1]*s]; function vec3_mults(v, s) = [v[0]*s, v[1]*s, v[2]*s]; function vec4_mults(v, s) = [v[0]*s, v[1]*s, v[2]*s, v[3]*s]; function vec3_dot(v1,v2) = v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]; function vec4_dot(v1,v2) = v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]+v1[3]*v2[3]; function vec4_lengthsqr(v) = v[0]*v[0]+v[1]*v[1]+v[2]*v[2]+v[3]*v[3]; // Sum of two vectors function VSUM(v1, v2) = [v1[0]+v2[0], v1[1]+v2[1], v1[2]+v2[2]]; function VSUB(v1, v2) = [v1[0]-v2[0], v1[1]-v2[1], v1[2]-v2[2]]; function VMULT(v1, v2) = [v1[0]*v2[0], v1[1]*v2[1], v1[2]*v2[2]]; // Magnitude of a vector // Gives the Euclidean norm function VLENSQR(v) = (v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); function VLEN(v) = sqrt(VLENSQR(v)); function VMAG(v) = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); // Returns the unit vector associated with a vector function VUNIT(v) = v/VMAG(v); function VNORM(v) = v/VMAG(v); // The scalar, or 'dot' product // law of cosines // if VDOT(v1,v2) == 0, they are perpendicular function SPROD(v1,v2) = v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]; function VDOT(v1v2) = SPROD(v1v2[0], v1v2[1]); // The vector, or Cross product // Given an array that contains two vectors function VPROD(vs) = [ (vs[0][1]*vs[1][2])-(vs[1][1]*vs[0][2]), (vs[0][2]*vs[1][0])-(vs[1][2]*vs[0][0]), (vs[0][0]*vs[1][1])-(vs[1][0]*vs[0][1])]; function VCROSS(v1, v2) = VPROD([v1,v2]); // Calculate the angle between two vectors function VANG(v1, v2) = acos(VDOT([v1,v2])/(VMAG(v1)*VMAG(v2))); // Calculate the rotations necessary to take a polygon, and apply // the rotate() transform, and get the polygon to be perpendicular to // the specified vector. function rotations(v) = [ VANG([0,1,0], [0,v[1],v[2]]), VANG([0,0,-1], [v[0],0,v[2]]), VANG([1,0,0], [v[0], v[1],0])]; // Get the appropriate rotations to place a cylinder in world space // This is helpful when trying to place a 'line segment' in space // Book: Essential Mathematics for Games and Interactive Applications (p.75) function LineRotations(v) = [ atan2(sqrt(v[0]*v[0]+v[1]*v[1]), v[2]), 0, atan2(v[1], v[0])+90]; // The following are already provided in OpenScad, but are // here for completeness function VMULTS(v, s) = [v[0]*s, v[1]*s, v[2]*s]; function VDIVS(v,s) = [v[0]/s, v[1]/s, v[2]/s]; function VADDS(v,s) = [v[0]+s, v[1]+s, v[2]+s]; function VSUBS(v,s) = [v[0]-s, v[1]-s, v[2]-s]; // Some more convenience routines. Not found in OpenScad, but primarily using OpenScad routines function VMIN(v1,v2) = [min(v1[0],v2[0]), min(v1[1],v2[1]), min(v1[2], v2[2])]; function VMIN3(v1, v2, v3) = VMIN(VMIN(v1,v2),v3); function VMIN4(v1, v2, v3, v4) = VMIN(VMIN3(v1, v2, v3), v4); function VMAX(v1,v2) = [max(v1[0],v2[0]), max(v1[1],v2[1]), max(v1[2], v2[2])]; function VMAX3(v1, v2, v3) = VMAX(VMAX(v1,v2),v3); function VMAX4(v1, v2, v3, v4) = VMAX(VMAX(v1, v2, v3), v4); //======================================= // // MATRIX Routines // //======================================= function MADD2X2(m1, m2) = [ [m1[0][0]+m2[0][0], m1[0][1]+m2[0][1]], [m1[1][2]+m2[1][0], m1[1][1]+m2[1][1]]]; // Returns the determinant of a 2X2 matrix // Matrix specified in row major order function DETVAL2X2(m) = m[0[0]]*m[1[1]] - m[0[1]]*m[1[0]]; // Returns the determinant of a 3X3 matrix function DETVAL(m) = m[0[0]]*DETVAL2X2([ [m[1[1]],m[1[2]]], [m[2[1]],m[2[2]]] ]) - m[0[1]]*DETVAL2X2([ [m[1[0]],m[1[2]]], [m[2[0]],m[2[2]]] ]) + m[0[2]]*DETVAL2X2([ [m[1[0]],m[1[1]]], [m[2[0]],m[2[1]]] ]); //========================================= // Matrix 4X4 Operations // // Upper left 3x3 == scaling, shearing, reflection, rotation (linear transformations) // Upper right 3x1 == Perspective transformation // Lower left 1x3 == translation // Lower right 1x1 == overall scaling // // Note that the data is stored in a single large array // which is column ordered. //========================================= m400 = 0; m401=4; m402=8; m403=12; m410 = 1; m411=5; m412=9; m413=13; m420 = 2; m421=6; m422=10; m423=14; m430 = 3; m431=7; m432=11; m433=15; function mat3_to_mat4(m) = [ [m[0][0], m[0][1], m[0][2], 0], [m[1][0], m[1][1], m[1][2], 0], [m[2][0], m[2][1], m[2][2], 0], [m[3][0], m[3][1], m[3][2], 1], ]; function mat4_identity() = [ [1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]]; function mat4_transpose(m) = [ mat4_col(m,0), mat4_col(m,1), mat4_col(m,2), mat4_col(m,3) ]; function mat4_col(m, col) = [ m[0][col], m[1][col], m[2][col], m[3][col] ]; function mat4_row(m, row) = m[row]; function mat4_add(m1, m2) = m1 + m2; // Multiply two 4x4 matrices together // This is one of the workhorse mechanisms of the // graphics system function mat4_mult_mat4(m1, m2) = [ [vec4_dot(m1[0], mat4_col(m2,0)), vec4_dot(m1[0], mat4_col(m2,1)), vec4_dot(m1[0], mat4_col(m2,2)), vec4_dot(m1[0], mat4_col(m2,3))], [vec4_dot(m1[1], mat4_col(m2,0)), vec4_dot(m1[1], mat4_col(m2,1)), vec4_dot(m1[1], mat4_col(m2,2)), vec4_dot(m1[1], mat4_col(m2,3))], [vec4_dot(m1[2], mat4_col(m2,0)), vec4_dot(m1[2], mat4_col(m2,1)), vec4_dot(m1[2], mat4_col(m2,2)), vec4_dot(m1[2], mat4_col(m2,3))], [vec4_dot(m1[3], mat4_col(m2,0)), vec4_dot(m1[3], mat4_col(m2,1)), vec4_dot(m1[3], mat4_col(m2,2)), vec4_dot(m1[3], mat4_col(m2,3))], ]; // This is the other workhorse routine // Most transformations are of a vector and // a transformation matrix. function vec4_mult_mat4(vec, mat) = [ vec4_dot(vec, mat4_col(mat,0)), vec4_dot(vec, mat4_col(mat,1)), vec4_dot(vec, mat4_col(mat,2)), vec4_dot(vec, mat4_col(mat,3)), ]; function vec4_mult_mat34(vec, mat) = [ vec4_dot(vec, mat4_col(mat,0)), vec4_dot(vec, mat4_col(mat,1)), vec4_dot(vec, mat4_col(mat,2)) ]; // Linear Transformations // Translate function transform_translate(xyz) = [ [1, 0, 0, xyz[0]], [0, 1, 0, xyz[1]], [0, 0, 1, xyz[2]], [0, 0, 0, 1] ]; // Scale function transform_scale(xyz) = [ [xyz[0],0,0,0], [0,xyz[1],0,0], [0,0,xyz[2],0], [0,0,0,1] ]; // Rotation function transform_rotx(angle) = [ [1, 0, 0, 0], [0, cos(angle), -sin(angle), 0], [0, sin(angle), cos(angle), 0], [0, 0, 0, 1] ]; function transform_rotz(deg) = [ [cos(deg), -sin(deg), 0, 0], [sin(deg), cos(deg), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1] ]; function transform_roty(deg) = [ [cos(deg), 0, sin(deg), 0], [0, 1, 0, 0], [-sin(deg), 0, cos(deg), 0], [0, 0, 0, 1] ]; //======================================= // QUATERNION // // As a data structure, the quaternion is represented as: // x, y, z, w //======================================= function quat_new(x, y, z, w) = [x, y, z, w]; function quat_identity() = [0,0,0,1]; function _quat(a, s, c) = [a[0]*s, a[1]*s, a[2]*s, c]; /* Function: quat Description: Create a quaternion which represents a rotation around a specified axis by a given angle. Parameters axis - vec3 angle - The amount of rotation in degrees */ function quat(axis, angle) = _quat( VNORM(axis), s=sin(angle/2), c=cos(angle/2)); // Basic quaternion functions function quat_add(q1, q2) = [q1[0]+q2[0], q1[1]+q2[1], q1[2]+q2[2], q1[3]+q2[3]]; function quat_adds(q1, s) = [q1[0], q1[1], q1[2], q1[3]+s]; function quat_sub(q1, q2) = [q1[0]-q2[0], q1[1]-q2[1], q1[2]-q2[2], q1[3]-q2[3]]; function quat_subs(q1, s) = [q1[0], q1[1], q1[2], q1[3]-s]; function scalar_sub_quat(s, q1) = [-q1[0], -q1[1], -q1[2], s-q1[3]]; // Multiply two quaternions function quat_mult(a, r) = [ a[1]*r[2] - a[2]*r[1] + r[3]*a[0] + a[3]*r[0], a[2]*r[0] - a[0]*r[2] + r[3]*a[1] + a[3]*r[1], a[0]*r[1] - a[1]*r[0] + r[3]*a[2] + a[3]*r[2], a[3]*r[3] - a[0]*r[0] - a[1]*r[1] - a[2]*r[2] ]; function quat_mults(q1, s) = [q1[0]*s, q1[1]*s,q1[2]*s,q1[3]*s]; function quat_divs(q1, s) = [q1[0]/s, q1[1]/s,q1[2]/s,q1[3]/s]; function quat_neg(q1) = [-q1[0], -q1[1],-q1[2],-q1[3]]; function quat_dot(q1, q2) = q1[0]*q2[0]+q1[1]*q2[1]+q1[2]*q2[2]+ q1[3]*q2[3]; function quat_norm(q) = sqrt(q[0]*q[0]+q[1]*q[1]+q[2]*q[2]+q[3]*q[3]); function quat_normalize(q) = q/quat_norm(q); function quat_conj(q) = [-q[0], -q[1], -q[2], q[3]]; function quat_distance(q1, q2) = quat_norm(quat_sub(q1-q2)); // Converting quaternion to matrix4x4 function quat_to_mat4_s(q) = (vec4_lengthsqr(q)!=0) ? 2/vec4_lengthsqr(q) : 0; function quat_to_mat4_xyzs(q, s) = [q[0]*s,q[1]*s, q[2]*s]; function quat_to_mat4_X(xyzs, x) = xyzs*x; function _quat_xyzsw(xyzs, w) = xyzs*w; function _quat_XYZ(xyzs, q)= [ quat_to_mat4_X(xyzs, q[0]), quat_to_mat4_X(xyzs, q[1]), quat_to_mat4_X(xyzs,q[2]) ]; function _quat_to_mat4(xyzsw, XYZ) = [ [(1.0-(XYZ[1][1]+XYZ[2][2])), (XYZ[0][1]-xyzsw[2]), (XYZ[0][2]+xyzsw[1]), 0], [(XYZ[0][1]+xyzsw[2]), (1-(XYZ[0][0]+XYZ[2][2])), (XYZ[1][2]-xyzsw[0]), 0], [(XYZ[0][2]-xyzsw[1]), (XYZ[1][2]+xyzsw[0]), (1.0-(XYZ[0][0]+XYZ[1][1])), 0], [0, 0, 0, 1] ]; function quat_to_mat4(q) = _quat_to_mat4( _quat_xyzsw(quat_to_mat4_xyzs(q, quat_to_mat4_s(q)),q[3]), _quat_XYZ(quat_to_mat4_xyzs(q, quat_to_mat4_s(q)), q)); //======================================= // // Helper Routines // //======================================= function AvgThree(v1,v2,v3) = (v1+v2+v3)/3; function AvgFour(v1,v2,v3,v4) = (v1+v2+v3+v4)/4; function CenterOfGravity3(p0, p1, p2) = [ AvgThree(p0[0], p1[0], p2[0]), AvgThree(p0[1], p1[1], p2[1]), AvgThree(p0[2], p1[2], p2[2])]; function CenterOfGravity4(p0, p1, p2, p3) = [ AvgThree(p0[0], p1[0], p2[0], p3[0]), AvgThree(p0[1], p1[1], p2[1], p3[1]), AvgThree(p0[2], p1[2], p2[2], p3[2])]; function lerp1( p0, p1, u) = (1-u)*p0 + u*p1; function lerp(v1, v2, u) = [ lerp1(v1[0], v2[0],u), lerp1(v1[1], v2[1],u), lerp1(v1[2], v2[2],u) ]; //======================================= // // Cubic Curve Routines // //======================================= function quadratic_U(u) = [3*(u*u), 2*u, 1, 0]; function cubic_U(u) = [u*u*u, u*u, u, 1]; function ccerp(U, M, G) = vec4_mult_mat34(vec4_mult_mat4(U, M), G); function cubic_hermite_M() = [ [2, -2, 1, 1], [-3, 3, -2, -1], [0, 0, 1, 0], [1, 0, 0, 0] ]; function cubic_bezier_M() = [ [-1, 3, -3, 1], [3, -6, 3, 0], [-3, 3, 0, 0], [1, 0, 0, 0] ]; function cubic_catmullrom_M() = [ [-1, 3, -3, 1], [2, -5, 4, -1], [-1, 0, 1, 0], [0, 2, 0, 0] ]; /* To use the B-spline, you must use a multiplier of 1/6 on the matrix itself Also, the parameter matrix is [(t-ti)^3, (t-ti)^2, (t-ti), 1] and the geometry is [Pi-3, Pi-2, Pi-1, Pi] Reference: http://spec.winprog.org/curves/ */ function cubic_bspline_M() = [ [-1, 2, -3, 1], [3, -6, 3, 0], [-3, 0, 3, 0], [1, 4, 1, 0], ]; //======================================= // // Bezier Curve Routines // //======================================= /* Bernstein Basis Functions These are the coefficients for bezier curves */ // For quadratic curve (parabola) function Basis02(u) = pow((1-u), 2); function Basis12(u) = 2*u*(1-u); function Basis22(u) = u*u; // For cubic curves, these functions give the weights per control point. function Basis03(u) = pow((1-u), 3); function Basis13(u) = 3*u*(pow((1-u),2)); function Basis23(u) = 3*(pow(u,2))*(1-u); function Basis33(u) = pow(u,3); // Given an array of control points // Return a point on the quadratic Bezier curve as specified by the // parameter: 0<= 'u' <=1 function Bern02(cps, u) = [Basis02(u)*cps[0][0], Basis02(u)*cps[0][1], Basis02(u)*cps[0][2]]; function Bern12(cps, u) = [Basis12(u)*cps[1][0], Basis12(u)*cps[1][1], Basis12(u)*cps[1][2]]; function Bern22(cps, u) = [Basis22(u)*cps[2][0], Basis22(u)*cps[2][1], Basis22(u)*cps[2][2]]; function berp2(cps, u) = Bern02(cps,u)+Bern12(cps,u)+Bern22(cps,u); //=========== // Cubic Beziers - described by 4 control points //=========== // Calculate a singe point along a cubic bezier curve // Given a set of 4 control points, and a parameter 0 <= 'u' <= 1 // These functions will return the exact point on the curve function PtOnBez2D(p0, p1, p2, p3, u) = [ Basis03(u)*p0[0]+Basis13(u)*p1[0]+Basis23(u)*p2[0]+Basis33(u)*p3[0], Basis03(u)*p0[1]+Basis13(u)*p1[1]+Basis23(u)*p2[1]+Basis33(u)*p3[1]]; // Given an array of control points // Return a point on the cubic Bezier curve as specified by the // parameter: 0<= 'u' <=1 function Bern03(cps, u) = [Basis03(u)*cps[0][0], Basis03(u)*cps[0][1], Basis03(u)*cps[0][2]]; function Bern13(cps, u) = [Basis13(u)*cps[1][0], Basis13(u)*cps[1][1], Basis13(u)*cps[1][2]]; function Bern23(cps, u) = [Basis23(u)*cps[2][0], Basis23(u)*cps[2][1], Basis23(u)*cps[2][2]]; function Bern33(cps, u) = [Basis33(u)*cps[3][0], Basis33(u)*cps[3][1], Basis33(u)*cps[3][2]]; function berp(cps, u) = Bern03(cps,u)+Bern13(cps,u)+Bern23(cps,u)+Bern33(cps,u); // Calculate a point on a Bezier mesh // Given the mesh, and the parametric 'u', and 'v' values function berpm(mesh, uv) = berp( [berp(mesh[0], uv[0]), berp(mesh[1], uv[0]), berp(mesh[2], uv[0]), berp(mesh[3], uv[0])], uv[1]); //======================================== // Bezier Mesh normals //======================================== // The following uses a partial derivative at each point. // It is very expensive. // For each point, calculate a partial derivative in both 'u' and 'v' directions, then do a cross // product between those two to get the normal vector // Partial derivative in the 'u' direction function Bern03du(mesh, uv) = (Basis02(uv[0]) * Basis03(uv[1]) * (mesh[0][1]-mesh[0][0])) + (Basis12(uv[0]) * Basis03(uv[1]) * (mesh[0][2]-mesh[0][1]))+ (Basis22(uv[0]) * Basis03(uv[1]) * (mesh[0][3]-mesh[0][2])); function Bern13du(mesh, uv) = ( Basis02(uv[0]) * Basis13(uv[1]) * (mesh[1][1]-mesh[1][0])) + ( Basis12(uv[0]) * Basis13(uv[1]) * (mesh[1][2]-mesh[1][1])) + ( Basis22(uv[0]) * Basis13(uv[1]) * (mesh[1][3]-mesh[1][2])); function Bern23du(mesh, uv) = (Basis02(uv[0]) * Basis23(uv[1]) * (mesh[2][1]-mesh[2][0])) + (Basis12(uv[0]) * Basis23(uv[1]) * (mesh[2][2]-mesh[2][1])) + (Basis22(uv[0]) * Basis23(uv[1]) * (mesh[2][3]-mesh[2][2])); function Bern33du(mesh, uv) = (Basis02(uv[0]) * Basis33(uv[1]) * (mesh[3][1]-mesh[3][0])) + (Basis12(uv[0]) * Basis33(uv[1]) * (mesh[3][2]-mesh[3][1])) + (Basis22(uv[0]) * Basis33(uv[1]) * (mesh[3][3]-mesh[3][2])); // Partial derivative in the 'v' direction function Bern03dv(mesh, uv) = (Basis02(uv[1]) * Basis03(uv[0]) * (mesh[1][0]-mesh[0][0])) + (Basis12(uv[1]) * Basis03(uv[0]) * (mesh[2][0]-mesh[1][0]))+ (Basis22(uv[1]) * Basis03(uv[0]) * (mesh[3][0]-mesh[2][0])); function Bern13dv(mesh, uv) = ( Basis02(uv[1]) * Basis13(uv[0]) * (mesh[1][1]-mesh[0][1])) + ( Basis12(uv[1]) * Basis13(uv[0]) * (mesh[2][1]-mesh[1][1])) + ( Basis22(uv[1]) * Basis13(uv[0]) * (mesh[3][1]-mesh[2][1])); function Bern23dv(mesh, uv) = (Basis02(uv[1]) * Basis23(uv[0]) * (mesh[1][2]-mesh[0][2])) + (Basis12(uv[1]) * Basis23(uv[0]) * (mesh[2][2]-mesh[1][2])) + (Basis22(uv[1]) * Basis23(uv[0]) * (mesh[3][2]-mesh[2][2])); function Bern33dv(mesh, uv) = (Basis02(uv[1]) * Basis33(uv[0]) * (mesh[1][3]-mesh[0][3])) + (Basis12(uv[1]) * Basis33(uv[0]) * (mesh[2][3]-mesh[1][3])) + (Basis22(uv[1]) * Basis33(uv[0]) * (mesh[3][3]-mesh[2][3])); function Bern3du(mesh, uv) = Bern03du(mesh, uv) + Bern13du(mesh, uv) + Bern23du(mesh, uv) + Bern33du(mesh, uv); function Bern3dv(mesh, uv) = Bern03dv(mesh, uv) + Bern13dv(mesh, uv) + Bern23dv(mesh, uv) + Bern33dv(mesh, uv); // Calculate the normal at a specific u/v on a Bezier patch function nberpm(mesh, uv) = VUNIT(VPROD([Bern3du(mesh, uv), Bern3dv(mesh, uv)])); // Given a mesh of control points, and an array that contains the // row and column of the quad we want, return the quad as an // ordered set of points. The winding will be counter clockwise function GetControlQuad(mesh, rc) = [ mesh[rc[0]+1][rc[1]], mesh[rc[0]][rc[1]], mesh[rc[0]][rc[1]+1], mesh[rc[0]+1][rc[1]+1] ]; // Given a mesh, and the 4 parametric points, return a quad that has the appropriate // points along the curve, in counter clockwise order function GetCurveQuad(mesh, u1v1, u2v2) = [ berpm(mesh, [u1v1[0],u2v2[1]]), berpm(mesh, u1v1), berpm(mesh, [u2v2[0],u1v1[1]]), berpm(mesh, u2v2)]; function GetCurveQuadNormals(mesh, u1v1, u2v2) = [[ berpm(mesh, [u1v1[0],u2v2[1]]), berpm(mesh, u1v1), berpm(mesh, [u2v2[0],u1v1[1]]), berpm(mesh, u2v2), ],[ nberpm(mesh, [u1v1[0],u2v2[1]]), nberpm(mesh, u1v1), nberpm(mesh, [u2v2[0],u1v1[1]]), nberpm(mesh, u2v2), ]]; //======================================= // // Hermite Curve Routines // //======================================= /* Hermite Curve Basis Functions Expressed in terms of cubic Bernstein basis functions For cubic Hermite curves these functions give the weights per control point. http://en.wikipedia.org/wiki/Cubic_Hermite_spline */ // To express in terms of Bernstein cubic basis functions function HERMp0(u) = Basis03(u)+Basis13(u); // h00 function HERMm0(u) = 1/3 * Basis13(u); // h10 function HERMp1(u) = Basis33(u) + Basis23(u); // h01 function HERMm1(u) = -1/3 * Basis23(u); // h11 // Given an array of control points // Return a point on the Hermite curve as specified by the // parameter: 0<= 'u' <=1 function herp1(cps, u) = HERMp0(u)*cps[0] + HERMm0(u)*cps[1] + HERMp1(u)*cps[2] + HERMm1(u)*cps[3]; // Hermite Interpolation function herp(cps, u) = [herp1([cps[0][0][0], cps[0][1][0], cps[1][0][0], cps[1][1][0]]), herp1([cps[0][0][1], cps[0][1][1], cps[1][0][1], cps[1][1][1]]), herp1([cps[0][0][2], cps[0][1][2], cps[1][0][2], cps[1][1][2]])]; // Calculate a point on a Hermite mesh // Given the mesh, and the parametric 'u', and 'v' values function herpm(mesh, uv) = herp( [herp(mesh[0], uv[0]), herp(mesh[1], uv[0]), herp(mesh[2], uv[0]), herp(mesh[3], uv[0])], uv[1]); // Given a mesh, and the 4 parametric points, return a quad that has the appropriate // points along the curve, in counter clockwise order function GetHermiteQuad(mesh, u1v1, u2v2) = [ herpm(mesh, [u1v1[0],u2v2[1]]), herpm(mesh, u1v1), herpm(mesh, [u2v2[0],u1v1[1]]), herpm(mesh, u2v2)]; // Given a first hermite curve, 'cpsu' // and a second hermite curve 'cpsv' // sweep the first, cpsu, along the second cpsv // Calculate a patch for the given u,v parameters function GetHermSweepQuad(cpsu, cpsv, uv1, uv2) = [ herp(cpsu, uv1[0])+herp(cpsv, uv1[1]), herp(cpsu, uv2[0])+herp(cpsv, uv1[1]), herp(cpsu, uv2[0])+herp(cpsv, uv2[1]), herp(cpsu, uv1[0])+herp(cpsv, uv2[1]) ]; // moreShapes library // 2D and 3D utility shape functions // Author: Damian Axford // Updated: 8 Apr 2013 // Public Domain // some borrowed from nophead / Mendel90 utils.scad //include //include // fudge eta = 0.01; module line(start, end, r) { hull() { translate(start) sphere(r=r); translate(end) sphere(r=r); } } module roundedRect(size, radius, center=false) { x = size[0]; y = size[1]; z = size[2]; translate([center?-x/2:0, center?-y/2:0, center?-z/2:0]) linear_extrude(height=z) hull() { translate([radius, radius, 0]) circle(r=radius); translate([x - radius, radius, 0]) circle(r=radius); translate([x - radius, y - radius, 0]) circle(r=radius); translate([radius, y - radius, 0]) circle(r=radius); } } module roundedRectX(size, radius, center=false) { // X-axis aligned roundedRect translate([0,0,center?0:size[2]]) rotate([0,90,0]) roundedRect([size[2],size[1],size[0]], radius, center); } module roundedRectY(size, radius, center=false) { // Y-axis aligned roundedRect translate([0,0,center?0:size[2]]) rotate([-90,0,0]) roundedRect([size[0],size[2],size[1]], radius, center); } module allRoundedRect(size, radius, center=false) { // lazy implementation - must do better // runs VERY slow translate([center?-size[0]/2:0, center?-size[1]/2:0, center?-size[2]/2:0]) hull() { for (x=[0,size[0]], y=[0,size[1]], z=[0,size[2]]) { translate([x,y,z]) sphere(r=radius); } } } module torusSlice(r1, r2, start_angle, end_angle, convexity=10, r3, $fn=96) { rx = r1 + r2; ry = rx; trx = rx* sqrt(2) + 1; try = ry* sqrt(2) + 1; a0 = (4 * start_angle + 0 * end_angle) / 4; a1 = (3 * start_angle + 1 * end_angle) / 4; a2 = (2 * start_angle + 2 * end_angle) / 4; a3 = (1 * start_angle + 3 * end_angle) / 4; a4 = (0 * start_angle + 4 * end_angle) / 4; if(end_angle > start_angle) intersection() { rotate_extrude(convexity=convexity) translate([r1,0,0]) difference() { circle(r2, $fn=$fn/4); circle(r3, $fn=$fn/4); } // WAS: translate([0,0,-r2-1]) linear_extrude(height=2*r2+2) translate([0,0,-r2-1]) linear_extrude(height=2*r2+2) polygon([ [0,0], [trx * cos(a0), try * sin(a0)], [trx * cos(a1), try * sin(a1)], [trx * cos(a2), try * sin(a2)], [trx * cos(a3), try * sin(a3)], [trx * cos(a4), try * sin(a4)], [0,0] ]); } } module torusSlice_only_inner_pipes(r1, r2, start_angle, end_angle, convexity=10, r3, $fn=64) { rx = r1 + r2; ry = rx; trx = rx* sqrt(2) + 1; try = ry* sqrt(2) + 1; a0 = (4 * start_angle + 0 * end_angle) / 4; a1 = (3 * start_angle + 1 * end_angle) / 4; a2 = (2 * start_angle + 2 * end_angle) / 4; a3 = (1 * start_angle + 3 * end_angle) / 4; a4 = (0 * start_angle + 4 * end_angle) / 4; if(end_angle > start_angle) intersection() { rotate_extrude(convexity=convexity) translate([r1,0,0]) //difference() { //circle(r2, $fn=$fn/4); circle(r3, $fn=$fn/4); //} // WAS: translate([0,0,-r2-1]) linear_extrude(height=2*r2+2) translate([0,0,-r2-1]) linear_extrude(height=2*r2+2) polygon([ [0,0], [trx * cos(a0), try * sin(a0)], [trx * cos(a1), try * sin(a1)], [trx * cos(a2), try * sin(a2)], [trx * cos(a3), try * sin(a3)], [trx * cos(a4), try * sin(a4)], [0,0] ]); } } module trapezoid(a,b,h,aOffset=0,center=false) { // lies in x/y plane // edges a,b are paralle to x axis // h is in direction of y axis // b is anchored at origin, extends along positive x axis // a is offset along y by h, extends along positive x axis // a if offset along x axis, from y axis, by aOffset // centering is relative to edge b translate([center?-b/2:0, center?-h/2:0, 0]) polygon(points=[ [0,0], [aOffset,h], [aOffset + a, h], [b,0]]); } module trapezoidPrism(a,b,h,aOffset,height,center=false) { translate([0,0, center?-height/2:0]) linear_extrude(height=height) trapezoid(a,b,h,aOffset,center); } module arrangeShapesOnAxis(axis=[1,0,0], spacing=50) { for (i=[0:$children-1]) { translate([spacing * axis[0] *i, spacing * axis[1]*i, spacing * axis[2]*i]) children(i); } } module arrangeShapesOnGrid(xSpacing=50, ySpacing=50, cols=3, showLocalAxes=false) { // layout is cols, rows for (i=[0:$children-1]) { translate([(i - floor(i / cols)*cols) * xSpacing, floor(i / cols) * ySpacing, 0]) { children(i); if (showLocalAxes) { color("red") line([0,0,0], [xSpacing/2,0,0], 0.2); color("green") line([0,0,0], [0, ySpacing/2,0], 0.2); color("blue") line([0,0,0], [0, 0,xSpacing], 0.2); } } } } module slot(h, r, l, center = true) linear_extrude(height = h, convexity = 6, center = center) hull() { translate([l/2,0,0]) circle(r = r, center = true); translate([-l/2,0,0]) circle(r = r, center = true); } module fillet(r, h) { // ready to be unioned onto another part, eta fudge included // extends along x, y, z translate([r / 2, r / 2, 0]) difference() { cube([r + eta, r + eta, h], center = true); translate([r/2, r/2, 0]) cylinder(r = r, h = h + 1, center = true); } } module right_triangle(width, height, h, center = true) { linear_extrude(height = h, center = center) polygon(points = [[0,0], [width, 0], [0, height]]); } module rounded_square(w, h, r) { // 2D union() { square([w - 2 * r, h], center = true); square([w, h - 2 * r], center = true); for(x = [-w/2 + r, w/2 - r]) for(y = [-h/2 + r, h/2 - r]) translate([x, y]) circle(r = r); } } // // Cylinder with rounded ends // module rounded_cylinder(r, h, r2, roundBothEnds=false) { rotate_extrude() union() { square([r - r2, h]); translate([0,roundBothEnds?r2:0,0]) square([r, roundBothEnds? h-2*r2 : h - r2]); translate([r - r2, h - r2]) circle(r = r2); if (roundBothEnds) { translate([r - r2, r2]) circle(r = r2); } } } module sector(r, a, h, , center = true) { linear_extrude(height = h, center = center) intersection() { circle(r = r, center = true); polygon(points = [ [0, 0], [2 * r * cos(a / 2), 2 * r * sin(a / 2)], [2 * r * cos(a / 2), -2 * r * sin(a / 2)], ]); } } module tube(or, ir, h, center = true) { linear_extrude(height = h, center = center, convexity = 5) difference() { circle(or); circle(ir); } }