├── .gitignore ├── images ├── customizer.png ├── animation_size.gif ├── render_options.png ├── animation_options.gif └── animation_top_plate.gif ├── .gitmodules ├── modules ├── constants.scad ├── gridfinity.scad ├── components.scad └── shapes.scad ├── README.md ├── LICENSE └── models.scad /.gitignore: -------------------------------------------------------------------------------- 1 | *.gcode 2 | *.stl 3 | -------------------------------------------------------------------------------- /images/customizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smkent/modular-drawers-gridfinity/HEAD/images/customizer.png -------------------------------------------------------------------------------- /images/animation_size.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smkent/modular-drawers-gridfinity/HEAD/images/animation_size.gif -------------------------------------------------------------------------------- /images/render_options.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smkent/modular-drawers-gridfinity/HEAD/images/render_options.png -------------------------------------------------------------------------------- /images/animation_options.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smkent/modular-drawers-gridfinity/HEAD/images/animation_options.gif -------------------------------------------------------------------------------- /images/animation_top_plate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/smkent/modular-drawers-gridfinity/HEAD/images/animation_top_plate.gif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Chamfers-for-OpenSCAD"] 2 | path = third_party/Chamfers-for-OpenSCAD 3 | url = https://github.com/SebiTimeWaster/Chamfers-for-OpenSCAD.git 4 | [submodule "gridfinity-rebuilt-openscad"] 5 | path = third_party/gridfinity-rebuilt-openscad 6 | url = https://github.com/kennetek/gridfinity-rebuilt-openscad 7 | -------------------------------------------------------------------------------- /modules/constants.scad: -------------------------------------------------------------------------------- 1 | // Base dimensions of each drawer housing unit, excluding connectors 2 | base_unit_width = 48; 3 | base_unit_height = 66; // 2 + (6*7)+ceil(h_base+bp_h_bot)+(2*housing_wall_thickness); 4 | base_unit_depth = 44; 5 | 6 | // Calculated width/height to add for each unit beyond the first 7 | extra_unit_width = base_unit_width; 8 | extra_unit_height = base_unit_height; 9 | 10 | // Wall thickness 11 | housing_wall_thickness = 5.0; 12 | drawer_wall_thickness = 3.0; 13 | back_wall_thickness = housing_wall_thickness * 0.6; 14 | 15 | // Outer chamfering 16 | chamfer_amount = 4.0; 17 | 18 | // Connector and channel configuration 19 | dovetail_size = housing_wall_thickness * 0.6; 20 | dovetail_depth_percent = 0.8; 21 | connector_support_tolerance = dovetail_size * 0.2; 22 | 23 | // Drawer handle base size 24 | drawer_handle_base_size = base_unit_height/2-2*housing_wall_thickness-drawer_wall_thickness; 25 | -------------------------------------------------------------------------------- /modules/gridfinity.scad: -------------------------------------------------------------------------------- 1 | include <../third_party/gridfinity-rebuilt-openscad/standard.scad>; 2 | use <../third_party/gridfinity-rebuilt-openscad/gridfinity-rebuilt-utility.scad>; 3 | use <../third_party/gridfinity-rebuilt-openscad/gridfinity-rebuilt-baseplate.scad>; 4 | 5 | use 6 | 7 | module gridfinity_baseplate_extruded_bottom(w, d, gf_type, extrude=true) { 8 | union() { 9 | translate([0,0,0*h_base]) 10 | gridfinityBaseplate(d, w, l_grid, 0, 0, gf_type, true, 0, 0, 0); 11 | if (extrude) { 12 | if (gf_type == 0) { 13 | translate([0,0,-bp_h_bot]) 14 | linear_extrude(height=bp_h_bot) 15 | projection(cut=true) 16 | gridfinityBaseplate(d, w, l_grid, 0, 0, gf_type, true, 0, 0, 0); 17 | } 18 | } 19 | } 20 | } 21 | 22 | module gridfinity_baseplate_cut(w, d, gf_type, base_fill, extrude=true, deep=true) { 23 | xx = d * l_grid - 0.5; 24 | yy = w * l_grid - 0.5; 25 | base_cut_extra_factor = (gf_type > 0 ? 0.5 : 0); 26 | no_magnet_thin = (gf_type == 0 && !deep); 27 | translate([0,0,no_magnet_thin ? h_base : 0]) 28 | difference() { 29 | rounded_rectangle(xx, yy, h_base * (no_magnet_thin ? 1 : 2), r_fo1 / 2); 30 | if (base_fill == 0) { 31 | translate([0, 0, -h_base * base_cut_extra_factor]) 32 | rounded_rectangle(xx, yy, h_base, r_fo1 / 2); 33 | } 34 | translate([0, 0, no_magnet_thin ? 0 : h_base]) 35 | gridfinity_baseplate_extruded_bottom(w, d, gf_type, extrude=extrude); 36 | if (base_fill == 3) { 37 | difference() { 38 | translate([0, 0, -h_base * (1 + base_cut_extra_factor)]) 39 | rounded_rectangle(xx, yy, h_base * 2, r_fo1 / 2); 40 | rotate([-90,-90,0]) 41 | wall_honeycomb(xx, yy, h_base * 2, partial=true); 42 | } 43 | } 44 | } 45 | } 46 | 47 | 48 | -------------------------------------------------------------------------------- /modules/components.scad: -------------------------------------------------------------------------------- 1 | include 2 | include 3 | 4 | module dovetail_cut(height) { 5 | dt_base_size = dovetail_size * 4; 6 | dt_base_depth = height * dovetail_depth_percent; 7 | rotate([0,90,0]) 8 | union() { 9 | translate([-dt_base_size,-dt_base_size/2,dt_base_depth-dt_base_size]) 10 | rotate([90,0,90]) 11 | prism(dt_base_size, dt_base_size, dt_base_size); 12 | translate([-dt_base_size,dt_base_size/2,dt_base_depth-dt_base_size/2]) 13 | rotate([90,0,0]) 14 | prism(dt_base_size, dt_base_size, dt_base_size); 15 | translate([-dt_base_size,dt_base_size/2,dt_base_depth+dt_base_size/2]) 16 | rotate([180,0,0]) 17 | prism(dt_base_size, dt_base_size, dt_base_size); 18 | } 19 | } 20 | 21 | 22 | module dovetail_connector(height, dt_scale=1.0, channel=false, cut=true) { 23 | // Create a dovetail connector 24 | dt_size = dovetail_size * dt_scale; 25 | dt_x_size = dt_size * (channel ? 0.75 : 0.75); 26 | difference() { 27 | rotate([90,0,90]) 28 | linear_extrude(height=height * (cut ? dovetail_depth_percent : 1.0)) 29 | polygon( 30 | points=[ 31 | [-dt_size, 0], 32 | [dt_size, 0], 33 | [dt_x_size * 2, dt_size], 34 | [-dt_x_size * 2, dt_size] 35 | ] 36 | ); 37 | if (cut) dovetail_cut(height); 38 | } 39 | } 40 | 41 | module dovetail_channel(height) { 42 | dovetail_connector(height, dt_scale=1.15, channel=true, cut=false); 43 | } 44 | 45 | module dovetail_connector_support(height, channel=true) { 46 | sw = housing_wall_thickness; 47 | sh = dovetail_size * 4.5 + (channel ? 4 : 2) * connector_support_tolerance - 3; 48 | rotate([0,90,0]) 49 | translate([housing_wall_thickness/2,0,height/2]) 50 | translate([-sw/2,-sh/2,-height/2]) 51 | chamferCube([ 52 | sw, 53 | sh, 54 | height 55 | ], 56 | [0,0,channel ? [1,0,0,1] : [0,1,1,0]], 57 | chamfer_amount * 0.25 58 | ); 59 | } 60 | 61 | module wall_diamonds(depth_units, wall_thickness, lip_increment) { 62 | ww = base_unit_depth - 2 * lip_increment; 63 | 64 | rotate([0,0,180]) 65 | for (unit = [0:depth_units - 1]) { 66 | translate([unit * base_unit_depth,0,0]) 67 | linear_extrude(height=wall_thickness) 68 | polygon( 69 | [ 70 | [0, 0], 71 | [ww * 0.5, -ww * 0.5], 72 | [ww, 0], 73 | [ww * 0.5, ww * 0.5], 74 | ] 75 | ); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /modules/shapes.scad: -------------------------------------------------------------------------------- 1 | use <../third_party/Chamfers-for-OpenSCAD/Chamfer.scad>; 2 | 3 | include 4 | 5 | 6 | module prism(l, w, h){ 7 | // https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Primitive_Solids 8 | polyhedron(points=[[0,0,0], [l,0,0], [l,w,0], [0,w,0], [0,w,h], [l,w,h]], faces=[[0,1,2,3],[5,4,3,2],[0,4,5,1],[0,3,4],[5,2,1]]); 9 | } 10 | 11 | module hex_grid(x, y, h, separation, hex_size, chamfer_amount, partial=true) { 12 | hex_width = hex_size * sin(360 / 6); 13 | slop = 0.05; 14 | 15 | module hex_grid_conditional_hexagon(x_offset, y_offset) { 16 | if( 17 | partial || ( 18 | x_offset > -x / 2 + (hex_width + separation) / 2 19 | && x_offset < x / 2 - (hex_width + separation) / 2 20 | && y_offset > -y / 2 + (hex_width + separation) / 2 21 | && y_offset < y / 2 - (hex_width + separation) / 2 22 | ) 23 | ) { 24 | translate([x_offset, y_offset, -slop]) 25 | rotate([0, 0, 30]) 26 | cylinder(h + 2 * slop, hex_size / 2, hex_size / 2, $fn=6); 27 | } 28 | } 29 | 30 | if (x >= 1.25 * hex_size && y >= 1.25 * hex_size) { 31 | difference() { 32 | union() { 33 | for (y_offset = [-y / (1.5 * hex_size) : 2 : y / (1.5 * hex_size)]) { 34 | for (x_offset = [-x / (1.5 * hex_size) : x / (1.5 * hex_size)]) { 35 | hex_grid_conditional_hexagon( 36 | x_offset * (hex_width + separation), 37 | y_offset * hex_width 38 | ); 39 | hex_grid_conditional_hexagon( 40 | x_offset * (hex_width + separation) + (hex_width + separation) / 2, 41 | (y_offset + 1) * hex_width 42 | ); 43 | } 44 | } 45 | } 46 | difference() { 47 | translate([0, 0,h/2]) 48 | cube([2*x,2*y,h+4*slop], true); 49 | translate([-x/2,-y/2,0]) 50 | chamferCube([x,y,h], [0,0,[1,1,1,1]], chamfer_amount-1); 51 | } 52 | } 53 | } 54 | } 55 | 56 | module wall_honeycomb(x, y, h, sideways=false, partial=true) { 57 | hex_size = 14.0; 58 | separation = 3.0; 59 | if (sideways) { 60 | rotate([90,90,90]) 61 | hex_grid(y, x, h, separation, hex_size, chamfer_amount, partial=partial); 62 | } else { 63 | rotate([90,0,90]) 64 | hex_grid(x, y, h, separation, hex_size, chamfer_amount, partial=partial); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gridfinity Modular Interlocking Drawers, OpenSCAD customizable 2 | 3 | This is an [OpenSCAD][openscad]-configurable parametric model for modular 4 | interlocking drawers with (optional) [Gridfinity][gridfinity] baseplates. 5 | 6 | ![Sizing examples](/images/animation_size.gif) 7 | ![Configuration examples](/images/animation_options.gif) 8 | 9 | An optional top plate / shelf model is included. 10 | 11 | ![Top plate examples](/images/animation_top_plate.gif) 12 | 13 | ## Why? 14 | 15 | There are a number of other Gridfinity-compatible modular drawer systems, such 16 | as [Modular Gridfinity Drawers][modular-gridfinity-drawers] and 17 | [Gridfinity Modular Workbench Drawers][gridfinity-modular-workbench-drawers]. 18 | 19 | However, I liked the interlocking look of the drawer housings on models like 20 | [Modular Drawers 2.0 by O3D][o3d-modular-drawers-2], [Modular Small Parts 21 | Drawers by Rich Dionne][modular-small-parts-drawers], and [Interlocking Small 22 | Parts Storage System by JamesThePrinter][interlocking-small-parts-storage], and 23 | [Spiralize/Vase Mode Modular Drawers by RetromanIE][vase-mode-drawers]. 24 | 25 | I wanted Gridfinity-compatible customizable drawers where the housings interlock 26 | directly without additional parts. In particular, I liked the chamfered look of 27 | O3D's modular drawers. 28 | 29 | ## Configurable Features 30 | 31 | * Arbitrary drawer sizes in Gridfinity increments 32 | * Gridfinity baseplates with or without magnets, or plain drawers 33 | * Wall, base, and back fill for both the drawer and housing 34 | * Drawer catch on housing 35 | * Drawer side height (or none at all) 36 | * Integrated drawer pull or screw holes for a separate pull 37 | 38 | ## Setup 39 | 40 | This model depends on additional submodules. 41 | 42 | To set everything up, first clone this repository. Then, in the repository 43 | directory, run: 44 | 45 | ```console 46 | git submodule init 47 | git submodule update 48 | ``` 49 | 50 | Now the repository's `models.scad` can be opened in OpenSCAD. 51 | 52 | ## Customizing 53 | 54 | The model options appear in OpenSCAD's Customizer: 55 | 56 | ![Customizer screenshot](/images/customizer.png) 57 | 58 | ## Model export 59 | 60 | When rendering your customized model to export for printing, select "Print 61 | Orientation" for "Render Position" within the top section, Rendering: 62 | 63 | ![Rendering options](/images/render_options.png) 64 | 65 | You may wish to enable the housing or drawer individually for printing. 66 | 67 | ## Attribution 68 | 69 | I used the code for [Grant Emsley's Customizable Bigger Mini Drawers 70 | Ultimate](https://www.printables.com/model/293400) as the starting point for 71 | this model, although the code has been completely rewritten. 72 | 73 | This model depends on these components: 74 | 75 | * [Chamfers for OpenSCAD][chamfers-for-openscad] 76 | * [Gridfinity Rebuilt in OpenSCAD][gridfinity-rebuilt-openscad] 77 | 78 | ## Licensing 79 | 80 | [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International (CC 81 | BY-NC-SA 4.0)][license] 82 | 83 | Third party components have their own licenses. 84 | 85 | 86 | [chamfers-for-openscad]: https://github.com/SebiTimeWaster/Chamfers-for-OpenSCAD 87 | [gridfinity-modular-workbench-drawers]: https://www.printables.com/model/282728-gridfinity-modular-workbench-drawers 88 | [gridfinity-rebuilt-openscad]: https://github.com/kennetek/gridfinity-rebuilt-openscad 89 | [gridfinity]: https://www.youtube.com/watch?v=ra_9zU-mnl8 90 | [interlocking-small-parts-storage]: https://www.printables.com/model/359558-interlocking-small-parts-storage-system 91 | [license]: https://creativecommons.org/licenses/by-nc-sa/4.0/ 92 | [modular-gridfinity-drawers]: https://www.printables.com/model/402298-modular-gridfinity-drawers 93 | [modular-small-parts-drawers]: https://www.printables.com/model/51468-modular-small-parts-drawers 94 | [o3d-modular-drawers-2]: https://www.thingiverse.com/thing:2539830 95 | [openscad]: https://openscad.org/ 96 | [vase-mode-drawers]: https://www.printables.com/model/1558-spiralizevase-mode-modular-drawers 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Attribution-NonCommercial-ShareAlike 4.0 International 2 | 3 | ======================================================================= 4 | 5 | Creative Commons Corporation ("Creative Commons") is not a law firm and 6 | does not provide legal services or legal advice. Distribution of 7 | Creative Commons public licenses does not create a lawyer-client or 8 | other relationship. Creative Commons makes its licenses and related 9 | information available on an "as-is" basis. Creative Commons gives no 10 | warranties regarding its licenses, any material licensed under their 11 | terms and conditions, or any related information. Creative Commons 12 | disclaims all liability for damages resulting from their use to the 13 | fullest extent possible. 14 | 15 | Using Creative Commons Public Licenses 16 | 17 | Creative Commons public licenses provide a standard set of terms and 18 | conditions that creators and other rights holders may use to share 19 | original works of authorship and other material subject to copyright 20 | and certain other rights specified in the public license below. The 21 | following considerations are for informational purposes only, are not 22 | exhaustive, and do not form part of our licenses. 23 | 24 | Considerations for licensors: Our public licenses are 25 | intended for use by those authorized to give the public 26 | permission to use material in ways otherwise restricted by 27 | copyright and certain other rights. Our licenses are 28 | irrevocable. Licensors should read and understand the terms 29 | and conditions of the license they choose before applying it. 30 | Licensors should also secure all rights necessary before 31 | applying our licenses so that the public can reuse the 32 | material as expected. Licensors should clearly mark any 33 | material not subject to the license. This includes other CC- 34 | licensed material, or material used under an exception or 35 | limitation to copyright. More considerations for licensors: 36 | wiki.creativecommons.org/Considerations_for_licensors 37 | 38 | Considerations for the public: By using one of our public 39 | licenses, a licensor grants the public permission to use the 40 | licensed material under specified terms and conditions. If 41 | the licensor's permission is not necessary for any reason--for 42 | example, because of any applicable exception or limitation to 43 | copyright--then that use is not regulated by the license. Our 44 | licenses grant only permissions under copyright and certain 45 | other rights that a licensor has authority to grant. Use of 46 | the licensed material may still be restricted for other 47 | reasons, including because others have copyright or other 48 | rights in the material. A licensor may make special requests, 49 | such as asking that all changes be marked or described. 50 | Although not required by our licenses, you are encouraged to 51 | respect those requests where reasonable. More considerations 52 | for the public: 53 | wiki.creativecommons.org/Considerations_for_licensees 54 | 55 | ======================================================================= 56 | 57 | Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International 58 | Public License 59 | 60 | By exercising the Licensed Rights (defined below), You accept and agree 61 | to be bound by the terms and conditions of this Creative Commons 62 | Attribution-NonCommercial-ShareAlike 4.0 International Public License 63 | ("Public License"). To the extent this Public License may be 64 | interpreted as a contract, You are granted the Licensed Rights in 65 | consideration of Your acceptance of these terms and conditions, and the 66 | Licensor grants You such rights in consideration of benefits the 67 | Licensor receives from making the Licensed Material available under 68 | these terms and conditions. 69 | 70 | 71 | Section 1 -- Definitions. 72 | 73 | a. Adapted Material means material subject to Copyright and Similar 74 | Rights that is derived from or based upon the Licensed Material 75 | and in which the Licensed Material is translated, altered, 76 | arranged, transformed, or otherwise modified in a manner requiring 77 | permission under the Copyright and Similar Rights held by the 78 | Licensor. For purposes of this Public License, where the Licensed 79 | Material is a musical work, performance, or sound recording, 80 | Adapted Material is always produced where the Licensed Material is 81 | synched in timed relation with a moving image. 82 | 83 | b. Adapter's License means the license You apply to Your Copyright 84 | and Similar Rights in Your contributions to Adapted Material in 85 | accordance with the terms and conditions of this Public License. 86 | 87 | c. BY-NC-SA Compatible License means a license listed at 88 | creativecommons.org/compatiblelicenses, approved by Creative 89 | Commons as essentially the equivalent of this Public License. 90 | 91 | d. Copyright and Similar Rights means copyright and/or similar rights 92 | closely related to copyright including, without limitation, 93 | performance, broadcast, sound recording, and Sui Generis Database 94 | Rights, without regard to how the rights are labeled or 95 | categorized. For purposes of this Public License, the rights 96 | specified in Section 2(b)(1)-(2) are not Copyright and Similar 97 | Rights. 98 | 99 | e. Effective Technological Measures means those measures that, in the 100 | absence of proper authority, may not be circumvented under laws 101 | fulfilling obligations under Article 11 of the WIPO Copyright 102 | Treaty adopted on December 20, 1996, and/or similar international 103 | agreements. 104 | 105 | f. Exceptions and Limitations means fair use, fair dealing, and/or 106 | any other exception or limitation to Copyright and Similar Rights 107 | that applies to Your use of the Licensed Material. 108 | 109 | g. License Elements means the license attributes listed in the name 110 | of a Creative Commons Public License. The License Elements of this 111 | Public License are Attribution, NonCommercial, and ShareAlike. 112 | 113 | h. Licensed Material means the artistic or literary work, database, 114 | or other material to which the Licensor applied this Public 115 | License. 116 | 117 | i. Licensed Rights means the rights granted to You subject to the 118 | terms and conditions of this Public License, which are limited to 119 | all Copyright and Similar Rights that apply to Your use of the 120 | Licensed Material and that the Licensor has authority to license. 121 | 122 | j. Licensor means the individual(s) or entity(ies) granting rights 123 | under this Public License. 124 | 125 | k. NonCommercial means not primarily intended for or directed towards 126 | commercial advantage or monetary compensation. For purposes of 127 | this Public License, the exchange of the Licensed Material for 128 | other material subject to Copyright and Similar Rights by digital 129 | file-sharing or similar means is NonCommercial provided there is 130 | no payment of monetary compensation in connection with the 131 | exchange. 132 | 133 | l. Share means to provide material to the public by any means or 134 | process that requires permission under the Licensed Rights, such 135 | as reproduction, public display, public performance, distribution, 136 | dissemination, communication, or importation, and to make material 137 | available to the public including in ways that members of the 138 | public may access the material from a place and at a time 139 | individually chosen by them. 140 | 141 | m. Sui Generis Database Rights means rights other than copyright 142 | resulting from Directive 96/9/EC of the European Parliament and of 143 | the Council of 11 March 1996 on the legal protection of databases, 144 | as amended and/or succeeded, as well as other essentially 145 | equivalent rights anywhere in the world. 146 | 147 | n. You means the individual or entity exercising the Licensed Rights 148 | under this Public License. Your has a corresponding meaning. 149 | 150 | 151 | Section 2 -- Scope. 152 | 153 | a. License grant. 154 | 155 | 1. Subject to the terms and conditions of this Public License, 156 | the Licensor hereby grants You a worldwide, royalty-free, 157 | non-sublicensable, non-exclusive, irrevocable license to 158 | exercise the Licensed Rights in the Licensed Material to: 159 | 160 | a. reproduce and Share the Licensed Material, in whole or 161 | in part, for NonCommercial purposes only; and 162 | 163 | b. produce, reproduce, and Share Adapted Material for 164 | NonCommercial purposes only. 165 | 166 | 2. Exceptions and Limitations. For the avoidance of doubt, where 167 | Exceptions and Limitations apply to Your use, this Public 168 | License does not apply, and You do not need to comply with 169 | its terms and conditions. 170 | 171 | 3. Term. The term of this Public License is specified in Section 172 | 6(a). 173 | 174 | 4. Media and formats; technical modifications allowed. The 175 | Licensor authorizes You to exercise the Licensed Rights in 176 | all media and formats whether now known or hereafter created, 177 | and to make technical modifications necessary to do so. The 178 | Licensor waives and/or agrees not to assert any right or 179 | authority to forbid You from making technical modifications 180 | necessary to exercise the Licensed Rights, including 181 | technical modifications necessary to circumvent Effective 182 | Technological Measures. For purposes of this Public License, 183 | simply making modifications authorized by this Section 2(a) 184 | (4) never produces Adapted Material. 185 | 186 | 5. Downstream recipients. 187 | 188 | a. Offer from the Licensor -- Licensed Material. Every 189 | recipient of the Licensed Material automatically 190 | receives an offer from the Licensor to exercise the 191 | Licensed Rights under the terms and conditions of this 192 | Public License. 193 | 194 | b. Additional offer from the Licensor -- Adapted Material. 195 | Every recipient of Adapted Material from You 196 | automatically receives an offer from the Licensor to 197 | exercise the Licensed Rights in the Adapted Material 198 | under the conditions of the Adapter's License You apply. 199 | 200 | c. No downstream restrictions. You may not offer or impose 201 | any additional or different terms or conditions on, or 202 | apply any Effective Technological Measures to, the 203 | Licensed Material if doing so restricts exercise of the 204 | Licensed Rights by any recipient of the Licensed 205 | Material. 206 | 207 | 6. No endorsement. Nothing in this Public License constitutes or 208 | may be construed as permission to assert or imply that You 209 | are, or that Your use of the Licensed Material is, connected 210 | with, or sponsored, endorsed, or granted official status by, 211 | the Licensor or others designated to receive attribution as 212 | provided in Section 3(a)(1)(A)(i). 213 | 214 | b. Other rights. 215 | 216 | 1. Moral rights, such as the right of integrity, are not 217 | licensed under this Public License, nor are publicity, 218 | privacy, and/or other similar personality rights; however, to 219 | the extent possible, the Licensor waives and/or agrees not to 220 | assert any such rights held by the Licensor to the limited 221 | extent necessary to allow You to exercise the Licensed 222 | Rights, but not otherwise. 223 | 224 | 2. Patent and trademark rights are not licensed under this 225 | Public License. 226 | 227 | 3. To the extent possible, the Licensor waives any right to 228 | collect royalties from You for the exercise of the Licensed 229 | Rights, whether directly or through a collecting society 230 | under any voluntary or waivable statutory or compulsory 231 | licensing scheme. In all other cases the Licensor expressly 232 | reserves any right to collect such royalties, including when 233 | the Licensed Material is used other than for NonCommercial 234 | purposes. 235 | 236 | 237 | Section 3 -- License Conditions. 238 | 239 | Your exercise of the Licensed Rights is expressly made subject to the 240 | following conditions. 241 | 242 | a. Attribution. 243 | 244 | 1. If You Share the Licensed Material (including in modified 245 | form), You must: 246 | 247 | a. retain the following if it is supplied by the Licensor 248 | with the Licensed Material: 249 | 250 | i. identification of the creator(s) of the Licensed 251 | Material and any others designated to receive 252 | attribution, in any reasonable manner requested by 253 | the Licensor (including by pseudonym if 254 | designated); 255 | 256 | ii. a copyright notice; 257 | 258 | iii. a notice that refers to this Public License; 259 | 260 | iv. a notice that refers to the disclaimer of 261 | warranties; 262 | 263 | v. a URI or hyperlink to the Licensed Material to the 264 | extent reasonably practicable; 265 | 266 | b. indicate if You modified the Licensed Material and 267 | retain an indication of any previous modifications; and 268 | 269 | c. indicate the Licensed Material is licensed under this 270 | Public License, and include the text of, or the URI or 271 | hyperlink to, this Public License. 272 | 273 | 2. You may satisfy the conditions in Section 3(a)(1) in any 274 | reasonable manner based on the medium, means, and context in 275 | which You Share the Licensed Material. For example, it may be 276 | reasonable to satisfy the conditions by providing a URI or 277 | hyperlink to a resource that includes the required 278 | information. 279 | 3. If requested by the Licensor, You must remove any of the 280 | information required by Section 3(a)(1)(A) to the extent 281 | reasonably practicable. 282 | 283 | b. ShareAlike. 284 | 285 | In addition to the conditions in Section 3(a), if You Share 286 | Adapted Material You produce, the following conditions also apply. 287 | 288 | 1. The Adapter's License You apply must be a Creative Commons 289 | license with the same License Elements, this version or 290 | later, or a BY-NC-SA Compatible License. 291 | 292 | 2. You must include the text of, or the URI or hyperlink to, the 293 | Adapter's License You apply. You may satisfy this condition 294 | in any reasonable manner based on the medium, means, and 295 | context in which You Share Adapted Material. 296 | 297 | 3. You may not offer or impose any additional or different terms 298 | or conditions on, or apply any Effective Technological 299 | Measures to, Adapted Material that restrict exercise of the 300 | rights granted under the Adapter's License You apply. 301 | 302 | 303 | Section 4 -- Sui Generis Database Rights. 304 | 305 | Where the Licensed Rights include Sui Generis Database Rights that 306 | apply to Your use of the Licensed Material: 307 | 308 | a. for the avoidance of doubt, Section 2(a)(1) grants You the right 309 | to extract, reuse, reproduce, and Share all or a substantial 310 | portion of the contents of the database for NonCommercial purposes 311 | only; 312 | 313 | b. if You include all or a substantial portion of the database 314 | contents in a database in which You have Sui Generis Database 315 | Rights, then the database in which You have Sui Generis Database 316 | Rights (but not its individual contents) is Adapted Material, 317 | including for purposes of Section 3(b); and 318 | 319 | c. You must comply with the conditions in Section 3(a) if You Share 320 | all or a substantial portion of the contents of the database. 321 | 322 | For the avoidance of doubt, this Section 4 supplements and does not 323 | replace Your obligations under this Public License where the Licensed 324 | Rights include other Copyright and Similar Rights. 325 | 326 | 327 | Section 5 -- Disclaimer of Warranties and Limitation of Liability. 328 | 329 | a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE 330 | EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS 331 | AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF 332 | ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, 333 | IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, 334 | WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR 335 | PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, 336 | ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT 337 | KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT 338 | ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. 339 | 340 | b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE 341 | TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, 342 | NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, 343 | INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, 344 | COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR 345 | USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN 346 | ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR 347 | DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR 348 | IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. 349 | 350 | c. The disclaimer of warranties and limitation of liability provided 351 | above shall be interpreted in a manner that, to the extent 352 | possible, most closely approximates an absolute disclaimer and 353 | waiver of all liability. 354 | 355 | 356 | Section 6 -- Term and Termination. 357 | 358 | a. This Public License applies for the term of the Copyright and 359 | Similar Rights licensed here. However, if You fail to comply with 360 | this Public License, then Your rights under this Public License 361 | terminate automatically. 362 | 363 | b. Where Your right to use the Licensed Material has terminated under 364 | Section 6(a), it reinstates: 365 | 366 | 1. automatically as of the date the violation is cured, provided 367 | it is cured within 30 days of Your discovery of the 368 | violation; or 369 | 370 | 2. upon express reinstatement by the Licensor. 371 | 372 | For the avoidance of doubt, this Section 6(b) does not affect any 373 | right the Licensor may have to seek remedies for Your violations 374 | of this Public License. 375 | 376 | c. For the avoidance of doubt, the Licensor may also offer the 377 | Licensed Material under separate terms or conditions or stop 378 | distributing the Licensed Material at any time; however, doing so 379 | will not terminate this Public License. 380 | 381 | d. Sections 1, 5, 6, 7, and 8 survive termination of this Public 382 | License. 383 | 384 | 385 | Section 7 -- Other Terms and Conditions. 386 | 387 | a. The Licensor shall not be bound by any additional or different 388 | terms or conditions communicated by You unless expressly agreed. 389 | 390 | b. Any arrangements, understandings, or agreements regarding the 391 | Licensed Material not stated herein are separate from and 392 | independent of the terms and conditions of this Public License. 393 | 394 | 395 | Section 8 -- Interpretation. 396 | 397 | a. For the avoidance of doubt, this Public License does not, and 398 | shall not be interpreted to, reduce, limit, restrict, or impose 399 | conditions on any use of the Licensed Material that could lawfully 400 | be made without permission under this Public License. 401 | 402 | b. To the extent possible, if any provision of this Public License is 403 | deemed unenforceable, it shall be automatically reformed to the 404 | minimum extent necessary to make it enforceable. If the provision 405 | cannot be reformed, it shall be severed from this Public License 406 | without affecting the enforceability of the remaining terms and 407 | conditions. 408 | 409 | c. No term or condition of this Public License will be waived and no 410 | failure to comply consented to unless expressly agreed to by the 411 | Licensor. 412 | 413 | d. Nothing in this Public License constitutes or may be interpreted 414 | as a limitation upon, or waiver of, any privileges and immunities 415 | that apply to the Licensor or You, including from the legal 416 | processes of any jurisdiction or authority. 417 | 418 | ======================================================================= 419 | 420 | Creative Commons is not a party to its public 421 | licenses. Notwithstanding, Creative Commons may elect to apply one of 422 | its public licenses to material it publishes and in those instances 423 | will be considered the “Licensor.” The text of the Creative Commons 424 | public licenses is dedicated to the public domain under the CC0 Public 425 | Domain Dedication. Except for the limited purpose of indicating that 426 | material is shared under a Creative Commons public license or as 427 | otherwise permitted by the Creative Commons policies published at 428 | creativecommons.org/policies, Creative Commons does not authorize the 429 | use of the trademark "Creative Commons" or any other trademark or logo 430 | of Creative Commons without its prior written consent including, 431 | without limitation, in connection with any unauthorized modifications 432 | to any of its public licenses or any other arrangements, 433 | understandings, or agreements concerning use of licensed material. For 434 | the avoidance of doubt, this paragraph does not form part of the 435 | public licenses. 436 | 437 | Creative Commons may be contacted at creativecommons.org. 438 | 439 | -------------------------------------------------------------------------------- /models.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Gridfinity Modular Interlocking Drawers 3 | 4 | Attribution: 5 | 6 | - This is a near-complete redesign of Grant Emsley's Customizable Bigger Mini Drawers Ultimate (https://www.printables.com/model/293400) 7 | - Grant's model is a redesign of Karkovski's Modular Storage - Bigger Mini Drawers Ultimate (https://www.thingiverse.com/thing:2420368) 8 | - Original design by zeropage (https://www.thingiverse.com/thing:1889761) 9 | */ 10 | 11 | use ; 12 | include ; 13 | 14 | include 15 | use 16 | use 17 | use 18 | 19 | /* [Rendering] */ 20 | // Select Print Orientation to render models for export. You may wish to enable only the housing or drawer using the options after this one to export them separately. 21 | Render_Position = 0 ; // [0: Print Orientation, 1: Drawer Closed, 2: Drawer Open, 3: Stacked] 22 | 23 | Create_Housing = true; 24 | Create_Drawer = true; 25 | Create_Top_or_Bottom_Plate = true; 26 | 27 | /* [Size] */ 28 | // 3 or greater is recommended. 2 may fit a Gridfinity base, drawer walls will automatically be removed. 1 is available but will not work properly for Gridfinity bases. 29 | Width_Units = 3; // [1:10] 30 | Depth_Units = 3; // [1:10] 31 | // In 6u Gridfinity height increments (e.g. 1.0 = 6u). 0.5 is available but is not tall enough for a Gridfinity bin. 32 | Height_Units = 1.0; // [0.5:0.5:4] 33 | 34 | /* [Housing Settings] */ 35 | Housing_Back_Wall_Fill = 3; // [-1: None, 0: Solid, 3: Honeycomb] 36 | Housing_Top_Fill = 0; // [0: Solid, 1: Diamond, 3: Honeycomb] 37 | Housing_Bottom_Fill = 0; // [0: Solid, 1: Diamond, 3: Honeycomb] 38 | Housing_Left_Wall_Fill = 0; // [0: Solid, 1: Diamond, 3: Honeycomb] 39 | Housing_Right_Wall_Fill = 0; // [0: Solid, 1: Diamond, 3: Honeycomb] 40 | 41 | // Drawer catch(es) are placed on the top inner housing near the front 42 | Enable_Drawer_Catch = true; 43 | 44 | /* [Drawer Settings] */ 45 | Drawer_Bottom_Type = 0; // [-1: None, 0: Gridfinity without magnets, 2: Gridfinity with magnets] 46 | 47 | // Applies if one of the Gridfinity options are selected for Drawer Bottom Type 48 | Gridfinity_Base_Fill = -1; // [-1: None, 0: Solid, 3: Honeycomb] 49 | 50 | Drawer_Back_Wall_Fill = 3; // [0: Solid, 3: Honeycomb] 51 | Drawer_Side_Wall_Fill = 3; // [0: Solid, 3: Honeycomb] 52 | // Reduce this value to reduce the drawer sides height 53 | Drawer_Sides_Height_Ratio = 0.55; // [0.0:0.05:1.0] 54 | 55 | Pull_Type = 3; // [-1: None, 0: Screw Holes, 1: Conical Top Pull, 2: Cup Top Pull, 3: Triangular Pull variant 1, 4: Triangular Pull variant 2] 56 | // Applies when Pull Type is set to one of the integrated pull types 57 | Enable_Drawer_Face_Behind_Pull = true; 58 | // In millimeters. Applies when Pull Type is set to Screw Holes 59 | Pull_Screw_Hole_Diameter = 3.0; // [1.0:0.1:15.0] 60 | // In millimeters. Set to 0 for a single hole instead of two. Applies when Pull Type is set to Screw Holes 61 | Pull_Screw_Hole_Separation = 10.0; // [0.0:0.5:50.0] 62 | 63 | /* [Top or Bottom Plate Settings] */ 64 | Plate_Model_Type = 1; // [0: Bottom Plate, 1: Top Plate] 65 | 66 | // Fills other than Solid are not recommended with Top Plate Gridfinity base types 67 | Plate_Fill = 0; // [0: Solid, 1: Diamond, 3: Honeycomb] 68 | 69 | // Applies when Model Type is set to Top Plate 70 | Top_Plate_Type = 1; // [0: Plate, 1: Shelf] 71 | Top_Plate_Base_Type = 0; // [-1: None, 0: Gridfinity without magnets, 2: Gridfinity with magnets] 72 | Enable_Top_Plate_Left_Side = true; 73 | Enable_Top_Plate_Right_Side = true; 74 | Enable_Top_Plate_Back = true; 75 | Top_Plate_Back_Wall_Fill = 0; // [0: Solid, 3: Honeycomb] 76 | 77 | /* [Advanced Housing Settings] */ 78 | // Reduce this to reduce the thickness of the wall faces 79 | Housing_Wall_Thickness_Ratio = 0.25; // [0.2:0.05:1.0] 80 | 81 | // Drawer catch sizing on the top interior of the housing 82 | Catch_Size = 2.5; 83 | // Drawer catch placement distance from the front of the housing 84 | Catch_Setback = 2; 85 | 86 | /* [Advanced Drawer Settings] */ 87 | // All units in mm unless otherwise specified. Changing these may make your boxes incompatible with other ones. 88 | 89 | // Make drawer this much smaller than the inner measurements of the housing on 4 sides. (Default 0.5mm) 90 | Drawer_Tolerance = 0.5; // [0:.1:1] 91 | 92 | // Options other than Solid may not print properly and are not recommended 93 | Drawer_Face_Fill = 0; // [0: Solid, 3: Honeycomb] 94 | 95 | // Reduce base thickness when possible 96 | Thinner_Base = true; 97 | 98 | /* [Compatibility-Altering Settings] */ 99 | // Changing any of these options will break the interlocking compatibility with other units. 100 | 101 | Add_Connectors = true; 102 | 103 | /* [Development Toggles] */ 104 | 105 | Preview_Full_Render = false; 106 | 107 | // Only generate housing cross-section to print for fit testing 108 | Housing_Cross_Section = false; 109 | 110 | module __end_customizer_options__() { } 111 | 112 | /* Computed values */ 113 | 114 | // Full housing exterior size 115 | housing_outer_width = base_unit_width + (Width_Units - 1)*extra_unit_width; 116 | housing_outer_height = base_unit_height + (Height_Units -1)*extra_unit_height; 117 | // Depth excluding handle 118 | housing_outer_depth = housing_wall_thickness + back_wall_thickness + base_unit_depth * Depth_Units + 2; 119 | 120 | // Full drawer exterior size 121 | drawer_outer_width = housing_outer_width - 2*housing_wall_thickness - 2*Drawer_Tolerance; 122 | drawer_outer_height = housing_outer_height - 2*housing_wall_thickness - 2*Drawer_Tolerance; 123 | drawer_outer_depth = housing_outer_depth - back_wall_thickness; 124 | 125 | // Full drawer interior size 126 | drawer_inner_width = drawer_outer_width - 2 * drawer_wall_thickness; 127 | drawer_inner_height = drawer_outer_height - 2 * drawer_wall_thickness; 128 | drawer_inner_depth = drawer_outer_depth - 2 * drawer_wall_thickness; 129 | 130 | // Full plate exterior size 131 | plate_outer_width = base_unit_width + (Width_Units - 1)*extra_unit_width; 132 | plate_outer_height = base_unit_height + (Height_Units -1)*extra_unit_height; 133 | plate_outer_depth = housing_wall_thickness + back_wall_thickness + base_unit_depth * Depth_Units + 2; 134 | 135 | 136 | module housing_back_opening(lip_increment) { 137 | translate([ 138 | housing_outer_depth-back_wall_thickness, 139 | lip_increment / 2 * housing_wall_thickness, 140 | lip_increment / 2 * housing_wall_thickness 141 | ]) 142 | chamferCube( 143 | [ 144 | back_wall_thickness, 145 | housing_outer_width-lip_increment*housing_wall_thickness, 146 | housing_outer_height-lip_increment*housing_wall_thickness 147 | ], 148 | [[1, 1, 1, 1], [0, 0, 0, 0], [0, 0, 0, 0]], chamfer_amount-0.5 149 | ); 150 | } 151 | 152 | module housing_back_honeycomb(lip_increment) { 153 | // Rear 154 | translate([ 155 | housing_outer_depth-back_wall_thickness, 156 | housing_outer_width/2, 157 | housing_outer_height/2, 158 | ]) 159 | wall_honeycomb( 160 | housing_outer_width-2*housing_wall_thickness-2*lip_increment, 161 | housing_outer_height-2*housing_wall_thickness-2*lip_increment, 162 | back_wall_thickness, 163 | sideways=true 164 | ); 165 | } 166 | 167 | module housing_top_diamond(lip_increment) { 168 | if (Width_Units > 1) for (unit = [1:Width_Units-1]) { 169 | translate([ 170 | housing_outer_depth - (housing_wall_thickness + lip_increment), 171 | base_unit_width * unit, 172 | housing_outer_height - housing_wall_thickness 173 | ]) 174 | wall_diamonds(Depth_Units, housing_wall_thickness, lip_increment); 175 | } 176 | } 177 | 178 | module housing_bottom_diamond(lip_increment) { 179 | if (Width_Units > 1) for (unit = [1:Width_Units-1]) { 180 | translate([ 181 | housing_outer_depth - (housing_wall_thickness + lip_increment), 182 | base_unit_width * unit, 183 | 0, 184 | ]) 185 | wall_diamonds(Depth_Units, housing_wall_thickness, lip_increment); 186 | } 187 | } 188 | 189 | module housing_left_wall_diamond(lip_increment) { 190 | if (Height_Units > 0.5) for (unit = [1:Height_Units*2-1]) { 191 | rotate([270,0,0]) 192 | translate([ 193 | housing_outer_depth - (housing_wall_thickness + lip_increment), 194 | -base_unit_height/2 * unit, 195 | (housing_outer_width - housing_wall_thickness) 196 | ]) 197 | wall_diamonds(Depth_Units, housing_wall_thickness, lip_increment); 198 | } 199 | } 200 | 201 | module housing_right_wall_diamond(lip_increment) { 202 | if (Height_Units > 0.5) for (unit = [1:Height_Units*2-1]) { 203 | rotate([270,0,0]) 204 | translate([ 205 | housing_outer_depth - (housing_wall_thickness + lip_increment), 206 | -base_unit_height/2 * unit, 207 | 0 208 | ]) 209 | wall_diamonds(Depth_Units, housing_wall_thickness, lip_increment); 210 | } 211 | } 212 | 213 | module housing_top_honeycomb(lip_increment) { 214 | translate([ 215 | housing_outer_depth/2, 216 | housing_outer_width/2, 217 | housing_outer_height, 218 | ]) 219 | rotate([0,90,0]) 220 | wall_honeycomb( 221 | housing_outer_width-2*housing_wall_thickness-2*lip_increment, 222 | housing_outer_depth-2*housing_wall_thickness-2*lip_increment, 223 | back_wall_thickness, 224 | sideways=true, 225 | partial=false 226 | ); 227 | } 228 | 229 | module housing_bottom_honeycomb(lip_increment) { 230 | translate([ 231 | housing_outer_depth/2, 232 | housing_outer_width/2, 233 | housing_wall_thickness, 234 | ]) 235 | rotate([0,90,0]) 236 | wall_honeycomb( 237 | housing_outer_width-2*housing_wall_thickness-2*lip_increment, 238 | housing_outer_depth-2*housing_wall_thickness-2*lip_increment, 239 | back_wall_thickness, 240 | sideways=true, 241 | partial=false 242 | ); 243 | } 244 | 245 | module housing_right_wall_honeycomb(lip_increment) { 246 | translate([ 247 | housing_outer_depth/2, 248 | 0, 249 | housing_outer_height/2, 250 | ]) 251 | rotate([0,0,90]) 252 | wall_honeycomb( 253 | housing_outer_depth-2*housing_wall_thickness-2*lip_increment, 254 | housing_outer_height-2*housing_wall_thickness-2*lip_increment, 255 | housing_wall_thickness, 256 | sideways=false, 257 | partial=false 258 | ); 259 | } 260 | 261 | module housing_left_wall_honeycomb(lip_increment) { 262 | translate([ 263 | housing_outer_depth/2, 264 | housing_outer_width - housing_wall_thickness, 265 | housing_outer_height/2, 266 | ]) 267 | rotate([0,0,90]) 268 | wall_honeycomb( 269 | housing_outer_depth-2*housing_wall_thickness-2*lip_increment, 270 | housing_outer_height-2*housing_wall_thickness-2*lip_increment, 271 | housing_wall_thickness, 272 | sideways=false, 273 | partial=false 274 | ); 275 | } 276 | 277 | module housing_box() { 278 | // thickness reduction factor 279 | trf = 2 * Housing_Wall_Thickness_Ratio; 280 | difference() { 281 | // Create box and remove the interior 282 | chamferCube( 283 | [ 284 | housing_outer_depth, 285 | housing_outer_width, 286 | housing_outer_height 287 | ], 288 | [[1, 1, 1, 1], 0, 0], 289 | chamfer_amount 290 | ); 291 | translate([-(back_wall_thickness - Drawer_Tolerance), housing_wall_thickness, housing_wall_thickness]) 292 | chamferCube([ 293 | housing_outer_depth, 294 | housing_outer_width - 2 * housing_wall_thickness, 295 | housing_outer_height - 2 * housing_wall_thickness 296 | ], [[1, 1, 1, 1], 0, 0], 297 | chamfer_amount - 0.5 298 | ); 299 | // Reduce interior wall thickness 300 | translate([ 301 | housing_wall_thickness * 2, 302 | housing_wall_thickness / 2 * trf, 303 | housing_wall_thickness / 2 * (Width_Units <= 1 ? (4 - trf) : trf) 304 | ]) 305 | chamferCube([ 306 | ( 307 | housing_outer_depth - (back_wall_thickness - Drawer_Tolerance) 308 | ) - housing_wall_thickness * 4, 309 | housing_outer_width - housing_wall_thickness * trf, 310 | housing_outer_height - housing_wall_thickness * (Width_Units <= 1 ? 2 : trf) 311 | ], [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 1]], 312 | chamfer_amount - 0.5 313 | ); 314 | // Render walls 315 | if (Housing_Back_Wall_Fill == -1) housing_back_opening(3); 316 | else if (Housing_Back_Wall_Fill == 3) housing_back_honeycomb(3); 317 | 318 | if (Housing_Top_Fill == 1) housing_top_diamond(2); 319 | else if (Housing_Top_Fill == 3) housing_top_honeycomb(3); 320 | 321 | if (Housing_Bottom_Fill == 1) housing_bottom_diamond(2); 322 | else if (Housing_Bottom_Fill == 3) housing_bottom_honeycomb(3); 323 | 324 | if (Housing_Left_Wall_Fill == 1) housing_left_wall_diamond(2); 325 | else if (Housing_Left_Wall_Fill == 3) housing_left_wall_honeycomb(3); 326 | 327 | if (Housing_Right_Wall_Fill == 1) housing_right_wall_diamond(2); 328 | else if (Housing_Right_Wall_Fill == 3) housing_right_wall_honeycomb(3); 329 | } 330 | } 331 | 332 | module housing_connector_add() { 333 | // Top 334 | for (unit = [1:Width_Units]) { 335 | translate([0,(unit-1)*extra_unit_width+base_unit_width/2,housing_outer_height]) 336 | union() { 337 | dovetail_connector(housing_outer_depth); 338 | dovetail_connector_support(housing_outer_depth, channel=false); 339 | } 340 | } 341 | // Right 342 | for (unit = [0:0.5:Height_Units-0.5]) { 343 | rotate([90,0,0]) 344 | translate([0,unit*extra_unit_height+base_unit_height/4,0]) 345 | union() { 346 | dovetail_connector(housing_outer_depth); 347 | dovetail_connector_support(housing_outer_depth, channel=false); 348 | } 349 | } 350 | // Left 351 | for (unit = [0:0.5:Height_Units-0.5]) { 352 | rotate([90,0,0]) 353 | translate([ 354 | 0, 355 | unit*extra_unit_height+base_unit_height/4, 356 | -housing_outer_width+housing_wall_thickness 357 | ]) 358 | dovetail_connector_support(housing_outer_depth, channel=true); 359 | } 360 | // Bottom 361 | for(unit = [1:Width_Units]) { 362 | translate([0, (unit-1)*extra_unit_width, housing_wall_thickness]) 363 | translate([0, base_unit_width/2, 0]) 364 | dovetail_connector_support(housing_outer_depth, channel=true); 365 | } 366 | } 367 | 368 | module housing_connector_subtract() { 369 | // Left 370 | for (unit = [0:0.5:Height_Units-0.5]) { 371 | rotate([90,0,0]) 372 | translate([ 373 | 0, 374 | unit*extra_unit_height+base_unit_height/4, 375 | -housing_outer_width 376 | ]) 377 | dovetail_channel(housing_outer_depth); 378 | } 379 | // Bottom 380 | for(unit = [1:Width_Units]) { 381 | translate([0,(unit-1)*extra_unit_width,0]) 382 | translate([0,base_unit_width/2]) 383 | dovetail_channel(housing_outer_depth); 384 | } 385 | } 386 | 387 | module housing() { 388 | union() { 389 | difference() { 390 | union() { 391 | housing_box(); 392 | if (Add_Connectors) { 393 | housing_connector_add(); 394 | } 395 | } 396 | if (Add_Connectors) { 397 | housing_connector_subtract(); 398 | } 399 | } 400 | if (Enable_Drawer_Catch) { 401 | // Create drawer catches 402 | for (unit = [1:Width_Units]) { 403 | translate([Catch_Setback,(unit-1)*extra_unit_width,housing_outer_height-housing_wall_thickness]) drawer_catch(); 404 | } 405 | } 406 | } 407 | } 408 | 409 | module housing_render() { 410 | color("aliceblue") 411 | if (Preview_Full_Render) { 412 | render() 413 | housing(); 414 | } else { 415 | housing(); 416 | } 417 | } 418 | 419 | module drawer_catch() { 420 | cw = dovetail_size * 3; 421 | cs = Catch_Size; 422 | translate([cs,base_unit_width / 2 + cw/2,-cs]) 423 | rotate([90,0,0]) 424 | linear_extrude(height=cw) 425 | polygon( 426 | points=[ 427 | [-cs/4, 0], 428 | [cs/4, 0], 429 | [cs, cs], 430 | [-cs, cs] 431 | ] 432 | ); 433 | } 434 | 435 | module drawer_front_honeycomb(lip_increment) { 436 | // Front 437 | translate([ 438 | 0, 439 | drawer_outer_width/2, 440 | drawer_outer_height/2, 441 | ]) 442 | wall_honeycomb( 443 | drawer_inner_width-2*lip_increment, 444 | drawer_inner_height-2*lip_increment, 445 | drawer_wall_thickness, 446 | sideways=true, 447 | partial=false 448 | ); 449 | } 450 | 451 | module drawer_back_honeycomb(lip_increment) { 452 | // Back 453 | translate([ 454 | drawer_outer_depth - drawer_wall_thickness, 455 | drawer_outer_width/2, 456 | drawer_outer_height/2, 457 | ]) 458 | wall_honeycomb( 459 | drawer_inner_width-2*lip_increment, 460 | drawer_inner_height-2*lip_increment, 461 | drawer_wall_thickness, 462 | sideways=true, 463 | partial=false 464 | ); 465 | } 466 | 467 | module drawer_side_honeycomb(lip_increment, height_ratio=1.0) { 468 | // Left 469 | translate([ 470 | drawer_outer_depth/2, 471 | drawer_outer_width - drawer_wall_thickness, 472 | drawer_outer_height/2 * height_ratio, 473 | ]) 474 | rotate([0,0,90]) 475 | wall_honeycomb( 476 | drawer_inner_depth-2*lip_increment, 477 | (drawer_inner_height-2*lip_increment) * height_ratio, 478 | drawer_wall_thickness, 479 | sideways=true, 480 | partial=false 481 | ); 482 | 483 | // Right 484 | translate([ 485 | drawer_outer_depth/2, 486 | 0, 487 | drawer_outer_height/2 * height_ratio, 488 | ]) 489 | rotate([0,0,90]) 490 | wall_honeycomb( 491 | drawer_inner_depth-2*lip_increment, 492 | (drawer_inner_height-2*lip_increment) * height_ratio, 493 | drawer_wall_thickness, 494 | sideways=true, 495 | partial=false 496 | ); 497 | } 498 | 499 | module drawer_box(base_height_reduction=0) { 500 | height_ratio_auto_min = (Drawer_Bottom_Type == 0 ? Thinner_Base ? 0.02 : 0.055 : 0.1); 501 | height_ratio = (Drawer_Bottom_Type >= 0 && Width_Units <= 2) ? height_ratio_auto_min : Drawer_Sides_Height_Ratio; 502 | if (Drawer_Sides_Height_Ratio > 0 && Drawer_Bottom_Type >= 0 && Width_Units <= 2) { 503 | echo("Automatically removing drawer sides with this configuration"); 504 | } 505 | difference() { 506 | chamferCube([drawer_outer_depth, drawer_outer_width, drawer_outer_height], [[1, 1, 1, 1], 0, 0], chamfer_amount-0.5); 507 | translate([ 508 | drawer_wall_thickness, 509 | drawer_wall_thickness, 510 | ( 511 | Drawer_Bottom_Type >= 0 512 | ? ( 513 | Drawer_Bottom_Type >= 1 ? h_base * 2 : h_base * 2 514 | ) - 0.7 515 | : drawer_wall_thickness 516 | ) - base_height_reduction 517 | ]) 518 | chamferCube([ 519 | drawer_outer_depth - 2 * drawer_wall_thickness, 520 | drawer_outer_width - 2 * drawer_wall_thickness, 521 | drawer_outer_height 522 | ], 523 | [(Drawer_Bottom_Type >= 0) ? 0 : [1, 1, 0, 0], 0, 0], 524 | chamfer_amount - 0.5 * 2 525 | ); 526 | // Reduce or remove drawer sides 527 | translate([0, 0, drawer_outer_height * height_ratio + 2 * drawer_wall_thickness]) 528 | union() { 529 | translate([drawer_wall_thickness, 0, 0]) 530 | cube([ 531 | drawer_outer_depth - 2 * drawer_wall_thickness, 532 | drawer_wall_thickness, 533 | ( 534 | drawer_outer_height - drawer_wall_thickness * 2 535 | ) * (1 - height_ratio) 536 | ]); 537 | translate([drawer_wall_thickness, drawer_outer_width - drawer_wall_thickness, 0]) 538 | cube([ 539 | drawer_outer_depth - 2 * drawer_wall_thickness, 540 | drawer_wall_thickness, 541 | ( 542 | drawer_outer_height - drawer_wall_thickness * 2 543 | ) * (1 - height_ratio) 544 | ]); 545 | } 546 | // Remove drawer pull interior 547 | if (Pull_Type >= 1) { 548 | if (!Enable_Drawer_Face_Behind_Pull) { 549 | translate([drawer_wall_thickness,drawer_outer_width*0.5,drawer_outer_height+drawer_wall_thickness]) 550 | rotate([270,0,90]) 551 | linear_extrude(height=drawer_wall_thickness) 552 | drawer_pull_handle_interior(); 553 | } 554 | } else if (Pull_Type == 0) { 555 | drawer_pull_screw_holes(diameter=Pull_Screw_Hole_Diameter, separation=Pull_Screw_Hole_Separation); 556 | } 557 | // Apply back and wall fills 558 | if (Drawer_Face_Fill == 3 && Height_Units > 0.5) { 559 | drawer_front_honeycomb(1); 560 | } 561 | if (Drawer_Back_Wall_Fill == 3 && Height_Units > 0.5) { 562 | drawer_back_honeycomb(1); 563 | } 564 | if (Drawer_Side_Wall_Fill == 3 && Height_Units > 0.5) { 565 | drawer_side_honeycomb(1, height_ratio); 566 | } 567 | } 568 | } 569 | 570 | module drawer_pull_handle_shape_triangular_pull(width_units, scale_factor, variant=1) { 571 | module half_circle_cylinder(size) { 572 | linear_extrude(height=size) 573 | difference() { 574 | circle(size, $fn=20); 575 | translate([-size, -size]) 576 | square([size*2, size]); 577 | } 578 | } 579 | 580 | base_height = drawer_handle_base_size * scale_factor; 581 | 582 | width_offset = base_height / 2 + (width_units - 1) * (base_unit_width / 8); 583 | angle_offset = 12 / (3 - scale_factor); 584 | height = base_height * 0.75; 585 | depth = base_height / 2; 586 | cyl_point_size = 2.0; 587 | 588 | translate([0, 0, -(height)]) 589 | rotate([90,0,0]) 590 | hull() 591 | union() { 592 | for(x = [0:1:1]) { 593 | mirror([x,0,0]) 594 | translate([width_units * 2, 0, 0]) 595 | rotate([90,0,0]) 596 | union() { 597 | translate([width_offset - angle_offset, depth, -height]) 598 | half_circle_cylinder(cyl_point_size); 599 | translate([width_offset, 0, -height]) 600 | half_circle_cylinder(cyl_point_size); 601 | translate([variant == 1 ? width_offset / 2 : width_offset, 0, 0]) 602 | half_circle_cylinder(cyl_point_size); 603 | } 604 | } 605 | } 606 | } 607 | 608 | module drawer_pull_handle_shape_cup_pull(width_units, scale_factor) { 609 | base_height = drawer_handle_base_size * 1.4 * scale_factor; 610 | hw = base_height; 611 | width_offset = max(0, (width_units - 2)) * (base_unit_width / 4); 612 | module circle_quarter(size) { 613 | intersection() { 614 | circle(d=size); 615 | square(size); 616 | } 617 | } 618 | 619 | module sphere_eighth(size) { 620 | rotate_extrude(angle=90, $fn=100) 621 | circle_quarter(size); 622 | } 623 | 624 | union() { 625 | translate([width_offset, 0, 0]) 626 | rotate([90,90,0]) 627 | sphere_eighth(hw); 628 | 629 | translate([width_offset, 0, 0]) 630 | rotate([180,90,0]) 631 | linear_extrude(height=width_offset* 2, $fn=100) 632 | circle_quarter(hw); 633 | 634 | translate([-width_offset, 0, 0]) 635 | rotate([90,90,270]) 636 | sphere_eighth(hw); 637 | } 638 | } 639 | 640 | module drawer_pull_handle_shape_conical_pull(width_units, scale_factor) { 641 | base_height = drawer_handle_base_size * 1.4 * scale_factor; 642 | hw = base_height *0.65; 643 | width_offset = max(0, (width_units - 2)) * (base_unit_width / 4); 644 | hh = base_height / 2; 645 | rotate([0,180,0]) 646 | difference() { 647 | union() { 648 | translate([-width_offset,0,0]) 649 | cylinder(hw, hh, $fn=100); 650 | 651 | translate([width_offset,0,0]) 652 | cylinder(hw, hh, $fn=100); 653 | 654 | translate([width_offset,0,0]) 655 | rotate([-90,0,90]) 656 | linear_extrude(height=2 * width_offset) 657 | projection() 658 | rotate([90,0,0]) 659 | cylinder(hw, hh); 660 | } 661 | translate([-hw * 2 * Width_Units, 0, -1]) 662 | cube([hw * 4 * Width_Units, hh, hw * 1.1]); 663 | } 664 | } 665 | 666 | module drawer_pull_handle_shape() { 667 | scale_factor = (Height_Units <= 0.5 || Width_Units <= 1 ? 1 : 2); 668 | if (Pull_Type == 1) { 669 | drawer_pull_handle_shape_conical_pull(Width_Units, scale_factor); 670 | } else if (Pull_Type == 2) { 671 | drawer_pull_handle_shape_cup_pull(Width_Units, scale_factor); 672 | } else if (Pull_Type == 3) { 673 | drawer_pull_handle_shape_triangular_pull(Width_Units, scale_factor); 674 | } else if (Pull_Type == 4) { 675 | drawer_pull_handle_shape_triangular_pull(Width_Units, scale_factor, variant=2); 676 | } 677 | } 678 | 679 | module drawer_pull_handle_interior() { 680 | projection(cut=true) 681 | translate([0,0,drawer_wall_thickness]) 682 | rotate([90,0,0]) 683 | drawer_pull_handle_shape(); 684 | } 685 | 686 | module drawer_pull_handle() { 687 | cut_scale_adjust = (Pull_Type >= 2) ? (1.0 - 0.03 / Width_Units * 3) : 1.0; 688 | cut_height_adjust = (Pull_Type >= 2) ? 1.5 : 1.0; 689 | translate([0, drawer_wall_thickness, 0]) 690 | difference() { 691 | drawer_pull_handle_shape(); 692 | scale([cut_scale_adjust, 1.0, 1.0]) 693 | translate([0, drawer_handle_base_size / 18, drawer_wall_thickness * cut_height_adjust]) 694 | drawer_pull_handle_shape(); 695 | } 696 | } 697 | 698 | module drawer_handle() { 699 | if (Pull_Type >= 1) { 700 | translate([0,drawer_outer_width*0.5,drawer_outer_height]) 701 | rotate([0,0,270]) 702 | drawer_pull_handle(); 703 | } 704 | } 705 | 706 | module drawer_pull_screw_holes(diameter=3.0, separation=10.0) { 707 | for(i = [-1:2:1]) { 708 | translate([0, drawer_outer_width / 2 + i * separation / 2, drawer_outer_height / 2]) 709 | rotate([90,0,90]) 710 | linear_extrude(height=drawer_wall_thickness) 711 | circle(d=diameter, $fn=20); 712 | } 713 | } 714 | 715 | module drawer_gridfinity_baseplate_cut() { 716 | if (Drawer_Bottom_Type >= 0) { 717 | translate([drawer_inner_depth/2 + drawer_wall_thickness, drawer_inner_width/2 + drawer_wall_thickness, 0]) 718 | gridfinity_baseplate_cut(Width_Units, Depth_Units, Drawer_Bottom_Type, Gridfinity_Base_Fill, extrude=(Drawer_Bottom_Type == 0), deep=true); 719 | } 720 | } 721 | 722 | module drawer() { 723 | base_height_reduction = (Thinner_Base == true 724 | ? (Drawer_Bottom_Type == 0 725 | ? h_base - (Width_Units <= 1 ? 0.6 : drawer_wall_thickness) 726 | : 0) 727 | : 0); 728 | union() { 729 | difference() { 730 | union() { 731 | drawer_box(base_height_reduction); 732 | drawer_handle(); 733 | } 734 | translate([0, 0, -base_height_reduction]) 735 | drawer_gridfinity_baseplate_cut(); 736 | } 737 | } 738 | } 739 | 740 | module drawer_render() { 741 | color("darkseagreen") 742 | if (Preview_Full_Render) { 743 | render() 744 | drawer(); 745 | } else { 746 | drawer(); 747 | } 748 | } 749 | 750 | module plate_back_honeycomb(lip_increment) { 751 | // Rear 752 | translate([ 753 | 0, 754 | plate_outer_width/2, 755 | plate_outer_height/2, 756 | ]) 757 | wall_honeycomb( 758 | plate_outer_width-2*housing_wall_thickness-2*lip_increment, 759 | plate_outer_height-2*housing_wall_thickness-2*lip_increment, 760 | back_wall_thickness, 761 | sideways=true 762 | ); 763 | } 764 | 765 | 766 | module plate_bottom_diamond(lip_increment) { 767 | if (Width_Units > 1) for (unit = [1:Width_Units-1]) { 768 | translate([ 769 | plate_outer_depth - (housing_wall_thickness + lip_increment), 770 | base_unit_width * unit, 771 | 0, 772 | ]) 773 | wall_diamonds(Depth_Units, housing_wall_thickness, lip_increment); 774 | } 775 | } 776 | 777 | module plate_bottom_honeycomb(lip_increment) { 778 | translate([ 779 | plate_outer_depth/2, 780 | plate_outer_width/2, 781 | housing_wall_thickness, 782 | ]) 783 | rotate([0,90,0]) 784 | wall_honeycomb( 785 | plate_outer_width-2*housing_wall_thickness-2*lip_increment, 786 | plate_outer_depth-2*housing_wall_thickness-2*lip_increment, 787 | housing_wall_thickness, 788 | sideways=true, 789 | partial=false 790 | ); 791 | } 792 | 793 | module baseplate_bottom_box() { 794 | render() 795 | difference() { 796 | translate([0,0,-housing_wall_thickness]) 797 | chamferCube( 798 | [ 799 | plate_outer_depth, 800 | plate_outer_width, 801 | housing_wall_thickness * 2, 802 | ], 803 | [[1, 1, 1, 1], 0, 0], 804 | chamfer_amount 805 | ); 806 | translate([0,0,-housing_wall_thickness]) 807 | cube([ 808 | plate_outer_depth, 809 | plate_outer_width, 810 | housing_wall_thickness 811 | ]); 812 | } 813 | } 814 | 815 | module baseplate_top_box() { 816 | enable_back = Top_Plate_Type == 1 && Enable_Top_Plate_Back; 817 | enabled_side_count = (Enable_Top_Plate_Left_Side ? 1 : 0) + (Enable_Top_Plate_Right_Side ? 1 : 0); 818 | enable_sides = Top_Plate_Type == 1 && enabled_side_count; 819 | render() 820 | difference() { 821 | chamferCube( 822 | [ 823 | plate_outer_depth, 824 | plate_outer_width, 825 | plate_outer_height, 826 | ], 827 | [[1, 1, 1, 1], 0, 0], 828 | chamfer_amount 829 | ); 830 | translate([ 831 | enable_back ? back_wall_thickness : 0, 832 | Enable_Top_Plate_Left_Side ? housing_wall_thickness : 0, 833 | housing_wall_thickness + ( 834 | Top_Plate_Base_Type >= 0 835 | ? (Top_Plate_Base_Type == 0 ? h_base : h_base * 1.5) 836 | : 0 837 | ) 838 | ]) 839 | chamferCube( 840 | [ 841 | plate_outer_depth + (enable_back ? 0 : back_wall_thickness * 2), 842 | plate_outer_width - housing_wall_thickness * enabled_side_count, 843 | plate_outer_height, 844 | ], 845 | [ 846 | Top_Plate_Base_Type < 0 847 | ? [Enable_Top_Plate_Left_Side ? 1 : 0, Enable_Top_Plate_Right_Side ? 1 : 0, 0, 0] 848 | : 0, 849 | (enable_back && Top_Plate_Base_Type < 0) ? [1, 0, 0, 0] : 0, 850 | 0 851 | ], 852 | chamfer_amount 853 | ); 854 | } 855 | } 856 | 857 | module baseplate_shelf_walls_cut() { 858 | for (side = [0:1:1]) { 859 | sz = ( 860 | min(Height_Units * base_unit_height, Depth_Units * base_unit_depth) 861 | - housing_wall_thickness - h_base * 1.5 862 | ); 863 | rotate([90,180,0]) 864 | translate([ 865 | -plate_outer_depth, 866 | -plate_outer_height, 867 | -(side ? housing_wall_thickness : plate_outer_width) 868 | ]) 869 | linear_extrude(height=housing_wall_thickness) 870 | polygon([ 871 | [0, 0], 872 | [0, sz], 873 | [sz, 0], 874 | ]); 875 | } 876 | } 877 | 878 | module plate_gridfinity_baseplate_cut(gf_type) { 879 | translate([ 880 | plate_outer_depth / 2, 881 | plate_outer_width / 2, 882 | Top_Plate_Base_Type > 0 ? h_base / 2 : 0 883 | ]) 884 | difference() { 885 | gridfinity_baseplate_cut(Width_Units, Depth_Units, gf_type, extrude=false, deep=false); 886 | translate([0,0,-h_base*.5]) 887 | cube([plate_outer_depth, plate_outer_width, h_base * 2], center=true); 888 | } 889 | } 890 | 891 | module plate_box() { 892 | difference() { 893 | // Create baseplate 894 | if (Plate_Model_Type == 0) baseplate_bottom_box(); else baseplate_top_box(); 895 | // Create Gridfinity baseplate 896 | if (Plate_Model_Type == 1 && Top_Plate_Base_Type >= 0) plate_gridfinity_baseplate_cut(Top_Plate_Base_Type); 897 | // Render walls 898 | 899 | if (Plate_Fill == 1) plate_bottom_diamond(2); 900 | else if (Plate_Fill == 3) plate_bottom_honeycomb(3); 901 | 902 | if (Plate_Model_Type == 1 && Enable_Top_Plate_Back) { 903 | if (Top_Plate_Back_Wall_Fill == 3) plate_back_honeycomb(3); 904 | } 905 | } 906 | } 907 | 908 | module plate_connector_add() { 909 | if (Plate_Model_Type == 1) { 910 | // Top plate dovetail channel supports 911 | for(unit = [1:Width_Units]) { 912 | translate([0, (unit-1)*extra_unit_width, housing_wall_thickness]) 913 | translate([0, base_unit_width/2, 0]) 914 | dovetail_connector_support(plate_outer_depth, channel=true); 915 | } 916 | } else { 917 | // Bottom plate dovetail connectors 918 | for (unit = [1:Width_Units]) { 919 | translate([ 920 | plate_outer_depth, 921 | (unit - 1) * extra_unit_width+base_unit_width / 2, 922 | housing_wall_thickness 923 | ]) 924 | rotate([0,0,180]) 925 | union() { 926 | dovetail_connector(plate_outer_depth); 927 | dovetail_connector_support(plate_outer_depth, channel=false); 928 | } 929 | } 930 | } 931 | } 932 | 933 | module plate_connector_subtract() { 934 | if (Plate_Model_Type == 1) { 935 | // Top plate dovetail channels 936 | for(unit = [1:Width_Units]) { 937 | translate([0,(unit-1)*extra_unit_width,0]) 938 | translate([0,base_unit_width/2]) 939 | dovetail_channel(plate_outer_depth); 940 | } 941 | } 942 | } 943 | 944 | module plate() { 945 | union() { 946 | difference() { 947 | union() { 948 | plate_box(); 949 | if (Add_Connectors) { 950 | plate_connector_add(); 951 | } 952 | } 953 | if (Add_Connectors) { 954 | plate_connector_subtract(); 955 | } 956 | baseplate_shelf_walls_cut(); 957 | } 958 | } 959 | } 960 | 961 | module plate_render() { 962 | color("powderblue") 963 | if (Preview_Full_Render) { 964 | render() 965 | plate(); 966 | } else { 967 | plate(); 968 | } 969 | } 970 | 971 | module housing_cross_section(slice_depth=1) { 972 | translate([ 973 | 0, -housing_wall_thickness, -housing_wall_thickness 974 | ]) 975 | difference() { 976 | cube([ 977 | housing_outer_depth + 2 * housing_wall_thickness, 978 | housing_outer_width + 2 * housing_wall_thickness, 979 | housing_outer_height + 2 * housing_wall_thickness 980 | ]); 981 | translate([ 982 | Catch_Size / 2, 0, 0 983 | ]) 984 | cube([ 985 | slice_depth, 986 | housing_outer_width + 2 * housing_wall_thickness, 987 | housing_outer_height + 2 * housing_wall_thickness 988 | ]); 989 | } 990 | } 991 | 992 | module main_render() { 993 | if (Render_Position == 0) { 994 | // Print Orientation 995 | if (Create_Drawer) { 996 | translate([drawer_outer_width / 2, -housing_outer_depth, 0]) 997 | rotate([0,0,90]) 998 | drawer_render(); 999 | } 1000 | if (Create_Housing) { 1001 | translate([housing_outer_width/2, 0, housing_outer_depth]) 1002 | rotate([0,90,90]) 1003 | housing_render(); 1004 | } 1005 | if (Create_Top_or_Bottom_Plate) { 1006 | rotate([0,0, !(Create_Housing || Create_Drawer) ? 180 : 0]) 1007 | translate([plate_outer_width/2, (Create_Housing ? housing_outer_height + 5 : 0), 0]) 1008 | rotate([0,0,90]) 1009 | plate_render(); 1010 | } 1011 | } else { 1012 | rotate([0,0,90]) 1013 | union() { 1014 | if (Create_Housing) { 1015 | translate([0, -housing_outer_width / 2, 0]) 1016 | housing_render(); 1017 | } 1018 | if (Create_Drawer) { 1019 | translate([ 1020 | (Render_Position == 2 1021 | ? (Catch_Setback + 5 * Catch_Size) - housing_outer_depth 1022 | : 0), 1023 | -drawer_outer_width / 2, 1024 | ( 1025 | -(drawer_outer_height - housing_outer_height) / 2 1026 | + (Render_Position == 3 1027 | ? housing_outer_height 1028 | : 0) 1029 | ) 1030 | ]) 1031 | drawer_render(); 1032 | } 1033 | if (Create_Top_or_Bottom_Plate) { 1034 | translate([ 1035 | plate_outer_depth, 1036 | plate_outer_width / 2, 1037 | Render_Position == 3 1038 | ? -(5 + plate_outer_height) 1039 | : housing_outer_height + 5 1040 | ]) 1041 | rotate([0,0,180]) 1042 | plate_render(); 1043 | } 1044 | } 1045 | } 1046 | } 1047 | 1048 | if (Housing_Cross_Section) { 1049 | slice_depth = Catch_Setback + 1.8 * Catch_Size; 1050 | translate([housing_outer_width/2, 0, slice_depth]) 1051 | rotate([0,90,90]) 1052 | difference() { 1053 | housing(); 1054 | if (Housing_Cross_Section) housing_cross_section(slice_depth=slice_depth); 1055 | } 1056 | } else { 1057 | main_render(); 1058 | } 1059 | --------------------------------------------------------------------------------