From 80c4d267a7287b643716f05b9b277bdd28fe83e6 Mon Sep 17 00:00:00 2001 From: Kienan Stewart Date: Tue, 5 Aug 2025 20:56:30 -0400 Subject: [PATCH] Import various existing scad files --- Fan Filter Adapter.scad | 67 ++ Nut_Job.scad | 573 +++++++++++ customizable_fan_cover.scad | 351 +++++++ diy case shim.scad | 29 + ikea leg extension.scad | 22 + miniature base round.scad | 120 +++ parametric_butt_hinge_3.8.scad | 1710 ++++++++++++++++++++++++++++++++ soap holder.scad | 23 + 8 files changed, 2895 insertions(+) create mode 100644 Fan Filter Adapter.scad create mode 100644 Nut_Job.scad create mode 100644 customizable_fan_cover.scad create mode 100644 diy case shim.scad create mode 100644 ikea leg extension.scad create mode 100644 miniature base round.scad create mode 100644 parametric_butt_hinge_3.8.scad create mode 100644 soap holder.scad diff --git a/Fan Filter Adapter.scad b/Fan Filter Adapter.scad new file mode 100644 index 0000000..e8e377c --- /dev/null +++ b/Fan Filter Adapter.scad @@ -0,0 +1,67 @@ +// filter settings +// this is for the "go around the outside of the filer" style +wall_thickness = 3; // mm +wall_height = 4; // mm +outer_ring_diameter = 181; +outer_ring_thickness = 7; + +// fan settings +fan_diameter = 140; // mm +fan_wall_height = 10; +fan_base_height = 5; +fan_base_width = 3; +fan_screwhole_diameter = 5; +fan_corner_offset = 5; + +// taper settings +taper_height = 25; + +// ring +linear_extrude(wall_height) { + difference() { + circle(outer_ring_diameter / 2, $fa = 1); + circle(outer_ring_diameter/2 - outer_ring_thickness, $fa = 1); + }; +}; + +// fan base +screw_offset = (fan_diameter / 2) - fan_corner_offset; +translate([0, 0, -fan_wall_height - taper_height]) { +linear_extrude(fan_base_height) { + difference() { + square(fan_diameter, center = true); + circle(fan_diameter/2 - fan_base_width, $fa = 1); + // Screw holes + translate([screw_offset, screw_offset, 0]) { + circle(fan_screwhole_diameter / 2, $fa = 1); + }; + translate([-screw_offset, screw_offset, 0]) { + circle(fan_screwhole_diameter / 2, $fa = 1); + }; + translate([screw_offset, -screw_offset, 0]) { + circle(fan_screwhole_diameter / 2, $fa = 1); + }; + translate([-screw_offset, -screw_offset, 0]) { + circle(fan_screwhole_diameter / 2, $fa = 1); + }; + }; +}; +}; + +// taper from fan edge to holder +translate([0, 0, -fan_wall_height-2]) { + union() { + difference() { + cylinder(taper_height, r1 = fan_diameter / 2, r2 = outer_ring_diameter /2, center = true, $fa = 1); + cylinder(taper_height, r1 = fan_diameter / 2 - wall_thickness, r2 = outer_ring_diameter /2 - outer_ring_thickness, center = true, $fa = 1); + }; + translate([0, 0, -19]) { + linear_extrude(7) { + difference() { + circle(fan_diameter/2, $fa = 1); + circle(fan_diameter/2 - fan_base_width, $fa = 1); + }; + }; + }; + }; +}; \ No newline at end of file diff --git a/Nut_Job.scad b/Nut_Job.scad new file mode 100644 index 0000000..5d78e7d --- /dev/null +++ b/Nut_Job.scad @@ -0,0 +1,573 @@ +/* 'Nut Job' nut, bolt, washer and threaded rod factory by Mike Thompson 1/12/2013, Thingiverse: mike_linus + * + * Licensing: This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Australia License. + * Further information is available here - http://creativecommons.org/licenses/by-nc-sa/3.0/au/deed.en_GB + * + * v2 8/12/2013 - added socket head types + * v3 2/11/2014 - adjusted wing nut algorithm for better behaviour with unusual nut sizes and added ISO262 metric references + * v4 31/12/2014 - added optional texture to socket heads, added ability to change the number of facets for a hex head + * and adjusted wingnut base level on certain nut sizes + * v5 11/1/2015 - added phillips and slot drive types and improved texture handling + * v6 21/2/2015 - added wing ratio to wingnuts + * v7 6/3/2016 - added extended options to control number of facets on nuts, square sockets (or any number of facets) and socket depth control + * v8 1/1/2017 - modified library code to remove dependence on deprecated 'assign' statement + * + * This script generates nuts, bolts, washers and threaded rod using the library + * script: polyScrewThead.scad (modified/updated version polyScrewThread_r1.scad) + * http://www.thingiverse.com/thing:8796, CC Public Domain + * + * Defaults are for a 8mm diameter bolts, rod, matching nuts and wing nuts that work well together + * without cleanup or modification. Some default parameters such as the nut outer diameter are deliberately + * altered to produce a snug fit that can still be hand tightened. This may need to be altered + * depending on individual printer variances, slicing tools, filament etc. Suggest printing a matching + * bolt and nut and adjusting as necessary. Note: slow print speeds, low temperatures and solid + * fill are recommended for best results. + */ + +/* [Component Type] */ + +type = "bolt";//[nut,bolt,rod,washer] + +/* [Bolt and Rod Options] */ + +//Head type - Hex, Socket Cap, Button Socket Cap or Countersunk Socket Cap (ignored for Rod) +head_type = "hex";//[hex,socket,button,countersunk] +//Drive type - Socket, Phillips, Slot (ignored for Hex head type and Rod) +drive_type = "socket";//[socket,phillips,slot] +//Distance between flats for the hex head or diameter for socket or button head (ignored for Rod) +head_diameter = 12; +//Height of the head (ignored for Rod) +head_height = 5; +//Diameter of drive type (ignored for Hex head and Rod) +drive_diameter = 5; +//Width of slot aperture for phillips or slot drive types +slot_width = 1; +//Depth of slot aperture for slot drive type +slot_depth = 2; +//Surface texture (socket head only) +texture = "exclude";//[include,exclude] +//Outer diameter of the thread +thread_outer_diameter = 8; +//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5) +thread_step = 2; +//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees) +step_shape_degrees = 45; +//Length of the threaded section +thread_length = 25; +//Countersink in both ends +countersink = 2; +//Length of the non-threaded section +non_thread_length = 0; +//Diameter for the non-threaded section (-1: Same as inner diameter of the thread, 0: Same as outer diameter of the thread, value: The given value) +non_thread_diameter = 0; + +/* [Nut Options] */ + +//Type: Normal or WingNut +nut_type = "normal";//[normal,wingnut] +//Distance between flats for the hex nut +nut_diameter = 12; +//Height of the nut +nut_height = 6; +//Outer diameter of the bolt thread to match (usually set about 1mm larger than bolt diameter to allow easy fit - adjust to personal preferences) +nut_thread_outer_diameter = 9; +//Thread step or Pitch (2mm works well for most applications ref. ISO262: M3=0.5,M4=0.7,M5=0.8,M6=1,M8=1.25,M10=1.5) +nut_thread_step = 2; +//Step shape degrees (45 degrees is optimised for most printers ref. ISO262: 30 degrees) +nut_step_shape_degrees = 45; +//Wing radius ratio. The proportional radius of the wing on the wing nut compared to the nut height value (default = 1) +wing_ratio = 1; +wing_radius=wing_ratio * nut_height; + +/* [Washer Options] */ + +//Inner Diameter (suggest making diameter slightly larger than bolt diameter) +inner_diameter = 8; +//Outer Diameter +outer_diameter = 14; +//Thickness +thickness = 2; + +/* [Extended Options] */ + +//Number of facets for hex head type or nut. Default is 6 for standard hex head and nut +facets = 6; +//Number of facets for hole in socket head. Default is 6 for standard hex socket +socket_facets = 6; +//Depth of hole in socket head. Default is 3.5 +socket_depth = 3.5; +//Resolution (lower values for higher resolution, but may slow rendering) +resolution = 0.5; +nut_resolution = resolution; + +//Hex Bolt +if (type=="bolt" && head_type=="hex") +{ + hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter); +} + +//Rod +if (type=="rod") +{ + hex_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,0,non_thread_length,non_thread_diameter); +} + +//Hex Nut (normally slightly larger outer diameter to fit on bolt correctly) +if (type=="nut" && nut_type=="normal") +{ + hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution); +} + +//Wing Nut variation of hex nut. Cylinders added to each side of nut for easy turning - ideal for quick release applications +if (type=="nut" && nut_type=="wingnut") +{ + rotate([0,0,30])hex_nut(nut_diameter,nut_height,nut_thread_step,nut_step_shape_degrees,nut_thread_outer_diameter,nut_resolution); //nut + translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing + mirror(1,0,0)translate([(nut_diameter/2)+wing_radius-1,1.5,wing_radius/2+1])rotate([90,0,0])wing(); //attach wing +} + +module wing() +{ + difference() + { + cylinder(r=wing_radius,h=3,$fn=64); //cylinder + union() + { + translate([-wing_radius,-wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhang so flush with base of nut + rotate([0,0,90])translate([-wing_radius,wing_radius-1,-0.5])cube([wing_radius*2,wing_radius/2,wing_radius*2]); //remove overhangs so flush with side of nut + } + } +} + +//Washer +if (type=="washer") +{ + difference() + { + cylinder(r=outer_diameter/2,h=thickness,$fn=100); + translate([0,0,-0.1])cylinder(r=inner_diameter/2,h=thickness+0.2,$fn=100); + } +} + +//Socket Head Bolt +if (type=="bolt" && head_type!="hex") +{ + socket_screw(thread_outer_diameter,thread_step,step_shape_degrees,thread_length,resolution,countersink,head_diameter,head_height,non_thread_length,non_thread_diameter); +} + +module phillips_base() +{ + linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]); + translate([(drive_diameter-slot_width)/2,0,(drive_diameter+slot_width)/2])rotate([0,90,0])linear_extrude(slot_width)polygon(points=[[0,0],[(drive_diameter-slot_width)/2,9/5*(drive_diameter-slot_width)/2],[(drive_diameter+slot_width)/2,9/5*(drive_diameter-slot_width)/2],[drive_diameter,0]]); +} + +module phillips_fillet() +{ + union() + { + translate([-(drive_diameter-slot_width)/2-(slot_width/2),slot_width/2,0])rotate([90,0,0])phillips_base(); + translate([0,0,9/5*(drive_diameter-slot_width)/2])union() + { + inner_curve(); + rotate([0,0,90])inner_curve(); + rotate([0,0,180])inner_curve(); + rotate([0,0,270])inner_curve(); + } + } +} + +module inner_curve() +{ + translate([slot_width/2,-slot_width/2,0])rotate([0,90,0])linear_fillet(9/5*(drive_diameter-slot_width)/2,drive_diameter/10); +} + +//basic 2d profile used for fillet shape +module profile(radius) +{ + difference() + { + square(radius); + circle(r=radius); + } +} + +//linear fillet for use along straight edges +module linear_fillet(length,profile_radius) +{ + translate([0,-profile_radius,profile_radius])rotate([0,90,0])linear_extrude(height=length,convexity=10)profile(profile_radius); +} + +module phillips_drive() +{ + intersection() + { + phillips_fillet(); + cylinder(9/5*(drive_diameter-slot_width)/2,drive_diameter/2+(slot_width/2),slot_width/2); + } +} + +module socket_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd) +{ + ntr=od/2-(st/2)*cos(lf0)/sin(lf0); + $fn=60; + + difference() + { + union() + { + if (head_type=="socket") + { + socket_head(hg,df); + } + + if (head_type=="button") + { + button_head(hg,df); + } + + if (head_type=="countersunk") + { + countersunk_head(hg,df); + } + + translate([0,0,hg]) + if ( ntl == 0 ) + { + cylinder(h=0.01, r=ntr, center=true); + } + else + { + if ( ntd == -1 ) + { + cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false); + } + else if ( ntd == 0 ) + { + union() + { + cylinder(h=ntl-st/2,r=od/2, $fn=floor(od*PI/rs), center=false); + translate([0,0,ntl-st/2]) + cylinder(h=st/2, + r1=od/2, r2=ntr, + $fn=floor(od*PI/rs), center=false); + } + } + else + { + cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false); + } + } + translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs); + } + //create opening for specific drive type + if (drive_type=="socket") + { + cylinder(r=drive_diameter/2,h=socket_depth,$fn=socket_facets); //socket + #translate([0,0,socket_depth])cylinder(r1=drive_diameter/2,r2=0,h=drive_diameter/3,$fn=socket_facets); //socket tapers at base to allow printing without bridging and improve socket grip + } + else + { + if (drive_type=="phillips") + { + translate([0,0,-0.001])phillips_drive(); + } + else //slot + { + translate([-(drive_diameter)/2,slot_width/2,-0.001])rotate([90,0,0])cube([drive_diameter,slot_depth,slot_width]); + } + } + } +} + +module socket_head(hg,df) +{ + texture_points=2*PI*(head_diameter/2); + texture_offset=head_diameter/18; + texture_radius=head_diameter/24; + + rd0=df/2/sin(60); + x0=0; x1=df/2; x2=x1+hg/2; + y0=0; y1=hg/2; y2=hg; + + intersection() + { + cylinder(h=hg, r=rd0, $fn=60, center=false); + rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5)) + polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]); + } + + if (texture=="include") //add texture to socket head. Adjust texture density and size using texture variables above + { + for (i= [1:texture_points]) + { + translate([cos(360/texture_points*i)*(head_diameter/2+texture_offset), sin(360/texture_points*i)*(head_diameter/2+texture_offset), 1 ]) + rotate([0,0,360/texture_points*i])cylinder(r=texture_radius,h=head_height*0.6,$fn=3); + } + } + +} + +module button_head(hg,df) +{ + rd0=df/2/sin(60); + x0=0; x1=df/2; x2=x1+hg/2; + y0=0; y1=hg/2; y2=hg; + + intersection() + { + cylinder(h=hg, r1=drive_diameter/2 + 1, r2=rd0, $fn=60, center=false); + rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5)) + polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]); + } +} + +module countersunk_head(hg,df) +{ + rd0=df/2/sin(60); + x0=0; x1=df/2; x2=x1+hg/2; + y0=0; y1=hg/2; y2=hg; + + intersection() + { + cylinder(h=hg, r1=rd0, r2=thread_outer_diameter/2-0.5, $fn=60, center=false); + + rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5)) + polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]); + } +} + +/* Library included below to allow customizer functionality + * + * polyScrewThread_r1.scad by aubenc @ Thingiverse + * + * Modified by mike_mattala @ Thingiverse 1/1/2017 to remove deprecated assign + * + * This script contains the library modules that can be used to generate + * threaded rods, screws and nuts. + * + * http://www.thingiverse.com/thing:8796 + * + * CC Public Domain + */ + +module screw_thread(od,st,lf0,lt,rs,cs) +{ + or=od/2; + ir=or-st/2*cos(lf0)/sin(lf0); + pf=2*PI*or; + sn=floor(pf/rs); + lfxy=360/sn; + ttn=round(lt/st+1); + zt=st/sn; + + intersection() + { + if (cs >= -1) + { + thread_shape(cs,lt,or,ir,sn,st); + } + + full_thread(ttn,st,sn,zt,lfxy,or,ir); + } +} + +module hex_nut(df,hg,sth,clf,cod,crs) +{ + + difference() + { + hex_head(hg,df); + + hex_countersink_ends(sth/2,cod,clf,crs,hg); + + screw_thread(cod,sth,clf,hg,crs,-2); + } +} + + +module hex_screw(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd) +{ + ntr=od/2-(st/2)*cos(lf0)/sin(lf0); + + union() + { + hex_head(hg,df); + + translate([0,0,hg]) + if ( ntl == 0 ) + { + cylinder(h=0.01, r=ntr, center=true); + } + else + { + if ( ntd == -1 ) + { + cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false); + } + else if ( ntd == 0 ) + { + union() + { + cylinder(h=ntl-st/2, + r=od/2, $fn=floor(od*PI/rs), center=false); + + translate([0,0,ntl-st/2]) + cylinder(h=st/2, + r1=od/2, r2=ntr, + $fn=floor(od*PI/rs), center=false); + } + } + else + { + cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false); + } + } + + translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs); + } +} + +module hex_screw_0(od,st,lf0,lt,rs,cs,df,hg,ntl,ntd) +{ + ntr=od/2-(st/2)*cos(lf0)/sin(lf0); + + union() + { + hex_head_0(hg,df); + + translate([0,0,hg]) + if ( ntl == 0 ) + { + cylinder(h=0.01, r=ntr, center=true); + } + else + { + if ( ntd == -1 ) + { + cylinder(h=ntl+0.01, r=ntr, $fn=floor(od*PI/rs), center=false); + } + else if ( ntd == 0 ) + { + union() + { + cylinder(h=ntl-st/2, + r=od/2, $fn=floor(od*PI/rs), center=false); + + translate([0,0,ntl-st/2]) + cylinder(h=st/2, + r1=od/2, r2=ntr, + $fn=floor(od*PI/rs), center=false); + } + } + else + { + cylinder(h=ntl, r=ntd/2, $fn=ntd*PI/rs, center=false); + } + } + + translate([0,0,ntl+hg]) screw_thread(od,st,lf0,lt,rs,cs); + } +} + +module thread_shape(cs,lt,or,ir,sn,st) +{ + if ( cs == 0 ) + { + cylinder(h=lt, r=or, $fn=sn, center=false); + } + else + { + union() + { + translate([0,0,st/2]) + cylinder(h=lt-st+0.005, r=or, $fn=sn, center=false); + + if ( cs == -1 || cs == 2 ) + { + cylinder(h=st/2, r1=ir, r2=or, $fn=sn, center=false); + } + else + { + cylinder(h=st/2, r=or, $fn=sn, center=false); + } + + translate([0,0,lt-st/2]) + if ( cs == 1 || cs == 2 ) + { + cylinder(h=st/2, r1=or, r2=ir, $fn=sn, center=false); + } + else + { + cylinder(h=st/2, r=or, $fn=sn, center=false); + } + } + } +} + +module full_thread(ttn,st,sn,zt,lfxy,or,ir) +{ + if(ir >= 0.2) + { + for(i=[0:ttn-1]) + { + for(j=[0:sn-1]) + { + pt = [[0,0,i*st-st], + [ir*cos(j*lfxy), ir*sin(j*lfxy), i*st+j*zt-st ], + [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt-st ], + [0,0,i*st], + [or*cos(j*lfxy), or*sin(j*lfxy), i*st+j*zt-st/2 ], + [or*cos((j+1)*lfxy), or*sin((j+1)*lfxy), i*st+(j+1)*zt-st/2 ], + [ir*cos(j*lfxy), ir*sin(j*lfxy), i*st+j*zt ], + [ir*cos((j+1)*lfxy), ir*sin((j+1)*lfxy), i*st+(j+1)*zt ], + [0,0,i*st+st]]; + + polyhedron(points=pt,faces=[[1,0,3],[1,3,6],[6,3,8],[1,6,4], //changed triangles to faces (to be deprecated) + [0,1,2],[1,4,2],[2,4,5],[5,4,6],[5,6,7],[7,6,8], + [7,8,3],[0,2,3],[3,2,7],[7,2,5] ]); + } + } + } + else + { + echo("Step Degrees too agresive, the thread will not be made!!"); + echo("Try to increase de value for the degrees and/or..."); + echo(" decrease the pitch value and/or..."); + echo(" increase the outer diameter value."); + } +} + +module hex_head(hg,df) +{ + rd0=df/2/sin(60); + x0=0; x1=df/2; x2=x1+hg/2; + y0=0; y1=hg/2; y2=hg; + + intersection() + { + cylinder(h=hg, r=rd0, $fn=facets, center=false); + + rotate_extrude(convexity=10, $fn=6*round(df*PI/6/0.5)) + polygon([ [x0,y0],[x1,y0],[x2,y1],[x1,y2],[x0,y2] ]); + } +} + +module hex_head_0(hg,df) +{ + cylinder(h=hg, r=df/2/sin(60), $fn=6, center=false); +} + +module hex_countersink_ends(chg,cod,clf,crs,hg) +{ + translate([0,0,-0.1]) + cylinder(h=chg+0.01, + r1=cod/2, + r2=cod/2-(chg+0.1)*cos(clf)/sin(clf), + $fn=floor(cod*PI/crs), center=false); + + translate([0,0,hg-chg+0.1]) + cylinder(h=chg+0.01, + r1=cod/2-(chg+0.1)*cos(clf)/sin(clf), + r2=cod/2, + $fn=floor(cod*PI/crs), center=false); +} \ No newline at end of file diff --git a/customizable_fan_cover.scad b/customizable_fan_cover.scad new file mode 100644 index 0000000..2716f01 --- /dev/null +++ b/customizable_fan_cover.scad @@ -0,0 +1,351 @@ +/* + * Customizable Fan Cover - https://www.thingiverse.com/thing:2802474 + * by Dennis Hofmann - https://www.thingiverse.com/mightynozzle/about + * created 2018-02-22 + * updated 2018-03-27 + * version v1.2 + * + * Changelog + * -------------- + * v1.2: + * - better quality of rounded corners and crosshair pattern + * v1.1: + * - added support line option for crosshair and square pattern + * v1.0: + * - final design + * -------------- + * + * This work is licensed under the Creative Commons - Attribution - Non-Commercial - ShareAlike license. + * https://creativecommons.org/licenses/by-nc-sa/3.0/ + */ + + + // Parameter Section // +//-------------------// + +/* [Global Settings] */ + +// Choose between presets of known and common fan sizes. +fan_preset = "40"; //[25:25mm,30:30mm,40:40mm,50:50mm,60:60mm,70:70mm,80:80mm,92:92mm,120:120mm,140:140mm,custom:Custom Fan Settings] + +// If you want save to save about 40% filament and about 20% printing time, you can choose the reduced frame option. +frame_option = "reduced"; //[full,reduced] + +// Minimum Border Size. I recommend to use two/four/six... times of your line width setting of your slicer. Simplify3D uses 0.48mm line width by default if you are using a 0.4mm nozzle. Cura uses 0.4mm line width. +min_border_size_in_millimeter = 1.92; + +// Choose between a grill pattern +grill_pattern = "honeycomb"; //[honeycomb,grid,line,triangle,crosshair,square,dot,aperture] + +// Set the angle of the pattern around the center of the cover. +grill_pattern_rotation = 0; //[0:5:360] + +// Size of the pattern lines. I recommend to use two/four/six... times of your line width setting of your slicer. Simplify3D uses 0.48mm line width by default if you are using a 0.4mm nozzle. Cura uses 0.4mm line width. +line_size_in_millimter = 0.96; //[0.3:0.02:4] + +// Space between two lines of your pattern. If you choose aperture pattern, this value set the size of the center octagon. +line_space_in_millimeter = 6; //[1:0.1:50] + +// If you need countersunk holes, you can activate it here. Bottom = first layer. Top = last layer. +screw_hole_chamfer = "no"; //[no,top,bottom,top_and_bottom] + +// Activate or deactivate rounded corners. The radius will be automatically calculated. +rounded_corners = "yes"; //[yes,no] + +// number of straight lines supporting the crosshair or square pattern. +number_of_support_lines = 2; //[1:1:36] + +/* [Custom Fan Settings] */ + +// Cover size. E.g. 80 for a 80mm Fan +cover_size_in_millimeter = 40; + +// E.g. 2.9 for M2.5, 3.3 for M3, 4.4 for M4 +screw_hole_diameter_in_millimeter = 3.3; + +// 25mm fan: 20 | 30mm fan: 24 | 40mm fan: 32 | 50mm fan: 40 | 60mm fan: 50 | 70mm fan: 61.5 | 80mm fan: 71.5 | 92mm fan: 82.5 | 120mm fan: 105 | 140mm fan: 126 +screw_hole_distance_in_millimeter = 32; + +// Height of the outer frame of the cover +cover_height_in_millimeter = 3; + +// Height of the pattern inside the cover frame +grill_pattern_height_in_millimeter = 1; + +/* [Hidden] */ +min_border_size = min_border_size_in_millimeter; +line_size = line_size_in_millimter; +line_space = line_space_in_millimeter; + + // Program Section // +//-----------------// + +if(fan_preset == "25") { + fan_cover(cover_size = 25, screw_hole_dia = 2.9, screw_hole_distance = 20, cover_h = 2, grill_pattern_h = 1); +} +if(fan_preset == "30") { + fan_cover(cover_size = 30, screw_hole_dia = 3.3, screw_hole_distance = 24, cover_h = 2.5, grill_pattern_h = 1.1); +} +if(fan_preset == "40") { + fan_cover(cover_size = 40, screw_hole_dia = 3.3, screw_hole_distance = 32, cover_h = 2.7, grill_pattern_h = 1.2); +} +if(fan_preset == "50") { + fan_cover(cover_size = 50, screw_hole_dia = 4.4, screw_hole_distance = 40, cover_h = 2.9, grill_pattern_h = 1.3); +} +if(fan_preset == "60") { + fan_cover(cover_size = 60, screw_hole_dia = 4.4, screw_hole_distance = 50, cover_h = 3, grill_pattern_h = 1.3); +} +if(fan_preset == "70") { + fan_cover(cover_size = 70, screw_hole_dia = 4.4, screw_hole_distance = 61.5, cover_h = 3, grill_pattern_h = 1.4); +} +if(fan_preset == "80") { + fan_cover(cover_size = 80, screw_hole_dia = 4.4, screw_hole_distance = 71.5, cover_h = 3.2, grill_pattern_h = 1.5); +} +if(fan_preset == "92") { + fan_cover(cover_size = 92, screw_hole_dia = 4.4, screw_hole_distance = 82.5, cover_h = 3.5, grill_pattern_h = 1.6); +} +if(fan_preset == "120") { + fan_cover(cover_size = 120, screw_hole_dia = 4.4, screw_hole_distance = 105, cover_h = 4, grill_pattern_h = 1.8); +} +if(fan_preset == "140") { + fan_cover(cover_size = 140, screw_hole_dia = 4.4, screw_hole_distance = 126, cover_h = 4.5, grill_pattern_h = 2.0); +} +if(fan_preset == "custom") { + fan_cover(cover_size = cover_size_in_millimeter, screw_hole_dia = screw_hole_diameter_in_millimeter, screw_hole_distance = screw_hole_distance_in_millimeter, cover_h = cover_height_in_millimeter, grill_pattern_h = grill_pattern_height_in_millimeter); +} + + + // Module Section // +//----------------// + +module fan_cover(cover_size, screw_hole_dia, screw_hole_distance, cover_h, grill_pattern_h) { + corner_size = cover_size - screw_hole_distance; + corner_r = rounded_corners == "yes" ? corner_size / 2 : 0; + screw_pos = (cover_size - corner_size) / 2; + color("DodgerBlue") difference() { + union() { + linear_extrude(height = cover_h, convexity = 20) { + difference() { + offset(r=corner_r, $fn = ceil(corner_r * 8)) { + offset(r=-corner_r) { + square([cover_size, cover_size], center = true); + } + } + if(frame_option == "reduced") { + offset(r=corner_r, $fn = ceil(corner_r * 8)) { + offset(r=-corner_r) { + square([cover_size - min_border_size*2, cover_size - min_border_size*2], center = true); + } + } + } + if(frame_option == "full") { + circle(d = cover_size - min_border_size * 2, $fn = cover_size); + } + } + if(frame_option == "reduced") { + for(y = [-1:2:1]) { + for(x = [-1:2:1]) { + translate([screw_pos * x, screw_pos * y, -2]) { + circle(d = corner_size, $fn = ceil(corner_r * 8)); + } + } + } + } + } + linear_extrude(height = grill_pattern_h, convexity = 20) { + intersection() { + offset(r=corner_r, $fn = ceil(corner_r * 8)) { + offset(r=-corner_r) { + square([cover_size, cover_size], center = true); + } + } + rotate(grill_pattern_rotation) { + if(grill_pattern == "honeycomb") { + honeycomb_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "grid") { + grid_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "line") { + line_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "triangle") { + triangle_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "crosshair") { + crosshair_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "square") { + square_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "dot") { + dot_pattern(cover_size, line_size, line_space); + } + if(grill_pattern == "aperture") { + aperture_pattern(cover_size, line_size, line_space); + } + + } + } + } + // HACK: REMOVE ME + for(y = [-1:2:1]) { + for(x= [-1:2:1]) { + translate([screw_pos * x - 9*x, screw_pos * y - 9 * y, -2]) { + leg(20, 15); + } + } + } + + } + for(y = [-1:2:1]) { + for(x = [-1:2:1]) { + translate([screw_pos * x, screw_pos * y, -2]) { + screw_hole(cover_h, screw_hole_dia); + } + + } + } + } +} + +module leg(leg_h, leg_dia) { + translate([0, 0, 2]) { + cylinder(leg_h, d = leg_dia, $fn = 360); + }; +} + +module screw_hole(cover_h, screw_hole_dia) { + cylinder(h = cover_h + 4, d = screw_hole_dia, $fn = 16); + if(screw_hole_chamfer == "bottom" || screw_hole_chamfer == "top_and_bottom") { + translate([0, 0, 2.9 - screw_hole_dia]) { + cylinder(h = screw_hole_dia, d1 = screw_hole_dia * 4, d2 = screw_hole_dia); + } + } + if(screw_hole_chamfer == "top" || screw_hole_chamfer == "top_and_bottom") { + translate([0, 0, cover_h + screw_hole_dia/4]) { + cylinder(h = screw_hole_dia, d2 = screw_hole_dia * 4, d1 = screw_hole_dia); + } + } +} + +module grid_pattern(size, line_size, line_space) { + num = ceil(size / (line_size + line_space) * 1.42); + for(x = [floor(-num / 2) : ceil(num / 2)]) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center=true); + } + rotate(90) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center = true); + } + } + } +} + +module triangle_pattern(size, line_size, line_space) { + num = ceil(size / (line_size + line_space) * 1.42); + for(x = [floor(-num / 2):ceil(num / 2)]) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center = true); + } + rotate(60) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center = true); + } + } + rotate(120) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center = true); + } + } + } +} + +module line_pattern(size, line_size, line_space) { + num = ceil(size / (line_size + line_space)*1.42); + for(x = [floor(-num / 2):ceil(num / 2)]) { + translate([x * (line_size + line_space), 0]) { + square([line_size, num *(line_size + line_space)], center=true); + } + } +} + +module crosshair_pattern(size, line_size, line_space) { + line = (line_size + line_space) * 2; + num = ceil(size / line * 1.42); + for(n = [1:num]) { + difference() { + circle(d = n * line + line_size * 2, $fn = ceil(n * line + line_size * 2)); + circle(d = n * line, $fn = ceil(n * line + line_size * 2)); + } + } + for(rot=[0:90 / number_of_support_lines * 2:180]) { + rotate(rot + 45) square([size * 2, line_size], center = true); + } +} + +module square_pattern(size, line_size, line_space) { + line = (line_size + line_space) * 2; + num = ceil(size / line * 1.42); + for(n = [1:num]) { + difference() { + square([n * line + line_size * 2, n * line + line_size * 2], center = true); + square([n * line, n * line], center = true); + } + } + for(rot=[0:90 / number_of_support_lines * 2:180]) { + rotate(rot + 45) square([size * 2, line_size], center = true); + } +} + +module honeycomb_pattern(size, line_size, line_space) { + min_rad = (line_space / 2 * sqrt(3)) / 2 + line_size / 2; + y_offset = sqrt(min_rad * min_rad * 4 - min_rad * min_rad); + num_x = ceil(size / min_rad / 2) * 1.42; + num_y = ceil(size / y_offset) * 1.42; + difference() { + square([size * 1.42, size * 1.42], center = true); + for(y = [floor(-num_y / 2) : ceil(num_y / 2)]) { + odd = (y % 2 == 0) ? 0 : min_rad; + for(x = [floor(-num_x / 2) : ceil(num_x / 2)]) { + translate([x * min_rad * 2 + odd, y * y_offset]) { + rotate(30) { + circle(d=line_space, $fn=6); + } + } + } + } + } +} + +module dot_pattern(size, line_size, line_space) { + rad = line_space / 2; + y_offset = sqrt((rad + line_size / 2) * (rad + line_size / 2) * 4 - (rad + line_size / 2) * (rad + line_size / 2)); + num_x = ceil(size / rad / 2) * 1.42; + num_y = ceil(size / y_offset) * 1.42; + difference() { + square([size * 1.42, size * 1.42], center = true); + for(y = [floor(-num_y / 2) : ceil(num_y / 2)]) { + odd = (y % 2 == 0) ? 0 : rad + line_size / 2; + for(x = [floor(-num_x / 2) : ceil(num_x / 2)]) { + translate([x * (rad + line_size / 2) * 2 + odd, y * y_offset]) { + rotate(30) { + circle(d=line_space); + } + } + } + } + } +} + +module aperture_pattern(size, line_size, line_space) { + circle(d = line_space, $fn = 8); + for(rot = [1:2:15]) { + rotate(360 / 16 * rot) { + translate([line_space / 2 * cos(360 / 16) - line_size, -line_size]) { + square([line_size, size]); + } + } + } +} diff --git a/diy case shim.scad b/diy case shim.scad new file mode 100644 index 0000000..11adcfc --- /dev/null +++ b/diy case shim.scad @@ -0,0 +1,29 @@ +// 1U is 44.45mm +// the wood plates are ~3mm +h = 44.45 - 6; + +// using 1/2" #8 wood screws, we want a pilot hole through +// the PLA spacer. I'm not sure if the pilot hole should be +// treated as a softwood or hardwood size. Experiment and +// find out. +// d_soft is definitely too narrow +// d_hard was too narrow as well. +// 3.5mm was still too narrow. I'm going to switch to 2.5mm +// and try using a M2.5 machine screw, since I have a tap +d_next = 2.5; +d_hard = 3.175; +d_soft = 2.38252; +d_outer = 10; + +plate = true; + +difference () { + if (plate) { + translate([2, 2, 1]) { + cube([16, 16, 2], center = true); + }; + + } + cylinder(h, d = d_outer, $fn = 360); + cylinder(h, d = d_next, $fn = 360); +}; diff --git a/ikea leg extension.scad b/ikea leg extension.scad new file mode 100644 index 0000000..496e6ff --- /dev/null +++ b/ikea leg extension.scad @@ -0,0 +1,22 @@ +// extension ikea square table legs + +lip_height = 25.4; // 1" +lip_thickness = 10; // 1cm +extension_height = 76.2; // 3" + +// probably shouldnt need to change these +inner_w = 25.4 * 2; // 2" +inner_t = 25.4 * 2; // 2" + +w = lip_thickness + inner_w; +h = lip_thickness + inner_t; + +union() { + difference() { + cube([w, h, lip_height], true); + cube([inner_w, inner_t, lip_height], true); + } + translate([0, 0, -extension_height/2-lip_height/2]) { + cube([w, h, extension_height], true); + } +} diff --git a/miniature base round.scad b/miniature base round.scad new file mode 100644 index 0000000..7b560a2 --- /dev/null +++ b/miniature base round.scad @@ -0,0 +1,120 @@ +// @see https://www.dakkadakka.com/wiki/en/Basing_Guidelines_for_Warhammer_40,000 + +// Old base: 28, primaris: 32, hero: 40, dreadnought: 65 +diameter = 32.0; + +// Should the base not have the same radius in the x and y axis? +oblong = false; + +// Oblong diameter. A biker is on a 25x70 base, for eg. +oblong_diameter = 70; + +// 3.5mm for older 28mm bases; newer bases are 4mm +base_height = 4; + +// should a slot be added? +slotted = false; + +// Width of the slot. 2.5 for 28mm bases +slot_width = 2.5; + +// Length of the slot +slot_length = 24; + +// Offset along the x axis +slot_offset_x = 3; + +// Offset along the y axis +slot_offset_y = 0; + +// Should a magnet holder be added underneath +magnet_holder = false; + +// Diameter in mm +magnet_diameter = 6; + +// Depth of the magnet holder walls in mm. +magnet_height = 2; + +// Thickness of magnet wall in mm +magnet_wall_thickness = 2; + +magnet_offset_x = 5; +magnet_offset_y = 0; +magnet_offset_z = 0; +magnet_offset = [magnet_offset_x, magnet_offset_y, magnet_offset_z]; +slot_offset = [slot_offset_x, slot_offset_y, 0]; + +module magnet_holder(diameter, height, offset = [], thickness = 2) { + translate(offset) { + difference() { + cylinder(d = diameter + thickness, h = height, $fn = 360); + cylinder(d = diameter, h = height+0.01, $fn = 360); + } + } +} + +module slot_cutout(width, length, height, offset) { + translate(offset) { + cube([width, length, height + 1], true); + } +} + +module base(r1, r2, h, slot = false, slot_width = 0, slot_length = 0, +magnet = false, magnet_diameter = 0, magnet_height = 0, magnet_offset = [], t = 1, slot_offset = [], +magnet_wall_thickness = 2) { + + difference() { + scale([r2/r1, 1]) { + difference() { + cylinder(r1 = r1, r2 = r1 - t, h = h, $fn = 360); + cylinder(r1 = r1 - t, r2 = r1 - (2*t), h = h - t, $fn = 360); + } + } + if (slot) { + translate([0, 0, h / 2]) { + slot_cutout(slot_width, slot_length, h, slot_offset); + }; + } + } + + if (magnet) { + // Move it up to abut against our top + if ((h - magnet_height) > 0) { + translate([0, 0, h - t]) { + cylinder(h - magnet_height, d = magnet_diameter + magnet_wall_thickness, center = true, $fn =360); + }; + } + translate([0, 0, 0]) { + magnet_holder(magnet_diameter, magnet_height, magnet_offset, magnet_wall_thickness); + } + } + + if (slot) { + translate([0, 0, h/2]) { + difference() { + translate(slot_offset) {cube([slot_width + (2*t), slot_length + (2*t), h], true); } + slot_cutout(slot_width, slot_length, h, slot_offset); + } + } + } + +} + +r1 = diameter / 2; +r2 = oblong ? oblong_diameter / 2 : r1; +base( + r1 = r1, + r2 = r2, + h = base_height, + slot = slotted, + slot_width = slot_width, + slot_length = slot_length, + magnet = magnet_holder, + magnet_diameter = magnet_diameter, + magnet_height = magnet_height, + magnet_offset = magnet_offset, + t = 1, + slot_offset = slot_offset, + magnet_wall_thickness = magnet_wall_thickness +); \ No newline at end of file diff --git a/parametric_butt_hinge_3.8.scad b/parametric_butt_hinge_3.8.scad new file mode 100644 index 0000000..19aaba3 --- /dev/null +++ b/parametric_butt_hinge_3.8.scad @@ -0,0 +1,1710 @@ +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Title: Parametric Butt Hinge +// Version: 3.8 +// Release Date: 2021-05-08 (ISO 8601) +// Author: Rohin Gosling +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// +// Description: +// +// - Parametric butt hinge, designed to be printed in one step. +// +// - While the entire working hinge may be printed in one step, it is also possible to disabled the internal fused pin, and leave a shaft +// which may be used in the case where an external pin is desired. +// +// Release Notes: +// +// - Version 3.8 +// * Corrected the title comment at the top of the file from to refernce the correct project title, "Parametric Butt Hinge". +// * Previous versions had the title comment, "Parametric Caged Bearing". This was an old copy and paste error that made it +// all the way through to version 3.7. Fixed now. +// +// - Version 3.7 +// * Added a comment for the "knuckle_count" parameter, to notify users to always set an odd knuckle count in order to ensure that the unified +// pin is supported by male knuckles on both ends. An even number of knuckles will leave one end of the unified pin unsupported by a male knuckle. +// * Added a constraint to the "knuckle_count" parameter, to force an odd knuckle count. Even knuckle counts are now incemented to next odd number. +// Example: +// - A knuckle count of 4, will be incremented to 5. +// - A knuckle count of 10 will, with be incremented to 11 +// +// - Version 3.6 +// * Removed extended ASCII characters to make the source code compatible with the UTF-8 encoding required by the Thingiverse Customizer. +// +// - Version 3.5 +// * For hexagonal counterbores, the hexagonal cut has been rotated 90 degrees, to reduce the need for support material. +// The overhang angle for hexagonal counterbore is 60 degrees. +// +// - Version 3.4 +// * Added support for linear and parabolic gusset curves. +// * Factored out all SCG (Solid Constructive Geometry) overlap constants, and consolidated them into one global constant, "SCG_OVERLAP". +// +// - Version 3.3 +// * Placed pin shaft parameters into their own parameter group in the Thingiverse Customizer. +// +// - Version 3.2 +// * Added support for independently configurable top and bottom pin shaft counterbores. +// * Added an assembly option to flip the model about the z-axis. +// Usefull for viewing the top and bottom pin shaft counterbores. +// * Configured the hinge throw angle Thingiverse Customizer parameter step size, to 5 degrees. +// +// - Version 3.1 +// * Added a counterbore feature for the pin shaft. +// - While this feature may be enabled for one piece hinges that have the fused pin enabled, it is primarily designed for use with two +// piece configurations that have their pin disabled. For instance, when the hinge is to be assembled using an external pin. +// - In the case where a hinge is to be assembled using an external pin, it may be desired to have a pin shaft counterbore, where the end +// caps of the knuckle joint array are counterbored, in order to allow the pin or bolt heads of the pin shaft, to be set flush with the +// top and bottom edges of the hinge leaves. +// - To accommodate both hexagonal and square bolt heads and nuts, the counterbore may be set to any one of circular, square or hexagonal. +// * Added private text feature. +// - A public parameter for the Thingiverse Customizer has not been added yet, however, users may set the 'text_enabled' local variable +// in the 'leaf' module to 'true', which will enable a single row of inscribed text on each leaf. +// - By default this feature will only work when exactly 4 fastener holes have been selected via the 'fastener_count' parameter, simply +// because there is a nice open space available between the fastener holes for placing text, when 4 fastener holes are chosen. +// - Text for the male and female leaves may be specified independently. +// +// - Version 3.0 +// * Added a parameter to disable the hinge pin, for when users would like to assemble with an external pin. +// * Changed the main Thingiverse Customizer parameter group name from "Hinge Options", to "Assembly Options". +// * Added a parameter to set the hinge throw angle. +// Users can now set the throw angle, either for analysis and visualisation purposes, or for when the hinge is to be printed vertically +// standing up on edge, in which case a throw angle of 120 degrees may be used to help keep the hinge stable on the build plate. +// * Corrected spelling errors in the comments. +// +// - Version 2.9 +// * Added support for parabolic knuckle gussets to strengthen the knuckle joints. +// Knuckle gussets may now be toggled on and off with the 'enable_knuckle_gusset' parameter. +// +// - Version 2.8 +// * Added the parameter, 'fastener_column_count', to enable single column fastener arrangements, that can be used for +// piano hinge style fastener arrangements. +// +// - Version 2.7 +// * Rewrote the knuckle cutter module using mode generic math, that enables more parametizable control over knuckle joint +// dimensions and configuration. +// +// - Version 2.6 +// * Added support for countersunk fastener holes. Now users can select either counterbore or countersunk. +// dimensions and configuration. +// * Started tracking release updates. +// +// - Version 1.0 - 2.5 +// * N/A - Release updates not tracked. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Constants: +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +// System constants. + +C_CONSTANT = 0 + 0; // Used to hide constant values from Thingiverse. Add to other constants to hide them as well. +C_NONE = C_CONSTANT + 0; + +// Leaf gender. + +C_FEMALE = C_CONSTANT + 0; +C_MALE = C_CONSTANT + 1; + +// SCG constants. + +SCG_OVERLAP = C_CONSTANT + 0.01; // Used for overlapping Boolean operations in order to avoid Boolean edge artefacts. +C_NEGATIVE = C_CONSTANT + 0; // Used for subtractive Boolean tools. +C_POSITIVE = C_CONSTANT + 1; // Used for additive Boolean tools. + +// Bolt head shapes. + +C_CIRCULAR = C_CONSTANT + 0; +C_SQUARE = C_CONSTANT + 1; +C_HEXAGONAL = C_CONSTANT + 2; + +// Curve Type + +C_FUNCTION_LINEAR = C_CONSTANT + 1; // y = ax + b +C_FUNCTION_CIRCULAR = C_CONSTANT + 2; // r2 = x2 + y2 +C_FUNCTION_PARABOLIC = C_CONSTANT + 3; // y = a2(x - j)2 + k ...Vertex form. + +// Minimum and maximum constraints. + +C_MIN_HINGE_WIDTH = C_CONSTANT + 1.0; +C_MIN_HINGE_HEIGHT = C_CONSTANT + 1.0; +C_MIN_LEAF_GAUGE = C_CONSTANT + 1.0; +C_MIN_COMPONENT_CLEARENCE = C_CONSTANT + 0.1; +C_MAX_COMPONENT_CLEARENCE = C_CONSTANT + 1.0; +C_MIN_KNUCKLE_COUNT = C_CONSTANT + 3; +C_MAX_KNUCKLE_COUNT = C_CONSTANT + 15; +C_MIN_KNUCKLE_GUSSET_WIDTH = C_CONSTANT + 1.0; +C_MIN_FASTENER_MARGIN = C_CONSTANT + 1.0; +C_MIN_PIN_DIAMETER = C_CONSTANT + 1.0; +C_MIN_COUNTER_SINK_DEPTH_STOP = C_CONSTANT + 1.0; +C_MIN_FASTENER_THREAD_DIAMETER = C_CONSTANT + 0.0; +C_MIN_FASTENER_COUNT = C_CONSTANT + 3; +C_MIN_FASTENER_COLUMN_COUNT = C_CONSTANT + 1; +C_MAX_FASTENER_COLUMN_COUNT = C_CONSTANT + 2; +C_MIN_TESSELLATION = C_CONSTANT + 32; +C_MAX_TESSELLATION = C_CONSTANT + 256; +C_MIN_THROW_ANGLE = C_CONSTANT + -90; +C_MAX_THROW_ANGLE = C_CONSTANT + 180; +C_DEFAULT_THROW_ANGLE = C_CONSTANT + 0; +C_MIN_PIN_SHAFT_COUNTERBORE_DIAMETER = C_CONSTANT + 0.1; +C_MIN_PIN_SHAFT_COUNTERBORE_WALL_THICKNESS = C_CONSTANT + 0.3; +C_MIN_PIN_SHAFT_COUNTERBORE_DEPTH = C_CONSTANT + 0.0; + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Thingiverse Parameters. +// +// - These parameters are used to integrate with the Thingiverse Customizer, and should only be used by the +// class member variables specified in the "Model parameters" section below. +// +// - These Thingiverse Parameters should never be accessed from inside any module. We do this to enforce +// principles of object orientation. +// +// - By separating concerns between variables exposed to Thingiverse vs. variables used internally by the +// SCAD model (class), we are better able to manage the ordering and grouping of variables exposed to +// Thingiverse, vs. the ordering of variables used internally by the model. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +/* [Assembly Options] */ + +enable_male_leaf = 1; // [ 0:No, 1:Yes ] +enable_female_leaf = 1; // [ 0:No, 1:Yes ] +enable_fillet = 1; // [ 0:No, 1:Yes ] +// Turn this off to omit the hinge pin from the female leaf. +enable_pin = 0; // [ 0:No, 1:Yes ] +// Turn this off to set a custom pin diameter. Auto pin size is equal to the leaf gauge. +enable_auto_pin_size = 1; // [ 0:No, 1:Yes ] +enable_pin_shaft_counterbore = 0; // [ 0:No, 1:Yes ] +enable_fasteners = 1; // [ 0:No, 1:Yes ] +knuckle_gusset_type = 0; // [ 0:None, 1:Linear, 2:Circular, 3:Parabolic ] +// From +180 degrees fully closed, to -90 degrees fully opened. Default = 0 (ie. Opened flat). +throw_angle = 90.0; // [ -90 : 5 : 180 ] +// Rotates the model 180 degrees about the z-axis. +flip_model = 0; // [ 0:No, 1:Yes ] +// Recommended value is 64 or greater. +resolution = 128; +component_color = "Silver"; + +/* [Hinge Parameters] */ + +hinge_width = 25.0; +leaf_height = 20.0; +// Leaf and knuckle thickness. Values greater than 3mm recommended. +leaf_gauge = 5.0; +// Recomended values between 0.3 and 4.0. Better quality below 3.0, tough to loosen. +component_clearance = 0.4; +// Knuckle count must be an odd number, so that the pin is supported on both ends. +knuckle_count = 3; // [3:2:31] +// Manual pin diameter setting. Only has effect, if "Enable Auto Pin Size" is set to "No". +pin_diameter = 2.0; +parametric_pin_diameter = ( enable_auto_pin_size == 1 ) ? leaf_gauge : pin_diameter; + +/* [Pin Shaft Parameters] */ + +top_pin_shaft_counterbore_diameter = 6.5; +top_pin_shaft_counterbore_depth = 2.5; +top_pin_shaft_counterbore_shape = 0; // [ 0:Circular, 1:Square, 2:Hexagonal ] +bottom_pin_shaft_counterbore_diameter = 6.0; +bottom_pin_shaft_counterbore_depth = 2.5; +bottom_pin_shaft_counterbore_shape = 2; // [ 0:Circular, 1:Square, 2:Hexagonal ] + +/* [Fastener Parameters] */ + +// For countersunk, the chamfer angle may be adjusted using the other parameters. +fstener_head_type = 0; // [ 0:Counterbored, 1:Countersunk ] +counter_sink_depth = 2.5; +fastener_thread_diameter = 3.5; +// Add 0.5mm to 1.0mm to the fastener head diameter, to allow for head clearance. +fastener_head_diameter = 4.5; +fastener_count = 3; // [3:32] +fastener_column_count = 2; // [1,2] +// Distance from the edge of the head diameter, to the edges of the leaves. +fastener_margin = 3; + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Model parameters and geometric constraints. (Class member variables). +// +// - If we treat an OpenSCAD file as though it is an object oriented class, then we can prefix global variables +// with "m_", to denote class membership. +// - As an alternative to "m_", we could also use "this_" as a standard. However, "m_" is shorter and faster to type. +// - Another advantage of this convention, is that we can arrange parameters meant for display in Thingiverse, in +// an order that makes sense to the user, while arranging the member versions of the parameters in an order +// that better accommodates constraint computation. +// +// - Once we have defined global variables as member variables of a class, in this case the class represented +// by the SCAD file, then we are free to better manage global vs local scope of class member +// variables, vs. local module (method) variables. +// +// - Thingiverse only integrates constant literal values. So as long as we reference other parameters or +// initialize variables as expressions, then none of these will appear in the Thingiverse customizer. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +// Assembly Options. + +m_male_leaf_enabled = ( enable_male_leaf == 1 ) ? true : false; +m_female_leaf_enabled = ( enable_female_leaf == 1 ) ? true : false; +m_leaf_fillet_enabled = ( enable_fillet == 1 ) ? true : false; +m_pin_enabled = ( enable_pin == 1 ) ? true : false; +m_pin_auto_size_enabled = ( enable_auto_pin_size == 1 ) ? true : false; +m_pin_shaft_counterbore_enabled = ( enable_pin_shaft_counterbore == 1 ) ? true : false; +m_fasteners_enabled = ( enable_fasteners == 1 ) ? true : false; +m_knuckle_gusset_type = knuckle_gusset_type; +m_flip_model = ( flip_model == 1 ) ? true : false; +m_throw_angle = clip ( throw_angle, C_MIN_THROW_ANGLE, C_MAX_THROW_ANGLE ); + +// Leaf Parameters. + +m_hinge_width = ( hinge_width <= C_MIN_HINGE_WIDTH ) ? C_MIN_HINGE_WIDTH : hinge_width; +m_leaf_width = m_hinge_width / 2.0; +m_leaf_height = ( leaf_height <= C_MIN_HINGE_HEIGHT ) ? C_MIN_HINGE_HEIGHT : leaf_height; +m_leaf_gauge = clip ( leaf_gauge, C_MIN_LEAF_GAUGE, m_leaf_width/2.0 ); + +// Mechanical Properties. + +m_component_clearance = clip ( component_clearance, C_MIN_COMPONENT_CLEARENCE, C_MAX_COMPONENT_CLEARENCE ); +m_knuckle_outer_radius = m_leaf_gauge * 2.0; +m_knuckle_count = clip ( 1 + 2 * floor ( knuckle_count / 2 ), C_MIN_KNUCKLE_COUNT, C_MAX_KNUCKLE_COUNT ); +m_fastener_margin = clip ( fastener_margin, C_MIN_FASTENER_MARGIN, ( m_leaf_width - m_leaf_gauge - fastener_head_diameter )/2.0 ); +m_knuckle_gusset_width = clip ( m_fastener_margin, C_MIN_KNUCKLE_GUSSET_WIDTH, m_leaf_width - m_leaf_gauge - m_component_clearance ); + +// Pin Parameters. + +m_pin_diameter = clip ( pin_diameter, C_MIN_PIN_DIAMETER, 2.0*m_leaf_gauge - C_MIN_PIN_DIAMETER ); +m_parametric_pin_diameter = ( m_pin_auto_size_enabled == true ) ? m_leaf_gauge : m_pin_diameter; +m_top_pin_shaft_counterbore_diameter = clip ( top_pin_shaft_counterbore_diameter, C_MIN_PIN_SHAFT_COUNTERBORE_DIAMETER, 2.0*m_leaf_gauge - C_MIN_PIN_SHAFT_COUNTERBORE_WALL_THICKNESS ); +m_top_pin_shaft_counterbore_depth = clip ( top_pin_shaft_counterbore_depth, C_MIN_PIN_SHAFT_COUNTERBORE_DEPTH, 0.66*( m_leaf_height / ( m_knuckle_count + m_component_clearance )) ); +m_top_pin_shaft_counterbore_shape = top_pin_shaft_counterbore_shape; +m_bottom_pin_shaft_counterbore_diameter = clip ( bottom_pin_shaft_counterbore_diameter, C_MIN_PIN_SHAFT_COUNTERBORE_DIAMETER, 2.0*m_leaf_gauge - C_MIN_PIN_SHAFT_COUNTERBORE_WALL_THICKNESS ); +m_bottom_pin_shaft_counterbore_depth = clip ( bottom_pin_shaft_counterbore_depth, C_MIN_PIN_SHAFT_COUNTERBORE_DEPTH, 0.8*( m_leaf_height / m_knuckle_count) ); +m_bottom_pin_shaft_counterbore_shape = bottom_pin_shaft_counterbore_shape; + +// Fastener parameters. + +m_fstener_head_type = fstener_head_type; +m_counter_sink_depth = clip ( counter_sink_depth, 0.0, m_leaf_gauge - C_MIN_COUNTER_SINK_DEPTH_STOP ); +m_fastener_head_diameter = clip ( fastener_head_diameter, fastener_thread_diameter, m_leaf_width - m_leaf_gauge - m_component_clearance - 2.0*m_fastener_margin ); +m_fastener_thread_diameter = clip ( fastener_thread_diameter, C_MIN_FASTENER_THREAD_DIAMETER, m_fastener_head_diameter ); +m_fastener_column_count = clip ( fastener_column_count, C_MIN_FASTENER_COLUMN_COUNT, C_MAX_FASTENER_COLUMN_COUNT ); +m_fastener_count = clip ( fastener_count, C_MIN_FASTENER_COUNT, m_fastener_column_count*( m_leaf_height - 2.0*m_fastener_margin )/(m_fastener_head_diameter + m_component_clearance)); +m_leaf_fillet_radius = m_fastener_head_diameter / 2.0 + m_fastener_margin; + +// Model parameters. + +m_resolution = clip ( resolution, C_MIN_TESSELLATION, C_MAX_TESSELLATION ); +m_component_color = component_color; + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// DEBUG: Console Output. +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +C_DEBUG_ENABLED = false; + +if ( C_DEBUG_ENABLED ) +{ + echo ( m_hinge_width = m_hinge_width ); + echo ( m_leaf_width = m_leaf_width ); + echo ( absolute_leaf_width = m_leaf_width - m_leaf_gauge ); + echo ( m_leaf_height = m_leaf_height ); + echo ( m_leaf_gauge = m_leaf_gauge ); + echo ( m_component_clearance = m_component_clearance ); + echo ( m_knuckle_outer_radius = m_knuckle_outer_radius ); + echo ( m_knuckle_count = m_knuckle_count ); + echo ( m_knuckle_gusset_width = m_knuckle_gusset_width ); + echo ( m_leaf_fillet_radius = m_leaf_fillet_radius ); + echo ( m_fastener_margin = m_fastener_margin ); + echo ( m_pin_diameter = m_pin_diameter ); + echo ( m_parametric_pin_diameter = m_parametric_pin_diameter ); + echo ( absolute_pin_diameter = m_parametric_pin_diameter - m_component_clearance / 2.0 ); + echo ( m_pin_shaft_counterbore_diameter = m_pin_shaft_counterbore_diameter ); + echo ( m_pin_shaft_counterbore_depth = m_pin_shaft_counterbore_depth ); +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: Main +// Module Type: Model +// +// Description: +// +// - Program entry point. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +main(); + +module main () +{ + // Initialize model resolution. + + $fn = m_resolution; + + // Generate hinge assembly. + + rotate ( [ 0.0, 0.0, ( m_flip_model ) ? 180.0 : 0.0 ] ) + { + if ( m_female_leaf_enabled ) rotate ( [ 0.0, -m_throw_angle, 0.0 ] ) leaf ( C_FEMALE ); + if ( m_male_leaf_enabled ) leaf ( C_MALE ); + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: leaf. +// Module Type: Component. +// +// Description: +// +// - Creates a hinge leaf component, whose gender may be selected through the gender argument. +// +// - Note: +// The text option is not made public to the Thingiverse Customizer at this time. +// However, you can add and configure text here in the code. +// +// Parameters: +// +// - gender: +// The gender (male, female), of the leaf. The female leaf holds the pin. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module leaf ( gender ) +{ + // Text configuration. + + text_enabled = false; + text_string_female = "0.4"; + text_string_male = "RG"; + text_female_font_size = 8; + text_male_font_size = 8; + + // Compute the gender angle. + // - 0 degrees for female, and 180 degrees for male. + // - In other words, we leave the female leaf un-rotated, but we rotate the male leaf 180 degrees, to place it at an + // opposing orientation to the female. + + gender_angle = ( gender == C_FEMALE ) ? 0 : 180; + + // Create leaves. + + rotate ( [ 0, 0, gender_angle ] ) + { + color ( m_component_color ) + difference () + { + // Cut pin hole. + + workpiece_leaf_knuckle_pin ( gender ); + + // Cut fstener holes. + + if ( m_fasteners_enabled ) + { + tool_cutter_fastener_set ( m_fastener_count, m_fastener_column_count, 0 ); + } + + // Cut pin shaft counterbore into female leaf. + + if ( m_pin_shaft_counterbore_enabled && gender == C_FEMALE ) + { + tool_cutter_pin_shaft_counterbore + ( + diameter_top = m_top_pin_shaft_counterbore_diameter, + depth_top = m_top_pin_shaft_counterbore_depth, + shape_top = m_top_pin_shaft_counterbore_shape, + diameter_bottom = m_bottom_pin_shaft_counterbore_diameter, + depth_bottom = m_bottom_pin_shaft_counterbore_depth, + shape_bottom = m_bottom_pin_shaft_counterbore_shape + ); + } + + // Cut text. + // - We will only cut text into the leaves, if we are using exactly 4 fasteners per leaf. + // - All other leaf counts will not leave enough space for the text to fit easily. So we only add text, if we are using 4 fasteners. + + if ( text_enabled && m_fastener_count == 4 ) + { + if ( gender == C_FEMALE ) tool_cutter_text ( text_string_female, text_female_font_size ); + if ( gender == C_MALE ) tool_cutter_text ( text_string_male, text_male_font_size ); + } + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_leaf_knuckle_pin +// Module Type: Workpiece. +// +// Description: +// +// - This module creates the workpiece used to construct either a male or female leaves. +// +// Parameters: +// +// - gender: +// The gender (male, female), of the leaf. The female leaf holds the pin. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_leaf_knuckle_pin ( gender ) +{ + // Initialize local variables. + + d = m_parametric_pin_diameter; + c = m_component_clearance; + e = SCG_OVERLAP; + + // Combine pin with leaf and knuckle. + + if ( gender == C_FEMALE ) + { + if ( m_pin_enabled ) + { + // Fuse the pin to the female leaf by default. + + union () + { + dc = d - c/2.0; + + workpiece_leaf_knuckle ( C_FEMALE ); + pin ( dc, m_leaf_height - e ); + } + } + else + { + // Cut a hole for an external pin if selected by the user. + + difference () + { + dc = d + c/2.0; + + workpiece_leaf_knuckle ( C_FEMALE ); + pin ( dc, m_leaf_height + e ); + } + } + } + else + { + // Cut a hole for the pin to pass throug in the male leaf. + + difference () + { + dc = d + c/2.0; + + workpiece_leaf_knuckle ( C_MALE ); + pin ( dc, m_leaf_height + e ); + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: pin +// Module Type: Component. +// +// Description: +// +// - Hinge pin component. +// +// Parameters: +// +// - diameter: +// Diameter of the cylinder used to create the pin. +// +// - length: +// Length of the cylinder used to create the pin. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module pin ( diameter, length ) +{ + rotate ( [ 90, 0, 0 ] ) + { + // Initialize pin dimensions. + + tx = 0; + ty = 0; + tz = -length/2; + + // Create pin. + + translate ( [ tx, ty, tz ] ) + { + cylinder ( d = diameter, h = length ); + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_leaf_knuckle +// Module Type: Workpiece. +// +// Description: +// +// - Workpiece used to cut away knuckle structures. +// +// Parameters: +// +// - gender: +// The gender (male, female), of the leaf. The female leaf holds the pin. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_leaf_knuckle ( gender ) +{ + // Initialize local variables. + + gender_flipped = ( gender == C_MALE ) ? C_FEMALE : C_MALE; + + w = m_leaf_width; + l = m_leaf_height; + h = m_leaf_gauge; + r = m_leaf_fillet_radius; + d = m_knuckle_outer_radius; + + // Create workpiece. + + difference () + { + difference () + { + // leaf and knuckle work piece. + + translate ( [ 0, -l/2, 0 ] ) + { + union () + { + // Leaf. + + workpiece_leaf ( w, l, h, r ); + + // Knuckle. + + rotate ( [ -90, 0, 0 ] ) cylinder ( d = d, h = l ); + + // Gusset array. + + if ( m_knuckle_gusset_type != C_NONE ) + { + translate ( [ 0, l/2, 0 ] ) + workpiece_gusset_array + ( + gender = gender, + curve = m_knuckle_gusset_type, + scg_type = C_POSITIVE, + fill_component_clearance = false + ); + } + } + } + + // Cut knuckle gaps. + + tool_cutter_knuckle_array + ( + gender = gender, + fill_component_clearance = true, + size = 2.0*m_leaf_gauge + m_component_clearance + ); + } + + // Cut opposing gusset groves. + + if ( m_knuckle_gusset_type != C_NONE ) + { + workpiece_gusset_array + ( + gender = gender_flipped, + curve = m_knuckle_gusset_type, + scg_type = C_NEGATIVE, + fill_component_clearance = true + ); + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_leaf +// Module Type: Workpiece. +// +// Description: +// +// - Workpiece used to cut away leaf structures. +// +// Parameters: +// +// - w: +// Width of a single leaf. +// +// - l: +// Length of the leaf. +// +// - h: +// Height of the leaf. +// +// - r: +// Radius of the hinge knuckle. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_leaf ( w, l, h, r ) +{ + translate ( [ 0, 0, -h ] ) + { + union () + { + if ( m_leaf_fillet_enabled ) + { + // Leaf. + + cube ( [ w-r, l, h] ); + translate ( [ 0, r, 0 ] ) cube ( [ w, l-2*r, h] ); + + // Fillet corcers. + + translate ( [ w - r, r, 0 ] ) cylinder ( r = r, h = h ); + translate ( [ w - r, l-r, 0 ] ) cylinder ( r = r, h = h ); + } + else + { + // Leaf. + + cube ( [ w, l, h] ); + } + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_gusset_array +// Module Type: Workpiece. +// +// Description: +// +// - Create an array of cutting blocks. Mostly used to cut gusset workpiece into individual gusset positives or negatives. +// +// Parameters: +// +// - gender: +// Specifies the number of knuckle joints based on hinge leaf gender. +// +// - scg_type: +// Solid Constructive Geometry type. +// If C_POSITIVE, then add to the base work piece. +// If C_NAGATIVE, then subtract from the base workpiece. +// +// - fill_component_clearance: +// If true, then create wide cutting blocks that fill in the component clearance. +// If false, then create narrow cutting blocks that leave space for the component clearance. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_gusset_array ( gender, curve, scg_type, fill_component_clearance ) +{ + // Compute cutting block size. + + cutting_block_size = m_leaf_gauge + m_knuckle_gusset_width; + leaf_height = ( scg_type == C_NEGATIVE ) ? m_leaf_height + SCG_OVERLAP : m_leaf_height; + + // Compute cutting block translation. + + xt = cutting_block_size/2.0; + yt = 0.0; + zt = 0.0; + + // Create gusset array. + + difference () + { + // Create a solid cylindrical gusset cylinder. + + workpiece_gusset + ( + width = m_knuckle_gusset_width, + height = leaf_height, + knuckle_radus = m_leaf_gauge, + curve = curve, + scg_type = scg_type + ); + + // Use the knuckle array cutter tool, to cut knuckle gaps into the gusset to match the dimensions of the knuckles. + + translate ( [ xt, yt, zt ] ) + tool_cutter_knuckle_array + ( + gender = gender, + fill_component_clearance = !fill_component_clearance, + size = cutting_block_size + ); + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_gusset +// Module Type: Workpiece +// +// Description: +// +// - Knuckle gusset used to strengthen the knuckle joints. +// +// Parameters: +// +// - width +// The length of the gusset, as measured from the edge of the knuckle. +// We name it "width", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - height +// The thickness of the gusset. +// We name this "height", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - knuckle_radus +// Outer radius of the knuckles. This is also the same value as the leaf gauge. +// +// - curve +// The mathematical function used to describe the shape of the gusset. +// - Linear +// - Parabolic +// - Circular +// +// - scg_type +// Solid Constructive Geometry (SCG) mode. +// - If set to 0 or C_NEGATVE, to create the version of the component used for cutting away from the leaves. +// - If set to 1 or C_POSITIVE, to create the version of the component used for adding to the leaves and knuckles. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_gusset ( width, height, knuckle_radus, curve, scg_type ) +{ + if ( curve == C_FUNCTION_LINEAR ) workpiece_gusset_linear ( width, height, knuckle_radus, scg_type ); + else if ( curve == C_FUNCTION_CIRCULAR ) workpiece_gusset_circular ( width, height, knuckle_radus, scg_type ); + else if ( curve == C_FUNCTION_PARABOLIC ) workpiece_gusset_parabolic ( width, height, knuckle_radus, scg_type ); +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_gusset_linear +// Module Type: Workpiece +// +// Description: +// +// - Linear knuckle gusset, called by workpiece_gusset. +// +// Parameters: +// +// - width +// The length of the gusset, as measured from the edge of the knuckle. +// We name it "width", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - height +// The thickness of the gusset. +// We name this "height", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - knuckle_radus +// Outer radius of the knuckles. This is also the same value as the leaf gauge. +// +// - scg_type +// Solid Constructive Geometry (SCG) mode. +// - If set to 0 or C_NEGATVE, to create the version of the component used for cutting away from the leaves. +// - If set to 1 or C_POSITIVE, to create the version of the component used for adding to the leaves and knuckles. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_gusset_linear ( width, height, knuckle_radus, scg_type ) +{ + CENTER = true; + DEBUG_ENABLED = true; + + // Initialize input values. + + w = width; // Gusset width. + h = height; // Hinge leaf height. + r = knuckle_radus; // Knuckle radius is equal to the leaf gauge. + s = w + r; // Cartesian position of the point where the gusset curve merges with the leaf. + g = m_leaf_gauge; // Leafe gauge. + c = SCG_OVERLAP; // Amount to overlap unions in order to prevent Boolean anomalies. + + // Compute gusset tangent curve. + + x = r*r/s; + y = sqrt ( r*r - x*x ); + a = y/( x - s ); + b = -a*s; + + // Compute work piece translation and dimensions. + + wp_xd = 0; // Work piece origin x. + wp_yd = 0; // Work piece origin y. + wp_zd = -g + c; // Work piece origin z. + + wp_w = s; // Work piece width (x axis length). + wp_g = g + y - c; // Work piece gauge (y access length. i.e. thickness). + wp_h = h; // Work piece height (y axis length). + + // Compute cutting tool translation and dimensions. + + ct_xd = 0; + ct_yd = 0; + ct_zd = 0; + + ct_w0 = x; + ct_w1 = s; + ct_g0 = y; + ct_g1 = 0; + ct_h = h + 2*c; + + // Compute and configure orientation. + + test_yd = 0; + + zs = ( scg_type == C_POSITIVE ) ? 1.0 : -1.0; + scale ( [ 1.0, -1.0, zs ] ) + difference() + { + translate ( [ wp_xd, wp_yd + test_yd, wp_zd ] ) rotate ( [ 90, 0, 0 ] ) + { + linear_extrude ( height = wp_h, center = CENTER ) rectangle ( w = wp_w, h = wp_g ); + } + + translate ( [ ct_xd, ct_yd + test_yd, ct_zd ] ) rotate ( [ 90, 0, 0 ] ) + { + linear_extrude ( height = ct_h, center = CENTER ) triangle ( [ [ ct_w0, ct_g0 ],[ ct_w1+c, ct_g0+c ],[ ct_w1, ct_g1 ] ] ); + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_gusset_circular +// Module Type: Workpiece +// +// Description: +// +// - Linear knuckle gusset, called by workpiece_gusset. +// +// Parameters: +// +// - width +// The length of the gusset, as measured from the edge of the knuckle. +// We name it "width", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - height +// The thickness of the gusset. +// We name this "height", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - knuckle_radus +// Outer radius of the knuckles. This is also the same value as the leaf gauge. +// +// - scg_type +// Solid Constructive Geometry (SCG) mode. +// - If set to 0 or C_NEGATVE, to create the version of the component used for cutting away from the leaves. +// - If set to 1 or C_POSITIVE, to create the version of the component used for adding to the leaves and knuckles. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_gusset_circular ( width, height, knuckle_radus, scg_type ) +{ + CENTER = true; + + // Initialize input values. + + w = width; // Gusset width. + g = knuckle_radus; // Knuckle radius is equal to the leaf gauge. + c = SCG_OVERLAP; // Amount to overlap unions in order to prevent Boolean anomalies. + + // Compute gusset radius. The radius of the circle, that is tangential to the knuckle cylinder. + + r = ( 2.0*g*w + w*w ) / ( 2.0*g ); + + // Compute gusset height. The point of intersection between the knuckle cylinder and the gusset cutter. + + h = ( g*r ) / sqrt ( g*g + 2.0*g*w + r*r + w*w ); + + // Compute intersection point between knuckle and gusset cutting tool, using gusset height. + // The coordinate of the intersection point are, p(x,h), where h is the vertical value of the coordinate. + + x = h*( g + w ) / r; + + // Compute gusset cutting tool translation. + + ctxd = g + w; + ctyd = c; + ctzd = r; + ctt = height + 2.0*x; + + // Compute gusset work piece translation and dimensions. + + wpw = g + w -x; + wph = h + c; + wpxd = x; + wpyd = 0.0; + wpzd = 0.0 - c; + wpt = height; + + // Initialize cutting plane and component scaling. + + xr = 90.0; + yr = 0.0; + zr = 0.0; + + xs = 1.0; + ys = -1.0; + zs = ( scg_type == C_POSITIVE ) ? 1.0 : -1.0; + + // Generate gusset. + + color ( "silver" ) + scale ( [ xs, ys, zs ] ) + difference () + { + translate ( [ wpxd, wpyd, wpzd ] ) rotate ( [ xr, yr, zr ] ) linear_extrude ( height = wpt, center = CENTER ) rectangle ( w = wpw, h = wph ); + translate ( [ ctxd, ctyd, ctzd ] ) rotate ( [ xr, yr, zr ] ) linear_extrude ( height = ctt, center = CENTER ) circle ( r = r ); + } +} + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: workpiece_gusset_parabolic +// Module Type: Workpiece +// +// Description: +// +// - Parabolic knuckle gusset, called by workpiece_gusset. +// +// Parameters: +// +// - width +// The length of the gusset, as measured from the edge of the knuckle. +// We name it "width", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - height +// The thickness of the gusset. +// We name this "height", in order to maintain dimension naming consistency with the rest of the hinge dimensions. +// +// - knuckle_radus +// Outer radius of the knuckles. This is also the same value as the leaf gauge. +// +// - scg_type +// Solid Constructive Geometry (SCG) mode. +// - If set to 0 or C_NEGATVE, to create the version of the component used for cutting away from the leaves. +// - If set to 1 or C_POSITIVE, to create the version of the component used for adding to the leaves and knuckles. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module workpiece_gusset_parabolic ( width, height, knuckle_radus, scg_type ) +{ + CENTER = true; + DEBUG_ENABLED = true; + RESOLUTION = m_resolution / C_MIN_TESSELLATION; + + // Initialize input values. + + w = width; // Gusset width. + h = height; // Hinge leaf height. + r = knuckle_radus; // Knuckle radius is equal to the leaf gauge. + s = w + r; // Cartesian position of the point where the gusset curve merges with the leaf. + g = m_leaf_gauge; // Leafe gauge. + c = SCG_OVERLAP; // Amount to overlap unions in order to prevent Boolean anomalies. + + // Compute parabolic point of contact with the knuckle cylinder. + + i = sqrt ( 8.0*r*r + s*s ); // Common root. + x = ( i - s )/2.0; // x intercept. + y = sqrt ( r*r - x*x ); // y intercept. + + // Compute coefficient 'a' of vertex form parabola. + // y = a2(x-s)2 + + an = root4 ( 2.0 ) * root4 ( s*( i - s ) - 2.0*r*r ); // Numerator. + ad = sqrt ( s*( 5.0*s - 3.0*i ) + 4.0*r*r ); // Denominator. + a = an / ad; // Coefficient 'a' of, y = a2(x-s)2 + + // Compute work piece translation and dimensions. + + wp_xd = 0; // Work piece origin x. + wp_yd = 0; // Work piece origin y. + wp_zd = -g + c; // Work piece origin z. + + wp_w = s; // Work piece width (x axis length). + wp_g = g + y - c; // Work piece gauge (y access length. i.e. thickness). + wp_h = h; // Work piece height (y axis length). + + // Compute cutting tool translation and dimensions. + + ct_xd = 0; + ct_yd = 0; + ct_zd = 0; + + ct_w0 = x; + ct_w1 = s; + ct_g0 = y; + ct_g1 = 0; + ct_h = h + 2*c; + + // Compute positive or negative tool orientation. We flip the tool by using a negative unit scale. + + test_yd = 0; + zs = ( scg_type == C_POSITIVE ) ? 1.0 : -1.0; + scale ( [ 1.0, -1.0, zs ] ) + + // Create gusset work piece. + + difference() + { + // Work piece. + + translate ( [ wp_xd, wp_yd + test_yd, wp_zd ] ) rotate ( [ 90, 0, 0 ] ) + { + linear_extrude ( height = wp_h, center = CENTER ) rectangle ( w = wp_w, h = wp_g ); + } + + // Parabolic cutting tool. + + translate ( [ ct_xd, ct_yd + test_yd, ct_zd ] ) rotate ( [ 90, 0, 0 ] ) + { + linear_extrude ( height = ct_h, center = CENTER ) parabolic_conic_section ( a, s, 0, s, RESOLUTION ); + } + } +} + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: tool_cutter_fastener +// Module Type: Cutting Tool. +// +// Description: +// +// - Cutting tool used to cut fastener holes into leaf workpiece. +// +// Parameters: +// +// - z_offset: +// Depth of the cut. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module tool_cutter_fastener ( z_offset, ) +{ + // Initialize local variables. + + id = m_fastener_thread_diameter; // Inner diameter. + od = m_fastener_head_diameter; // Outer diameter. + t = m_fstener_head_type; // 0 = Fillister (Pan head), 1 = flat countersunk. + d = m_counter_sink_depth; // Depth of head counter sink. + z0 = z_offset - m_leaf_gauge; // Vertical position of head. + z1 = z_offset - m_counter_sink_depth; // Vertical position of thread. + h0 = m_leaf_gauge; // Height of head. + h1 = m_counter_sink_depth; // Height of thread. + c = SCG_OVERLAP; + + // Create cutting tool. + + union () + { + // Thread + + translate ( [ 0, 0, z0 - c ] ) + { + cylinder ( d = id, h = h0 + 2.0*c ); + } + + // Head. + + union () + { + // Fastener head. + + translate ( [ 0, 0, z1 ] ) + { + // Fillister (Pan head). + + if ( t == 0 ) + { + d_top = od; + d_bottom = od; + h = h1 + c; + cylinder ( d2 = d_top, d1 = d_bottom, h = h ); + } + + // Flat countersunk. + + if ( t == 1 ) + { + d_top = od + c; + d_bottom = id; + h = h1 + c; + cylinder ( d2 = d_top, d1 = d_bottom, h = h ); + } + } + + // Cutting tool extention. + + translate ( [ 0, 0, c ] ) + { + cylinder ( d = od, h = m_leaf_gauge ); + } + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: tool_cutter_fastener_set +// Module Type: Cutting Tool. +// +// Description: +// +// - Cutting tool used to cut fastener holes into leaf workpiece. +// +// Parameters: +// +// - fastener_count: +// Number of fastener holes to be cut into a single leaf. +// The total number of fastener holes on the whole hinge, will be twice the value of fastener_count. +// i.e. 'fastener_count' holes, on each leaf. +// +// - fastener_column_count: +// Number of fastener column per leaf. This value can be either 1, or 2. +// +// - z_offset: +// Depth of the cut. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module tool_cutter_fastener_set ( fastener_count, fastener_column_count, z_offset ) +{ + // Relative origin. + + xo = m_leaf_gauge + m_component_clearance/2 + m_fastener_head_diameter/2 + m_fastener_margin; + yo = -m_leaf_height/2 + m_fastener_head_diameter/2 + m_fastener_margin; + + // Column offsets. + + col0 = 0; + col1 = m_leaf_width - m_fastener_head_diameter/2 - m_fastener_margin - xo; + + // Loop configuration. + + even = ( fastener_count % 2 ) ? false : true; + n1 = fastener_count - 1; + n2 = round ( fastener_count / 2 ) - 1; + k1 = ( m_leaf_height - m_fastener_head_diameter - 2*m_fastener_margin ) / n1; + k2 = ( m_leaf_height - m_fastener_head_diameter - 2*m_fastener_margin ) / n2; + + // Generate fastener cutting tool. + + // One column of fastener holes, if we have selected one fastener hole column. + + if ( fastener_column_count == 1 ) + { + for ( row = [ 0 : n1 ] ) + { + cx = ( col0 + col1 ) / 2.0; + tx = xo + cx; + ty = yo + row * k1; + tz = 0; + + translate ( [ tx, ty, tz ] ) tool_cutter_fastener ( z_offset ); + } + } + + // Two columns of fastener holes, if we have selected two fastener hole column. + + if ( fastener_column_count == 2 ) + { + for ( col = [ 0 : 1 ] ) + { + // Column 0, offset translation when we have an odd number of fasteners. + + if ( col == 0 ) + { + m = ( even ) ? 0 : 1; + + for ( row = [ 0 : n2 - m ] ) + { + cx = ( col == 0 ) ? col0 : col1; + tx = xo + cx; + ty = ( even ) ? yo + row * k2 : yo + row * k2 + k2/2; + tz = 0; + + translate ( [ tx, ty, tz ] ) tool_cutter_fastener ( z_offset ); + } + } + + // Column 1. + + if ( col == 1 ) + { + for ( row = [ 0 : n2 ] ) + { + cx = ( col == 0 ) ? col0 : col1; + tx = xo + cx; + ty = yo + row * k2; + tz = 0; + + translate ( [ tx, ty, tz ] ) tool_cutter_fastener ( z_offset ); + } + } + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: tool_cutter_text. +// Module Type: Tool cutter. +// +// Description: +// +// - Inscribes a string of text onto a surface. +// +// Parameters: +// +// - string: +// The string of text we would like to inscribes. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module tool_cutter_text ( string, size ) +{ + // create text cutter. + + font = "Ariel:style=Bold"; + height = 0.15*6.0; + xd = 20.0; + yd = 0.0; + zd = height; + + translate ( [ xd, yd, -zd ] ) + { + rotate ( [ 0.0, 0.0, -90.0 ] ) + { + linear_extrude ( height = height + SCG_OVERLAP ) + { + text ( string, font = font, size = size, valign = "center", halign = "center" ); + } + } + } +} + + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: tool_cutter_knuckle_array +// Module Type: Cutting tool. +// +// Description: +// +// - Create an array of cutting blocks. Mostly used to cut gusset workpiece into individual gusset positives or negatives. +// +// Parameters: +// +// - gender: +// Specifies the number of knuckle joints based on hinge leaf gender. +// +// - fill_component_clearance: +// If true, then create wide cutting blocks that fill in the component clearance. +// If false, then create narrow cutting blocks that leave space for the component clearance. +// +// - size: +// Specifies the dimension used for the x and z axes. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module tool_cutter_knuckle_array ( gender, fill_component_clearance, size ) +{ + // Initialize local variables. + + n = m_knuckle_count; + h = m_leaf_height; + c = m_component_clearance; + e = SCG_OVERLAP; + + // Compute knuckle width and segment width. + + k = ( h + c )/n - c; + s = ( fill_component_clearance ) ? k + 2.0*c : k; + + // Compute segment offset. + + o = ( fill_component_clearance ) ? c : 0.0; + + // Generate block array. + + a = 0; + b = ( gender == C_MALE ) ? n/2 : n/2-1; + g = ( gender == C_MALE ) ? 0.0 : k + c; + + for ( i = [ a : b ] ) + { + // Compute translation index. + + ki = g + 2.0*i*(k + c ) - h/2.0 - o; + + // Initialize translation. + + xt = -size/2.0; + yt = ki; + zt = -size/2.0; + + // Initialize cutting block dimensions. + + cube_x = size; + cube_y = s; + cube_z = 2.0*size; + + // Create cutting block. + + color ( "red" ) + translate ( [ xt, yt, zt ] ) + cube ( [ cube_x, cube_y, cube_z ] ); + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: tool_cutter_pin_shaft_counterbore +// Module Type: Cutting tool. +// +// Description: +// +// - Cuts a counterbore into the endcaps of the pin shaft. +// - If the model is being printed in its default orientation, open and flat on the build plate, then support material may be required +// for the counterbore. +// +// Parameters: +// +// - diameter: +// Specifies the diameter of the counterbore. +// For square and hexagonal counterbore shapes, the diameter refers to the inscribed circle of those shapes. +// +// - depth: +// Specifies the depth of the cut. +// +// - shape: +// The shame of the counterbore. +// - Circular +// - Square +// - Hexagonal +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module tool_cutter_pin_shaft_counterbore ( diameter_top, depth_top, shape_top, diameter_bottom, depth_bottom, shape_bottom ) +{ + // Local constants. + + TOP_COUNTER_BORE_ENABLED = true; + BOTTOM_COUNTER_BORE_ENABLED = true; + + // Initialize local variables. + + d0 = diameter_bottom; + d1 = diameter_top; + h0 = depth_bottom + SCG_OVERLAP; + h1 = depth_top + SCG_OVERLAP; + + // Compute diameter of polygon, inscribed by a circle of radius d/2. + + s = 6; // Polygon sides. + a = 360.0/(2.0*s); // Half the angle between each point of the polygon. + + x0 = d0/2.0; // Bottom: Run of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + y0 = x0*tan ( a ); // Bottom: Rise of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + r0 = sqrt ( x0*x0 + y0*y0 ); // Bottom: Hypotenuse of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + + x1 = d1/2.0; // Top: Run of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + y1 = x1*tan ( a ); // Top: Rise of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + r1 = sqrt ( x1*x1 + y1*y1 ); // Top: Hypotenuse of the right angle triangle who hypotenuse is equal to half the radius of our new circle. + + // Compute cutting tool translation. + + xd = 0.0; + yd0 = ( h0 - m_leaf_height )/2.0 - SCG_OVERLAP; + yd1 = -( h1 - m_leaf_height )/2.0 + SCG_OVERLAP; + zd = 0.0; + + // Compute polygon rotation angle. + + rx = 90.0; + ry = ( s%2 == 0 ) ? a+90 : -a/2.0; + rz = 0.0; + + // Create top counterbore cutting tool. + + if ( BOTTOM_COUNTER_BORE_ENABLED ) + { + color ( "red" ) + if ( shape_bottom == C_CIRCULAR ) + { + translate ( [ xd, yd0, zd ] ) rotate ( [ rx, ry, rz ] ) cylinder ( h = h0, r = d0/2.0, center = true ); + } + else if ( shape_bottom == C_SQUARE ) + { + translate ( [ xd, yd0, zd ] ) cube ( [ diameter_bottom, h0, diameter_bottom ], center = true ); + } + else if ( shape_bottom == C_HEXAGONAL ) + { + translate ( [ xd, yd0, zd ] ) rotate ( [ rx, ry+90, rz ] ) cylinder ( h = h0, r = r0, center = true, $fn = s ); + } + } + + // Create bottom counterbore cutting tool. + + if ( TOP_COUNTER_BORE_ENABLED ) + { + color ( "red" ) + if ( shape_top == C_CIRCULAR ) + { + translate ( [ xd, yd1, zd ] ) rotate ( [ rx, ry, rz ] ) cylinder ( h = h1, r = d1/2.0, center = true ); + } + else if ( shape_top == C_SQUARE ) + { + translate ( [ xd, yd1, zd ] ) cube ( [ diameter_top, h1, diameter_top ], center = true ); + } + else if ( shape_top == C_HEXAGONAL ) + { + translate ( [ xd, yd1, zd ] ) rotate ( [ rx, ry, rz ] ) cylinder ( h = h1, r = r1, center = true, $fn = s ); + } + } +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: parabolic_conic_section. +// Module Type: 2D Shape. +// +// Description: +// +// - Creates a parabolic conic section object. +// +// Parameters: +// +// - radius: +// Radius of the cylinder sector. +// +// - a: +// Coefficient of the vertex form parabola, y = a2(x-b)2. +// +// - x: +// x Coordinate of parabola turning point. +// Note: +// For the sake of simplifying the actual parabolic function used in the recursive +// function "parabolic_vector", we use a call to "translate" to set the vertex, rather than the parabolic function its self. +// +// - y: +// y Coordinate of parabola turning point. +// Note: +// For the sake of simplifying the actual parabolic function used in the recursive +// function "parabolic_vector", we use a call to "translate" to set the vertex, rather than the parabolic function its self. +// +// - domain: +// The input domain x, over which to compute the parabolic function. +// +// - resolution: +// The input domain step size, is used to control the resolution of the shape. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module parabolic_conic_section ( a, x, y, domain, resolution ) +{ + // Generate 2D geometry + + t = 0.0; + n = resolution; + i = 0; + d = domain; + v = []; + + points = parabolic_vector ( a, t, n, i, d, v ); + + // Generate 3D object. + + translate ( [ x, y, 0 ] ) + polygon ( points ); +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: triangle. +// Module Type: 2D Shape. +// +// Description: +// +// - Creates a 2D triangle from three vertices. +// +// Parameters: +// +// - x0, x1, x2 +// x coordinates of vertices 0 through 2. +// +// - y0, y1, y2 +// y coordinates of vertices 0 through 2. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module triangle ( v ) +{ + polygon ( points = v ); +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: Rectangle. +// Module Type: 2D Shape. +// +// Description: +// +// - Creates a 2D rectangle. +// +// Parameters: +// +// - w +// Rectangle width. +// +// - h +// Rectangle height. +// +// - center +// Center the rectangle about the origin (0,0,0) if true, else place the rectangle in the positive quadrant. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +module rectangle ( w, h, center ) +{ + scale ( [ w, h ] ) square ( size = 1.0, center = center ); +} + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Function: root4 +// +// Description: +// +// - Compute the 4th root of x. +// +// Return Value: +// +// - Return the 4th root of x. +// +// Parameters: +// +// - x +// Input value. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +function root4 ( x ) = ( x >= 0 ) ? sqrt ( sqrt ( x ) ) : 0; + + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Function: Clip +// +// Description: +// +// - Clips an input value, to a minimum and maximum value. +// +// x_min <= x <= x_max +// +// Parameters: +// +// - x +// Input value. +// +// - x_min +// Minimal value constraint. Any x less than x_min, is set to x_min. +// +// - x_max +// Maximum value constraint. Any x greater than x_max, is set to x_max. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +function clip ( x, x_min, x_max ) = ( x < x_min ) ? x_min : ( x > x_max ) ? x_max : x; + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Function: Generate a parabolic vector point array. +// +// Description: +// +// - Recursive function to generate an array of 2D points on a parabolic curve. +// +// - The parabolic vertex form is used. +// +// y = a2(x-b)2 +// +// Where, +// +// a is the horizontal scale of the parabola. +// (b,c) is the Cartesian position of the turning point. +// +// +// Parameters: +// +// - a +// Horizontal scale of the parabola. +// +// - x +// Horizontal input domain. +// +// - n +// Tessellation factor. Number of points to compute for each half of the parabola object, with both halves sharing the turning point. +// For example: +// - A tessellation factor of 1, will compute 3 points in total. Two for each half, with one out of the two on each half being the +// Shared turning point. +// - A tessellation factor of 2, with compute 5 points in total, 3 for each half with a shared turning point. +// +// - i +// Point index. Initialize to zero. +// +// - d +// Input domain. The x range over which to compute the function. +// -d <= x <= d +// +// - v +// Vector that will be populated with geometry points. Initialize to empty vector, []. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +function parabolic_vector ( a, x, n, i, d, v ) = +( + i > 2.0*d*n + + // Recursive terminating condition: + // Return an empty vector. + + ? concat ( v, [] ) + + // Recursive general condition: + // Compute the next point on the parabolic path. + // x = i/n - 1 + // y = a2(i/n - 1)2 + + : parabolic_vector ( a, x, n, i + 1, d, concat ( v, [ [ i/n - d, a*a*(i/n - d)*(i/n - d) ] ] ) ) +); + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Function: funtion_name +// +// Description: +// +// - Function description. +// +// Return Value: +// +// - Description of return value. +// +// Parameters: +// +// - x +// Argument x description. +// +// - y +// Argument y description. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- +// Module: module_name +// Module Type: [ 2D Shape, Profile, Tool, Workpiece, Component ] +// Description: +// +// - Module description. +// +// Parameters: +// +// - x +// Argument x description. +// +// - y +// Argument y description. +// +// -------------------------------------+---------------------------------------+---------------------------------------+--------------------------------------- + diff --git a/soap holder.scad b/soap holder.scad new file mode 100644 index 0000000..04873d6 --- /dev/null +++ b/soap holder.scad @@ -0,0 +1,23 @@ +// 1.5" x 2.25" +// 2.5" tall + +height = 2.5 * 25.4; +width = 3.0 * 25.4; +depth = 2.0 * 25.4; + + +thickness = 4; + +difference() { + cube([width, depth, height]); + translate([0, thickness/2, thickness/2]) { + cube([width, depth - thickness, height - thickness]); + } + translate([(width-(2.25*25.4))/2, (depth-(1.5*25.4))/2, height-thickness]) { + + cube([2.25*25.4, 1.5*25.4, thickness]); + } + // remove the bulk for testing + //cube([width, depth, height - thickness+2]); +}; +