├── LICENSE.txt ├── README.md ├── images └── smooth_prim.png └── smooth_prim.scad /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenSCAD smooth primitives library 2 | 3 |

Demo image

4 | 5 | This library is a collection of some smooth primitives, i.e. having specified 6 | rounded edges, for use in other designs. It's not a comprehensive collection, 7 | but a useful set. 8 | 9 | * SmoothCylinder 10 | * HollowCylinder 11 | * SmoothHole 12 | * ChamferHole 13 | * SmoothCube 14 | * SmoothXYCube 15 | * SmoothCorner 16 | * SmoothWall 17 | * SmoothHollowCube 18 | * CutCorner 19 | * CutCube 20 | 21 | 22 | -------------------------------------------------------------------------------- /images/smooth_prim.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rcolyer/smooth-prim/0d0038f984465f3eb0f6026a4d5de2f79d2dcea8/images/smooth_prim.png -------------------------------------------------------------------------------- /smooth_prim.scad: -------------------------------------------------------------------------------- 1 | // Created in 2016-2019 by Ryan A. Colyer. 2 | // This work is released with CC0 into the public domain. 3 | // https://creativecommons.org/publicdomain/zero/1.0/ 4 | 5 | 6 | module SmoothCylinder(radius, height, smooth_rad) { 7 | $fa = ($fa >= 12) ? 1 : $fa; 8 | $fs = ($fs >= 2) ? 0.4 : $fs; 9 | 10 | if (radius > smooth_rad) { 11 | rotate_extrude(convexity=10) 12 | hull() { 13 | square([radius-smooth_rad, height]); 14 | intersection() { 15 | translate([0, -0.1*height]) 16 | square([1.1*radius, 1.2*height]); 17 | union() { 18 | translate([radius-smooth_rad, smooth_rad]) 19 | circle(r=smooth_rad); 20 | translate([radius-smooth_rad, height-smooth_rad]) 21 | circle(r=smooth_rad); 22 | } 23 | } 24 | } 25 | } 26 | else { 27 | rotate_extrude(convexity=10) 28 | hull() { 29 | intersection() { 30 | translate([0, -0.1*height]) 31 | square([1.1*radius, 1.2*height]); 32 | union() { 33 | translate([0, radius]) 34 | circle(r=radius); 35 | translate([0, height-radius]) 36 | circle(r=radius); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | 43 | 44 | module HollowCylinder(outer_rad, inner_rad, height) { 45 | $fa = ($fa >= 12) ? 1 : $fa; 46 | $fs = ($fs >= 2) ? 0.4 : $fs; 47 | 48 | rad_diff = outer_rad-inner_rad; 49 | rotate_extrude(convexity=10) 50 | translate([(outer_rad+inner_rad)/2, 0, 0]) 51 | hull() { 52 | translate([0, rad_diff/2, 0]) 53 | circle(r=rad_diff/2); 54 | translate([0, height-rad_diff/2, 0]) 55 | circle(r=rad_diff/2); 56 | } 57 | } 58 | 59 | module SmoothHole(radius, height, smooth_rad, 60 | position=[0,0,0], rotation=[0,0,0]) { 61 | $fa = ($fa >= 12) ? 1 : $fa; 62 | $fs = ($fs >= 2) ? 0.4 : $fs; 63 | 64 | extra_height = 0.002 * height; 65 | 66 | difference() { 67 | children(); 68 | translate(position) 69 | rotate(rotation) 70 | translate([0, 0, -extra_height/2]) 71 | difference() { 72 | translate([0, 0, -extra_height]) 73 | cylinder(r=radius+smooth_rad, h=height+2*extra_height); 74 | HollowCylinder(radius+2*smooth_rad, radius, height+extra_height); 75 | } 76 | } 77 | } 78 | 79 | 80 | module ChamferHole(radius, height, chamfer_size, 81 | position=[0,0,0], rotation=[0,0,0]) { 82 | $fa = ($fa >= 12) ? 1 : $fa; 83 | $fs = ($fs >= 2) ? 0.4 : $fs; 84 | 85 | cham = chamfer_size < height/2 - 0.001 ? chamfer_size : height/2 - 0.001; 86 | extra_height = 0.002 * height; 87 | height_ex = height+extra_height; 88 | 89 | difference() { 90 | children(); 91 | translate(position) 92 | rotate(rotation) 93 | translate([0, 0, -extra_height/2]) 94 | rotate_extrude() 95 | polygon([[0,0], [0,height_ex], [radius+cham,height_ex], 96 | [radius,height_ex-cham], [radius,cham], 97 | [radius+cham,0]]); 98 | } 99 | } 100 | 101 | 102 | module SmoothCube(size, smooth_rad) { 103 | $fa = ($fa >= 12) ? 1 : $fa; 104 | $fs = ($fs >= 2) ? 0.4 : $fs; 105 | 106 | size = is_num(size) ? [size, size, size] : size; 107 | smooth_rad = is_num(smooth_rad) ? [smooth_rad, smooth_rad, smooth_rad] : 108 | smooth_rad; 109 | smooth_base = smooth_rad[0]; 110 | scales = smooth_rad / smooth_base; 111 | 112 | scalex = scales[0] * ((smooth_rad[0] < size[0]/2) ? 1 : size[0]/(2*smooth_rad[0])); 113 | scaley = scales[1] * ((smooth_rad[1] < size[1]/2) ? 1 : size[1]/(2*smooth_rad[1])); 114 | scalez = scales[2] * ((smooth_rad[2] < size[2]/2) ? 1 : size[2]/(2*smooth_rad[2])); 115 | smoothx = smooth_rad[0] * scalex / scales[0]; 116 | smoothy = smooth_rad[1] * scaley / scales[1]; 117 | smoothz = smooth_rad[2] * scalez / scales[2]; 118 | 119 | hull() { 120 | translate([smoothx, smoothy, smoothz]) 121 | scale([scalex, scaley, scalez]) 122 | sphere(r=smooth_base); 123 | translate([size[0]-smoothx, smoothy, smoothz]) 124 | scale([scalex, scaley, scalez]) 125 | sphere(r=smooth_base); 126 | translate([smoothx, size[1]-smoothy, smoothz]) 127 | scale([scalex, scaley, scalez]) 128 | sphere(r=smooth_base); 129 | translate([smoothx, smoothy, size[2]-smoothz]) 130 | scale([scalex, scaley, scalez]) 131 | sphere(r=smooth_base); 132 | translate([size[0]-smoothx, size[1]-smoothy, smoothz]) 133 | scale([scalex, scaley, scalez]) 134 | sphere(r=smooth_base); 135 | translate([size[0]-smoothx, smoothy, size[2]-smoothz]) 136 | scale([scalex, scaley, scalez]) 137 | sphere(r=smooth_base); 138 | translate([smoothx, size[1]-smoothy, size[2]-smoothz]) 139 | scale([scalex, scaley, scalez]) 140 | sphere(r=smooth_base); 141 | translate([size[0]-smoothx, size[1]-smoothy, size[2]-smoothz]) 142 | scale([scalex, scaley, scalez]) 143 | sphere(r=smooth_base); 144 | } 145 | } 146 | 147 | 148 | module SmoothXYCube(size, smooth_rad) { 149 | $fa = ($fa >= 12) ? 1 : $fa; 150 | $fs = ($fs >= 2) ? 0.4 : $fs; 151 | 152 | size = is_num(size) ? [size, size, size] : size; 153 | 154 | scalex = (smooth_rad < size[0]/2) ? 1 : size[0]/(2*smooth_rad); 155 | scaley = (smooth_rad < size[1]/2) ? 1 : size[1]/(2*smooth_rad); 156 | smoothx = smooth_rad * scalex; 157 | smoothy = smooth_rad * scaley; 158 | 159 | linear_extrude(size[2]) hull() { 160 | translate([smoothx, smoothy]) 161 | scale([scalex, scaley]) 162 | circle(r=smooth_rad); 163 | translate([size[0]-smoothx, smoothy]) 164 | scale([scalex, scaley]) 165 | circle(r=smooth_rad); 166 | translate([smoothx, size[1]-smoothy]) 167 | scale([scalex, scaley]) 168 | circle(r=smooth_rad); 169 | translate([size[0]-smoothx, size[1]-smoothy]) 170 | scale([scalex, scaley]) 171 | circle(r=smooth_rad); 172 | } 173 | } 174 | 175 | 176 | module SmoothCorner(height, width, inner_curv=0) { 177 | $fa = ($fa >= 12) ? 1 : $fa; 178 | $fs = ($fs >= 2) ? 0.4 : $fs; 179 | 180 | translate([width+inner_curv, width+inner_curv, 0]) 181 | rotate([0, 0, 180]) 182 | rotate_extrude(angle=90, convexity=10) 183 | translate([width/2+inner_curv, 0, 0]) 184 | hull() { 185 | translate([0, width/2, 0]) 186 | circle(r=width/2); 187 | translate([0, height-width/2, 0]) 188 | circle(r=width/2); 189 | } 190 | } 191 | 192 | 193 | module SmoothWall(width, height, wall_width) { 194 | $fa = ($fa >= 12) ? 1 : $fa; 195 | $fs = ($fs >= 2) ? 0.4 : $fs; 196 | 197 | rotate([-90, 180, -90]) 198 | linear_extrude(width) 199 | translate([wall_width/2, 0, 0]) 200 | hull() { 201 | translate([0, wall_width/2, 0]) 202 | circle(r=wall_width/2); 203 | translate([0, height-wall_width/2, 0]) 204 | circle(r=wall_width/2); 205 | } 206 | } 207 | 208 | 209 | module SmoothHollowCube(size, wall_width, inner_curv=0) { 210 | $fa = ($fa >= 12) ? 1 : $fa; 211 | $fs = ($fs >= 2) ? 0.4 : $fs; 212 | 213 | size = is_num(size) ? [size, size, size] : size; 214 | 215 | min_ww_size = min(size[0]/2, size[1]/2, size[2]); 216 | wall_width = wall_width < min_ww_size ? wall_width : min_ww_size; 217 | max_inner_curv = (min(size[0], size[1])-2*wall_width)/2; 218 | inner_curv = inner_curv < max_inner_curv ? inner_curv : max_inner_curv; 219 | 220 | SmoothCorner(size[2], wall_width, inner_curv); 221 | translate([size[0], 0, 0]) 222 | rotate([0, 0, 90]) 223 | SmoothCorner(size[2], wall_width, inner_curv); 224 | translate([size[0], size[1], 0]) 225 | rotate([0, 0, 180]) 226 | SmoothCorner(size[2], wall_width, inner_curv); 227 | translate([0, size[1], 0]) 228 | rotate([0, 0, 270]) 229 | SmoothCorner(size[2], wall_width, inner_curv); 230 | 231 | wall_lengthx = size[0]-2*wall_width-2*inner_curv+0.002; 232 | wall_lengthy = size[1]-2*wall_width-2*inner_curv+0.002; 233 | wall_height = size[2]; 234 | 235 | translate([size[0], wall_width+inner_curv-0.001, 0]) 236 | rotate([0, 0, 90]) 237 | SmoothWall(wall_lengthy, wall_height, wall_width); 238 | translate([wall_width, wall_width+inner_curv-0.001, 0]) 239 | rotate([0, 0, 90]) 240 | SmoothWall(wall_lengthy, wall_height, wall_width); 241 | translate([wall_width+inner_curv-0.001, 0, 0]) 242 | SmoothWall(wall_lengthx, wall_height, wall_width); 243 | translate([wall_width+inner_curv-0.001, size[1]-wall_width, 0]) 244 | SmoothWall(wall_lengthx, wall_height, wall_width); 245 | } 246 | 247 | 248 | module CutCorner(insetby, cornerpos=[0,0,0], positives=[undef,undef,undef]) { 249 | clearance = 4*insetby + 0.001; 250 | positives = positives==[undef,undef,undef] ? cornerpos : positives; 251 | angle_offset = (positives[0]?90:0) + (positives[1]?-90:0) 252 | + ((positives[0]&&positives[1])?180:0); 253 | 254 | difference() { 255 | children(); 256 | translate(cornerpos) 257 | rotate([0, 0, 225+angle_offset]) 258 | rotate([0, positives[2]?-45:45, 0]) 259 | translate([-insetby, -clearance/2, -clearance/2]) 260 | cube([clearance, clearance, clearance]); 261 | } 262 | } 263 | 264 | module CutCube(size, insetby=0) { 265 | size = is_num(size) ? [size, size, size] : size; 266 | 267 | CutCorner(insetby, [0,0,0]) 268 | CutCorner(insetby, [0,0,size[2]]) 269 | CutCorner(insetby, [0,size[1],0]) 270 | CutCorner(insetby, [0,size[1],size[2]]) 271 | CutCorner(insetby, [size[0],0,0]) 272 | CutCorner(insetby, [size[0],0,size[2]]) 273 | CutCorner(insetby, [size[0],size[1],0]) 274 | CutCorner(insetby, [size[0],size[1],size[2]]) 275 | cube(size); 276 | } 277 | 278 | 279 | 280 | module Demo() { 281 | translate([0, -50, 0]) 282 | HollowCylinder(22, 20, 10); 283 | 284 | SmoothHole(5, 10, 2) 285 | SmoothCylinder(22, 10, 2); 286 | 287 | translate([30, -30, 0]) SmoothCube([10, 14, 18], 2); 288 | 289 | translate([50, 0, 0]) CutCube([15, 14, 18], 3); 290 | 291 | translate([30, 0, 0]) SmoothXYCube([10, 14, 18], 2); 292 | 293 | translate([57, -23, 0]) SmoothCylinder(5, 18, 2); 294 | 295 | translate([35, -60, 0]) SmoothHollowCube([22, 20, 14], 3, 2); 296 | 297 | translate([80, 0, 0]) 298 | ChamferHole(4, 10, 2, [10, 10, 0]) 299 | cube([20, 20, 10]); 300 | } 301 | 302 | 303 | Demo(); 304 | 305 | 306 | --------------------------------------------------------------------------------