├── .devcontainer ├── devcontainer.json └── dockerfile ├── .gitignore ├── LICENSE ├── README.md ├── TODO.md ├── assembly-guide ├── README.md └── gifs │ ├── addMagnetsToMagnetModules.gif │ ├── addMagnetsToSideWall.gif │ ├── attachSideConnectorModulesToYBars.gif │ ├── attachXBarWithYBar.gif │ ├── attachXYPlates.gif │ ├── attachXYTrays.gif │ ├── connectXYTrayWithMainRails.gif │ ├── insertDowelsIntoSideWall.gif │ ├── insertFeet.gif │ ├── propUpBottomXYTraywithSideWalls.gif │ ├── screwFeet.gif │ ├── screwXBarAndYBar.gif │ ├── slideHexNutToFeet.gif │ ├── slideHexNutsIntoYBar.gif │ └── slideHexNutsIntoYBarXYPlate.gif ├── config ├── common.scad ├── dowel.scad ├── magnet.scad ├── print.scad └── rackFrame.scad ├── helper ├── apply.scad ├── common.scad ├── dovetail.scad ├── filet.scad ├── keystone1.scad ├── keystone2.scad ├── math.scad ├── matrix.scad ├── screws.scad └── sine.scad ├── media ├── bom │ ├── dowel.gif │ ├── fhcs_long.gif │ ├── fhcs_medium.gif │ ├── fhcs_short.gif │ ├── glue.gif │ ├── hex_nut.gif │ └── magnet.gif ├── features │ ├── slideOpt.gif │ ├── stackConnectorOpt.gif │ └── swivelOpt.gif └── renders │ ├── output.png │ └── rackDisplayRounded.png ├── misc ├── animate.sh └── bomAnimations.scad ├── rack-mount ├── angle-bracket │ ├── README.md │ ├── animate.gif │ ├── animate.scad │ └── entry.scad ├── catalog │ ├── 120mm-Rear-Case-Fan.scad │ ├── 120mm-fan-tray_mod.scad │ ├── LG_Slim_DVD_Tray.scad │ ├── Minisforum_NAB9-tray_mod.scad │ ├── Minisforum_UM890PRO-tray_mod.scad │ ├── README.md │ ├── Unifi-8-PoE-Switch_Tray+Adapter.scad │ ├── beelink-EQi12-box_mod.scad │ ├── beelink-EQi12-tray_mod.scad │ ├── beelink-mini-s-tray.scad │ ├── brush-strip-plate.scad │ ├── mele-quieter-tray.scad │ ├── parts │ │ └── sfxPSU.scad │ └── rpi-sbc-simple-box │ │ ├── case.scad │ │ ├── frontPlate.scad │ │ └── top.scad ├── common.scad ├── dimensionHelper.scad ├── enclosed-box │ ├── README.md │ ├── animate.gif │ ├── animate.scad │ ├── entry.scad │ ├── frontBoxHolder.scad │ ├── helper.scad │ └── sideRail.scad ├── patch-panel │ ├── README.md │ ├── animate.gif │ ├── animate.scad │ ├── entry.scad │ └── patchPanel.scad ├── plateBase.scad ├── rackEars.scad └── tray │ ├── README.md │ ├── animate.gif │ ├── animate.scad │ ├── entry.scad │ └── tray.scad ├── rack ├── assembly │ ├── addMagnetsToMagnetModules.scad │ ├── addMagnetsToSideWall.scad │ ├── attachSideConnectorModulesToYBars.scad │ ├── attachXBarWithYBar.scad │ ├── attachXYPlates.scad │ ├── attachXYTrays.scad │ ├── common.scad │ ├── connectXYTrayWithMainRails.scad │ ├── finalBuilds.scad │ ├── insertDowelsIntoSideWall.scad │ ├── insertFeet.scad │ ├── propUpBottomXYTraywithSideWalls.scad │ ├── screwFeet.scad │ ├── screwXBarAndYBar.scad │ ├── slideHexNutToFeet.scad │ ├── slideHexNutsIntoYBar.scad │ └── slideHexNutsIntoYBarXYPlate.scad ├── connector │ ├── basePlateYBarConnectors.scad │ ├── connectors.scad │ ├── mainRailYBarConnectors.scad │ ├── sideModuleYBarConnectors.scad │ ├── stackYBarConnectors.scad │ ├── transformations.scad │ └── xBarYBarConnectors.scad ├── mainRail.scad ├── print │ ├── eval_P.scad │ ├── hingeModule.scad │ ├── magnetModuleLeft_P.scad │ ├── magnetModuleRight_P.scad │ ├── mainRail_P.scad │ ├── rackFeet_P.scad │ ├── rackJoiner_P.scad │ ├── sideWallLeft_P.scad │ ├── sideWallRight_P.scad │ ├── stackConnectorBottom_P.scad │ ├── xBar_P.scad │ ├── xyPlate_P.scad │ ├── yBarLeft_P.scad │ └── yBarRight_P.scad ├── sharedVariables.scad ├── side │ ├── hingeModule.scad │ ├── magnetModule.scad │ ├── sideWallBase.scad │ ├── sideWallLeft.scad │ ├── sideWallMagnetMount.scad │ ├── sideWallRight.scad │ └── sideWallVariables.scad ├── stackEnds.scad ├── xBar.scad ├── xyPlate.scad └── yBar.scad ├── rbuild.py └── stl ├── micro └── rack │ ├── eval_P.stl │ ├── hingeModule.stl │ ├── magnetModuleLeft_P.stl │ ├── magnetModuleRight_P.stl │ ├── mainRail_P.stl │ ├── rackFeet_P.stl │ ├── rackJoiner_P.stl │ ├── sideWallLeft_P.stl │ ├── sideWallRight_P.stl │ ├── stackConnectorBottom_P.stl │ ├── xBar_P.stl │ ├── xyPlate_P.stl │ ├── yBarLeft_P.stl │ ├── yBarRight_P.stl │ └── yBar_P.stl ├── mini └── rack │ ├── eval_P.stl │ ├── hingeModule.stl │ ├── magnetModuleLeft_P.stl │ ├── magnetModuleRight_P.stl │ ├── mainRail_P.stl │ ├── rackFeet_P.stl │ ├── rackJoiner_P.stl │ ├── sideWallLeft_P.stl │ ├── sideWallRight_P.stl │ ├── stackConnectorBottom_P.stl │ ├── xBar_P.stl │ ├── xyPlate_P.stl │ ├── yBarLeft_P.stl │ ├── yBarRight_P.stl │ └── yBar_P.stl └── nano └── rack ├── eval_P.stl ├── hingeModule.stl ├── magnetModuleLeft_P.stl ├── magnetModuleRight_P.stl ├── mainRail_P.stl ├── rackFeet_P.stl ├── rackJoiner_P.stl ├── sideWallLeft_P.stl ├── sideWallRight_P.stl ├── stackConnectorBottom_P.stl ├── xBar_P.stl ├── xyPlate_P.stl ├── yBarLeft_P.stl ├── yBarRight_P.stl └── yBar_P.stl /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/debian 3 | { 4 | "build": { "dockerfile": "dockerfile" }, 5 | "features": { 6 | "ghcr.io/devcontainers/features/python:1": { 7 | "installTools": true, 8 | "version": "latest" 9 | } 10 | }, 11 | 12 | "customizations": { 13 | }, 14 | 15 | "remoteUser": "vscode", 16 | "mounts": [ 17 | "source=rackstack-bashhistory,target=/commandhistory,type=volume" 18 | ] 19 | } -------------------------------------------------------------------------------- /.devcontainer/dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:bookworm 2 | 3 | RUN wget -qO- https://files.openscad.org/OBS-Repository-Key.pub | sudo tee /etc/apt/trusted.gpg.d/obs-openscad-nightly.asc \ 4 | && echo "deb https://download.opensuse.org/repositories/home:/t-paul/Debian_12/ ./" >> /etc/apt/sources.list.d/openscad.list \ 5 | && apt-get update && export DEBIAN_FRONTEND=noninteractive \ 6 | && apt-get -y install --no-install-recommends openscad openscad-nightly libpng-dev libjpeg-dev libtiff-dev 7 | 8 | 9 | RUN apt-get -y install --no-install-recommends imagemagick 10 | 11 | RUN curl -sL http://www.lcdf.org/gifsicle/gifsicle-1.91.tar.gz | tar -zx \ 12 | && cd gifsicle-1.91 \ 13 | && ./configure --disable-gifview \ 14 | && make install 15 | 16 | ARG USERNAME=vscode 17 | RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \ 18 | && mkdir /commandhistory \ 19 | && touch /commandhistory/.bash_history \ 20 | && chown -R $USERNAME /commandhistory \ 21 | && echo "$SNIPPET" >> "/home/$USERNAME/.bashrc" 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | [#]* 3 | .idea 4 | 5 | **/stl/* 6 | !/stl/mini/ 7 | !/stl/micro/ 8 | !/stl/nano/ 9 | 10 | **/tmp/ 11 | 12 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Zhao Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rackstack 2 | 3 | ![display](media/renders/rackDisplayRounded.png) 4 | 5 | ### A modular 3d-printable mini rack system 6 | - ***Mount Anything:*** Perfect for organizing SBCs, mini PCs, small switches, power hubs, etc. 7 | - ***Fully customizable:*** Fully written in OpenSCAD. Everything, from the dimensions of the rack, to the roundness of the corners, can be modified with a simple code change. 8 | - ***Printable from home:*** Designed to be printed with conventional FDM printers. Requires minimal supports when printing, and final assembly needs only a few easy-to-source parts. 9 | - ***No cage nuts:*** Sliding hex nut design for the front rails allows one to easily mount items, without dealing with cage nuts. 10 | - ***Stackable:*** Individual racks can be easily stacked and fastened together. Mix and match different color and design combinations! 11 | 12 | ## Assembly 13 | 14 | Pre-generated STLs for roughly 200mm^3 (mini), 180mm^3 (micro), and 100mm^3 (nano) rack frames can be found in the [stl](stl) dir. 15 | These STLs are generated from the files in [rack/print](rack/print) - further information about printing these parts 16 | (supports, orientation) can be found in these files. 17 | 18 | ### Assembly Instructions 19 | Please see [the assembly guide](./assembly-guide). 20 | 21 | ### BOM - Required Tools: 22 | - 3d FDM Printer - build size requirements depend on configured rack profile 23 | - M3 Allen Key (for constructing the rack) 24 | - M4 Allen Key (for mounting rack-mount items) 25 | 26 | ### BOM - Single Rack: 27 | 28 | | Item | Name | Quantity | Comment | 29 | |---------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------| 30 | | | M3x12 FHCS | 12 | Extras are useful and inexpensive. If you plan on eventually stacking multiple racks together, don't hesitate to get a lot more. | 31 | | | M3x16 FHCS | 16 | ☝️ | 32 | | | M3 hex nut | 28 | ☝️ | 33 | | | 3x10 steel dowel pin | 4 | 3mm diameter, 10mm height | 34 | | | 6x2 neodymium disc magnet | 8 | 6mm diameter, 2mm height | 35 | | | super glue | \>= 2ml | Used to glue magnets to plastic | 36 | 37 | 38 | ### Printing - Single Rack: 39 | | Part | Quantity | 40 | |------------------------------------------------------|----------| 41 | | [Left Y-Bar](./rack/print/yBarLeft_P.scad) | 2 | 42 | | [Right Y-Bar](./rack/print/yBarRight_P.scad) | 2 | 43 | | [X-Bar](./rack/print/xBar_P.scad) | 4 | 44 | | [Main Rail](./rack/print/mainRail_P.scad) | 4 | 45 | | [Left Side Wall](./rack/print/sideWallLeft_P.scad) | 1 | 46 | | [Right Side Wall](./rack/print/sideWallRight_P.scad) | 1 | 47 | | [XY-Plate](./rack/print/xyPlate_P.scad) | 2 | 48 | | [Feet](./rack/print/rackFeet_P.scad) (optional) | 2 | 49 | 50 | 51 | #### Notes: 52 | - ⚠️ Before printing the actual parts. It's recommended to print this evaluation part: [eval_P.scad](./rack/print/eval_P.scad) to test tolerances. 53 | If you find the fits too tight/loose, you can adjust them [here](./config/print.scad). Please make sure also adjust the layer height in that file, too. 54 | - Omitted actual plastic for printing. Any conventional 3d printing plastic should do (PLA, PETG, ABS), 55 | but beware of PLA's thermal limits. Higher infill is recommended for all parts. 56 | - For joining two racks, you will need to print 4 [rackJoiners](./rack/print/rackJoiner_P.scad), as well as 8 M3 hex nuts, and 8 M3x12 FHCS. 57 | - Main front rails use M4 hex nuts and screws. 58 | - Side rails are mounted using M3 hex nuts and screws. 59 | 60 | 61 | ## Configuring + Generating STLs 62 | A python script: [rbuild.py](./rbuild.py) is provided to generate different project stls. **Before running the script**, please 63 | configure the path to the OpenSCAD binary in [rbuild.py](./rbuild.py). 64 | 65 | Requirements: 66 | - `openscad` CLI binary 67 | - `python3` 68 | 69 | ### Examples: 70 | Generate all project files for the `micro` profile: 71 | 72 | `python3 rbuild.py -b all -c micro` 73 | 74 | This will build all the parts defined in [rack/print](./rack/print), and put the STLs in [stl/micro](./stl/micro). 75 | You can also provide a `-dz {n}` parameter to adjust the height of the generated rack. Configuring other rack 76 | variables can be done in [config/rackframe.scad](./config/rackFrame.scad). 77 | 78 | For generating a specific part, and putting it into a new `stl/custom` target directory: 79 | 80 | `python3 rbuild.py -b yBar -c micro -t custom` 81 | 82 | `rbuild.py` also support an optional `--nightly` flag, which will run a nightly build of OpenSCAD. Please make sure the 83 | path to the nightly build is also configured in [rbuild.py](./rbuild.py). 84 | 85 | 86 | ### ⚠️ Important: 87 | Before committing to a full print, please try printing an evaluation print: [eval_P.scad](./rack/print/eval_P.scad) to test tolerances. 88 | It is very likely you will need to edit the default tolerances in [print.scad](./config/print.scad) for a nice fit. 89 | 90 | 91 | ## Trays, Boxes, etc 92 | 93 | Some parametric rack mount systems can be found in [rack-mount](./rack-mount). The following rack-mount systems 94 | are currently supported: 95 | - [Enclosed Box](./rack-mount/enclosed-box): For mounting box-shaped objects, without the need for any mounting holes on the box. 96 | - [Tray](./rack-mount/tray): Simple tray mounted using only the front rails. Can be configured to have screw mounts at the bottom of the tray. 97 | - [Patch Panel](./rack-mount/patch-panel): Linear patch panel array. Number of keystone slots can be configured. 98 | - [Angle Brackets](./rack-mount/angle-bracket): Simple Angle Bracket mounting system. Derived from the Enclosed Box system. 99 | 100 | ## Rack Mount Catalog 101 | 102 | There is a small catalog of various pre-configured rack-mountable parts in the [catalog](./rack-mount/catalog) directory. Please feel free to add anything even remotely 103 | useful! 104 | 105 | ## Designing rack-mount items 106 | 107 | Some useful variables for designing your own rack-mount items can be found in [rack-mount/dimensionHelper.scad](./rack-mount/dimensionHelper.scad). 108 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # case-project TODO list 2 | 3 | ### Actual TODO 4 | 5 | - Finish refactoring files to use the application style, like in `yBar.scad`. 6 | - Figure out why sidewall build is so slow. 7 | - Clean up `sharedVariables.scad`. 8 | - Parallel builds with cli tool. 9 | - Beef up rack-mount designs. 10 | - Make handles. 11 | 12 | ### Ideas 13 | - Start using matrices for transformations. Enforce strict local/global hierarchy of matrix transformations. (Kinda done) 14 | - GitHub CI for OpenSCAD stl building 15 | - Instead of using names like 'mainRailSlotToInnerYZFace', experiment with a central distance/dimension directory. Like: 16 | ```openscad 17 | // Define identifiers: 18 | mainRailSlot = "mainRailSlotIdentifier"; 19 | yBarInnerYZ = "yBarInnerYZFaceIdentifier"; 20 | 21 | // Define distance 22 | setDx(mainRailSlot, yBarInnerYZ, 3); 23 | 24 | // Define dimensions 25 | setX(mainRailSlot, 10); 26 | setX(yBarInnerYZ, 0); 27 | 28 | // Then, you can get distances like: 29 | dx(mainRailSlot, yBarInnerYZ); // == 3 30 | dx(yBarInnerYZ, mainRailSlot); // == 3 31 | 32 | dx(mainRailSlot, blah); //throw error 33 | 34 | // And you could possible chain distances like: 35 | 36 | dx(yBarInnerYZ, mainRailSlot, sideConnectorSlot); 37 | // which would eval to: 38 | // dx(yBarInnerYZ, mainRailSlot) + x(mainRailSlot) + dx(mainRailSlot, sideConnectorSlot); 39 | // OR 40 | dx(yBarInnerYZ, mainRailSlot, sideConnectorSlot, includeFirst=true, includeLast=true); 41 | // which would eval to: 42 | // x(yBarInnerYZ) + dx(yBarInnerYZ, mainRailSlot, sideConnectorSlot) + x(sideConnectorSlot); 43 | ``` 44 | Hmm after looking into it a bit it seems that OpenScad's non-reassign rule is going to get in the way. Might still be 45 | worthwhile implement variable subsystem? 46 | 47 | -------------------------------------------------------------------------------- /assembly-guide/README.md: -------------------------------------------------------------------------------- 1 | # Rack Assembly Instructions 2 | 3 | **Note**: Please see 4 | - [BOM](../README.md#bom---single-rack) for required materials, beyond the actual 3D printed parts. 5 | - [Print BOM](../README.md#printing---single-rack) for all the required 3D printed parts for a single rack. 6 | 7 | ## Instructions: 8 | Slide in M3 hex nuts into the Y-bars - 4 times. Each corner should have 3 hex nuts: 9 | ![slideHexNutsIntoYBar](./gifs/slideHexNutsIntoYBar.gif) 10 | 11 | Glue magnets into magnet side-modules on the Y-bars - 4 times: 12 | ![addMagnetsToMagnetModules](./gifs/addMagnetsToMagnetModules.gif) 13 | 14 | Glue top and bottom magnets into side walls - 2 times: 15 | ![addMagnetsToSideWall](./gifs/addMagnetsToSideWall.gif) 16 | 17 | Insert top and bottom dowel rods into side walls - 2 times: 18 | ![insertDowelsIntoSideWall](./gifs/insertDowelsIntoSideWall.gif) 19 | 20 | Slide Y-bars into X-bars. Do this 2 times. You should end up with 2 trays. Orientation on the Y-bars matters here; make sure that the magnets are on the same side: 21 | ![attachXBarWithYBar](./gifs/attachXBarWithYBar.gif) 22 | 23 | Screw together Y-bars and X-bars using 4 M3x16 FHCS screws (one screw per corner) - 2 times: 24 | ![screwXBarAndYBar](./gifs/screwXBarAndYBar.gif) 25 | 26 | 27 | 28 | 29 | 30 | Screw in the main rails with M3x16 FHCS screws. 31 | ![connectXYTrayWithMainRails](./gifs/connectXYTrayWithMainRails.gif) 32 | 33 | Prop us the side wall using the side modules. Make sure the dowels are inserted properly, and the magnets align: 34 | ![propUpBottomXYTraywithSideWalls](./gifs/propUpBottomXYTraywithSideWalls.gif) 35 | 36 | Slide in the other (top) tray. Make sure the magnets and dowels align. Screw in the main rails with M3x16 FHCS screws: 37 | ![attachXYTrays](./gifs/attachXYTrays.gif) 38 | 39 | Attach the top and bottom plates with 4 M3x12 screws each: 40 | ![slideHexNutsIntoYBarXYPlate](./gifs/slideHexNutsIntoYBarXYPlate.gif) 41 | ![attachXYPlates](./gifs/attachXYPlates.gif) 42 | 43 | Slide in hex nuts into feet - 2 times: 44 | ![slideHexNutToFeet](./gifs/slideHexNutToFeet.gif) 45 | 46 | Insert feet into the bottom of the rack: 47 | ![insertFeet](./gifs/insertFeet.gif) 48 | 49 | Screw in feet using 4 M3x12 FHCS screws: 50 | ![screwFeet](./gifs/screwFeet.gif) 51 | 52 | 53 | **FIN** 54 | -------------------------------------------------------------------------------- /assembly-guide/gifs/addMagnetsToMagnetModules.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/addMagnetsToMagnetModules.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/addMagnetsToSideWall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/addMagnetsToSideWall.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/attachSideConnectorModulesToYBars.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/attachSideConnectorModulesToYBars.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/attachXBarWithYBar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/attachXBarWithYBar.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/attachXYPlates.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/attachXYPlates.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/attachXYTrays.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/attachXYTrays.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/connectXYTrayWithMainRails.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/connectXYTrayWithMainRails.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/insertDowelsIntoSideWall.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/insertDowelsIntoSideWall.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/insertFeet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/insertFeet.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/propUpBottomXYTraywithSideWalls.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/propUpBottomXYTraywithSideWalls.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/screwFeet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/screwFeet.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/screwXBarAndYBar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/screwXBarAndYBar.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/slideHexNutToFeet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/slideHexNutToFeet.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/slideHexNutsIntoYBar.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/slideHexNutsIntoYBar.gif -------------------------------------------------------------------------------- /assembly-guide/gifs/slideHexNutsIntoYBarXYPlate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/assembly-guide/gifs/slideHexNutsIntoYBarXYPlate.gif -------------------------------------------------------------------------------- /config/common.scad: -------------------------------------------------------------------------------- 1 | // Config aggregator file + some extra configs 2 | include <./dowel.scad> 3 | include <./magnet.scad> 4 | include <./rackFrame.scad> 5 | include <./print.scad> 6 | 7 | 8 | sideWallVentilation = true; -------------------------------------------------------------------------------- /config/dowel.scad: -------------------------------------------------------------------------------- 1 | /* 2 | Dowel pin config used in side wall hinge module and side wall 3 | */ 4 | 5 | dowelPinR = 1.5; 6 | dowelPinH = 10; 7 | -------------------------------------------------------------------------------- /config/magnet.scad: -------------------------------------------------------------------------------- 1 | include <./print.scad> 2 | 3 | // Dimensions for small cylindrical neodymium magnets that I bought off Amazon 4 | magnetR = 3; 5 | magnetH = 1.7; 6 | 7 | magnetRSlack = 0.1; 8 | magnetHSlack = 0.05; 9 | 10 | magnetRSlacked = magnetR + magnetRSlack; 11 | magnetHSlacked = magnetH + magnetHSlack; -------------------------------------------------------------------------------- /config/print.scad: -------------------------------------------------------------------------------- 1 | // Print settings config file 2 | 3 | /********************************************************************************************************************** 4 | Slack config to standardize different usages of slack/tolerance values. 5 | 6 | The purpose of this config is to introduce some consistency with how slack values defined in code. E.g. Why is the 7 | slack value defined as 0.3 in this file, but 0.4 in another? This will also allow model-level adjustments for tighter 8 | fitting parts, for when it might not convenient to adjust the actual 3d printer (using a 3d printer at a makerspace, 9 | for example). 10 | 11 | Some important details: 12 | - The general philosophy for slack applications in this project is to subtract space from sockets, while not 13 | modifying the plugs. 14 | - Values are signed. Positive values can be interpreted as how much to remove from the socket along some dimension. 15 | - These values depend on print orientation and it's assumed parts are printed in their recommended orientations. 16 | */ 17 | xySlack = 0.25; 18 | 19 | radiusXYSlack = xySlack/2; 20 | 21 | zSlack = 0.0; 22 | 23 | overhangSlack = 0.5; 24 | 25 | supportedOverhangSlack = 0.5; 26 | 27 | // special slack cases, change if neccessary 28 | xBarYBarDovetailSlack = xySlack; 29 | 30 | /********************************************************************************************************************** 31 | Printer/slicer config, mainly used for calculating some special overhangs. 32 | */ 33 | defaultLayerHeight = 0.3; 34 | -------------------------------------------------------------------------------- /config/rackFrame.scad: -------------------------------------------------------------------------------- 1 | // Manually set this variable to toggle rack profile 2 | profileName = "default"; 3 | 4 | fixedSideModules = true; 5 | 6 | _profileConfigs = [ 7 | // You can introduce a custom profile like: 8 | /* 9 | ["custom", [ 10 | ["maxUnitWidth", 180], 11 | ["maxUnitDepth", 120], 12 | ["numRailScrews", 10] 13 | ]], // To generate the stls for this custom profile, you would do: 14 | // $ python3 rbuild.py -b all -c custom 15 | */ 16 | ["default", [ // Default config. Will be overwritten by any other profiles 17 | ["maxUnitWidth", 180], 18 | ["maxUnitDepth", 180], 19 | ["numRailScrews", 12], 20 | ["screwDiff", 10], 21 | ["mainRailScrewType", "m4"], 22 | ["mainRailSideMountScrewType", "m4"], 23 | ["rackFrameScrewType", "m3"], 24 | ["baseRoundness", 5], 25 | ]], 26 | ["nano", [ 27 | ["maxUnitWidth", 105], 28 | ["maxUnitDepth", 105], 29 | ["numRailScrews", 10] 30 | ]], 31 | ["micro", [ 32 | ["maxUnitWidth", 180], 33 | ["maxUnitDepth", 180], 34 | ["numRailScrews", 12] 35 | ]], 36 | ["mini", [ 37 | ["maxUnitWidth", 205], 38 | ["maxUnitDepth", 205], 39 | ["numRailScrews", 16] 40 | ]] 41 | ]; 42 | 43 | function _getConfigValueRaw(profile, varName) = profile[search([varName], profile)[0]][1]; 44 | function _getProfileRaw(profileName) = _profileConfigs[search([profileName], _profileConfigs)[0]][1]; 45 | 46 | function _getConfigValueOrThrowError(val) = val == undef? assert(false, "blah") 0: val; 47 | function _getConfigValueOrDefault(val, default) = val == undef? default: val; 48 | 49 | function getConfigValue(profile, varName) = 50 | _getConfigValueOrDefault( 51 | _getConfigValueRaw(profile, varName), 52 | _getConfigValueOrThrowError(_getConfigValueRaw(getProfile("default"), varName)) 53 | ); 54 | 55 | function getProfile(profileName) = _getConfigValueOrThrowError(_getProfileRaw(profileName)); 56 | 57 | function getConfig(name) = getConfigValue(profile, name); 58 | 59 | profile = getProfile(profileName); 60 | 61 | // Maximum width for rack-mount units. Change this according your max expected enclosure width. 62 | // Changing this will directly affect the required build volume. 63 | maxUnitWidth = getConfig("maxUnitWidth"); 64 | 65 | // Maximum (recommended) unit depth. There technically isn't a max unit depth because there's no physical bound on 66 | // how far a rack unit can extrude back. This parameter basically controls the distance between the front of the front 67 | // rails and the back of the back rails. Changing this will directly affect the required build volume. 68 | maxUnitDepth = getConfig("maxUnitDepth"); 69 | 70 | // Vertical distance between the midpoint of adjacent screws mounts. Affects build volume. 71 | screwDiff = getConfig("screwDiff"); 72 | 73 | // Number screw slots on the main rail. Affects build volume. 74 | numRailScrews = getConfig("numRailScrews"); 75 | 76 | // Screw type used for rackmount units. See screws.scad. 77 | mainRailScrewType = getConfig("mainRailScrewType"); 78 | 79 | // Screw type used to affix side rails. 80 | mainRailSideMountScrewType = getConfig("mainRailSideMountScrewType"); 81 | 82 | // Screw type used for constructing the actual rack frame. 83 | rackFrameScrewType = getConfig("rackFrameScrewType"); 84 | // Currently, only m3 screws are supported here (tolerance issues) 85 | assert(rackFrameScrewType == "m3"); 86 | 87 | // Fillet radius for main rack profile 88 | baseRoundness = getConfig("baseRoundness"); 89 | -------------------------------------------------------------------------------- /helper/apply.scad: -------------------------------------------------------------------------------- 1 | // Modules for apply pattern 2 | 3 | module apply_p() { 4 | union() { 5 | children(0); 6 | children(1); 7 | } 8 | } 9 | 10 | module apply_n() { 11 | difference() { 12 | children(1); 13 | children(0); 14 | } 15 | } 16 | 17 | module apply_pn() { 18 | difference() { 19 | union() { 20 | children(0); 21 | children(2); 22 | } 23 | children(1); 24 | } 25 | } 26 | 27 | module apply_np() { 28 | difference() { 29 | union() { 30 | children(1); 31 | children(2); 32 | } 33 | children(0); 34 | } 35 | } -------------------------------------------------------------------------------- /helper/common.scad: -------------------------------------------------------------------------------- 1 | // Helper aggregator 2 | 3 | include <./apply.scad> 4 | include <./dovetail.scad> 5 | include <./filet.scad> 6 | include <./keystone1.scad> 7 | include <./keystone2.scad> 8 | include <./math.scad> 9 | include <./matrix.scad> 10 | include <./screws.scad> 11 | include <./sine.scad> 12 | 13 | $fn=64; 14 | -------------------------------------------------------------------------------- /helper/dovetail.scad: -------------------------------------------------------------------------------- 1 | include <./math.scad> 2 | 3 | // centered on z axis 4 | module dovetail( 5 | topWidth, 6 | bottomWidth, 7 | height, 8 | length, 9 | headExtension=0, 10 | baseExtension=0, 11 | frontFaceLength = 0, 12 | frontFaceScale = 0, 13 | backFaceLength = 0, 14 | backFaceScale = 0, 15 | ) { 16 | 17 | translate(v=[0,0,frontFaceLength]) 18 | linear_extrude(length-(frontFaceLength+backFaceLength)) 19 | dovetailFace(topWidth,bottomWidth,height,headExtension,baseExtension); 20 | 21 | translate(v=[0,0,frontFaceLength]) 22 | mirror(v=[0,0,1]) 23 | linear_extrude(frontFaceLength, scale=[frontFaceScale, frontFaceScale]) 24 | dovetailFace(topWidth,bottomWidth,height,headExtension,baseExtension); 25 | 26 | translate(v=[0,0,length-backFaceLength]) 27 | linear_extrude(backFaceLength, scale=[backFaceScale,1]) 28 | dovetailFace(topWidth,bottomWidth,height,headExtension,baseExtension); 29 | 30 | 31 | 32 | module dovetailFace(topWidth, bottomWidth, height, headExtension, baseExtension) { 33 | 34 | union() { 35 | // base 36 | polygon(points = 37 | [[-bottomWidth/2, 0], 38 | [-topWidth/2, height], 39 | [topWidth/2, height], 40 | [bottomWidth/2, 0]] 41 | ); 42 | 43 | polygon(points = 44 | [[-bottomWidth/2, -baseExtension], 45 | [-bottomWidth/2,0 ], 46 | [bottomWidth/2, 0], 47 | [bottomWidth/2, -baseExtension]] 48 | ); 49 | 50 | translate(v=[0,height]) 51 | polygon(points = 52 | [[-topWidth/2, headExtension], 53 | [-topWidth/2,0 ], 54 | [topWidth/2, 0], 55 | [topWidth/2, headExtension]] 56 | ); 57 | } 58 | } 59 | 60 | 61 | } -------------------------------------------------------------------------------- /helper/filet.scad: -------------------------------------------------------------------------------- 1 | include <./math.scad> 2 | 3 | module sphericalFiletEdge(width, depth, height, roundness) { 4 | rd = roundness; 5 | 6 | intersection() { 7 | minkowski() { 8 | sphere(r = rd); 9 | 10 | translate(v = [rd, rd, rd]) 11 | cube(size = [width*2, depth - 2*rd, height*2]); 12 | } 13 | 14 | cube(size = [width, depth, height]); 15 | } 16 | } 17 | 18 | module cylindricalFiletEdge(width, depth, height, roundness) { 19 | rd = roundness; 20 | 21 | intersection() { 22 | minkowski() { 23 | rotate(a = [90, 0, 0]) 24 | cylinder(r = rd, h = eps); 25 | 26 | translate(v = [rd, 0, rd]) 27 | cube(size = [width, depth, height]); 28 | } 29 | 30 | cube(size = [width, depth, height]); 31 | } 32 | } 33 | 34 | module cylindricalFiletNegative(p0, p1, n, r) { 35 | l = norm(p1-p0); 36 | 37 | alignTo(p0=p0, p1=p1, p2=p0-n) 38 | rotate(a=[45,0,0]) 39 | rotate(a=[0,90,0]) 40 | zFiletNegative(length = l, r = r); 41 | } 42 | 43 | module zFiletNegative(length, r) { 44 | 45 | p1 = [0,0,0]; 46 | p2 = [0,0,length]; 47 | n1 = [-1,-1,0] / norm([-1,-1,0]); 48 | dr = sqrt(2*r^2); 49 | 50 | difference() { 51 | 52 | hull() { 53 | translate(v=[0,0,0]) 54 | cube(size=[r,r,r]); 55 | 56 | translate(v = p2-[0,0,0.1]) 57 | cube(size=[r,r,0.1]); 58 | 59 | } 60 | 61 | hull() { 62 | translate(v = p1-n1*dr) 63 | cylinder(r = r, h=0.0001, $fn = 32); 64 | 65 | 66 | translate(v = p2-n1*dr) 67 | cylinder(r = r, h=0.0001, $fn = 32); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /helper/keystone1.scad: -------------------------------------------------------------------------------- 1 | include <../config/common.scad> 2 | 3 | // rj45 slot-to-slot keystone jack model and negative 4 | keystoneMainBodyWidth = 15.0; 5 | keystoneMainBodyHeight = 16.90; 6 | keystoneMainBodyDepth = 32.90; 7 | 8 | heightWithHookBody = 20.2; 9 | heightWithHookCatch = 21.30; 10 | widthWithSideLugs = 15.96; 11 | sideLugWidth = (widthWithSideLugs - keystoneMainBodyWidth) / 2.0; 12 | 13 | heightWithBottomLug = 17.5; 14 | 15 | frontToHookCatch = 8.35; 16 | frontToBottomLugBack = 8.23; 17 | frontToSideLugFront = 10.63; 18 | 19 | module rj45Keystone() { 20 | // main keystone body (no hooks or lugs) 21 | cube(size=[keystoneMainBodyWidth + xySlack, keystoneMainBodyDepth + xySlack, keystoneMainBodyHeight]); 22 | 23 | // slot for top hook 24 | translate(v=[0,frontToHookCatch,0]) 25 | cube(size=[keystoneMainBodyWidth + xySlack, keystoneMainBodyDepth-frontToHookCatch + xySlack, heightWithHookBody]); 26 | 27 | cube(size=[keystoneMainBodyWidth + xySlack, frontToHookCatch + xySlack, heightWithHookCatch]); 28 | 29 | // slots for side lugs 30 | translate(v=[-sideLugWidth, frontToSideLugFront,0]) 31 | cube(size=[widthWithSideLugs + xySlack, keystoneMainBodyDepth-frontToSideLugFront + xySlack, keystoneMainBodyHeight]); 32 | 33 | // slots for bottom lugs 34 | translate(v=[0,0,-(heightWithBottomLug-keystoneMainBodyHeight)]) 35 | cube(size=[keystoneMainBodyWidth + xySlack, frontToBottomLugBack + xySlack, keystoneMainBodyHeight]); 36 | 37 | } 38 | 39 | module rj45KeystoneJack_N() { 40 | translate(v=[0,-4,0.5]) // why? 41 | intersection() { 42 | translate(v=[-2.5,4,-4]) 43 | cube(size=[20,6,28]); 44 | rj45Keystone(); 45 | } 46 | } 47 | 48 | module keystone1(outerWidth, outerHeight) { 49 | rotate([0, 0, 180]) // To keep the same direction with keystone2 50 | difference() { 51 | translate([0, 0, 5.9 / 2]) 52 | cube([outerWidth, outerHeight, 5.9], center = true); 53 | 54 | translate([-(keystoneMainBodyWidth + xySlack) / 2, (heightWithHookCatch + heightWithBottomLug - keystoneMainBodyHeight) / 2, 0]) 55 | rotate([90, 0, 0]) 56 | rj45KeystoneJack_N(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /helper/keystone2.scad: -------------------------------------------------------------------------------- 1 | include <../config/common.scad> 2 | 3 | // These all constants are obtained by measuring rj45 keystone modules 4 | // in hand. frontToRearDepth and rearPanelThickness are adjusted after testing. 5 | // Before printing the Patch Panel, print a keystone2(outerWidth = 19, 6 | // outerHeight = 27) to test if it fits. 7 | frontWidth = 14.5; 8 | frontHeight = 16.2; 9 | 10 | frontToRearDepth = 8.4; 11 | 12 | rearWidth = frontWidth; 13 | rearHeight = 19.55; 14 | rearPanelThickness = 2 - supportedOverhangSlack; 15 | 16 | maximumWidth = frontWidth; 17 | maximumHeight = 22.15; 18 | 19 | lugHeight = 1.3; 20 | 21 | module keystone2(plateThickness = 1, outerWidth, outerHeight) { 22 | assert(outerWidth > maximumWidth); 23 | assert(outerHeight > maximumHeight); 24 | 25 | difference() { 26 | // Outer cube 27 | translate([-outerWidth / 2, -outerHeight / 2, 0]) 28 | cube([outerWidth, outerHeight, frontToRearDepth + rearPanelThickness]); 29 | 30 | // Front panel hole 31 | translate([-(frontWidth + xySlack) / 2, -(frontHeight + xySlack) / 2, 0]) 32 | cube([frontWidth + xySlack, frontHeight + xySlack, plateThickness]); 33 | 34 | // Middle cavity 35 | translate([-(maximumWidth + xySlack) / 2, -frontHeight / 2 - lugHeight - xySlack / 2, plateThickness]) 36 | cube([rearWidth + xySlack, outerHeight + 1000, frontToRearDepth - plateThickness]); 37 | 38 | // Rear panel hole 39 | translate([-(rearWidth + xySlack) / 2, -(frontHeight + xySlack) / 2, frontToRearDepth]) 40 | cube([rearWidth + xySlack, rearHeight + xySlack, rearPanelThickness]); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /helper/math.scad: -------------------------------------------------------------------------------- 1 | // Some misc math-y utils 2 | 3 | $fn=64; 4 | eps=0.001; 5 | rh=1; // preview render helper :) 6 | inf10 = 10; 7 | inf50 = 50; 8 | inf1000 = 1000; 9 | inf = inf1000; 10 | 11 | function unit(v) = v/norm(v); 12 | 13 | function lerp(a, b, t) = (b * t + a * (1 - t)); 14 | 15 | /* Example usage: 16 | for (i=mirror4XY(midpoint=[0,0,0], offsetX=90, offsetY=90)) 17 | { translate(v=i) something(); } 18 | */ 19 | module mirror4XY(p, dx, dy) { 20 | 21 | px = p[0]; 22 | py = p[1]; 23 | 24 | translate(v=[px, py, 0]) 25 | children(0); 26 | 27 | translate(v=[px+dx, py, 0]) 28 | children(0); 29 | 30 | translate(v=[px, py+dy, 0]) 31 | children(0); 32 | 33 | translate(v=[px+dx, py+dy, 0]) 34 | children(0); 35 | } 36 | 37 | /* Generate a 4x4 matrix that causes a change of basis 38 | such that the origin is at point p0, the x axis is aligned 39 | from p0 to p1 and point p2 is in the first quadrant. 40 | Copied and pasted from https://trmm.net/Basis/ 41 | */ 42 | module alignTo(p0,p1,p2) { 43 | 44 | x = unit(p1-p0); 45 | yp = unit(p2-p0); 46 | z = unit(cross(x,yp)); 47 | y = unit(cross(z,x)); 48 | 49 | multmatrix([ 50 | [x[0], y[0], z[0], p0[0]], 51 | [x[1], y[1], z[1], p0[1]], 52 | [x[2], y[2], z[2], p0[2]], 53 | [ 0, 0, 0, 1] 54 | ]) 55 | children(); 56 | } 57 | 58 | module halfspace(vpos, p) { 59 | 60 | ref = [0,0,-1]; 61 | 62 | if (cross(ref, vpos) == [0,0,0]) { 63 | translate(p) 64 | translate(v=[0,0, (ref*vpos) * -inf/2]) 65 | cube(size=[inf, inf, inf], center=true); 66 | } else { 67 | translate(p) 68 | alignPlanar(a=ref, b = vpos) 69 | translate(v = [0, 0, -inf/2]) 70 | cube(size = [inf, inf, inf], center = true); 71 | } 72 | 73 | } 74 | 75 | module alignPlanar(a,b) { 76 | rot_axis = cross(a,b); 77 | angle = acos(a*b/(norm(a)*norm(b))); 78 | 79 | rotate(v=rot_axis, a=angle) 80 | children(0); 81 | } -------------------------------------------------------------------------------- /helper/matrix.scad: -------------------------------------------------------------------------------- 1 | // Common matrix transformations 2 | 3 | identity = [ 4 | [1,0,0,0], 5 | [0,1,0,0], 6 | [0,0,1,0], 7 | [0,0,0,1], 8 | ]; 9 | 10 | function scale(v) = [ 11 | [v[0],0,0,0], 12 | [0,v[1],0,0], 13 | [0,0,v[2],0], 14 | [0,0,0,1] 15 | ]; 16 | 17 | function rotatex(a) = [ 18 | [1,0,0,0], 19 | [0,cos(a),-sin(a),0], 20 | [0,sin(a),cos(a),0], 21 | [0,0,0,1] 22 | ]; 23 | 24 | function rotatey(a) = [ 25 | [cos(a),0,sin(a),0], 26 | [0,1,0,0], 27 | [-sin(a),0,cos(a),0], 28 | [0,0,0,1] 29 | ]; 30 | 31 | function rotatez(a) = [ 32 | [cos(a),-sin(a),0,0], 33 | [sin(a),cos(a),0,0], 34 | [0,0,1,0], 35 | [0,0,0,1] 36 | ]; 37 | 38 | function rotate(a,v) = 39 | (v == undef) 40 | ? rotatez(a[2])*rotatey(a[1])*rotatex(a[0]) 41 | : rotateanv(a,v/sqrt(v*v)); 42 | 43 | function translate(v) = [ 44 | [1,0,0,v[0]], 45 | [0,1,0,v[1]], 46 | [0,0,1,v[2]], 47 | [0,0,0,1] 48 | ]; 49 | 50 | function mirrorabc(a,b,c) = [ 51 | [1-2*a*a,-2*a*b,-2*a*c,0], 52 | [-2*a*b,1-2*b*b,-2*b*c,0], 53 | [-2*a*c,-2*b*c,1-2*c*c,0], 54 | [0,0,0,1] 55 | ]; 56 | 57 | function mirrornv(nv) = mirrorabc(nv[0],nv[1],nv[2]); 58 | 59 | function mirror(v) = mirrornv(v/sqrt(v*v)); 60 | 61 | 62 | // taken from https://github.com/openscad/openscad/issues/1040#issuecomment-233208606 63 | function det4x4(a) = 64 | a[0][0]*a[1][1]*a[2][2]*a[3][3] + a[0][0]*a[1][2]*a[2][3]*a[3][1] + a[0][0]*a[1][3]*a[2][1]*a[3][2] 65 | + a[0][1]*a[1][0]*a[2][3]*a[3][2] + a[0][1]*a[1][2]*a[2][0]*a[3][3] + a[0][1]*a[1][3]*a[2][2]*a[3][0] 66 | + a[0][2]*a[1][0]*a[2][1]*a[3][3] + a[0][2]*a[1][1]*a[2][3]*a[3][0] + a[0][2]*a[1][3]*a[2][0]*a[3][1] 67 | + a[0][3]*a[1][0]*a[2][2]*a[3][1] + a[0][3]*a[1][1]*a[2][0]*a[3][2] + a[0][3]*a[1][2]*a[2][1]*a[3][0] 68 | - a[0][0]*a[1][1]*a[2][3]*a[3][2] - a[0][0]*a[1][2]*a[2][1]*a[3][3] - a[0][0]*a[1][3]*a[2][2]*a[3][1] 69 | - a[0][1]*a[1][0]*a[2][2]*a[3][3] - a[0][1]*a[1][2]*a[2][3]*a[3][0] - a[0][1]*a[1][3]*a[2][0]*a[3][2] 70 | - a[0][2]*a[1][0]*a[2][3]*a[3][1] - a[0][2]*a[1][1]*a[2][0]*a[3][3] - a[0][2]*a[1][3]*a[2][1]*a[3][0] 71 | - a[0][3]*a[1][0]*a[2][1]*a[3][2] - a[0][3]*a[1][1]*a[2][2]*a[3][0] - a[0][3]*a[1][2]*a[2][0]*a[3][1]; 72 | 73 | function invb00(a) = a[1][1]*a[2][2]*a[3][3] + a[1][2]*a[2][3]*a[3][1] + a[1][3]*a[2][1]*a[3][2] - a[1][1]*a[2][3]*a[3][2] - a[1][2]*a[2][1]*a[3][3] - a[1][3]*a[2][2]*a[3][1]; 74 | 75 | function invb01(a) = a[0][1]*a[2][3]*a[3][2] + a[0][2]*a[2][1]*a[3][3] + a[0][3]*a[2][2]*a[3][1] - a[0][1]*a[2][2]*a[3][3] - a[0][2]*a[2][3]*a[3][1] - a[0][3]*a[2][1]*a[3][2]; 76 | 77 | function invb02(a) = a[0][1]*a[1][2]*a[3][3] + a[0][2]*a[1][3]*a[3][1] + a[0][3]*a[1][1]*a[3][2] - a[0][1]*a[1][3]*a[3][2] - a[0][2]*a[1][1]*a[3][3] - a[0][3]*a[1][2]*a[3][1]; 78 | 79 | function invb03(a) = a[0][1]*a[1][3]*a[2][2] + a[0][2]*a[1][1]*a[2][3] + a[0][3]*a[1][2]*a[2][1] - a[0][1]*a[1][2]*a[2][3] - a[0][2]*a[1][3]*a[2][1] - a[0][3]*a[1][1]*a[2][2]; 80 | 81 | function invb10(a) = a[1][0]*a[2][3]*a[3][2] + a[1][2]*a[2][0]*a[3][3] + a[1][3]*a[2][2]*a[3][0] - a[1][0]*a[2][2]*a[3][3] - a[1][2]*a[2][3]*a[3][0] - a[1][3]*a[2][0]*a[3][2]; 82 | 83 | function invb11(a) = a[0][0]*a[2][2]*a[3][3] + a[0][2]*a[2][3]*a[3][0] + a[0][3]*a[2][0]*a[3][2] - a[0][0]*a[2][3]*a[3][2] - a[0][2]*a[2][0]*a[3][3] - a[0][3]*a[2][2]*a[3][0]; 84 | 85 | function invb12(a) = a[0][0]*a[1][3]*a[3][2] + a[0][2]*a[1][0]*a[3][3] + a[0][3]*a[1][2]*a[3][0] - a[0][0]*a[1][2]*a[3][3] - a[0][2]*a[1][3]*a[3][0] - a[0][3]*a[1][0]*a[3][2]; 86 | 87 | function invb13(a) = a[0][0]*a[1][2]*a[2][3] + a[0][2]*a[1][3]*a[2][0] + a[0][3]*a[1][0]*a[2][2] - a[0][0]*a[1][3]*a[2][2] - a[0][2]*a[1][0]*a[2][3] - a[0][3]*a[1][2]*a[2][0]; 88 | 89 | function invb20(a) = a[1][0]*a[2][1]*a[3][3] + a[1][1]*a[2][3]*a[3][0] + a[1][3]*a[2][0]*a[3][1] - a[1][0]*a[2][3]*a[3][1] - a[1][1]*a[2][0]*a[3][3] - a[1][3]*a[2][1]*a[3][0]; 90 | 91 | function invb21(a) = a[0][0]*a[2][3]*a[3][1] + a[0][1]*a[2][0]*a[3][3] + a[0][3]*a[2][1]*a[3][0] - a[0][0]*a[2][1]*a[3][3] - a[0][1]*a[2][3]*a[3][0] - a[0][3]*a[2][0]*a[3][1]; 92 | 93 | function invb22(a) = a[0][0]*a[1][1]*a[3][3] + a[0][1]*a[1][3]*a[3][0] + a[0][3]*a[1][0]*a[3][1] - a[0][0]*a[1][3]*a[3][1] - a[0][1]*a[1][0]*a[3][3] - a[0][3]*a[1][1]*a[3][0]; 94 | 95 | function invb23(a) = a[0][0]*a[1][3]*a[2][1] + a[0][1]*a[1][0]*a[2][3] + a[0][3]*a[1][1]*a[2][0] - a[0][0]*a[1][1]*a[2][3] - a[0][1]*a[1][3]*a[2][0] - a[0][3]*a[1][0]*a[2][1]; 96 | 97 | function invb30(a) = a[1][0]*a[2][2]*a[3][1] + a[1][1]*a[2][0]*a[3][2] + a[1][2]*a[2][1]*a[3][0] - a[1][0]*a[2][1]*a[3][2] - a[1][1]*a[2][2]*a[3][0] - a[1][2]*a[2][0]*a[3][1]; 98 | 99 | function invb31(a) = a[0][0]*a[2][1]*a[3][2] + a[0][1]*a[2][2]*a[3][0] + a[0][2]*a[2][0]*a[3][1] - a[0][0]*a[2][2]*a[3][1] - a[0][1]*a[2][0]*a[3][2] - a[0][2]*a[2][1]*a[3][0]; 100 | 101 | function invb32(a) = a[0][0]*a[1][2]*a[3][1] + a[0][1]*a[1][0]*a[3][2] + a[0][2]*a[1][1]*a[3][0] - a[0][0]*a[1][1]*a[3][2] - a[0][1]*a[1][2]*a[3][0] - a[0][2]*a[1][0]*a[3][1]; 102 | 103 | function invb33(a) = a[0][0]*a[1][1]*a[2][2] + a[0][1]*a[1][2]*a[2][0] + a[0][2]*a[1][0]*a[2][1] - a[0][0]*a[1][2]*a[2][1] - a[0][1]*a[1][0]*a[2][2] - a[0][2]*a[1][1]*a[2][0]; 104 | 105 | function inv4x4(a)= (1/det4x4(a))*[ 106 | [invb00(a),invb01(a),invb02(a),invb03(a)], 107 | [invb10(a),invb11(a),invb12(a),invb13(a)], 108 | [invb20(a),invb21(a),invb22(a),invb23(a)], 109 | [invb30(a),invb31(a),invb32(a),invb33(a)]]; 110 | 111 | -------------------------------------------------------------------------------- /helper/screws.scad: -------------------------------------------------------------------------------- 1 | /* Some common screw dimensions and helper functions/modules */ 2 | 3 | include <../config/common.scad> 4 | include <./math.scad> 5 | 6 | /********************************************************************************/ 7 | // M3 dimensions 8 | m3HoleRadiusSlack = radiusXYSlack; 9 | m3Diameter = 3.0; 10 | m3Radius = m3Diameter / 2.0; 11 | m3RadiusSlacked = m3Radius + m3HoleRadiusSlack; 12 | 13 | m3CounterSunkHeadRadius = 3; 14 | m3CounterSunkHeadLength = 1.7; 15 | 16 | m3HexNutWidthAcrossFlats = 5.41; 17 | m3HexNutWidthAcrossCorners = FtoG(m3HexNutWidthAcrossFlats); 18 | m3HexNutThickness = 2.2; 19 | 20 | /********************************************************************************/ 21 | // M4 dimensions 22 | m4HoleRadiusSlack = radiusXYSlack; 23 | m4Diameter = 4.0; 24 | m4Radius = m4Diameter / 2.0; 25 | m4RadiusSlacked = m4Radius + m4HoleRadiusSlack; 26 | 27 | m4CounterSunkHeadRadius = 4; 28 | m4CounterSunkHeadLength = 2.3; 29 | 30 | m4HexNutWidthAcrossFlats = 6.89; 31 | m4HexNutWidthAcrossCorners = FtoG(m4HexNutWidthAcrossFlats); 32 | m4HexNutThickness = 3.07; 33 | 34 | /********************************************************************************/ 35 | 36 | function screwRadiusSlacked(screwType) = 37 | (screwType == "m3") 38 | ? m3RadiusSlacked 39 | : (screwType == "m4") 40 | ? m4RadiusSlacked 41 | : error("Unsupported screw type"); 42 | 43 | function hexNutThickness(screwType) = 44 | (screwType == "m3") 45 | ? m3HexNutThickness 46 | : (screwType == "m4") 47 | ? m4HexNutThickness 48 | : error("Unsupported screw type"); 49 | 50 | 51 | module counterSunkHead_N(screwType, screwExtension=0, headExtension=0) { 52 | 53 | if (screwType == "m3") { 54 | translate(v = [0, 0, -m3CounterSunkHeadLength]) 55 | union() { 56 | cylinder(r1 = m3RadiusSlacked, r2 = m3CounterSunkHeadRadius, h = m3CounterSunkHeadLength); 57 | 58 | translate(v = [0, 0, -screwExtension]) 59 | cylinder(r = m3RadiusSlacked, h = screwExtension); 60 | 61 | translate(v = [0, 0, m3CounterSunkHeadLength]) 62 | cylinder(r = m3CounterSunkHeadRadius, h = headExtension); 63 | } 64 | } else if (screwType == "m4") { 65 | translate(v = [0, 0, -m4CounterSunkHeadLength]) 66 | union() { 67 | cylinder(r1 = m4RadiusSlacked, r2 = m4CounterSunkHeadRadius, h = m4CounterSunkHeadLength); 68 | 69 | translate(v = [0, 0, -screwExtension]) 70 | cylinder(r = m4RadiusSlacked, h = screwExtension); 71 | 72 | translate(v = [0, 0, m4CounterSunkHeadLength]) 73 | cylinder(r = m4CounterSunkHeadRadius, h = headExtension); 74 | } 75 | } else { 76 | error("Unsupported screw type"); 77 | } 78 | } 79 | 80 | module hexNut(screwType, center=true) { 81 | color([1, 1, 1]) 82 | if (screwType == "m3") { 83 | translate(v = [0, 0,-m3HexNutThickness/2]) 84 | difference() { 85 | cylinder(r=m3HexNutWidthAcrossCorners/2, h=m3HexNutThickness, $fn=6); 86 | cylinder(r=m3Radius,h=inf10, center=true); 87 | } 88 | } else if (screwType == "m4") { 89 | translate(v = [0, 0,-m4HexNutThickness/2]) 90 | difference() { 91 | cylinder(r=m4HexNutWidthAcrossCorners/2, h=m4HexNutThickness, $fn=6); 92 | cylinder(r=m4Radius,h=inf10, center=true); 93 | } 94 | } else { 95 | error("Unsupported screw type"); 96 | } 97 | } 98 | 99 | module hexNutPocket_N(screwType, openSide=true, backSpace=inf10, bridgeFront=false, bridgeBack=false ) { 100 | 101 | heightSlack = bridgeFront || bridgeBack ? overhangSlack: xySlack; 102 | 103 | if (screwType == "m3") { 104 | hexNutPocketHelper_N(m3RadiusSlacked, (m3HexNutWidthAcrossCorners+xySlack) / 2, m3HexNutThickness + heightSlack, openSide=openSide, backSpace=backSpace, bridgeFront=bridgeFront, bridgeBack=bridgeBack); 105 | } else if (screwType == "m4") { 106 | hexNutPocketHelper_N(m4RadiusSlacked, (m4HexNutWidthAcrossCorners+xySlack) / 2, m4HexNutThickness + heightSlack, openSide=openSide, backSpace=backSpace, bridgeFront=bridgeFront, bridgeBack=bridgeBack); 107 | } else { 108 | error("Unsupported screw type"); 109 | } 110 | } 111 | 112 | module hexNutPocketHelper_N(innerRadius, widthAcrossCorners, thickness, openSide=true, backSpace=inf10, bridgeFront=false, bridgeBack=false) { 113 | 114 | assert (!(bridgeFront && bridgeBack)); 115 | assert (!(openSide && bridgeFront)); 116 | 117 | union() { 118 | hull() { 119 | // hexagonal prism representing where the nut should fit 120 | cylinder(r = widthAcrossCorners, h = thickness, center = true, $fn = 6); 121 | 122 | // negative volume for sliding in the nut 123 | translate(v = [inf50, 0, 0]) 124 | cylinder(r = widthAcrossCorners, h = thickness, center = true, $fn = 6); 125 | } 126 | 127 | // negative volume for screw 128 | union() { 129 | // screw lead spacing 130 | translate(v = [0, 0, -backSpace]) 131 | cylinder(r = innerRadius, h = backSpace, $fn = 32); 132 | 133 | cylinder(r=innerRadius, h=inf50, $fn=32); 134 | 135 | if (bridgeFront) { 136 | union() { 137 | // first bridge layer 138 | translate(v=[0,0,thickness/2 + defaultLayerHeight/2]) 139 | cube(size=[2*innerRadius, GtoF(widthAcrossCorners)*2, defaultLayerHeight], center=true); 140 | // second bridge layer 141 | translate(v=[0,0,thickness/2 + defaultLayerHeight]) 142 | cube(size=[2*innerRadius, 2*innerRadius, defaultLayerHeight], center=true); 143 | } 144 | } 145 | 146 | if (bridgeBack) { 147 | union() { 148 | // first bridge layer 149 | translate(v=[0,0,-(thickness/2 + defaultLayerHeight/2)]) 150 | cube(size=[2*innerRadius, GtoF(widthAcrossCorners)*2, defaultLayerHeight], center=true); 151 | // second bridge layer 152 | translate(v=[0,0,-(thickness/2 + defaultLayerHeight)]) 153 | cube(size=[2*innerRadius, 2*innerRadius, defaultLayerHeight], center=true); 154 | } 155 | } 156 | } 157 | 158 | if (openSide) { 159 | hull() { 160 | translate(v = [inf50, 0, 0]) 161 | cylinder(r = innerRadius, h = inf50, $fn = 32); 162 | cylinder(r = innerRadius, h = inf50, $fn = 32); 163 | } 164 | } 165 | } 166 | } 167 | 168 | // Convert a regular hexagon widthAcrossFlats to widthAcrossCorners 169 | function FtoG(widthAcrossFlats) = widthAcrossFlats * (2 / sqrt(3)); 170 | 171 | // Convert a regular hexagon widthAcrossCorners to widthAcrossFlats 172 | function GtoF(widthAcrossCorners) = widthAcrossCorners * (sqrt(3) / 2); 173 | -------------------------------------------------------------------------------- /helper/sine.scad: -------------------------------------------------------------------------------- 1 | 2 | length = 100; 3 | resolution = 128; 4 | amplitude = 4.0; 5 | period = PI; 6 | shift = 0.0; 7 | 8 | function radToDeg(r) = r * (180 / PI); 9 | function sinR(r) = sin(radToDeg(r)); 10 | 11 | 12 | //sigmoid = function(x) exp(x) / (exp(x) + 1); 13 | //dsigmoid = function(x) sigmoid(x) * (1-sigmoid(x)); 14 | //amplitudeFunction = function(x) 5*dsigmoid(x - 3) + 1; 15 | amplitudeFunction = function(x) 1; 16 | 17 | module sineWave(length, resolution, amplitudeFunction, period, shift) { 18 | dx = length / resolution; 19 | p = period / (2*PI); 20 | 21 | for (i = [1:resolution]) { 22 | idx_prev = (i - 1) * dx; 23 | idx_curr = i * dx; 24 | hull() { 25 | translate(v = [idx_prev, amplitudeFunction(idx_prev) * sinR(p * idx_prev + shift), 0]) 26 | cube(size = [0.1, 2, 2]); 27 | 28 | translate(v = [idx_curr, amplitudeFunction(idx_curr) * sinR(p * idx_curr + shift), 0]) 29 | cube(size = [0.1, 2, 2]); 30 | 31 | translate(v = [idx_curr, -10, 0]) 32 | cube(size=[0.1,2,2]); 33 | translate(v = [idx_prev, -10, 0]) 34 | cube(size=[0.1,2,2]); 35 | } 36 | } 37 | } 38 | 39 | module sineWaveHull(length, resolution, amplitudeFunction, period, shift, hullDiff) { 40 | dx = length / resolution; 41 | p = period / (2*PI); 42 | 43 | for (i = [1:resolution]) { 44 | idx_prev = (i - 1) * dx; 45 | idx_curr = i * dx; 46 | hull() { 47 | translate(v = [idx_prev, amplitudeFunction(idx_prev) * sinR(p * idx_prev + shift), 0]) 48 | cube(size = [0.1, 2, 2]); 49 | 50 | translate(v = [idx_curr, amplitudeFunction(idx_curr) * sinR(p * idx_curr + shift), 0]) 51 | cube(size = [0.1, 2, 2]); 52 | 53 | translate(v = [idx_curr, -hullDiff, 0]) 54 | cube(size=[0.1,2,2]); 55 | translate(v = [idx_prev, -hullDiff, 0]) 56 | cube(size=[0.1,2,2]); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /media/bom/dowel.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/dowel.gif -------------------------------------------------------------------------------- /media/bom/fhcs_long.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/fhcs_long.gif -------------------------------------------------------------------------------- /media/bom/fhcs_medium.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/fhcs_medium.gif -------------------------------------------------------------------------------- /media/bom/fhcs_short.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/fhcs_short.gif -------------------------------------------------------------------------------- /media/bom/glue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/glue.gif -------------------------------------------------------------------------------- /media/bom/hex_nut.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/hex_nut.gif -------------------------------------------------------------------------------- /media/bom/magnet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/bom/magnet.gif -------------------------------------------------------------------------------- /media/features/slideOpt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/features/slideOpt.gif -------------------------------------------------------------------------------- /media/features/stackConnectorOpt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/features/stackConnectorOpt.gif -------------------------------------------------------------------------------- /media/features/swivelOpt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/features/swivelOpt.gif -------------------------------------------------------------------------------- /media/renders/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/renders/output.png -------------------------------------------------------------------------------- /media/renders/rackDisplayRounded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/media/renders/rackDisplayRounded.png -------------------------------------------------------------------------------- /misc/animate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # simple bash script to animate the frame dump from openscad 3 | 4 | name="anim" 5 | png_dir="./assembly-guide/gifs/tmp" 6 | target_dir="./assembly-guide/gifs" 7 | 8 | if [ -n "$1" ]; then 9 | name="$1" 10 | fi 11 | 12 | if [ -n "$2" ]; then 13 | png_dir="$2" 14 | fi 15 | 16 | if [ -n "$3" ]; then 17 | target_dir="$3" 18 | fi 19 | 20 | filename_without_extension="${name%.*}" 21 | 22 | # needs tomorrow night openscad theme 23 | # convert -resize 20% -delay 6 -loop 0 -transparent "#1d1f21" -dispose previous frame000*.png "$filename_without_extension".gif 24 | 25 | convert -delay 6 -loop 0 -dispose previous "$png_dir"/"$filename_without_extension"000*.png "$target_dir"/"$filename_without_extension".gif 26 | 27 | #gifsicle -O3 --colors=64 --scale 0.5 -i "$target_dir"/"$filename_without_extension".gif -o "$target_dir"/"$filename_without_extension".gif 28 | gifsicle -O3 --colors=64 --no-warnings --scale 0.5 -i "$target_dir"/"$filename_without_extension".gif -o "$target_dir"/"$filename_without_extension".gif -------------------------------------------------------------------------------- /misc/bomAnimations.scad: -------------------------------------------------------------------------------- 1 | include <../helper/screws.scad> 2 | include <../helper/magnet.scad> 3 | include <../helper/dowel.scad> 4 | 5 | $vpr=[85,0,25]; 6 | //$vpd=40; 7 | $vpd=30; 8 | //$vpd=25; 9 | $vpf=20; 10 | 11 | 12 | glue(tilt=15,r=360*$t); 13 | //m3FHCS(length=8,tilt=45,r=360*$t); 14 | // magnet(tilt=90, r=360*$t); 15 | // hingeDowel(tilt=90,r=360*$t); 16 | // m3Hex(tilt=90,r=360*$t); 17 | 18 | metalColor = [0.8,0.8,0.8]; 19 | 20 | module m3FHCS(length, tilt, r) { 21 | rotate(a=[0,0,r]) 22 | rotate(a=[tilt,0,0]) 23 | translate(v=[0,0,length/2]) 24 | color(metalColor) { 25 | difference() { 26 | counterSunkHead_N("m3", screwExtension = length-m3CounterSunkHeadLength, headExtension = 0.5); 27 | cylinder($fn = 6, r = 1.2); 28 | } 29 | } 30 | } 31 | 32 | module m3Hex(tilt, r) { 33 | 34 | rotate(a=[0,0,r]) 35 | rotate(a=[tilt,0,0]) 36 | color(metalColor) 37 | hexNut("m3"); 38 | } 39 | 40 | module hingeDowel(tilt, r) { 41 | rotate(a=[0,0,r]) 42 | rotate(a=[tilt,0,0]) 43 | translate(v=[0,0,-dowelPinH/2]) 44 | color(metalColor) 45 | cylinder(h = dowelPinH, r = dowelPinR); 46 | } 47 | 48 | 49 | module magnet(tilt, r) { 50 | rotate(a=[0,0,r]) 51 | rotate(a=[tilt,0,0]) 52 | translate(v=[0,0,-magnetH/2]) 53 | color(metalColor) 54 | cylinder(r = magnetR, h = magnetH); 55 | } 56 | 57 | module glue(tilt,r) { 58 | 59 | rotate(a=[0,0,r]) 60 | rotate(a=[0,tilt,0]) 61 | 62 | union() { 63 | // cap 64 | color([0.3,0.3,1]) 65 | translate(v = [0, 0, 4]) 66 | union() { 67 | cylinder(r1 = 0.8, r2 = 0.6, h = 2); 68 | 69 | gills = 8; 70 | for (i = [0:gills]) { 71 | translate(v = [0, 0, -0.1]) 72 | rotate(a = [0, 0, 360/gills*i]) 73 | rotate(a = [0, -6.5, 0]) 74 | cube(size = [0.9, 0.1, 2]); 75 | } 76 | } 77 | 78 | // body 79 | color([0.9,0.5,0.1]) 80 | hull() { 81 | translate(v = [0, 0, 4]) 82 | cylinder(r = 0.4, h = 0.1); 83 | 84 | translate(v = [0, 0, 3]) 85 | scale(v = [1.2, 1, 1]) 86 | cylinder(r = 1, h = 0.1); 87 | 88 | scale(v = [1.5, 1, 1]) 89 | cylinder(r = 1, h = 0.1); 90 | translate(v = [0, 0, -3]) 91 | cube(size = [4, 0.2, 0.1], center = true); 92 | } 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /rack-mount/angle-bracket/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Angle Bracket System 3 | 4 | 5 | ![demo](./animate.gif) 6 | 7 | Please see [entry.scad](./entry.scad) for config and building. Make sure the correct rack profile is configured in [rackFrame.scad](../../config/rackFrame.scad). 8 | 9 | ### BOM: 10 | 11 | | Item | Name | Quantity | Comment | 12 | |---------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------| 13 | | | M3x12 FHCS | 8 | Extras are useful and inexpensive. If you plan on eventually stacking multiple racks together, don't hesitate to get a lot more. | 14 | | | M3 hex nut | 8 | ☝️ | -------------------------------------------------------------------------------- /rack-mount/angle-bracket/animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/rack-mount/angle-bracket/animate.gif -------------------------------------------------------------------------------- /rack-mount/angle-bracket/animate.scad: -------------------------------------------------------------------------------- 1 | use <./entry.scad> 2 | 3 | $vpt = [120,-30,63]; 4 | $vpr = [74,0,25]; 5 | $vpd = 550; 6 | $vpf = 22.50; 7 | 8 | animateAngleBrackets(at=$t); 9 | 10 | 11 | module animateAngleBrackets(at=$t) { 12 | 13 | x = 150; 14 | y = 70; 15 | dx = abs(lerp(a=-20, b=20, t=at)); 16 | dy = abs(lerp(a=-50, b=50, t=at)); 17 | 18 | u = abs(at - 0.5) > 0.25 ? 3 : 4; 19 | 20 | angleBrackets(visualize=true, boxWidth=x+dx, boxDepth=y+dy, u=u); 21 | 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /rack-mount/angle-bracket/entry.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | 3 | use <../enclosed-box/sideRail.scad> 4 | 5 | 6 | /* 7 | Simple angle bracket mounting system. Mainly derived the enclosed box system. 8 | */ 9 | module angleBrackets ( 10 | // begin config //////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | // Does not affect any part dimensions. Set this to true to visualize how a box would be mounted. 13 | visualize = false, 14 | 15 | 16 | thickness = 3, 17 | boxWidth = 160, 18 | boxDepth = 120, 19 | sideVent = false, 20 | u = 3 21 | 22 | // end config ////////////////////////////////////////////////////////////////////////////////////////////////////////// 23 | ) { 24 | 25 | sideSupportRailBase(top=false, defaultThickness=thickness, railSideThickness=thickness, supportedZ=10*u-2*thickness, supportedY=boxDepth, supportedX=boxWidth, sideVent=sideVent); 26 | 27 | rightRailTrans = visualize 28 | ? translate(v=[boxWidth,0,0]) * mirror(v=[1,0,0]) 29 | : translate(v=[30,0,0]) * mirror(v=[1,0,0]); 30 | 31 | multmatrix(rightRailTrans) 32 | sideSupportRailBase(top=false, defaultThickness=thickness, railSideThickness=thickness, supportedZ=10*u-2*thickness, supportedY=boxDepth, supportedX=boxWidth, sideVent=sideVent); 33 | } 34 | 35 | 36 | angleBrackets(); 37 | -------------------------------------------------------------------------------- /rack-mount/catalog/120mm-Rear-Case-Fan.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a 120mm Case Fan. 5 | Not compatible with nano rack configuration. 6 | Designed to be mounted on the back in a reverse position, with the fan sitting on the outside of the case rather than the inside. 7 | Mesh Screens are advised on the fore and aft of the fan. 8 | Powered by USB to 3 or 2-pin connectors. 9 | MINIMUM HEIGHT IS 13 TOTAL U's, which is showcased as u = 12 in rackstack. 10 | 11 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 12 | */ 13 | 14 | 15 | difference () { 16 | bottomScrewTray ( 17 | u = 12, 18 | trayWidth = 130, 19 | trayDepth = 35, 20 | trayThickness = 3, 21 | frontLipHeight = 126, 22 | backLipHeight = 126, 23 | // mountPoints = [[15, 15], [15+105, 15],[15, 15+105], [15+105, 15+105]], 24 | frontThickness = 3, 25 | sideThickness = 3, 26 | mountPointElevation = 1, 27 | mountPointType = "m4", 28 | sideSupport = true, 29 | trayLeftPadding = 25 30 | ); 31 | 32 | translate (v=[-5,10,-5]) 33 | rotate (a=[90,0,0]) { 34 | // Center Cutout 35 | translate(v=[67.5, 67.5, -25]) 36 | rotate(a=[90,0,0]) 37 | rotate(a=[0,90,90]) 38 | cylinder(h=60, r1=60, r2=60, $fn=75); 39 | 40 | // fan screw hole 1 41 | translate(v=[15, 15, -25]) 42 | rotate(a=[90,0,0]) 43 | rotate(a=[0,90,90]) 44 | cylinder(h=65, r1=2.5, r2=2.5, $fn=25); 45 | 46 | // fan screw hole 2 47 | translate(v=[15+105, 15, -25]) 48 | rotate(a=[90,0,0]) 49 | rotate(a=[0,90,90]) 50 | cylinder(h=45, r1=2.5, r2=2.5, $fn=25); 51 | 52 | // fan screw hole 3 53 | translate(v=[15+105, 15+105, -25]) 54 | rotate(a=[90,0,0]) 55 | rotate(a=[0,90,90]) 56 | cylinder(h=65, r1=2.5, r2=2.5, $fn=25); 57 | 58 | // fan screw hole 4 59 | translate(v=[15, 15+105, -25]) 60 | rotate(a=[90,0,0]) 61 | rotate(a=[0,90,90]) 62 | cylinder(h=65, r1=2.5, r2=2.5, $fn=25); 63 | } 64 | } -------------------------------------------------------------------------------- /rack-mount/catalog/120mm-fan-tray_mod.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a 120mm Case Fan. 5 | Not compatible with nano rack configuration. 6 | 7 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 8 | */ 9 | 10 | 11 | difference () { 12 | bottomScrewTray ( 13 | u = 2, 14 | trayWidth = 140, 15 | trayDepth = 140, 16 | trayThickness = 3, 17 | frontLipHeight = 26, 18 | backLipHeight = 6, 19 | mountPoints = [[15, 15], [15+105, 15],[15, 15+105], [15+105, 15+105]], 20 | frontThickness = 3, 21 | sideThickness = 3, 22 | mountPointElevation = 1, 23 | mountPointType = "m4", 24 | sideSupport = true, 25 | trayLeftPadding = 15 26 | ); 27 | 28 | // Center Cutout 29 | translate(v=[67.5, 67.5, -50]) 30 | rotate(a=[90,0,0]) 31 | rotate(a=[0,90,90]) 32 | cylinder(h=60, r1=60, r2=60, $fn=75); 33 | 34 | // fan screw hole 1 35 | translate(v=[15, 15, -5]) 36 | rotate(a=[90,0,0]) 37 | rotate(a=[0,90,90]) 38 | cylinder(h=15.5, r1=2.5, r2=2.5, $fn=25); 39 | 40 | // fan screw hole 2 41 | translate(v=[15+105, 15, -5]) 42 | rotate(a=[90,0,0]) 43 | rotate(a=[0,90,90]) 44 | cylinder(h=15.5, r1=2.5, r2=2.5, $fn=25); 45 | 46 | // fan screw hole 3 47 | translate(v=[15+105, 15+105, -5]) 48 | rotate(a=[90,0,0]) 49 | rotate(a=[0,90,90]) 50 | cylinder(h=15.5, r1=2.5, r2=2.5, $fn=25); 51 | 52 | // fan screw hole 4 53 | translate(v=[15, 15+105, -5]) 54 | rotate(a=[90,0,0]) 55 | rotate(a=[0,90,90]) 56 | cylinder(h=15.5, r1=2.5, r2=2.5, $fn=25); 57 | } -------------------------------------------------------------------------------- /rack-mount/catalog/LG_Slim_DVD_Tray.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a LG Slim Portable DVD Writer. 5 | 6 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 7 | 8 | Remember to use Velcro strips to secure the drive to the tray! 9 | */ 10 | 11 | bottomScrewTray ( 12 | u = 1, 13 | trayWidth = 150, 14 | trayDepth = 145, 15 | trayThickness = 3, 16 | frontLipHeight = 0, 17 | backLipHeight = 0, 18 | // mountPoints = [[27.5, 34], [107, 34]], 19 | frontThickness = 3, 20 | sideThickness = 3, 21 | mountPointElevation = 1, 22 | mountPointType = "m3", 23 | sideSupport = true, 24 | trayLeftPadding = 15 25 | ); 26 | -------------------------------------------------------------------------------- /rack-mount/catalog/Minisforum_NAB9-tray_mod.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a MinisForum NAB9 mini PC. 5 | Not compatible with nano rack configuration. 6 | 7 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 8 | */ 9 | 10 | difference () { 11 | bottomScrewTray ( 12 | u = 5, 13 | trayWidth = 140, 14 | trayDepth = 140, 15 | trayThickness = 3, 16 | frontLipHeight = 3, 17 | backLipHeight = 5, 18 | mountPoints = [[20, 70], [20+95.25, 70]], 19 | frontThickness = 3, 20 | sideThickness = 3, 21 | mountPointElevation = 1, 22 | mountPointType = "m4", 23 | sideSupport = true, 24 | trayLeftPadding = 15 25 | ); 26 | 27 | // Side perforations for airflow 28 | translate(v=[-4, 5, 18]) { 29 | rotate(a=[0,90,0]) 30 | rotate(a=[0,0,45]) 31 | cube(size=[18, 18, 155]); 32 | 33 | translate(v=[0, 28, -04]) 34 | rotate(a=[0,90,0]) 35 | rotate(a=[0,0,45]) 36 | cube(size=[12,12,150]); 37 | 38 | translate(v=[0, 48, -06.5]) 39 | rotate(a=[0,90,0]) 40 | rotate(a=[0,0,45]) 41 | cube(size=[8,8,150]); 42 | 43 | 44 | // Bottom perforations for airflow 45 | // Front Right Cube 46 | translate(v=[60, 40, -50]) 47 | rotate(a=[90,0,0]) 48 | rotate(a=[0,90,90]) 49 | cube(size=[12,12,135]); 50 | 51 | // Front Center Cube 52 | translate(v=[80, 35, -50]) 53 | rotate(a=[90,0,45]) 54 | rotate(a=[0,90,90]) 55 | cube(size=[12,12,135]); 56 | 57 | // Front Left Cube 58 | translate(v=[95, 40, -50]) 59 | rotate(a=[90,0,0]) 60 | rotate(a=[0,90,90]) 61 | cube(size=[12,12,135]); 62 | 63 | // Center Right Cube 64 | translate(v=[50.5, 65, -50]) 65 | rotate(a=[90,0,45]) 66 | rotate(a=[0,90,90]) 67 | cube(size=[12,12,135]); 68 | 69 | // Center Left Cube 70 | translate(v=[110, 65, -50]) 71 | rotate(a=[90,0,45]) 72 | rotate(a=[0,90,90]) 73 | cube(size=[12,12,135]); 74 | 75 | // Rear Left Cube 76 | translate(v=[60, 75, -50]) 77 | rotate(a=[90,0,0]) 78 | rotate(a=[0,90,90]) 79 | cube(size=[12,12,135]); 80 | 81 | // Rear Center Cube 82 | translate(v=[80, 92, -50]) 83 | rotate(a=[90,0,45]) 84 | rotate(a=[0,90,90]) 85 | cube(size=[12,12,135]); 86 | 87 | // Rear Right Cube 88 | translate(v=[96, 75, -50]) 89 | rotate(a=[90,0,0]) 90 | rotate(a=[0,90,90]) 91 | cube(size=[12,12,135]); 92 | 93 | // Far Rear Right Cube 94 | translate(v=[100, 110, -50]) 95 | rotate(a=[90,0,0]) 96 | rotate(a=[0,90,90]) 97 | cube(size=[12,25,135]); 98 | 99 | // Far Rear Left Cube 100 | translate(v=[67.5, 110, -50]) 101 | rotate(a=[90,0,0]) 102 | rotate(a=[0,90,90]) 103 | cube(size=[12,25,135]); 104 | 105 | // Far Front Right Cube 106 | translate(v=[100, 3, -50]) 107 | rotate(a=[90,0,0]) 108 | rotate(a=[0,90,90]) 109 | cube(size=[12,25,135]); 110 | 111 | // Far Front Left Cube 112 | translate(v=[67.5, 3, -50]) 113 | rotate(a=[90,0,0]) 114 | rotate(a=[0,90,90]) 115 | cube(size=[12,25,135]); 116 | 117 | // Side Front Right Cube 118 | translate(v=[125, 25, -50]) 119 | rotate(a=[90,0,0]) 120 | rotate(a=[0,90,90]) 121 | cube(size=[25,12,135]); 122 | 123 | // Side Front Left Cube 124 | translate(v=[30, 25, -50]) 125 | rotate(a=[90,0,0]) 126 | rotate(a=[0,90,90]) 127 | cube(size=[25,12,135]); 128 | 129 | // Side Rear Right Cube 130 | translate(v=[125, 80, -50]) 131 | rotate(a=[90,0,0]) 132 | rotate(a=[0,90,90]) 133 | cube(size=[25,12,135]); 134 | 135 | // Side Rear Left Cube 136 | translate(v=[30, 80, -50]) 137 | rotate(a=[90,0,0]) 138 | rotate(a=[0,90,90]) 139 | cube(size=[25,12,135]); 140 | 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /rack-mount/catalog/Minisforum_UM890PRO-tray_mod.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a MinisForum UM890 PRO mini pc. 5 | Not compatible with nano rack configuration. 6 | 7 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 8 | */ 9 | 10 | difference () { 11 | bottomScrewTray ( 12 | u = 6, 13 | trayWidth = 140, 14 | trayDepth = 140, 15 | trayThickness = 3, 16 | frontLipHeight = 3, 17 | backLipHeight = 5, 18 | mountPoints = [[67.5, 115], [67.5, 115-95.25]], 19 | frontThickness = 3, 20 | sideThickness = 3, 21 | mountPointElevation = 1, 22 | mountPointType = "m4", 23 | sideSupport = true, 24 | trayLeftPadding = 15 25 | ); 26 | 27 | // Side perforations for airflow 28 | translate(v=[-4, 5, 25]) { 29 | rotate(a=[0,90,0]) 30 | rotate(a=[0,0,45]) 31 | cube(size=[25, 25, 155]); 32 | 33 | 34 | translate(v=[0, 38.25, -08]) 35 | rotate(a=[0,90,0]) 36 | rotate(a=[0,0,45]) 37 | cube(size=[15,15,150]); 38 | 39 | translate(v=[0, 65, -12]) 40 | rotate(a=[0,90,0]) 41 | rotate(a=[0,0,45]) 42 | cube(size=[10,10,150]); 43 | 44 | // Bottom perforations for airflow 45 | // Front Right Cube 46 | translate(v=[60, 40, -50]) 47 | rotate(a=[90,0,0]) 48 | rotate(a=[0,90,90]) 49 | cube(size=[12,12,135]); 50 | 51 | // Front Center Cube 52 | translate(v=[80, 35, -50]) 53 | rotate(a=[90,0,45]) 54 | rotate(a=[0,90,90]) 55 | cube(size=[12,12,135]); 56 | 57 | // Front Left Cube 58 | translate(v=[95, 40, -50]) 59 | rotate(a=[90,0,0]) 60 | rotate(a=[0,90,90]) 61 | cube(size=[12,12,135]); 62 | 63 | // Center Right Cube 64 | translate(v=[50.5, 65, -50]) 65 | rotate(a=[90,0,45]) 66 | rotate(a=[0,90,90]) 67 | cube(size=[12,12,135]); 68 | 69 | // Center Left Cube 70 | translate(v=[110, 65, -50]) 71 | rotate(a=[90,0,45]) 72 | rotate(a=[0,90,90]) 73 | cube(size=[12,12,135]); 74 | 75 | // Rear Left Cube 76 | translate(v=[60, 75, -50]) 77 | rotate(a=[90,0,0]) 78 | rotate(a=[0,90,90]) 79 | cube(size=[12,12,135]); 80 | 81 | // Rear Center Cube 82 | translate(v=[80, 92, -50]) 83 | rotate(a=[90,0,45]) 84 | rotate(a=[0,90,90]) 85 | cube(size=[12,12,135]); 86 | 87 | // Rear Right Cube 88 | translate(v=[96, 75, -50]) 89 | rotate(a=[90,0,0]) 90 | rotate(a=[0,90,90]) 91 | cube(size=[12,12,135]); 92 | 93 | // Far Rear Right Cube 94 | translate(v=[110, 110, -50]) 95 | rotate(a=[90,0,0]) 96 | rotate(a=[0,90,90]) 97 | cube(size=[12,25,135]); 98 | 99 | // Far Rear Left Cube 100 | translate(v=[57.5, 110, -50]) 101 | rotate(a=[90,0,0]) 102 | rotate(a=[0,90,90]) 103 | cube(size=[12,25,135]); 104 | 105 | // Far Front Right Cube 106 | translate(v=[110, 3, -50]) 107 | rotate(a=[90,0,0]) 108 | rotate(a=[0,90,90]) 109 | cube(size=[12,25,135]); 110 | 111 | // Far Front Left Cube 112 | translate(v=[57.5, 3, -50]) 113 | rotate(a=[90,0,0]) 114 | rotate(a=[0,90,90]) 115 | cube(size=[12,25,135]); 116 | 117 | // Side Front Right Cube 118 | translate(v=[125, 25, -50]) 119 | rotate(a=[90,0,0]) 120 | rotate(a=[0,90,90]) 121 | cube(size=[25,12,135]); 122 | 123 | // Side Front Left Cube 124 | translate(v=[30, 25, -50]) 125 | rotate(a=[90,0,0]) 126 | rotate(a=[0,90,90]) 127 | cube(size=[25,12,135]); 128 | 129 | // Side Rear Right Cube 130 | translate(v=[125, 80, -50]) 131 | rotate(a=[90,0,0]) 132 | rotate(a=[0,90,90]) 133 | cube(size=[25,12,135]); 134 | 135 | // Side Rear Left Cube 136 | translate(v=[30, 80, -50]) 137 | rotate(a=[90,0,0]) 138 | rotate(a=[0,90,90]) 139 | cube(size=[25,12,135]); 140 | 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /rack-mount/catalog/README.md: -------------------------------------------------------------------------------- 1 | # Rack Mount Catalog 2 | 3 | A hodgepodge of various pre-configured rack mount parts. 4 | 5 | Please feel free to submit a pull request for any new parts you end up creating! Code quality is no object here. 6 | -------------------------------------------------------------------------------- /rack-mount/catalog/Unifi-8-PoE-Switch_Tray+Adapter.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a Unifi Lite 8PoE Switch. 5 | Included mounting adapter, mounting adapter seats two M3 Hex nuts using two M3x8 screws. 6 | DO NOT USE INCLUDED SCREWS FROM UNIFI, this will damage the switch. 7 | 8 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 9 | */ 10 | 11 | 12 | /////////////////////////////////////// 13 | /////////// MOUNTING TRAY //////////// 14 | ///////////////////////////////////// 15 | difference () { 16 | bottomScrewTray ( 17 | u = 3, 18 | trayWidth = 175, 19 | trayDepth = 110, 20 | trayThickness = 3, 21 | frontLipHeight = 7.5, 22 | backLipHeight = 7.5, 23 | mountPoints = [[54, 40], [54+60.325, 40]], 24 | frontThickness = 3, 25 | sideThickness = 3, 26 | mountPointElevation = 1, 27 | mountPointType = "m3", 28 | sideSupport = true, 29 | trayLeftPadding = 2.5 30 | ); 31 | 32 | // Front Center Cube 33 | translate(v=[87.5, 20, -50]) 34 | rotate(a=[90,0,45]) 35 | rotate(a=[0,90,90]) 36 | cube(size=[12,12,135]); 37 | 38 | // Center Right Cube 39 | translate(v=[58, 50, -50]) 40 | rotate(a=[90,0,45]) 41 | rotate(a=[0,90,90]) 42 | cube(size=[12,12,135]); 43 | 44 | // Center Left Cube 45 | translate(v=[117.5, 50, -50]) 46 | rotate(a=[90,0,45]) 47 | rotate(a=[0,90,90]) 48 | cube(size=[12,12,135]); 49 | 50 | // Rear Left Cube 51 | translate(v=[67.5, 60, -50]) 52 | rotate(a=[90,0,0]) 53 | rotate(a=[0,90,90]) 54 | cube(size=[12,12,135]); 55 | 56 | // Rear Center Cube 57 | translate(v=[87.5, 77, -50]) 58 | rotate(a=[90,0,45]) 59 | rotate(a=[0,90,90]) 60 | cube(size=[12,12,135]); 61 | 62 | // Rear Right Cube 63 | translate(v=[103.5, 60, -50]) 64 | rotate(a=[90,0,0]) 65 | rotate(a=[0,90,90]) 66 | cube(size=[12,12,135]); 67 | 68 | 69 | // Side Front Right Cube 70 | translate(v=[132.5, 10, -50]) 71 | rotate(a=[90,0,0]) 72 | rotate(a=[0,90,90]) 73 | cube(size=[25,12,135]); 74 | 75 | // Side Front Left Cube 76 | translate(v=[37.5, 10, -50]) 77 | rotate(a=[90,0,0]) 78 | rotate(a=[0,90,90]) 79 | cube(size=[25,12,135]); 80 | 81 | // Side Rear Right Cube 82 | translate(v=[132.5, 65, -50]) 83 | rotate(a=[90,0,0]) 84 | rotate(a=[0,90,90]) 85 | cube(size=[25,12,135]); 86 | 87 | // Side Rear Left Cube 88 | translate(v=[38.5, 65, -50]) 89 | rotate(a=[90,0,0]) 90 | rotate(a=[0,90,90]) 91 | cube(size=[25,12,135]); 92 | 93 | } 94 | 95 | /////////////////////////////////////// 96 | ////////// MOUNTING ADAPTER ////////// 97 | ///////////////////////////////////// 98 | 99 | // Seat Teeth 100 | translate (v= [0,1.5,0.1,]){ 101 | translate(v=[54.25 + 15 -1.25, 5.5 + 13.51875, 10+2.25]) { 102 | cube(size=[1.5875, 6.5, 1.5875]); 103 | } 104 | translate(v=[54.25 + 20 -1.25, 5.5 + 13.51875, 10+2.25]) { 105 | cube(size=[1.5875, 6.5, 1.5875]); 106 | } 107 | translate(v=[54.25 + 25 -1.25, 5.5 + 13.51875, 10+2.25]) { 108 | cube(size=[1.5875, 6.5, 1.5875]); 109 | } 110 | 111 | translate(v=[54.25 + 15 -1.25 + 22, 5.5 + 13.51875, 10+2.25]) { 112 | cube(size=[1.5875, 6.5, 1.5875]); 113 | } 114 | translate(v=[54.25 + 20 -1.25 + 22, 5.5 + 13.51875, 10+2.25]) { 115 | cube(size=[1.5875, 6.5, 1.5875]); 116 | } 117 | translate(v=[54.25 + 25 -1.25 + 22, 5.5 + 13.51875, 10+2.25]) { 118 | cube(size=[1.5875, 6.5, 1.5875]); 119 | } 120 | } 121 | 122 | // Seat Key 123 | difference () { 124 | translate(v=[54.25, 5.5 + 13.51875, 10+3.175]) { 125 | cube(size=[59.53125, 9.5, 1.5875]); 126 | } 127 | translate(v=[45 + 38.89375, 19, 12]) { 128 | cube(size=[1.5875, 10, 4.7625]); 129 | } 130 | } 131 | 132 | // Body 133 | difference () { 134 | translate(v=[45, 28.5, 9 + 0.5125]) { 135 | cube(size=[77.7875, 23.01875, 5.7625 - 0.5125]); 136 | } 137 | 138 | 139 | // Cut aways 140 | translate(v=[45, 22, 0]) { 141 | rotate(a=[0,0,45]){ 142 | translate(v=[-.25, -.25, 9]) { 143 | cube(size=[9.7525, 9.7525, 10]); 144 | } 145 | } 146 | } 147 | 148 | translate(v=[45+ 77.7875, 22, 0]) { 149 | rotate(a=[0,0,45]){ 150 | translate(v=[-.25, -.25, 9]) { 151 | cube(size=[9.7525, 9.7525, 10]); 152 | } 153 | } 154 | } 155 | // Screw holes 156 | translate(v=[54, 40, -10]) { 157 | cylinder(h=65, r1=1.65, r2=1.65, $fn=25); 158 | } 159 | 160 | translate(v=[54+60.325, 40, -10]) { 161 | cylinder(h=65, r1=1.65, r2=1.65, $fn=25); 162 | } 163 | translate(v=[54+60.325, 40, 12]) { 164 | cylinder(h=65, r1=3.25, r2=3.25, $fn=6); 165 | } 166 | translate(v=[54, 40, 12]) { 167 | cylinder(h=65, r1=3.25, r2=3.25, $fn=6); 168 | } 169 | } -------------------------------------------------------------------------------- /rack-mount/catalog/beelink-EQi12-box_mod.scad: -------------------------------------------------------------------------------- 1 | include <../../rack/sharedVariables.scad> 2 | include <../common.scad> 3 | include <../enclosed-box/helper.scad> 4 | 5 | use <../enclosed-box/sideRail.scad> 6 | use <../enclosed-box/frontBoxHolder.scad> 7 | 8 | /* 9 | Enclosed box mounting system: 10 | Helper file to use semi-enclosed side rails and a front plate to mount a box. 11 | This system does not require any mounting holes on the enclosed box. 12 | 13 | !!! Please also make sure that the correct rack frame preset is set in rackFrame.scad !!! 14 | */ 15 | module enclosedBoxSystem ( 16 | 17 | // begin config //////////////////////////////////////////////////////////////////////////////////////////////////////// 18 | 19 | // Does not affect any part dimensions. Set this to true to visualize how a box would be mounted. 20 | visualize = false, 21 | zOrientation = "middle", // ["middle" | "bottom"] 22 | recessSideRail = false, 23 | 24 | boxWidth = 126, 25 | boxHeight = 44.5, 26 | boxDepth = 126, 27 | 28 | railDefaultThickness = 1.5, 29 | railSideThickness = 3, 30 | 31 | frontPlateThickness = 3, 32 | frontPlateCutoutYSpace = 3, 33 | frontPlateCutoutXSpace = 5, 34 | 35 | // end config ////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 | 37 | ) { 38 | leftRailTrans = identity; 39 | rightRailTrans = visualize 40 | ? translate(v = [boxWidth, 0, 0])*mirror(v = [1, 0, 0]) 41 | : translate(v = [sideRailBaseWidth*2, 0, 0])*mirror(v = [1, 0, 0]); 42 | 43 | u = findU(boxHeight, railDefaultThickness); 44 | railBottomThickness = railBottomThickness(u, boxHeight, railDefaultThickness, zOrientation); 45 | frontBoxHolderTrans = visualize 46 | ? translate(v = [railSideThickness-(railSupportsDx-boxWidth)/2, 0, sideRailLowerMountPointToBottom- 47 | railBottomThickness])*mirror(v = [0, 1, 0])*rotate(a = [90, 0, 0]) 48 | : mirror(v = [0, 1, 0])*translate(v = [0, uDiff, frontPlateThickness-railBottomThickness]); 49 | 50 | if (visualize) { 51 | %cube(size = [boxWidth, boxDepth, boxHeight]); 52 | } 53 | 54 | multmatrix(leftRailTrans) 55 | sideSupportRailBase(top = true, recess = recessSideRail, defaultThickness = railDefaultThickness, supportedZ = 56 | boxHeight, supportedY = boxDepth, supportedX = boxWidth, zOrientation = zOrientation, railSideThickness = 57 | railSideThickness); 58 | 59 | multmatrix(rightRailTrans) 60 | sideSupportRailBase(top = true, recess = recessSideRail, defaultThickness = railDefaultThickness, supportedZ = 61 | boxHeight, supportedY = boxDepth, supportedX = boxWidth, zOrientation = zOrientation, railSideThickness = 62 | railSideThickness); 63 | 64 | multmatrix(frontBoxHolderTrans) 65 | frontBoxHolder( 66 | cutoutOffsetX = (rackMountScrewWidth-(boxWidth-2*frontPlateCutoutXSpace))/2, cutoutOffsetY = railBottomThickness+ 67 | frontPlateCutoutYSpace, 68 | cutoutX = boxWidth-2*frontPlateCutoutXSpace, cutoutY = boxHeight-2*frontPlateCutoutYSpace, 69 | zOrientation = zOrientation, supportedZ = boxHeight, supportWidth = max(10, boxWidth-(sideRailBaseWidth+10)), 70 | supportRailDefaultThickness = railDefaultThickness, plateThickness = frontPlateThickness 71 | ); 72 | } 73 | 74 | enclosedBoxSystem(); 75 | -------------------------------------------------------------------------------- /rack-mount/catalog/beelink-EQi12-tray_mod.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a beelink mini-s series mini pc. 5 | 6 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 7 | */ 8 | 9 | difference () { 10 | bottomScrewTray ( 11 | u = 5, 12 | trayWidth = 140, 13 | trayDepth = 140, 14 | trayThickness = 3, 15 | frontLipHeight = 3, 16 | backLipHeight = 5, 17 | frontThickness = 3, 18 | sideThickness = 3, 19 | mountPointElevation = 1, 20 | mountPointType = "m4", 21 | sideSupport = true, 22 | trayLeftPadding = 15 23 | ); 24 | 25 | 26 | // Side perforations for airflow 27 | translate(v=[-4, 5, 18]) { 28 | rotate(a=[0,90,0]) 29 | rotate(a=[0,0,45]) 30 | cube(size=[18, 18, 155]); 31 | 32 | translate(v=[0, 28, -04]) 33 | rotate(a=[0,90,0]) 34 | rotate(a=[0,0,45]) 35 | cube(size=[12,12,150]); 36 | 37 | translate(v=[0, 48, -06.5]) 38 | rotate(a=[0,90,0]) 39 | rotate(a=[0,0,45]) 40 | cube(size=[8,8,150]); 41 | 42 | 43 | // Bottom perforations for airflow 44 | // Front Right Cube 45 | translate(v=[60, 40, -50]) 46 | rotate(a=[90,0,0]) 47 | rotate(a=[0,90,90]) 48 | cube(size=[12,12,135]); 49 | 50 | // Front Center Cube 51 | translate(v=[80, 35, -50]) 52 | rotate(a=[90,0,45]) 53 | rotate(a=[0,90,90]) 54 | cube(size=[12,12,135]); 55 | 56 | // Front Left Cube 57 | translate(v=[95, 40, -50]) 58 | rotate(a=[90,0,0]) 59 | rotate(a=[0,90,90]) 60 | cube(size=[12,12,135]); 61 | 62 | // Center Right Cube 63 | translate(v=[50.5, 65, -50]) 64 | rotate(a=[90,0,45]) 65 | rotate(a=[0,90,90]) 66 | cube(size=[12,12,135]); 67 | 68 | // Center Left Cube 69 | translate(v=[110, 65, -50]) 70 | rotate(a=[90,0,45]) 71 | rotate(a=[0,90,90]) 72 | cube(size=[12,12,135]); 73 | 74 | // Rear Left Cube 75 | translate(v=[60, 75, -50]) 76 | rotate(a=[90,0,0]) 77 | rotate(a=[0,90,90]) 78 | cube(size=[12,12,135]); 79 | 80 | // Rear Center Cube 81 | translate(v=[80, 92, -50]) 82 | rotate(a=[90,0,45]) 83 | rotate(a=[0,90,90]) 84 | cube(size=[12,12,135]); 85 | 86 | // Rear Right Cube 87 | translate(v=[96, 75, -50]) 88 | rotate(a=[90,0,0]) 89 | rotate(a=[0,90,90]) 90 | cube(size=[12,12,135]); 91 | 92 | // Far Rear Right Cube 93 | translate(v=[100, 110, -50]) 94 | rotate(a=[90,0,0]) 95 | rotate(a=[0,90,90]) 96 | cube(size=[12,25,135]); 97 | 98 | // Far Rear Left Cube 99 | translate(v=[67.5, 110, -50]) 100 | rotate(a=[90,0,0]) 101 | rotate(a=[0,90,90]) 102 | cube(size=[12,25,135]); 103 | 104 | // Far Front Right Cube 105 | translate(v=[100, 3, -50]) 106 | rotate(a=[90,0,0]) 107 | rotate(a=[0,90,90]) 108 | cube(size=[12,25,135]); 109 | 110 | // Far Front Left Cube 111 | translate(v=[67.5, 3, -50]) 112 | rotate(a=[90,0,0]) 113 | rotate(a=[0,90,90]) 114 | cube(size=[12,25,135]); 115 | 116 | // Side Front Right Cube 117 | translate(v=[125, 25, -50]) 118 | rotate(a=[90,0,0]) 119 | rotate(a=[0,90,90]) 120 | cube(size=[25,12,135]); 121 | 122 | // Side Front Left Cube 123 | translate(v=[30, 25, -50]) 124 | rotate(a=[90,0,0]) 125 | rotate(a=[0,90,90]) 126 | cube(size=[25,12,135]); 127 | 128 | // Side Rear Right Cube 129 | translate(v=[125, 80, -50]) 130 | rotate(a=[90,0,0]) 131 | rotate(a=[0,90,90]) 132 | cube(size=[25,12,135]); 133 | 134 | // Side Rear Left Cube 135 | translate(v=[30, 80, -50]) 136 | rotate(a=[90,0,0]) 137 | rotate(a=[0,90,90]) 138 | cube(size=[25,12,135]); 139 | 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /rack-mount/catalog/beelink-mini-s-tray.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a beelink mini-s series mini pc. 5 | 6 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 7 | */ 8 | 9 | difference () { 10 | bottomScrewTray ( 11 | u = 4, 12 | trayWidth = 130, 13 | trayDepth = 110, 14 | trayThickness = 3, 15 | frontLipHeight = 3, 16 | backLipHeight = 5, 17 | mountPoints = [[44, 8], [44+35, 8]], 18 | frontThickness = 3, 19 | sideThickness = 3, 20 | mountPointElevation = 1, 21 | mountPointType = "m3", 22 | sideSupport = true, 23 | trayLeftPadding = 15 24 | ); 25 | 26 | 27 | // perforations for airflow 28 | translate(v=[-4, 5, 18]) { 29 | rotate(a=[0,90,0]) 30 | rotate(a=[0,0,45]) 31 | cube(size=[18, 18, 135]); 32 | 33 | 34 | translate(v=[0, 28, -4]) 35 | rotate(a=[0,90,0]) 36 | rotate(a=[0,0,45]) 37 | cube(size=[12,12,135]); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /rack-mount/catalog/brush-strip-plate.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | use <../plateBase.scad> 3 | 4 | 5 | /* 6 | A 4U (50mm tall) panel for nylon brush stripping to hide cable shame. 7 | 8 | Brush Weather Stripping (0.35" Wide x 0.35" Thick): https://amzn.to/3CLpWMp 9 | */ 10 | 11 | 12 | 13 | 14 | plateU=4; 15 | plateThickness=3; 16 | brushstripWidth=8.89; 17 | brushstripLength=8.89; 18 | //How far from the edge of the plate the Brush Strip Hole will begin 19 | brushstripOffset=15; 20 | //How far the ledges that the brush strip affix too 21 | brushstripHoleOverlap=5; 22 | 23 | 24 | /* Reckless copying and pasting begins here */ 25 | screwToXEdge=4.5; 26 | screwToYEdge=4.5; 27 | uDiff = screwDiff; 28 | filletR=2; 29 | screwDx = rackMountScrewWidth; 30 | screwDy = uDiff * plateU; 31 | plateLength = screwDx + 2*screwToXEdge; 32 | plateHeight = screwDy + 2*screwToYEdge; 33 | railDefaultThickness = 1.5; 34 | /* End of Reckless copying and pasting? */ 35 | 36 | 37 | brushstripLedgeX=plateLength - ((brushstripOffset-brushstripHoleOverlap)*2); 38 | brushstripLedgeY=5; 39 | brushstripLedgeZ=brushstripWidth*1.2; 40 | 41 | brushstripHoleX=plateLength - (brushstripOffset*2); 42 | brushstripHoleY=brushstripLength*2; 43 | brushstripHoleZ=plateThickness*2; 44 | midPlateY=(plateHeight)/2; 45 | 46 | echo("Plate Height: ", plateHeight); 47 | echo("Plate Length: ", plateLength); 48 | echo("Vertical distance between 2 main rail holes: ", screwDiff); 49 | echo("Horizontal distance between 2 opposing main rail holes: ", rackMountScrewWidth); 50 | echo("Distance between main rail screw, and main rail inner edge:", railScrewHoleToInnerEdge); 51 | echo("Max supported rack-mount width: ", maxUnitWidth); 52 | echo("Max recommended rack-mount depth: ", maxUnitDepth); 53 | 54 | 55 | difference(){ 56 | plateBase(U=plateU, plateThickness=plateThickness, screwToXEdge=4.5, screwToYEdge=5.0, screwType="m4", filletR=2); 57 | translate([brushstripOffset,midPlateY-(brushstripHoleY/2),0]) brushstripHole(); 58 | } 59 | translate([-screwToXEdge+(brushstripOffset-brushstripHoleOverlap),midPlateY-screwToYEdge+(brushstripHoleY/2),0]) cube([brushstripLedgeX, brushstripLedgeY, brushstripLedgeZ]); 60 | translate([-screwToXEdge+(brushstripOffset-brushstripHoleOverlap),midPlateY-screwToYEdge-(brushstripHoleY/2)- brushstripLedgeY,0]) cube([brushstripLedgeX, brushstripLedgeY, brushstripLedgeZ]); 61 | 62 | module brushstripHole(){ 63 | translate([-screwToXEdge,-screwToYEdge,-(plateThickness+1)]) cube([brushstripHoleX, brushstripHoleY, brushstripHoleZ]); 64 | } -------------------------------------------------------------------------------- /rack-mount/catalog/mele-quieter-tray.scad: -------------------------------------------------------------------------------- 1 | use <../tray/tray.scad> 2 | 3 | /* 4 | Simple tray for a mele quieter series mini pc. 5 | 6 | Please make sure to configure the correct rack frame dimensions in rackFrame.scad. 7 | */ 8 | 9 | bottomScrewTray ( 10 | u = 2, 11 | trayWidth = 145, 12 | trayDepth = 88, 13 | trayThickness = 3, 14 | frontLipHeight = 3, 15 | backLipHeight = 2, 16 | mountPoints = [[27.5, 34], [107, 34]], 17 | frontThickness = 3, 18 | sideThickness = 3, 19 | mountPointElevation = 1, 20 | mountPointType = "m3", 21 | sideSupport = true, 22 | trayLeftPadding = 10 23 | ); 24 | -------------------------------------------------------------------------------- /rack-mount/catalog/parts/sfxPSU.scad: -------------------------------------------------------------------------------- 1 | 2 | module sfxPowerSupply() { 3 | 4 | length = 125.0; 5 | width = 100.0; 6 | height = 64; 7 | screwD = 3.5; 8 | 9 | eps = 0.001; 10 | 11 | // body 12 | color([0,1,0]) cube(size=[length, width, height]); 13 | 14 | // main fan 15 | translate([62.5, 50,0]) 16 | color([0,1,1]) 17 | circle(d=88); 18 | 19 | 20 | // open faces 21 | translate([10,-eps, 7]) 22 | color([0,1,1]) 23 | cube(size=[length-20, eps, height-7]); 24 | 25 | 26 | // screw holes 27 | color([1,0,1]) 28 | translate([length-6, eps, height-6]) 29 | rotate(a=[90,0,0]) 30 | circle(d=screwD); 31 | 32 | color([1,0,1]) 33 | translate([6, eps, height-6]) 34 | rotate(a=[90,0,0]) 35 | circle(d=screwD); 36 | 37 | color([1,0,1]) 38 | translate([6, eps, 6]) 39 | rotate(a=[90,0,0]) 40 | circle(d=screwD); 41 | 42 | color([1,0,1]) 43 | translate([length-6, eps, 6]) 44 | rotate(a=[90,0,0]) 45 | circle(d=screwD); 46 | 47 | color([1,0,1]) 48 | translate([6, eps, height-(6+25.5)]) 49 | rotate(a=[90,0,0]) 50 | circle(d=screwD); 51 | 52 | color([1,0,1]) 53 | translate([length-6, eps, height-(6+25.5)]) 54 | rotate(a=[90,0,0]) 55 | circle(d=screwD); 56 | 57 | 58 | } 59 | 60 | sfxPowerSupply(); -------------------------------------------------------------------------------- /rack-mount/catalog/rpi-sbc-simple-box/case.scad: -------------------------------------------------------------------------------- 1 | include <../../../config/common.scad> 2 | include <../../../helper/common.scad> 3 | 4 | // All coordinates are in [x,y], or [x,y,z] format 5 | pcbDimensions = [56.0, 85.1]; // [x,y] 6 | pcbThickness = 1.42; 7 | 8 | // [+x, -x, +y, -y] 9 | pcbCaseSpace = [5, 2, 5, 2]; 10 | pcbRise = 3; 11 | caseWallThickness = 2; 12 | caseBottomThickness = 2; 13 | mountPointDiameter = 5; 14 | 15 | mountPoints = [ 16 | [3.65, 23.3,0], 17 | [3.65 + 49, 23.3,0], 18 | [3.65, 23.3 + 58,0], 19 | [3.65 + 49, 23.3 + 58,0] 20 | ]; 21 | 22 | 23 | module pcb() { 24 | cube(size=[pcbDimensions[0], pcbDimensions[1], pcbThickness]); 25 | } 26 | 27 | module pcbCaseBottom_() { 28 | translate(v=[-pcbCaseSpace[1]-caseWallThickness, -pcbCaseSpace[3]-caseWallThickness, -caseBottomThickness-pcbRise]) 29 | difference() { 30 | cube(size=[ 31 | pcbDimensions[0]+pcbCaseSpace[0]+pcbCaseSpace[1] + 2*caseWallThickness, 32 | pcbDimensions[1]+pcbCaseSpace[2]+pcbCaseSpace[3] + 2*caseWallThickness, 33 | 26 // 3u case, subtracted for and other bullshit 34 | ]); 35 | translate(v=[caseWallThickness, caseWallThickness, caseBottomThickness]) 36 | cube(size=[ 37 | pcbDimensions[0]+pcbCaseSpace[0]+pcbCaseSpace[1], 38 | pcbDimensions[1]+pcbCaseSpace[2]+pcbCaseSpace[3], 39 | 26 // 3u case, subtracted 4 for lid and bullshit 40 | ]); 41 | } 42 | } 43 | 44 | module pcbCaseWithRisers_() { 45 | union() { 46 | translate(v=[0,0,-pcbRise]){ 47 | 48 | difference () { 49 | mountPoints_N(pcbRise, mountPointDiameter/1.5, mountPointDiameter/2, 32, false); 50 | mountPoints_N(pcbRise + 2, 1.95/2 - 0.05, 1.95/2 - 0.05, 32, false); 51 | } 52 | } 53 | pcbCaseBottom_(); 54 | } 55 | } 56 | 57 | 58 | module cutoutProfileAirflow_N() { 59 | 60 | // bottom cutouts 61 | union() { 62 | for (i=[0:11]) { 63 | translate(v=[pcbDimensions[0]/2.0, i*6 + 10,0]) 64 | minkowski() { 65 | cube(size=[30,1,20], center=true); 66 | cylinder(h=1,r=1); 67 | } 68 | } 69 | } 70 | 71 | // back cutout 72 | translate(v=[5,pcbDimensions[1]+5,-1]) 73 | minkowski() { 74 | cube(size=[50,90,15], center=false); 75 | rotate(a=[90,0,0]) 76 | cylinder(h=1,r=2); 77 | } 78 | 79 | // front cutout 80 | translate(v=[pcbDimensions[0],2.5,1]) 81 | cube(size=[inf50, 80, 15]); 82 | } 83 | 84 | module mountPoints_N(cylHeight, cylRad1, cylRad2, cylFn, center) { 85 | for (i=[0:3]) { 86 | p = mountPoints[i]; 87 | translate(v=[p[0], p[1], p[2]]) 88 | cylinder(r1=cylRad1, r2=cylRad2, h=cylHeight, $fn=cylFn, center=center); 89 | } 90 | 91 | } 92 | 93 | // fucked up 94 | module cutoutProfile_N() { 95 | color([1,0,1]) 96 | union() { 97 | // front I/O 98 | mirror(v=[0,1,0]) 99 | translate(v=[1, -eps*100, pcbThickness-4]) 100 | cube(size=[58.0 + 0.1, inf50, 21 + 0.1]); 101 | 102 | // side I/O 103 | translate(v=[-48-3, (pcbDimensions[1]-54)-10, pcbThickness-4]) 104 | cube(size=[inf50, 64, 19]); 105 | } 106 | 107 | } 108 | 109 | 110 | module mainCase() { 111 | difference() { 112 | 113 | union() { 114 | pcbCaseWithRisers_(); 115 | 116 | // lugs 117 | // -4 118 | translate(v = [pcbDimensions[0]+caseWallThickness+pcbCaseSpace[0], -caseWallThickness-pcbCaseSpace[3], -pcbRise- 119 | caseBottomThickness]) 120 | cube(size = [2, 5, 5]); 121 | 122 | // 87.1 123 | translate(v = [pcbDimensions[0]+caseWallThickness+pcbCaseSpace[0], pcbDimensions[1]+pcbCaseSpace[2]-5+ 124 | caseWallThickness, -pcbRise-caseBottomThickness]) 125 | cube(size = [2, 5, 5]); 126 | 127 | // -> 87.1 + 4 = 91.1 128 | } 129 | 130 | union() { 131 | cutoutProfile_N(); 132 | cutoutProfileAirflow_N(); 133 | } 134 | } 135 | } 136 | 137 | mainCase(); 138 | -------------------------------------------------------------------------------- /rack-mount/catalog/rpi-sbc-simple-box/frontPlate.scad: -------------------------------------------------------------------------------- 1 | include <../../../config/common.scad> 2 | include <../../../helper/common.scad> 3 | include <../../../rack-mount/plateBase.scad> 4 | 5 | vU = 2; 6 | uHeight = 10; 7 | 8 | plateScrewDiffV = uHeight*vU; 9 | plateScrewDiffH = 180; 10 | 11 | plateScrewToHEdge = 4.5; 12 | plateScrewToVEdge = 5.5; 13 | 14 | frontPlateThickness = 2.5; 15 | 16 | frontPlateV = plateScrewDiffV + 2*plateScrewToHEdge; 17 | frontPlateH = plateScrewDiffH + 2*plateScrewToVEdge; 18 | 19 | 20 | plateScrewToBoxMin = 6; 21 | 22 | 23 | length = 150; 24 | resolution = 250; 25 | period = PI/6; 26 | shift = 0.0; 27 | amplitudeFunction = function(x) 2; 28 | 29 | module _frontPlateBody() { 30 | 31 | translate(v=[5,-3,-1]) 32 | rotate(a=[180,0,0]) 33 | sineWaveHull(length, resolution, amplitudeFunction, period+0.02, 2, 5); 34 | 35 | translate(v=[5,-3,4-1]) 36 | rotate(a=[180,0,0]) 37 | sineWaveHull(length, resolution, amplitudeFunction, period, 2 , 5); 38 | 39 | translate(v=[5,-3,8-1]) 40 | rotate(a=[180,0,0]) 41 | sineWaveHull(length, resolution, amplitudeFunction, period+0.03, 2.5, 5); 42 | 43 | translate(v=[5,-3,12-1]) 44 | rotate(a=[180,0,0]) 45 | sineWaveHull(length, resolution, amplitudeFunction, period+0.1, 3.3, 5); 46 | 47 | translate(v=[5,-3,16-1]) 48 | rotate(a=[180,0,0]) 49 | sineWaveHull(length, resolution, amplitudeFunction, period+0.1, 5, 5); 50 | 51 | translate(v=[5,-3,20-1]) 52 | rotate(a=[180,0,0]) 53 | sineWaveHull(length, resolution, amplitudeFunction, period+0.1, 6, 5); 54 | 55 | translate(v=[5,-3,24-1]) 56 | rotate(a=[180,0,0]) 57 | sineWaveHull(length, resolution, amplitudeFunction, period+0.08, 7, 5); 58 | } 59 | 60 | module _plateHole() { 61 | rotate(a=[90,0,0]) 62 | cylinder(r=m4RadiusSlacked, h=inf, center=true); 63 | } 64 | 65 | 66 | module frontPlate() { 67 | difference() { 68 | _frontPlateBody(); 69 | 70 | union() { 71 | // TODO: introduce helper modules for this pattern 72 | _plateHole(); 73 | 74 | translate(v=[plateScrewDiffH,0,0]) 75 | _plateHole(); 76 | 77 | translate(v=[0,0,plateScrewDiffV]) 78 | _plateHole(); 79 | 80 | translate(v=[plateScrewDiffH,0,plateScrewDiffV]) 81 | _plateHole(); 82 | 83 | } 84 | } 85 | } 86 | 87 | 88 | difference() { 89 | difference() { 90 | 91 | difference() { 92 | union() { 93 | plateBase(U=2, plateThickness=2.5, screwType="m4", filletR=2); 94 | rotate(a = [- 90, 0, 0]) 95 | frontPlate(); 96 | } 97 | 98 | union() { 99 | translate(v=[1,0,0]) 100 | rotate(a=[0,-15, 0]) 101 | translate(v=[0,-50,0]) 102 | cube(size=[100, 100, 30]); 103 | 104 | translate(v=[160-1,0,0]) 105 | mirror(v=[1,0,0]) { 106 | rotate(a = [0, - 15, 0]) 107 | translate(v = [0, - 50, 0]) 108 | cube(size = [100, 100, 30]); 109 | } 110 | } 111 | 112 | } 113 | // lug holes 114 | union() { 115 | translate(v=[150,-3,-frontPlateThickness]) 116 | cube(size=[5.2, 5.2, 3]); 117 | translate(v=[150-91.1,-3,-frontPlateThickness]) 118 | cube(size=[5.2, 5.2, 3]); 119 | } 120 | } 121 | for (i=[0:5]) { 122 | translate(v=[5,i*4 - 0.75,-10]) 123 | cube(size=[150, 1.5, 20]); 124 | } 125 | } -------------------------------------------------------------------------------- /rack-mount/catalog/rpi-sbc-simple-box/top.scad: -------------------------------------------------------------------------------- 1 | 2 | difference() { 3 | union() { 4 | cube(size=[67,95.7,1]); 5 | translate(v=[2,2,1]) 6 | cube(size=[63.2,92,3]); 7 | } 8 | 9 | union() { 10 | translate(v=[3.5, 3.8,1]) 11 | cube(size=[60,88,3]); 12 | 13 | for(i=[0:7]) { 14 | translate(v=[33.5,i*10 + 12,0]) 15 | minkowski() { 16 | cylinder(h=1,r=1); 17 | cube(size=[50,5,10], center=true); 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /rack-mount/common.scad: -------------------------------------------------------------------------------- 1 | 2 | include <../rack/sharedVariables.scad> 3 | include <../helper/common.scad> 4 | include <../config/common.scad> 5 | 6 | /* 7 | QoL redefinitions/variables/reimports for rack mount items 8 | */ 9 | 10 | uDiff = screwDiff; 11 | 12 | rackMountScrewXDist = 4.5; 13 | rackMountScrewZDist = 4.5; 14 | 15 | mainRailSideSupportToInnerEdge = frontFaceWidth - railSideMountThickness; 16 | railSupportsDx = 2*mainRailSideSupportToInnerEdge + maxUnitWidth; -------------------------------------------------------------------------------- /rack-mount/dimensionHelper.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | // OpenSCAD script to output some helpful dimensions for creating new rack-mount items: 4 | 5 | echo("Vertical distance between 2 main rail holes: ", screwDiff); 6 | echo("Horizontal distance between 2 opposing main rail holes: ", rackMountScrewWidth); 7 | 8 | echo("Distance between main rail screw, and main rail inner edge:", railScrewHoleToInnerEdge); 9 | 10 | echo("Max supported rack-mount width: ", maxUnitWidth); 11 | echo("Max recommended rack-mount depth: ", maxUnitDepth); 12 | 13 | -------------------------------------------------------------------------------- /rack-mount/enclosed-box/README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Enclosed Box Mounting System 4 | 5 | ![demo](./animate.gif) 6 | 7 | Please see [entry.scad](./entry.scad) for config and building. Make sure the correct rack profile is configured in [rackFrame.scad](../../config/rackFrame.scad). 8 | 9 | - This system comprises three parts: two side rails, and a front plate. 10 | - This system is useful for mounting heavier box-shaped things. It works by first sliding in the box inside the side rails (which secures `x`, `z`, and one half of the `y` axis), and then mounting the front plate along the main rails (fully securing the `y` axis). 11 | - Side rails will most likely require supports to print. 12 | - For wider boxes (nearing the `maxUnitWidth` of the rack), you will have to set `recessSideRail` to true. This is so the side rails do not collide with the main rack rails. 13 | 14 | ### BOM: 15 | 16 | | Item | Name | Quantity | Comment | 17 | |---------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------| 18 | | | M3x12 FHCS | 8 | Extras are useful and inexpensive. If you plan on eventually stacking multiple racks together, don't hesitate to get a lot more. | 19 | | | M3 hex nut | 8 | ☝️ | 20 | | | M4x12 FHCS | 4 | ☝️ | 21 | | | M4 hex nut | 4 | ☝️ | -------------------------------------------------------------------------------- /rack-mount/enclosed-box/animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/rack-mount/enclosed-box/animate.gif -------------------------------------------------------------------------------- /rack-mount/enclosed-box/animate.scad: -------------------------------------------------------------------------------- 1 | use <./entry.scad> 2 | 3 | $vpt = [120,-30,63]; 4 | $vpr = [74,0,25]; 5 | $vpd = 550; 6 | $vpf = 22.50; 7 | 8 | animateEnclosedBoxSystem(at=$t); 9 | 10 | module animateEnclosedBoxSystem(at=$t) { 11 | 12 | zOrientation = "middle"; 13 | x=150; 14 | y=70; 15 | z=20; 16 | dx = abs(lerp(a=-20, b=20, t=at)); 17 | dy = abs(lerp(a=-50, b=50, t=at)); 18 | dz = abs(lerp(a=-15, b=15, t=at)); 19 | 20 | enclosedBoxSystem(visualize=true, zOrientation=zOrientation, boxWidth=x+dx, boxDepth=y+dy, boxHeight=z+dz); 21 | } 22 | -------------------------------------------------------------------------------- /rack-mount/enclosed-box/entry.scad: -------------------------------------------------------------------------------- 1 | include <../../rack/sharedVariables.scad> 2 | include <../common.scad> 3 | include <./helper.scad> 4 | 5 | use <./sideRail.scad> 6 | use <./frontBoxHolder.scad> 7 | 8 | /* 9 | Enclosed box mounting system: 10 | Helper file to use semi-enclosed side rails and a front plate to mount a box. 11 | This system does not require any mounting holes on the enclosed box. 12 | 13 | !!! Please also make sure that the correct rack frame preset is set in rackFrame.scad !!! 14 | */ 15 | module enclosedBoxSystem ( 16 | 17 | // begin config //////////////////////////////////////////////////////////////////////////////////////////////////////// 18 | 19 | // Does not affect any part dimensions. Set this to true to visualize how a box would be mounted. 20 | visualize = false, 21 | zOrientation = "middle", // ["middle" | "bottom"] 22 | recessSideRail = false, 23 | 24 | boxWidth = 160, 25 | boxHeight = 27, 26 | boxDepth = 120, 27 | 28 | railDefaultThickness = 1.5, 29 | railSideThickness = 3, 30 | 31 | frontPlateThickness = 3, 32 | frontPlateCutoutYSpace = 3, 33 | frontPlateCutoutXSpace = 5, 34 | 35 | // end config ////////////////////////////////////////////////////////////////////////////////////////////////////////// 36 | 37 | ) { 38 | leftRailTrans = identity; 39 | rightRailTrans = visualize 40 | ? translate(v = [boxWidth, 0, 0])*mirror(v = [1, 0, 0]) 41 | : translate(v = [sideRailBaseWidth*2, 0, 0])*mirror(v = [1, 0, 0]); 42 | 43 | u = findU(boxHeight, railDefaultThickness); 44 | railBottomThickness = railBottomThickness(u, boxHeight, railDefaultThickness, zOrientation); 45 | frontBoxHolderTrans = visualize 46 | ? translate(v = [railSideThickness-(railSupportsDx-boxWidth)/2, 0, sideRailLowerMountPointToBottom- 47 | railBottomThickness])*mirror(v = [0, 1, 0])*rotate(a = [90, 0, 0]) 48 | : mirror(v = [0, 1, 0])*translate(v = [0, uDiff, frontPlateThickness-railBottomThickness]); 49 | 50 | if (visualize) { 51 | %cube(size = [boxWidth, boxDepth, boxHeight]); 52 | } 53 | 54 | multmatrix(leftRailTrans) 55 | sideSupportRailBase(top = true, recess = recessSideRail, defaultThickness = railDefaultThickness, supportedZ = 56 | boxHeight, supportedY = boxDepth, supportedX = boxWidth, zOrientation = zOrientation, railSideThickness = 57 | railSideThickness); 58 | 59 | multmatrix(rightRailTrans) 60 | sideSupportRailBase(top = true, recess = recessSideRail, defaultThickness = railDefaultThickness, supportedZ = 61 | boxHeight, supportedY = boxDepth, supportedX = boxWidth, zOrientation = zOrientation, railSideThickness = 62 | railSideThickness); 63 | 64 | multmatrix(frontBoxHolderTrans) 65 | frontBoxHolder( 66 | cutoutOffsetX = (rackMountScrewWidth-(boxWidth-2*frontPlateCutoutXSpace))/2, cutoutOffsetY = railBottomThickness+ 67 | frontPlateCutoutYSpace, 68 | cutoutX = boxWidth-2*frontPlateCutoutXSpace, cutoutY = boxHeight-2*frontPlateCutoutYSpace, 69 | zOrientation = zOrientation, supportedZ = boxHeight, supportWidth = max(10, boxWidth-(sideRailBaseWidth+10)), 70 | supportRailDefaultThickness = railDefaultThickness, plateThickness = frontPlateThickness 71 | ); 72 | } 73 | 74 | enclosedBoxSystem(); 75 | -------------------------------------------------------------------------------- /rack-mount/enclosed-box/frontBoxHolder.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | include <./helper.scad> 3 | use <../plateBase.scad> 4 | 5 | // To be used with closed sideRails to fully encapsulate a box 6 | 7 | module frontBoxHolder(plateThickness=3, cutoutOffsetX, cutoutOffsetY, cutoutX, cutoutY, supportedZ, supportWidth, supportDepth=5, supportRailDefaultThickness, zOrientation) { 8 | 9 | //assert(supportedZ + 2*supportThickness <= boxPlateScrewToYEdge*2 + u*uDiff); 10 | //assert(supportWidth < rackMountScrewWidth); // not tight, need to include screw radii 11 | 12 | u = findU(supportedZ, supportRailDefaultThickness); 13 | supportRailBottomThickness = railBottomThickness(u, supportedZ, supportRailDefaultThickness, zOrientation); 14 | 15 | difference() { 16 | union() { 17 | plateBase(U=u, plateThickness=plateThickness, screwToXEdge=boxPlateScrewToXEdge, screwToYEdge=boxPlateScrewToYEdge, screwType=mainRailScrewType, filletR=2); 18 | 19 | // bottom support 20 | translate(v=[(rackMountScrewWidth-supportWidth)/2, -boxPlateScrewToYEdge,0]) 21 | cube(size=[supportWidth, supportRailBottomThickness, supportDepth]); 22 | 23 | // top support 24 | translate(v=[(rackMountScrewWidth-supportWidth)/2, -boxPlateScrewToYEdge+supportRailBottomThickness+supportedZ,0]) 25 | cube(size=[supportWidth, supportRailDefaultThickness, supportDepth]); 26 | } 27 | 28 | union() { 29 | translate(v=[cutoutOffsetX, cutoutOffsetY-boxPlateScrewToYEdge,-inf/2]) 30 | minkowski() { 31 | r=2; 32 | cylinder(r=r,h=inf); 33 | translate(v=[r, r, 0]) 34 | cube(size = [cutoutX-2*r, cutoutY-2*r, inf]); 35 | } 36 | 37 | } 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /rack-mount/enclosed-box/helper.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | 3 | // Some utility functions, as well as global functions to make code cleaner 4 | 5 | sideRailLowerMountPointToBottom = uDiff/2; 6 | sideRailBaseWidth = 15; 7 | // distance between front and back main rail screw mounts 8 | sideRailScrewMountDist = yBarDepth - 2*(frontScrewSpacing + railFrontThickness + railSlotToXZ); 9 | 10 | boxPlateScrewToXEdge = 4.5; // wow these are named poorly 11 | boxPlateScrewToYEdge = 5; 12 | 13 | 14 | function findU(boxHeight, minRailThickness) = max(1, ceil((boxHeight + 2*minRailThickness)/uDiff) - 1); 15 | 16 | function railBottomThickness(u, boxHeight, minRailThickness, zOrientation) = 17 | (zOrientation == "middle") 18 | ? (((u+1) * uDiff) - boxHeight)/2 19 | : (zOrientation == "bottom") 20 | ? minRailThickness 21 | : minRailThickness; -------------------------------------------------------------------------------- /rack-mount/enclosed-box/sideRail.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | include <./helper.scad> 3 | 4 | // sideSupportRailBase(top=true, defaultThickness=1.5, supportedZ=27.2, supportedY=101.5, supportedX=159); 5 | 6 | 7 | module sideSupportRailBase(top=true, recess=false, supportedZ, supportedY, supportedX, zOrientation="middle", defaultThickness=2, railSideThickness=4, sideVent=true) { 8 | 9 | mountBlockDepth = 10; 10 | screwMountGlobalDz = screwDiff / 2.0; // vertical distance between local origin and main rail screw mount 11 | sideRailScrewToMainRailFrontDx = frontScrewSpacing+railFrontThickness; 12 | railLength = max(sideRailScrewMountDist + sideRailScrewToMainRailFrontDx + mountBlockDepth/2, supportedY+defaultThickness); 13 | railBaseThickness = defaultThickness; // default thickness value 14 | railBackThickness = 3; 15 | // Minimum U to enclose box, while also have a minimum defaultThickness for top and bottom. 16 | u = findU(supportedZ, railBaseThickness); 17 | railBottomThickness = railBottomThickness(u, supportedZ, railBaseThickness, zOrientation); 18 | 19 | assert(supportedX <= maxUnitWidth, "Configured supported width is too high for rack profile"); 20 | assert (zOrientation == "middle" || zOrientation == "bottom", "Z-Orientation not supported"); 21 | assert(railBottomThickness >= railBaseThickness); 22 | // require recessed rail if supportedX is close to maxUnitWidth 23 | assert(recess || (supportedX+2*railSideThickness <= maxUnitWidth), "Configured supported width requires recessed side rail."); 24 | 25 | railSideHeight = supportedZ + railBaseThickness + railBottomThickness + overhangSlack; 26 | frontMountPad = (sideRailScrewToMainRailFrontDx-mountBlockDepth/2); 27 | 28 | translate(v=[-railSideThickness, 0, -railBottomThickness]) 29 | applyMainRailMounts() 30 | sideSupportRailBase(); 31 | 32 | module applyMainRailMounts() { 33 | 34 | mountBlockExtension = (railSupportsDx - supportedX)/2 - railSideThickness; 35 | minHexNutPocketToXYDist = sideRailLowerMountPointToBottom; 36 | minHexNutPocketToXZDist = mountBlockDepth/2; 37 | minHexNutPocketToYZDist = 4; 38 | screwU = floor((railSideHeight) / uDiff)-1; 39 | 40 | apply_pn() { 41 | mountBlockPositive(); 42 | mountBlockNegative(); 43 | children(0); 44 | } 45 | 46 | module mountBlockPositive() { 47 | translate(v = [0, frontMountPad, 0]) 48 | singleMountBlockPositive(); 49 | translate(v=[0,frontMountPad+sideRailScrewMountDist, 0]) 50 | singleMountBlockPositive(); 51 | } 52 | 53 | module mountBlockNegative() { 54 | translate(v = [0, frontMountPad, 0]) 55 | singleMountBlockNegative(); 56 | translate(v=[0,frontMountPad+sideRailScrewMountDist, 0]) 57 | singleMountBlockNegative(); 58 | } 59 | 60 | module singleMountBlockPositive() { 61 | translate(v = [-mountBlockExtension, 0, 0]) 62 | cube(size = [mountBlockExtension, mountBlockDepth, railSideHeight]); 63 | } 64 | 65 | module singleMountBlockNegative() { 66 | nutPocket(); 67 | 68 | translate(v = [0, 0, uDiff*screwU]) 69 | nutPocket(); 70 | 71 | module nutPocket() { 72 | translate(v=[-mountBlockExtension+minHexNutPocketToYZDist, minHexNutPocketToXZDist, minHexNutPocketToXYDist]) 73 | rotate(a=[-90,0,90]) 74 | hexNutPocket_N(rackFrameScrewType, openSide=false, backSpace=min((railSideThickness-1)+mountBlockExtension-minHexNutPocketToYZDist,15)); 75 | } 76 | } 77 | } 78 | 79 | 80 | module sideSupportRailBase() { 81 | 82 | difference () { 83 | union() { 84 | cube(size = [sideRailBaseWidth, railLength, railBottomThickness]); 85 | 86 | cube(size = [railSideThickness, railLength, railSideHeight]); 87 | 88 | // back support 89 | translate(v = [0, max(railLength-railBackThickness, supportedY), 0]) 90 | cube(size = [sideRailBaseWidth, railBackThickness, railSideHeight]); 91 | 92 | // back support for box 93 | translate(v = [0, supportedY, 0]) 94 | cube(size = [sideRailBaseWidth, railBackThickness, railSideHeight]); 95 | 96 | // top support 97 | if (top) { 98 | translate(v = [0, 0, railSideHeight-railBaseThickness]) 99 | cube(size = [sideRailBaseWidth, railLength, railBaseThickness]); 100 | } 101 | } 102 | if (sideVent) { 103 | union() { 104 | distanceFromSeparator = 3; 105 | r = 4; 106 | 107 | ventDy1 = frontMountPad+mountBlockDepth+distanceFromSeparator; 108 | ventY1 = min(supportedY-(ventDy1+distanceFromSeparator), sideRailScrewMountDist-(2*distanceFromSeparator+mountBlockDepth)); 109 | 110 | ventDy2 = max(ventDy1, supportedY+railBackThickness+distanceFromSeparator); 111 | ventY2 = max(0, railLength - (ventDy2 + distanceFromSeparator + mountBlockDepth)); 112 | 113 | ventDz = railBottomThickness+distanceFromSeparator+r; 114 | ventZ = railSideHeight-(ventDz+distanceFromSeparator+r+railBaseThickness); 115 | 116 | if (ventY1 > 2*r) { 117 | translate(v = [0, ventDy1+r, ventDz]) 118 | minkowski() { 119 | cube(size = [inf, ventY1-2*r, ventZ]); 120 | sphere(r = r); 121 | } 122 | } 123 | 124 | if (ventY2 > 2*r) { 125 | translate(v = [0, ventDy2+r, ventDz]) 126 | minkowski() { 127 | cube(size = [inf, ventY2-2*r, ventZ]); 128 | sphere(r = r); 129 | } 130 | } 131 | 132 | frontCutTrans = recess ? translate(v=[0,frontMountPad,0]): translate(v=[0,xySlack,0]); 133 | multmatrix(frontCutTrans) { 134 | cylindricalFiletNegative(p0=[sideRailBaseWidth,0,0],p1=[sideRailBaseWidth,0,inf], n=[1,-1,0],r=r); 135 | halfspace(vpos=[0,-1,0], p=[0,0,0]); 136 | } 137 | 138 | } 139 | } 140 | } 141 | } 142 | 143 | } 144 | 145 | 146 | 147 | -------------------------------------------------------------------------------- /rack-mount/patch-panel/README.md: -------------------------------------------------------------------------------- 1 | # Patch Panel 2 | 3 | 4 | ![demo](./animate.gif) 5 | 6 | Please see [entry.scad](./entry.scad) for config and building. Make sure the correct rack profile is configured in [rackFrame.scad](../../config/rackFrame.scad). 7 | 8 | - Mount RJ-45 keystone jacks with this part. 9 | - Single part. Can be printed without supports. 10 | 11 | ### BOM: 12 | 13 | | Item | Name | Quantity | Comment | 14 | |---------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------| 15 | | | M4x12 FHCS | 4 | Extras are useful and inexpensive. If you plan on eventually stacking multiple racks together, don't hesitate to get a lot more. | 16 | | | M4 hex nut | 4 | ☝️ | -------------------------------------------------------------------------------- /rack-mount/patch-panel/animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/rack-mount/patch-panel/animate.gif -------------------------------------------------------------------------------- /rack-mount/patch-panel/animate.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | use <./entry.scad> 3 | 4 | $vpt = [110,-42,36]; 5 | $vpr = [77,0,23]; 6 | $vpd = 445; 7 | $vpf = 22.50; 8 | 9 | animatePatchPanel(at=$t); 10 | 11 | module animatePatchPanel(at=$t) { 12 | 13 | plateThickness = 3; 14 | keystoneSpacing = 19; 15 | allSlots = [2, 2, 2, 2, 2, 2, 2, 5, 2]; 16 | numSlots = max(1,ceil(at*8)); 17 | slots = [each [for (i=[1:numSlots]) allSlots[i]]]; 18 | 19 | rotate(a=[90,0,0]) 20 | render() 21 | patchPanelSystem(slots = slots, plateThickness=plateThickness, keystoneSpacing=keystoneSpacing); 22 | 23 | } -------------------------------------------------------------------------------- /rack-mount/patch-panel/entry.scad: -------------------------------------------------------------------------------- 1 | use <./patchPanel.scad> 2 | 3 | /* 4 | Parametric patch panel for rj45 keystones 5 | 6 | Please also make sure that the correct rack frame preset is set in rackFrame.scad. 7 | 8 | TODO add support for 2d arrays 9 | */ 10 | 11 | module patchPanelSystem ( 12 | 13 | // begin config //////////////////////////////////////////////////////////////////////////////////////////////////////// 14 | 15 | // 1 for the original keystone mount design, 2 for a visually cleaner keystone mount 16 | // but where the keystone is a bit harder to remove 17 | // 3 for a cube with the height of plateThickness, 18 | // 4 and 5 for cubes with the same height as keystone1 and 2 19 | slots = [2, 2, 2, 2, 2, 2, 2, 5, 2], 20 | plateThickness = 3, 21 | keystoneSpacing = 19, 22 | center = false 23 | 24 | // end config ////////////////////////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | ) { 27 | mirror(v = [0, 0, 1]) 28 | patchPanel(slots = slots, plateThickness = 3, keystoneSpacing = 19, center=center); 29 | } 30 | 31 | patchPanelSystem(); 32 | -------------------------------------------------------------------------------- /rack-mount/patch-panel/patchPanel.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | use <../plateBase.scad> 3 | 4 | /* 5 | Parametric patch panel - 6 | Please see ./entry.scad for configuring/printing 7 | 8 | Please also make sure that the correct rack frame preset is set in rackFrame.scad. 9 | */ 10 | 11 | module patchPanel(slots, plateThickness = 3, screwToXEdge = 4.5, screwToYEdge = 4.5, keystoneSpacing = 19, center = false) { 12 | slotsWidth = len(slots) * keystoneSpacing; 13 | slotsMinPadding = railScrewHoleToInnerEdge+4; 14 | plateLength = rackMountScrewWidth + 2 * screwToXEdge; 15 | plateHeight = 2 * uDiff + 2 * screwToXEdge; 16 | leftRailScrewToSlots = center 17 | ? (plateLength-(slotsWidth+slotsMinPadding))/2 18 | : slotsMinPadding; 19 | 20 | difference() { 21 | plateBase(U = 2, plateThickness = plateThickness, screwType = mainRailScrewType, screwToXEdge = screwToXEdge, screwToYEdge = screwToYEdge, filletR = 2); 22 | translate([leftRailScrewToSlots, -screwToYEdge - 10 * eps, -plateThickness - 10 * eps]) 23 | cube([slotsWidth, plateHeight + 2 * 10 * eps, plateThickness + 2 * 10 * eps]); 24 | } 25 | 26 | for(i = [0 : len(slots) - 1]) { 27 | translate([leftRailScrewToSlots + keystoneSpacing / 2 + i * keystoneSpacing, uDiff, -plateThickness]) 28 | let (slot = slots[i]) 29 | if (slot == 1) keystone1(outerWidth = keystoneSpacing, outerHeight = plateHeight); 30 | else if (slot == 2) keystone2(outerWidth = keystoneSpacing, outerHeight = plateHeight); 31 | else if (slot == 3) plate(outerWidth = keystoneSpacing, outerHeight = plateHeight, thickness = plateThickness); 32 | else if (slot == 4) plate(outerWidth = keystoneSpacing, outerHeight = plateHeight, thickness = 5.9); 33 | else if (slot == 5) plate(outerWidth = keystoneSpacing, outerHeight = plateHeight, thickness = 9.9); 34 | else assert(false); 35 | } 36 | } 37 | 38 | module plate(outerWidth, outerHeight, thickness) { 39 | translate([0, 0, thickness / 2]) 40 | cube([outerWidth, outerHeight, thickness], center = true); 41 | } 42 | -------------------------------------------------------------------------------- /rack-mount/plateBase.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | 4 | *plateBase(U=2, plateThickness=3, screwType="m4", filletR=2); 5 | 6 | module plateBase(U, plateThickness, screwType, screwToXEdge=4.5, screwToYEdge=4.5, filletR=2) { 7 | 8 | assert(floor(U) == U && U > 0) 9 | assert(plateThickness > 0); 10 | 11 | screwDx = rackMountScrewWidth; 12 | screwDy = uDiff * U; 13 | 14 | plateLength = screwDx + 2*screwToXEdge; 15 | plateHeight = screwDy + 2*screwToYEdge; 16 | 17 | translate(v=[-screwToXEdge,-screwToYEdge,-plateThickness]) // easier to work with 18 | difference() { 19 | base(); 20 | 21 | mirror4XY(p=[screwToXEdge, screwToYEdge], dx=screwDx, dy=screwDy) 22 | translate(v=[0,0,plateThickness]) 23 | cylinder(r=screwRadiusSlacked(screwType), h=inf, center=true); 24 | } 25 | 26 | module base() { 27 | minkowski() { 28 | translate(v=[filletR, filletR, 0]) 29 | cube(size = [plateLength-2*filletR, plateHeight-2*filletR, plateThickness]); 30 | cylinder(r=filletR, h=eps); 31 | } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /rack-mount/rackEars.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | // Rack ear modules. 4 | // To be used either by itself if the item supports it, or within another module 5 | 6 | rackEarModule(u=4, frontThickness=3, sideThickness=3, frontWidth=20, sideDepth=50, backPlaneHeight, support=true); 7 | 8 | module rackEarModule( 9 | u, 10 | frontThickness, 11 | sideThickness, 12 | frontWidth, 13 | sideDepth, 14 | backPlaneHeight, 15 | support=true 16 | ) { 17 | 18 | // check frontWidth is wide enough 19 | assert(frontWidth-sideThickness >= rackMountScrewXDist+railScrewHoleToInnerEdge); 20 | 21 | earHeight = u*uDiff + 2*rackMountScrewZDist; 22 | 23 | difference() { 24 | translate(v = [-rackMountScrewXDist, 0, -rackMountScrewZDist]) { 25 | // front 26 | cube(size = [frontWidth, frontThickness, earHeight]); 27 | 28 | // side 29 | hull() { 30 | translate(v = [frontWidth-sideThickness, 0, 0]) 31 | cube(size = [sideThickness, frontThickness, earHeight]); 32 | 33 | backSegmentPlane(); 34 | } 35 | 36 | if (support) { 37 | defaultExtraSpacing = 1; 38 | extraSpacing = frontWidth-(rackMountScrewXDist+railScrewHoleToInnerEdge+sideThickness) > defaultExtraSpacing 39 | ? defaultExtraSpacing 40 | : 0; // don't include extra spacing for support, if tray itself is too large 41 | 42 | hull() { 43 | translate(v= [rackMountScrewXDist+railScrewHoleToInnerEdge+extraSpacing,frontThickness,0]) 44 | cube(size = [sideThickness, eps, earHeight]); 45 | 46 | backSegmentPlane(); 47 | } 48 | } 49 | } 50 | 51 | rackMountHoles(); 52 | } 53 | 54 | module backSegmentPlane() { 55 | translate(v = [frontWidth-sideThickness, sideDepth, 0]) 56 | cube(size = [sideThickness, eps, backPlaneHeight]); 57 | } 58 | 59 | module rackMountHoles() { 60 | rotate(a=[90,0,0]) 61 | cylinder(r=screwRadiusSlacked(mainRailScrewType), h=inf, center=true); 62 | 63 | translate(v=[0,0,u*uDiff]) 64 | rotate(a=[90,0,0]) 65 | cylinder(r=screwRadiusSlacked(mainRailScrewType), h=inf, center=true); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /rack-mount/tray/README.md: -------------------------------------------------------------------------------- 1 | # Tray Mounting System 2 | 3 | ![demo](./animate.gif) 4 | 5 | Please see [entry.scad](./entry.scad) for config and building. Make sure the correct rack profile is configured in [rackFrame.scad](../../config/rackFrame.scad). 6 | 7 | - Mount on front rails. 8 | - Single part. Can be printed without supports. 9 | - Includes support for custom bottom screws (see `mountPoints` in config). This is useful for SBCs and mini PCs with bottom screws. 10 | 11 | ### BOM: 12 | 13 | | Item | Name | Quantity | Comment | 14 | |---------------------------------------------------------------|---------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------| 15 | | | M4x12 FHCS | 4 | Extras are useful and inexpensive. If you plan on eventually stacking multiple racks together, don't hesitate to get a lot more. | 16 | | | M4 hex nut | 4 | ☝️ | -------------------------------------------------------------------------------- /rack-mount/tray/animate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/rack-mount/tray/animate.gif -------------------------------------------------------------------------------- /rack-mount/tray/animate.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | use <./entry.scad> 3 | 4 | $vpt = [100,-42,36]; 5 | $vpr = [70,0,27]; 6 | $vpd = 445; 7 | $vpf = 22.50; 8 | 9 | animateTraySystem(at=$t); 10 | 11 | module animateTraySystem(at=$t) { 12 | 13 | trayU = max(1, ceil(at*3)); 14 | x=100; 15 | y=100; 16 | dx = abs(lerp(a=-40, b=40, t=at)); 17 | dy = abs(lerp(a=-30, b=30, t=at)); 18 | 19 | traySystem(trayU=trayU, baseWidth=x+dx, baseDepth=y+dy, mountPoints=[], backLipHeight=8); 20 | } -------------------------------------------------------------------------------- /rack-mount/tray/entry.scad: -------------------------------------------------------------------------------- 1 | use <./tray.scad> 2 | 3 | /* 4 | Parametric rack-mount tray: 5 | Dimensions can be adjusted using the variables below. You can also add mounting holes to fasten things that have 6 | screw holes at the bottom. 7 | 8 | !!! Please also make sure that the correct rack frame preset is set in rackFrame.scad !!! 9 | */ 10 | 11 | module traySystem ( 12 | 13 | // begin config //////////////////////////////////////////////////////////////////////////////////////////////////////// 14 | 15 | trayU = 2, 16 | 17 | // these dimensions are the total base width including padding not usable space. 18 | // e.g. 145 baseWidth and 3 sideThickness = 145-(3*2) = 139mm useable space. 19 | baseWidth = 145, 20 | baseDepth = 100, 21 | 22 | baseThickness = 3, // tray bottom thickness 23 | frontThickness = 3, // front plate thickness 24 | sideThickness = 3, 25 | 26 | backLipHeight = 2, 27 | frontLipHeight = 2, 28 | 29 | sideSupport = true, 30 | trayLeftPadding = 10, // extra space between the left rail and tray. configure this to move the tray left/right. 31 | 32 | mountPointType = "m3", 33 | mountPointElevation = 1, // basically standoff height 34 | 35 | // add/config standoff coordinates here. Format is [[x,y]] 36 | mountPoints = [ 37 | [30,10], 38 | [30+75,10], 39 | [30,10+75], 40 | [30+75,10+75], 41 | ] 42 | 43 | // end config ////////////////////////////////////////////////////////////////////////////////////////////////////////// 44 | 45 | ) { 46 | 47 | bottomScrewTray ( 48 | u = trayU, 49 | trayWidth = baseWidth, 50 | trayDepth = baseDepth, 51 | trayThickness = baseThickness, 52 | frontLipHeight = frontLipHeight, 53 | backLipHeight = backLipHeight, 54 | mountPoints = mountPoints, 55 | frontThickness = frontThickness, 56 | sideThickness = sideThickness, 57 | mountPointElevation = mountPointElevation, 58 | mountPointType = mountPointType, 59 | sideSupport = sideSupport, 60 | trayLeftPadding = trayLeftPadding 61 | ); 62 | } 63 | 64 | traySystem(); 65 | -------------------------------------------------------------------------------- /rack-mount/tray/tray.scad: -------------------------------------------------------------------------------- 1 | include <../common.scad> 2 | use <../rackEars.scad> 3 | 4 | /* 5 | Parametric rack-mount tray - 6 | Please see ./entry.scad for configuring/printing 7 | 8 | Please also make sure that the correct rack frame preset is set in rackFrame.scad. 9 | */ 10 | 11 | module bottomScrewTray(u, trayWidth, trayDepth, trayThickness, mountPoints, mountPointElevation, mountPointType, frontThickness, sideThickness, frontLipHeight, backLipHeight, trayLeftPadding, sideSupport=true) { 12 | 13 | lipThickness = sideThickness; 14 | 15 | screwDx = rackMountScrewWidth; // x dist between the mount holes 16 | screwDz = uDiff * u; 17 | 18 | plateLength = screwDx + 2*rackMountScrewXDist; 19 | plateHeight = screwDz + 2*rackMountScrewZDist; 20 | 21 | minScrewToTraySpacing = railScrewHoleToInnerEdge; 22 | 23 | leftScrewDistToTray = minScrewToTraySpacing + trayLeftPadding; 24 | 25 | leftScrewGlobalX = -leftScrewDistToTray; 26 | rightScrewGlobalX = screwDx + leftScrewGlobalX; 27 | 28 | // check (tray width)+(configured extra space) fits within the rack 29 | assert(trayWidth <= screwDx-(2*minScrewToTraySpacing + trayLeftPadding)); 30 | 31 | difference() { 32 | applyMountHoles() 33 | translate(v = [-sideThickness, -frontThickness, -trayThickness]) 34 | body(); 35 | } 36 | 37 | module body() { 38 | 39 | // base 40 | cube(size = [trayWidth, trayDepth, trayThickness]); 41 | 42 | // front lip 43 | translate(v = [0, 0, trayThickness]) 44 | cube(size = [trayWidth, lipThickness, frontLipHeight]); 45 | 46 | // back lip 47 | translate(v = [0, trayDepth-lipThickness, trayThickness]) 48 | cube(size = [trayWidth, lipThickness, backLipHeight]); 49 | 50 | translate(v = [leftScrewGlobalX, 0, rackMountScrewZDist]) 51 | rackEarModule(frontThickness = frontThickness, sideThickness = sideThickness, frontWidth = 52 | leftScrewDistToTray+rackMountScrewXDist+sideThickness, sideDepth = trayDepth-lipThickness, u = u, backPlaneHeight=trayThickness+backLipHeight, support=sideSupport); 53 | 54 | translate(v = [rightScrewGlobalX, 0, rackMountScrewZDist]) 55 | mirror(v = [1, 0, 0]) 56 | rackEarModule(frontThickness = frontThickness, sideThickness = sideThickness, frontWidth = 57 | rightScrewGlobalX-trayWidth+rackMountScrewXDist+sideThickness, sideDepth = trayDepth-lipThickness, u = u, backPlaneHeight=trayThickness+backLipHeight, support=sideSupport); 58 | } 59 | 60 | 61 | module applyMountHoles() { 62 | 63 | mountPointPosThickness = 2; 64 | 65 | if (len(mountPoints) > 0) { 66 | apply_pn() { 67 | for (i = [0:len(mountPoints)-1]) { 68 | x = mountPoints[i][0]; 69 | y = mountPoints[i][1]; 70 | 71 | translate(v = [x, y, 0]) 72 | cylinder(r = screwRadiusSlacked(mountPointType)+mountPointPosThickness, h = mountPointElevation); 73 | } 74 | 75 | for (i = [0:len(mountPoints)-1]) { 76 | x = mountPoints[i][0]; 77 | y = mountPoints[i][1]; 78 | 79 | translate(v = [x, y, -trayThickness]) 80 | mirror(v = [0, 0, 1]) 81 | counterSunkHead_N(mountPointType, inf, inf); 82 | } 83 | 84 | children(0); 85 | } 86 | } else { 87 | children(0); 88 | } 89 | 90 | } 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /rack/assembly/addMagnetsToMagnetModules.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./slideHexNutsIntoYBar.scad> 3 | 4 | $vpt = [21,-15,20]; 5 | $vpr = [65,0,40]; 6 | $vpd = 50; 7 | $vpf = 22.50; 8 | 9 | addMagnetsToMagnetModules(at=$t); 10 | 11 | module addMagnetsToMagnetModules(at=0) { 12 | t = lerp(a=6,b=0,t=at); 13 | 14 | 15 | if (!plasticMask) { 16 | 17 | if (fixedSideModules) { 18 | multmatrix(yBarToMagnetModuleTrans) 19 | slideHexNutsIntoYBar(at=1); 20 | } 21 | else { 22 | magnetModule(); 23 | } 24 | } 25 | 26 | function insertMagnetTrans(t=0) = 27 | translate(v=[sideWallConnW-(magnetFaceToSideWallConnOuterYEdge+magnetHSlacked) + t, 28 | magnetModuleMagnetMountDy, 29 | magnetModuleMagnetMountDz]) * 30 | rotate(a=[0,90,0]); 31 | 32 | 33 | 34 | multmatrix(insertMagnetTrans(t=t)) 35 | magnet(); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /rack/assembly/addMagnetsToSideWall.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | $vpt = [23,105,70]; 4 | $vpr = [84,0,133]; 5 | $vpd = 370; 6 | $vpf = 22.50; 7 | 8 | addMagnetsToSideWall(at=$t); 9 | 10 | module addMagnetsToSideWall(at=0) { 11 | t = lerp(a=8,b=0,t=at); 12 | 13 | if (!plasticMask) { 14 | sideWallLeft(); 15 | } 16 | 17 | function insertMagnetTrans(t=0) = 18 | translate(v=[sideWallThickness+t, magnetMountToYBarFront, magnetMountToYBarTop-sideWallZHingeTotalClearance]) * 19 | rotate(a=[0,90,0]); 20 | 21 | multmatrix(insertMagnetTrans(t=t)) 22 | magnet(); 23 | 24 | multmatrix(translate(v=[0,0,sideWallZ - 2*(magnetMountToYBarTop- sideWallZHingeTotalClearance)]) * insertMagnetTrans(t=t)) 25 | magnet(); 26 | } -------------------------------------------------------------------------------- /rack/assembly/attachSideConnectorModulesToYBars.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./screwXBarAndYBar.scad> 3 | use <./addMagnetsToMagnetModules.scad> 4 | 5 | 6 | /* 7 | NOTE You only need to do this step if the `fixedSideModules` option is set to false. 8 | */ 9 | 10 | $vpt = [116,90,18]; 11 | $vpr = [56,0,42]; 12 | $vpd = 550; 13 | $vpf = 22.50; 14 | 15 | attachSideConnectorModulesToYBars(at=$t); 16 | 17 | module attachSideConnectorModulesToYBars(at=0) { 18 | elevation = lerp(a=8, b=0, t=at); 19 | 20 | // side module to front corner ybar 21 | function sideModuleTrans(t=0) = 22 | translate(v=[sideWallConnW,0,t-sideWallConnLugDepression]) 23 | * yBarSideModuleConnectorTrans 24 | * mirror(v=[1,0,0]); // mirror for magnetModule 25 | 26 | screwXBarAndYBar(at=1); 27 | 28 | if (!fixedSideModules) { 29 | multmatrix(sideModuleTrans(elevation)) 30 | union() { 31 | 32 | 33 | addMagnetsToMagnetModules(at=1); 34 | 35 | 36 | translate(v=[yBarScrewHoleToOuterYEdge,yBarScrewHoleToFrontXEdge,sideWallConnLugDepression + 2*elevation]) 37 | caseScrewShort(); 38 | } 39 | 40 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * sideModuleTrans(elevation)) 41 | union() { 42 | 43 | addMagnetsToMagnetModules(at=1); 44 | 45 | 46 | translate(v=[yBarScrewHoleToOuterYEdge,yBarScrewHoleToFrontXEdge,sideWallConnLugDepression + 2*elevation]) 47 | caseScrewShort(); 48 | } 49 | 50 | multmatrix(yBarMirrorOtherCornerTrans * sideModuleTrans(elevation)) 51 | union() { 52 | 53 | if (!plasticMask) { 54 | hingeModule(); 55 | } 56 | 57 | translate(v=[yBarScrewHoleToOuterYEdge,yBarScrewHoleToFrontXEdge,sideWallConnLugDepression + 2*elevation]) 58 | caseScrewShort(); 59 | } 60 | 61 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * yBarMirrorOtherCornerTrans * sideModuleTrans(elevation)) 62 | union() { 63 | if (!plasticMask) { 64 | hingeModule(); 65 | } 66 | 67 | translate(v=[yBarScrewHoleToOuterYEdge,yBarScrewHoleToFrontXEdge,sideWallConnLugDepression + 2*elevation]) 68 | caseScrewShort(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rack/assembly/attachXBarWithYBar.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./slideHexNutsIntoYBar.scad> 3 | use <./addMagnetsToMagnetModules.scad> 4 | 5 | $vpt = [116,90,18]; 6 | $vpr = [56,0,42]; 7 | $vpd = 550; 8 | $vpf = 22.50; 9 | 10 | attachXBarWithYBar(at=$t); 11 | 12 | module attachXBarWithYBar(at=0) { 13 | 14 | t = lerp(a=20, b=0, t=at); 15 | 16 | // assemble x-y bar trays 17 | multmatrix(translate(v = [0, 0, t])) 18 | 19 | if (fixedSideModules) { 20 | multmatrix(inv4x4(yBarToMagnetModuleTrans)) 21 | addMagnetsToMagnetModules(at=1); // same as slideHexNutsIntoYBar, but the magnet has already been inserted 22 | } else { 23 | slideHexNutsIntoYBar(at=1); 24 | } 25 | 26 | 27 | multmatrix(translate(v = [0, 0, t])*xBarSpaceToYBarSpace*xBarMirrorOtherCornerTrans*yBarSpaceToXBarSpace) 28 | if (fixedSideModules) { 29 | multmatrix(inv4x4(yBarToMagnetModuleTrans)) 30 | addMagnetsToMagnetModules(at=1); 31 | } else { 32 | slideHexNutsIntoYBar(at=1); 33 | } 34 | 35 | if (!plasticMask) { 36 | multmatrix(xBarSpaceToYBarSpace) 37 | xBar(); 38 | 39 | multmatrix(yBarMirrorOtherCornerTrans*xBarSpaceToYBarSpace) 40 | xBar(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /rack/assembly/attachXYPlates.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./slideHexNutsIntoYBarXYPlate.scad> 3 | 4 | $vpt = [96,110,70]; 5 | $vpr = [68,0,36]; 6 | $vpd = 700; 7 | $vpf = 22.50; 8 | 9 | attachXYPlates(at=$t); 10 | 11 | module attachXYPlates(at=0,r=0) { 12 | 13 | t = lerp(a=10,b=0,t=at); 14 | 15 | // TODO fix xyPlate transformations 16 | function xyPlateToYBarTrans() = translate(v=[6,6,0]) * yBarBasePlateConnectorTrans; 17 | 18 | slideHexNutsIntoYBarXYPlate(at=1); 19 | 20 | multmatrix(xyPlateToYBarTrans()) 21 | xyPlateWithScrews(t=t); 22 | 23 | multmatrix(upperXYTrayTrans * xyPlateToYBarTrans()) 24 | xyPlateWithScrews(t=t); 25 | 26 | module xyPlateWithScrews(t=0) { 27 | 28 | module screw(t=0) { 29 | translate(v=[0,0,-t]) 30 | mirror(v=[0,0,1]) 31 | caseScrewMedium(); 32 | } 33 | 34 | if (!plasticMask) { 35 | translate(v = [0, 0, -t]) 36 | xyPlate(); 37 | } 38 | 39 | screw(t=2*t); 40 | 41 | translate(v=[xyPlateConnDx, 0,0]) 42 | screw(t=2*t); 43 | 44 | translate(v=[0, xyPlateConnDy,0]) 45 | screw(t=2*t); 46 | 47 | translate(v=[xyPlateConnDx, xyPlateConnDy,0]) 48 | screw(t=2*t); 49 | } 50 | } -------------------------------------------------------------------------------- /rack/assembly/attachXYTrays.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./attachSideConnectorModulesToYBars.scad> 3 | use <./propUpBottomXYTraywithSideWalls.scad> 4 | 5 | $vpt = [115,58,113]; 6 | $vpr = [98,0,17]; 7 | $vpd = 620; 8 | $vpf = 22.50; 9 | 10 | attachXYTrays(at=$t); 11 | 12 | module attachXYTrays(at=0,r=0) { 13 | 14 | t1 = lerp(a=12, b=0, t=min(1, 2*at)); 15 | t2 = lerp(a=16, b=0, t=max(0, 2*at - 1)); 16 | 17 | module singleScrew() { 18 | translate(v = [mainRailSlideHexOnYBarDx, mainRailSlideHexOnYBarDy, -5]) 19 | rotate(a=[-45,0,0]) 20 | translate(v=[0,0,14 + t2]) // length of caseScrewLong 21 | caseScrewMedium(); 22 | } 23 | 24 | translate(v=[0,0,t1]) 25 | multmatrix(upperXYTrayTrans) { 26 | attachSideConnectorModulesToYBars(at=1); 27 | 28 | if (at >= 1/2) { 29 | mirrorAllTrayCornersFromYBarSpace() 30 | multmatrix(yBarMainRailConnectorTrans) 31 | singleScrew(); 32 | } 33 | } 34 | 35 | propUpBottomXYTraywithSideWalls(at=1,r=r); 36 | } 37 | -------------------------------------------------------------------------------- /rack/assembly/common.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <../side/sideWallVariables.scad> 5 | include <../connector/connectors.scad> 6 | use <../mainRail.scad> 7 | use <../yBar.scad> 8 | use <../xBar.scad> 9 | use <../side/magnetModule.scad> 10 | use <../side/hingeModule.scad> 11 | use <../side/sideWallRight.scad> 12 | use <../side/sideWallLeft.scad> 13 | use <../stackEnds.scad> 14 | use <../xyPlate.scad> 15 | 16 | screwMask = false; 17 | plasticMask = false; 18 | sideSupportRailMask = true; 19 | 20 | xBarSpaceToYBarSpace = 21 | yBarXBarConnectorTrans * 22 | xBarConnectorToYBarConnectorTrans * 23 | inv4x4(xBarYBarConnectorTrans); 24 | 25 | yBarSpaceToXBarSpace = 26 | xBarYBarConnectorTrans * 27 | yBarConnectorToXBarConnectorTrans * 28 | inv4x4(yBarXBarConnectorTrans); 29 | 30 | upperXYTrayTrans = 31 | yBarMainRailConnectorTrans * 32 | mirrorMainRailOtherSideTrans * 33 | inv4x4(yBarMainRailConnectorTrans); 34 | 35 | function feetToYBarTrans(t=0) = 36 | translate(v=[connectorRectWidth/2,connectorRectDepth/2,-t]) * 37 | yBarStackConnectorTrans * 38 | mirror(v=[0,1,0]); 39 | 40 | function stackConnectorTrans(t=0) = 41 | upperXYTrayTrans * 42 | yBarStackConnectorTrans; 43 | 44 | module mirrorAllTrayCornersFromYBarSpace() { 45 | children(0); 46 | 47 | multmatrix(yBarMirrorOtherCornerTrans) 48 | children(0); 49 | 50 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * yBarMirrorOtherCornerTrans) 51 | children(0); 52 | 53 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * yBarMirrorOtherCornerTrans * yBarMirrorOtherCornerTrans) 54 | children(0); 55 | } 56 | 57 | secondStackTrans = upperXYTrayTrans * mirror(v=[0,0,1]); 58 | 59 | 60 | 61 | yBarToMagnetModuleTrans = 62 | translate(v=[13,0,sideWallConnLugDepression]) * 63 | inv4x4(yBarSideModuleConnectorTrans) * 64 | mirror(v=[1,0,0]); 65 | 66 | 67 | module caseScrewShort() { 68 | if (!screwMask) { 69 | color([1, 1, 1]) { 70 | difference() { 71 | scale(v = [0.9, 0.9, 0.9]) 72 | counterSunkHead_N(rackFrameScrewType, screwExtension = 8, headExtension = 0.5); 73 | 74 | cylinder($fn = 6, r = 1.5); 75 | } 76 | } 77 | } 78 | } 79 | 80 | module caseScrewMedium() { 81 | if (!screwMask) { 82 | color([1, 1, 1]) { 83 | difference() { 84 | scale(v = [0.9, 0.9, 0.9]) 85 | counterSunkHead_N(rackFrameScrewType, screwExtension = 12, headExtension = 0.5); 86 | 87 | cylinder($fn = 6, r = 1.5); 88 | } 89 | } 90 | } 91 | } 92 | 93 | module caseScrewLong() { 94 | if (!screwMask) { 95 | color([1, 1, 1]) { 96 | difference() { 97 | scale(v = [0.9, 0.9, 0.9]) 98 | counterSunkHead_N(rackFrameScrewType, screwExtension = 16, headExtension = 0.5); 99 | 100 | cylinder($fn = 6, r = 1.5); 101 | } 102 | } 103 | } 104 | } 105 | 106 | module hingeDowel() { 107 | if (!screwMask) { 108 | color([0, 1, 1]) 109 | cylinder(h = dowelPinH, r = dowelPinR); 110 | } 111 | } 112 | 113 | 114 | module magnet() { 115 | if (!screwMask) { 116 | color([1, 1, 1]) 117 | cylinder(r = magnetR, h = magnetH); 118 | } 119 | } 120 | 121 | module arrow(length) { 122 | color([1,0,1]) { 123 | translate(v = [0, 0, length-2]) 124 | cylinder(r1 = 2, r2 = 0.2, h = 2); 125 | 126 | cylinder(r = 1, h = length-2); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /rack/assembly/connectXYTrayWithMainRails.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./attachSideConnectorModulesToYBars.scad> 3 | 4 | $vpt = [96,110,70]; 5 | $vpr = [68,0,36]; 6 | $vpd = 700; 7 | $vpf = 22.50; 8 | 9 | connectXYTrayWithMainRails(at=$t); 10 | 11 | module connectXYTrayWithMainRails(at=0) { 12 | 13 | attachSideConnectorModulesToYBars(at=1); 14 | 15 | function mainRailTrans() = 16 | yBarMainRailConnectorTrans; 17 | 18 | module railAndScrew(at) { 19 | t1 = lerp(a=12, b=0, t=min(1, 2*at)); 20 | t2 = lerp(a=16, b=0, t=max(0, 2*at - 1)); 21 | 22 | if (!plasticMask) { 23 | translate(v=[0,0,t1]) 24 | mainRail(); 25 | } 26 | 27 | if (at >= 1/2) { 28 | multmatrix( 29 | translate(v = [mainRailSlideHexOnYBarDx, mainRailSlideHexOnYBarDy, -5])* 30 | rotate(a = [-45, 0, 0])) 31 | translate(v = [0, 0, 14 + t2]) // length of caseScrewLong 32 | caseScrewLong(); 33 | } 34 | } 35 | 36 | multmatrix(mainRailTrans()) 37 | railAndScrew(at=at); 38 | 39 | multmatrix(yBarMirrorOtherCornerTrans * mainRailTrans()) 40 | railAndScrew(at=at); 41 | 42 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * mainRailTrans()) 43 | railAndScrew(at=at); 44 | 45 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * yBarMirrorOtherCornerTrans * mainRailTrans()) 46 | railAndScrew(at=at); 47 | } -------------------------------------------------------------------------------- /rack/assembly/finalBuilds.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | include <./screwFeet.scad> 3 | 4 | // TODO fix this 5 | module finalBuilds () { 6 | 7 | // Final builds: 8 | // render() 9 | // finalSingle(); 10 | // finalDouble(); 11 | 12 | // Features: 13 | // render() 14 | // slideInNuts(at=$t); 15 | // stackable(at=$t); 16 | // sideSwivel(at=$t); 17 | 18 | module finalSingle(r=0) { 19 | screwFeet(at=1,r=r); 20 | 21 | for (i = [1, 5, 9, 13, 17]) { 22 | translate(v = [2, frontScrewSpacing+railFrontThickness+railSlotToXZ, i*10]) 23 | multmatrix(yBarMainRailConnectorTrans) 24 | union() { 25 | if (!sideSupportRailMask) { 26 | sideSupportRailBase("lBracket"); 27 | } 28 | 29 | rotate(a = [0, -90, 0]) 30 | caseScrewMedium(); 31 | 32 | translate(v = [0, sideRailScrewMountDist, 0]) 33 | rotate(a = [0, -90, 0]) 34 | caseScrewMedium(); 35 | } 36 | } 37 | 38 | multmatrix(xBarSpaceToYBarSpace*xBarMirrorOtherCornerTrans*yBarSpaceToXBarSpace) 39 | for (i = [1, 5, 9, 13, 17]) { 40 | translate(v = [2, frontScrewSpacing+railFrontThickness+railSlotToXZ, i*10]) 41 | multmatrix(yBarMainRailConnectorTrans) 42 | union() { 43 | if (!sideSupportRailMask) { 44 | sideSupportRailBase("lBracket"); 45 | } 46 | rotate(a=[0,-90,0]) 47 | caseScrewMedium(); 48 | 49 | translate(v=[0,sideRailScrewMountDist,0]) 50 | rotate(a=[0,-90,0]) 51 | caseScrewMedium(); 52 | } 53 | } 54 | } 55 | 56 | 57 | module finalDouble(r=0) { 58 | stackable(at=1,r=r); 59 | } 60 | 61 | module slideInNuts(at=0) { 62 | 63 | t = lerp(a=15,b=0,t=at); 64 | 65 | screwFeet(at=1); 66 | 67 | slideInScrew(t=t, i=1); 68 | slideInScrew(t=t, i=4); 69 | 70 | module slideInScrew(t=0, i=1) { 71 | translate(v = [railScrewHoleToOuterEdge + t, railFrontThickness/2, railFootThickness+(10*i)]) 72 | multmatrix(yBarMainRailConnectorTrans) 73 | rotate(a = [90, 0, 0]) 74 | hexNut(mainRailScrewType); 75 | } 76 | } 77 | 78 | module slideInStackConnectorNut(at=0) { 79 | 80 | t = lerp(a=10,b=0,t=at); 81 | 82 | module slidingNut(t=0) { 83 | translate(v=[connectorRectWidth/2,connectorRectDepth/2 - t,connectorBottomToScrew+stackConnectorDualSpacing/2]) 84 | rotate(a=[90,0,0]) 85 | rotate(a=[0,90,0]) 86 | hexNut(rackFrameScrewType); 87 | } 88 | 89 | slidingNut(t=t); 90 | 91 | mirror(v=[0,0,1]) 92 | slidingNut(t=t); 93 | 94 | translate(v=[0,connectorRectDepth,0]) 95 | mirror(v=[0,1,0]) 96 | stackConnectorDual(); 97 | } 98 | 99 | module stackable(at=0,r=0) { 100 | 101 | t1 = lerp(a=0, b=1, t=min(3*at, 1)); 102 | t2 = lerp(a=30, b=0, t=min(max(3*at-1,0),1)); 103 | t3 = lerp(a=15, b=0, t=max(3*at-2, 0)); 104 | 105 | module stackConnectors() { 106 | mirrorAllTrayCornersFromYBarSpace() 107 | multmatrix(stackConnectorTrans(t=0)) 108 | slideInStackConnectorNut(t1); 109 | } 110 | 111 | module singleTrayScrews() { 112 | screwTrans = feetToYBarTrans(t=0) * translate(v=[-t3 - 9,0,connectorBottomToScrew]) * rotate(a=[0,-90,0]); 113 | 114 | mirrorAllTrayCornersFromYBarSpace() 115 | multmatrix(screwTrans) 116 | caseScrewLong(); 117 | } 118 | 119 | if (!plasticMask) { 120 | translate(v = [0, 0, t2/2]) 121 | stackConnectors(); 122 | } 123 | 124 | if (at >= 2/3) { 125 | multmatrix(secondStackTrans) 126 | singleTrayScrews(); 127 | 128 | multmatrix(upperXYTrayTrans) 129 | singleTrayScrews(); 130 | } 131 | 132 | translate(v=[0,0,t2]) 133 | multmatrix(secondStackTrans) 134 | attachXYPlates(at=1,r=r); 135 | 136 | screwFeet(at=1,r=20); 137 | 138 | } 139 | 140 | module sideSwivel(at=0) { 141 | r = abs(lerp(a=-110,b=110,t=at)); 142 | 143 | finalSingle(r=r); 144 | } 145 | 146 | 147 | } -------------------------------------------------------------------------------- /rack/assembly/insertDowelsIntoSideWall.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./addMagnetsToSideWall.scad> 3 | 4 | $vpt = [65,120,96]; 5 | $vpr = [66,0,112]; 6 | $vpd = 500; 7 | $vpf = 22.50; 8 | 9 | insertDowelsIntoSideWall(at=$t); 10 | 11 | module insertDowelsIntoSideWall(at=0) { 12 | 13 | t = lerp(a=10, b=0, t=at); 14 | 15 | hingeHoleH = hingePoleH-sideWallConnLugDepression; 16 | 17 | addMagnetsToSideWall(at=1); 18 | 19 | translate(v=[hingePoleDx,hingePoleDy, (sideWallZ-hingeHoleH) + t]) 20 | hingeDowel(); 21 | 22 | translate(v=[hingePoleDx,hingePoleDy, (hingeHoleH-hingePoleH)-t]) 23 | hingeDowel(); 24 | 25 | } -------------------------------------------------------------------------------- /rack/assembly/insertFeet.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./slideHexNutToFeet.scad> 3 | use <./attachXYPlates.scad> 4 | 5 | $vpt = [95,90,10]; 6 | $vpr = [105,0,38]; 7 | $vpd = 650; 8 | $vpf = 22.50; 9 | 10 | insertFeet(at=$t); 11 | 12 | module insertFeet(at=0,r=0) { 13 | 14 | t = lerp(a=10,b=0,t=at); 15 | 16 | attachXYPlates(at=1,r=r); 17 | 18 | multmatrix(feetToYBarTrans(t=t)) 19 | slideHexNutToFeet(at=1); 20 | 21 | multmatrix(yBarMirrorOtherCornerTrans * feetToYBarTrans(t=t)) 22 | slideHexNutToFeet(at=1); 23 | } 24 | 25 | module screwFeet(at=0,r=0) { 26 | 27 | t = lerp(a=20, b=0, t=at); 28 | 29 | function screwTrans(t=0) = translate(v=[-t - 9,0,connectorBottomToScrew]) * rotate(a=[0,-90,0]); 30 | mirrorOtherFeetStackConnectorTrans = translate(v=[stackConnectorDx,0,0]) * mirror(v=[1,0,0]); 31 | 32 | 33 | module screwToFeetModule() { 34 | multmatrix(feetToYBarTrans(t = 0)*screwTrans(t = t)) 35 | caseScrewLong(); // we might want a longer screw? 36 | 37 | multmatrix(feetToYBarTrans(t = 0)*mirrorOtherFeetStackConnectorTrans*screwTrans(t = t)) 38 | caseScrewLong(); 39 | } 40 | 41 | screwToFeetModule(); 42 | 43 | multmatrix(yBarMirrorOtherCornerTrans) 44 | screwToFeetModule(); 45 | 46 | insertFeet(at=1,r=r); 47 | } 48 | -------------------------------------------------------------------------------- /rack/assembly/propUpBottomXYTraywithSideWalls.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./insertDowelsIntoSideWall.scad> 3 | use <./connectXYTrayWithMainRails.scad> 4 | 5 | $vpt = [96,110,70]; 6 | $vpr = [68,0,36]; 7 | $vpd = 700; 8 | $vpf = 22.50; 9 | 10 | propUpBottomXYTraywithSideWalls(at=$t, r=0); 11 | 12 | module propUpBottomXYTraywithSideWalls(at=0, r=0) { 13 | 14 | t = lerp(a=10,b=0,t=at); 15 | 16 | function sideWallToYBarTrans(t=0,r=0) = 17 | yBarMirrorOtherCornerTrans * 18 | yBarSideModuleConnectorTrans * // bring to y bar space 19 | mirror(v=[0,1,0]) * 20 | translate(v=[0,0,t]) * 21 | translate(v=[sideWallConnW/2.0, -hingePoleR, sideWallZHingeTotalClearance]) * // bring to side module space 22 | rotate(a=[0,0,-r]) * 23 | translate(v=[-hingePoleDx, -hingePoleDy, 0]); 24 | 25 | connectXYTrayWithMainRails(at=1); 26 | 27 | multmatrix(sideWallToYBarTrans(t=t, r=r)) 28 | insertDowelsIntoSideWall(at=1); 29 | 30 | multmatrix(xBarSpaceToYBarSpace * xBarMirrorOtherCornerTrans * yBarSpaceToXBarSpace * sideWallToYBarTrans(t=t,r=r)) 31 | insertDowelsIntoSideWall(at=1); 32 | } -------------------------------------------------------------------------------- /rack/assembly/screwFeet.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./insertFeet.scad> 3 | 4 | $vpt = [95,90,10]; 5 | $vpr = [105,0,38]; 6 | $vpd = 650; 7 | $vpf = 22.50; 8 | 9 | screwFeet(at=$t); 10 | 11 | module screwFeet(at=0,r=0) { 12 | 13 | t = lerp(a=20, b=0, t=at); 14 | 15 | function screwTrans(t=0) = translate(v=[-t - 9,0,connectorBottomToScrew]) * rotate(a=[0,-90,0]); 16 | mirrorOtherFeetStackConnectorTrans = translate(v=[stackConnectorDx,0,0]) * mirror(v=[1,0,0]); 17 | 18 | 19 | module screwToFeetModule() { 20 | multmatrix(feetToYBarTrans(t = 0)*screwTrans(t = t)) 21 | caseScrewMedium(); 22 | 23 | multmatrix(feetToYBarTrans(t = 0)*mirrorOtherFeetStackConnectorTrans*screwTrans(t = t)) 24 | caseScrewMedium(); 25 | } 26 | 27 | screwToFeetModule(); 28 | 29 | multmatrix(yBarMirrorOtherCornerTrans) 30 | screwToFeetModule(); 31 | 32 | insertFeet(at=1,r=r); 33 | } 34 | -------------------------------------------------------------------------------- /rack/assembly/screwXBarAndYBar.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./attachXBarWithYBar.scad> 3 | 4 | $vpt = [103,90,20]; 5 | $vpr = [68,0,36]; 6 | $vpd = 500; 7 | $vpf = 22.50; 8 | 9 | screwXBarAndYBar(at=$t); 10 | 11 | module screwXBarAndYBar(at=0) { 12 | screwExtension = lerp(a=16, b=0, t=at); 13 | 14 | // in x bar space 15 | function xBarYBarScrewTrans(extension) = 16 | translate(v=[27,xBarSideThickness + extension,8]) * rotate(a=[270,0,0]); 17 | 18 | attachXBarWithYBar(at=1); 19 | 20 | multmatrix(xBarSpaceToYBarSpace) 21 | union() { 22 | 23 | if (!plasticMask) { xBar(); } 24 | 25 | multmatrix(xBarYBarScrewTrans(screwExtension)) 26 | caseScrewLong(); 27 | 28 | multmatrix(xBarMirrorOtherCornerTrans * xBarYBarScrewTrans(screwExtension)) 29 | caseScrewLong(); 30 | } 31 | 32 | multmatrix(yBarMirrorOtherCornerTrans*xBarSpaceToYBarSpace) 33 | union() { 34 | if (!plasticMask) { xBar(); } 35 | 36 | multmatrix(xBarYBarScrewTrans(screwExtension)) 37 | caseScrewLong(); 38 | 39 | multmatrix(xBarMirrorOtherCornerTrans * xBarYBarScrewTrans(screwExtension)) 40 | caseScrewLong(); 41 | } 42 | } -------------------------------------------------------------------------------- /rack/assembly/slideHexNutToFeet.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | $vpt = [75,-10,-14]; 4 | $vpr = [74,0,120]; 5 | $vpd = 300; 6 | $vpf = 22.50; 7 | 8 | slideHexNutToFeet(at=$t); 9 | 10 | module slideHexNutToFeet(at=0) { 11 | 12 | t = lerp(a=8, b=0, t=at); 13 | 14 | module slideNut() { 15 | if (!screwMask) { 16 | rotate(a = [0, 0, 90]) 17 | rotate(a = [90, 0, 0]) 18 | hexNut(rackFrameScrewType); 19 | } 20 | } 21 | 22 | translate(v=[0,t,connectorBottomToScrew + 0.5]) // where does this come from again? slack? 23 | slideNut(); 24 | 25 | translate(v=[stackConnectorDx,t,connectorBottomToScrew + 0.5]) // where does this come from again? slack? 26 | slideNut(); 27 | 28 | if (!plasticMask) { 29 | rackFeet(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rack/assembly/slideHexNutsIntoYBar.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | 3 | $vpt = [43,66,41]; 4 | $vpr = [44,0,47]; 5 | $vpd = 350; 6 | $vpf = 22.50; 7 | 8 | slideHexNutsIntoYBar(at=$t); 9 | 10 | module slideHexNutsIntoYBar(at=0) 11 | { 12 | 13 | t = lerp(a=20,b=0,t=at); 14 | 15 | if (!plasticMask) { 16 | yBar(); 17 | } 18 | 19 | function sideModuleTrans(t=0) = 20 | yBarSideModuleConnectorTrans * 21 | translate(v = [yBarScrewHoleToOuterYEdge, yBarScrewHoleToFrontXEdge+t, -(4+sideWallConnLugDepression)]) * 22 | rotate(a=[0,0,90]); 23 | 24 | function mainRailTrans(t=0) = 25 | yBarMainRailConnectorTrans * 26 | translate(v = [mainRailSlideHexOnYBarDx+t, mainRailSlideHexOnYBarDy, -5]) * 27 | rotate(a=[-45,0,0]); 28 | 29 | function xBarTrans(t=0) = 30 | translate(v = [-5, 27, 8+t]) * 31 | yBarXBarConnectorTrans * 32 | rotate(a=[0,90,0]); 33 | 34 | module slideHexNutsOneCorner(t=0) { 35 | 36 | if (!fixedSideModules) { 37 | multmatrix(sideModuleTrans(t = t)) 38 | hexNut(rackFrameScrewType); 39 | } 40 | 41 | multmatrix(mainRailTrans(t = t)) 42 | hexNut(rackFrameScrewType); 43 | 44 | multmatrix(xBarTrans(t = t)) 45 | hexNut(rackFrameScrewType); 46 | } 47 | 48 | if (!screwMask) { 49 | slideHexNutsOneCorner(t = t); 50 | 51 | multmatrix(yBarMirrorOtherCornerTrans) 52 | slideHexNutsOneCorner(t = t); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /rack/assembly/slideHexNutsIntoYBarXYPlate.scad: -------------------------------------------------------------------------------- 1 | include <./common.scad> 2 | use <./attachXYTrays.scad> 3 | 4 | $vpt = [71,123,88]; 5 | $vpr = [44,0,47]; 6 | $vpd = 450; 7 | $vpf = 22.50; 8 | 9 | slideHexNutsIntoYBarXYPlate(at=$t); 10 | 11 | module slideHexNutsIntoYBarXYPlate(at=0) { 12 | 13 | t = lerp(a=12,b=0,t=at); 14 | 15 | attachXYTrays(at=1,r=0); 16 | 17 | slideHexNuts(t=t); 18 | 19 | multmatrix(upperXYTrayTrans) 20 | slideHexNuts(t=t); 21 | 22 | module plateHexNut(t) { 23 | multmatrix(yBarBasePlateConnectorTrans) 24 | translate(v=[basePlateYBarSlideNutDx+t, basePlateYBarSlideNutDy, 4 + plateBlockBaseConnRecession]) // TODO gotta rename these 25 | hexNut(rackFrameScrewType); 26 | } 27 | 28 | module slideHexNuts(t=0) { 29 | 30 | plateHexNut(t=t); 31 | 32 | translate(v=[xyPlateConnDx, 0,0]) 33 | plateHexNut(t=-t); 34 | 35 | translate(v=[0, xyPlateConnDy,0]) 36 | plateHexNut(t=t); 37 | 38 | translate(v=[xyPlateConnDx, xyPlateConnDy,0]) 39 | plateHexNut(t=-t); 40 | } 41 | } -------------------------------------------------------------------------------- /rack/connector/basePlateYBarConnectors.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | 5 | connectorTopThickness = screwRadiusSlacked(rackFrameScrewType)+0.5; 6 | 7 | module onYBarBasePlateConnectorPositive() { 8 | translate(v=[0,0,yBarWallThickness]) 9 | intersection() { 10 | cube(size = [yBarXYPlateBlockX, yBarXYPlateBlockY, yBarXYPlateBlockZ]); 11 | halfspace(vpos=[0, -1, -1], p=[0, yBarXYPlateBlockY-1, yBarXYPlateBlockZ-1]); 12 | } 13 | } 14 | 15 | module onYBarBasePlateConnectorNegative() { 16 | 17 | translate(v=[basePlateYBarSlideNutDx, basePlateYBarSlideNutDy, 4 + plateBlockBaseConnRecession]) 18 | mirror(v=[0,0,1]) 19 | hexNutPocket_N("m3", openSide=false, backSpace=5, bridgeBack=true); 20 | 21 | hull() { 22 | // This has always been a pretty annoying to fit part. Increasing slack to 2*radiusXYSlack to compensate. TODO fix 23 | translate(v = [basePlateYBarSlideNutDx, basePlateYBarSlideNutDy, plateBlockBaseConnRecession+overhangSlack]) 24 | roundCutSlice(radius = connectorTopThickness+2*radiusXYSlack); 25 | 26 | translate(v = [basePlateYBarSlideNutDx, basePlateYBarSlideNutDy, 0]) 27 | roundCutSlice(radius = plateBlockBaseConnY/2 + 2*radiusXYSlack); 28 | } 29 | 30 | } 31 | 32 | module onBasePlateToYBarConnectorPositive() { 33 | 34 | union() { 35 | translate(v=[basePlateConnPosX, basePlateConnPosY, 0]) 36 | yBarConnector(); 37 | 38 | translate(v=[basePlateConnPosX, basePlateConnPosY+xyPlateConnDy, 0]) 39 | yBarConnector(); 40 | 41 | translate(v=[basePlateConnPosX+xyPlateConnDx, basePlateConnPosY, 0]) 42 | rotate(a=[0,0,180]) 43 | yBarConnector(); 44 | 45 | translate(v=[basePlateConnPosX+xyPlateConnDx, basePlateConnPosY+xyPlateConnDy, 0]) 46 | rotate(a=[0,0,180]) 47 | yBarConnector(); 48 | 49 | } 50 | 51 | module yBarConnector() { 52 | difference() { 53 | hull() { 54 | translate(v=[0,0,plateBlockBaseConnRecession]) 55 | roundCutSlice(radius = connectorTopThickness, length=5); 56 | 57 | roundCutSlice(radius = plateBlockBaseConnY/2, length=15); 58 | } 59 | mirror(v=[0,0,1]) 60 | counterSunkHead_N(rackFrameScrewType, headExtension = eps, screwExtension = inf10); 61 | 62 | } 63 | } 64 | } 65 | 66 | module roundCutSlice(radius, length=inf50) { 67 | 68 | hull() { 69 | cylinder(r = radius, h = eps); 70 | 71 | translate(v = [length, -radius, 0]) 72 | cube(size = [eps, radius*2, eps]); 73 | } 74 | } -------------------------------------------------------------------------------- /rack/connector/connectors.scad: -------------------------------------------------------------------------------- 1 | // Connector Aggregator and utils 2 | 3 | include <../../helper/common.scad> 4 | include <../../config/common.scad> 5 | include <./xBarYBarConnectors.scad> 6 | include <./mainRailYBarConnectors.scad> 7 | include <./sideModuleYBarConnectors.scad> 8 | include <./stackYBarConnectors.scad> 9 | include <./basePlateYBarConnectors.scad> 10 | include <./transformations.scad> 11 | 12 | // Default is to apply the positive first 13 | module applyConnector(on, to, trans) { 14 | 15 | apply_pn() { 16 | multmatrix(trans) 17 | connectorPositive(on=on, to=to); 18 | 19 | multmatrix(trans) 20 | connectorNegative(on=on, to=to); 21 | 22 | children(0); 23 | } 24 | } 25 | 26 | module connectorDebug(on, to, trans) { 27 | 28 | color([0,1,0]) 29 | multmatrix(trans) 30 | connectorPositive(on=on, to=to); 31 | 32 | color([1,0,0]) 33 | multmatrix(trans) 34 | connectorNegative(on=on, to=to); 35 | } 36 | 37 | module applyConnectorDebug(on,to,trans) { 38 | 39 | echo("on: ", on, "-- to:", to); 40 | 41 | apply_p() { 42 | multmatrix(trans) 43 | connectorDebug(on=on,to=to,trans=trans); 44 | 45 | children(0); 46 | } 47 | } 48 | 49 | module connectorPositive(on, to) { 50 | 51 | if (on == "yBar" && to == "xBar") { 52 | onYBarToXBarPositive(); 53 | } else if (on == "yBar" && to == "basePlate") { 54 | onYBarBasePlateConnectorPositive(); 55 | } else if (on == "yBar" && to == "magnetModule") { 56 | onYBarMagnetModulePositive(); 57 | } else if (on == "yBar" && to == "hingeModule") { 58 | onYBarHingeModulePositive(); 59 | } else if (on == "basePlate" && to == "yBar") { 60 | onBasePlateToYBarConnectorPositive(); 61 | } else if (on == "xBar" && to == "yBar") { 62 | onXBarToYBarPositive(); 63 | } else if (on == "mainRail" && to == "yBar") { 64 | onMainRailYBarConnectorPositive(); 65 | } 66 | } 67 | 68 | module connectorNegative(on, to) { 69 | 70 | if (on == "yBar" && to == "xBar") { 71 | onYBarToXBarNegative(); 72 | } else if (on == "xBar" && to == "yBar") { 73 | onXBarToYBarNegative(); 74 | } else if (on == "yBar" && to == "sideModule") { 75 | onYBarSideModuleNegative(); 76 | } else if (on == "yBar" && to == "hingeModule") { 77 | onYBarHingeModuleNegative(); 78 | } else if (on == "yBar" && to == "mainRail") { 79 | onYBarToMainRailNegative(); 80 | } else if (on == "yBar" && to == "stackConnector") { 81 | onYBarStackConnectorNegative(); 82 | } else if (on == "yBar" && to == "basePlate") { 83 | onYBarBasePlateConnectorNegative(); 84 | } else if (on == "mainRail" && to == "yBar") { 85 | onMainRailYBarConnectorNegative(); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /rack/connector/mainRailYBarConnectors.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | 5 | mainRailSlideHexOnYBarDx = railSideMountThickness + 5; 6 | mainRailSlideHexOnYBarDy = railFrontThickness + 2; 7 | 8 | module onYBarToMainRailNegative() { 9 | 10 | slotSlack = xySlack; 11 | slotZSlack = zSlack; 12 | 13 | union() { 14 | translate(v=[-slotZSlack/2, -slotSlack/2,0]) 15 | cube(size = [railTotalWidth+slotZSlack, railTotalDepth + slotSlack, railFootThickness]); 16 | 17 | translate(v = [mainRailSlideHexOnYBarDx, mainRailSlideHexOnYBarDy, -5]) 18 | rotate(a=[-45,0,0]) 19 | hexNutPocket_N("m3", openSide=false, backSpace=5); 20 | } 21 | } 22 | 23 | module onMainRailYBarConnectorPositive() { 24 | cube(size = [frontFaceWidth, sideSupportDepth+railFrontThickness, railFootThickness]); 25 | 26 | // TODO magic numbers 27 | hull() { 28 | cube(size = [frontFaceWidth, railFrontThickness+8, railFootThickness+4]); 29 | translate(v = [0, railFrontThickness+14, 0]) 30 | cube(size = [frontFaceWidth, 1, railFootThickness]); 31 | } 32 | } 33 | 34 | 35 | module onMainRailYBarConnectorNegative() { 36 | 37 | screwOffset = 9; 38 | 39 | translate(v = [mainRailSlideHexOnYBarDx, mainRailSlideHexOnYBarDy + screwOffset, -5 + screwOffset]) 40 | rotate(a=[-45,0,0]) 41 | counterSunkHead_N(rackFrameScrewType, screwExtension=inf50, headExtension=inf50); 42 | 43 | } 44 | -------------------------------------------------------------------------------- /rack/connector/sideModuleYBarConnectors.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <../side/sideWallVariables.scad> 5 | include <../side/magnetModule.scad> 6 | include <../side/hingeModule.scad> 7 | 8 | 9 | module onYBarSideModuleNegative(fixed=false) { 10 | 11 | translate(v = [-xySlack/2, -xySlack/2, -sideWallConnLugDepression]) 12 | cube(size = [sideWallConnW+xySlack, sideWallConnD+xySlack, sideWallConnLugDepression]); 13 | 14 | 15 | if (!fixed) { 16 | translate(v = [yBarScrewHoleToOuterYEdge, yBarScrewHoleToFrontXEdge, -(4+sideWallConnLugDepression)]) 17 | rotate(a=[0,0,90]) 18 | hexNutPocket_N("m3", openSide=false, backSpace=5, bridgeFront=true); 19 | } 20 | } 21 | 22 | 23 | module onYBarMagnetModulePositive() { 24 | 25 | 26 | translate(v=[sideWallConnW,0,-sideWallConnLugDepression]) 27 | mirror(v=[1,0,0]) 28 | magnetModule(fixed=true); 29 | 30 | 31 | } 32 | 33 | module onYBarHingeModulePositive() { 34 | 35 | translate(v=[sideWallConnW,0,-sideWallConnLugDepression]) 36 | mirror(v=[1,0,0]) 37 | hingeModule(fixed=false); 38 | } 39 | 40 | module onYBarHingeModuleNegative() { 41 | 42 | translate(v=[sideWallConnW,0,-sideWallConnLugDepression]) 43 | mirror(v=[1,0,0]) 44 | difference() { 45 | cube(size=[sideWallConnW,sideWallConnD,sideWallConnLugDepression]); 46 | hingeModule(fixed=true); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rack/connector/stackYBarConnectors.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | 5 | 6 | module stackConnectorBase(rectSlack, topSlack=0.0) { 7 | 8 | wSlacked = connectorRectWidth + rectSlack; 9 | dSlacked = connectorRectDepth + rectSlack; 10 | topRSlacked = connectorTopR + rectSlack/2; 11 | 12 | module connRect() { 13 | linear_extrude(height=eps) 14 | square(size = [wSlacked, dSlacked]); 15 | } 16 | 17 | module connTop() { 18 | linear_extrude(height=eps) 19 | circle(r=topRSlacked); 20 | } 21 | 22 | hull() { 23 | connRect(); 24 | 25 | translate(v=[0,0,connectorTaperStartHeight]) 26 | connRect(); 27 | } 28 | 29 | hull() { 30 | translate(v = [0, 0, connectorTaperStartHeight]) 31 | connRect(); 32 | 33 | translate(v=[wSlacked/2, dSlacked/2, connectorTotalHeight+topSlack]) 34 | connTop(); 35 | } 36 | } 37 | 38 | 39 | module onYBarStackConnectorNegative() { 40 | 41 | wSlacked = connectorRectWidth + connectorRectSocketSlack; 42 | dSlacked = connectorRectDepth + connectorRectSocketSlack; 43 | 44 | bevelSlack = 0.6; 45 | bevelR = wSlacked + bevelSlack; 46 | bevelW = dSlacked + bevelSlack; 47 | bevelH = 0.6; 48 | 49 | screwExtension = 4; 50 | 51 | 52 | union() { 53 | stackConnectorBase(connectorRectSocketSlack, topSlack=0.4); 54 | 55 | translate(v = [-screwExtension, connectorRectDepth/2, connectorBottomToScrew]) 56 | rotate(a = [0, -90, 0]) 57 | counterSunkHead_N(rackFrameScrewType, screwExtension = 5, headExtension = 10); 58 | 59 | // bevel at the lip of the socket to guide the plug, as well as mitigate elephant foot during 3d printing 60 | hull() { 61 | translate(v = [0, 0, bevelH]) 62 | linear_extrude(height = eps) 63 | square(size = [wSlacked, dSlacked]); 64 | 65 | translate(v = [-bevelSlack/2, -bevelSlack/2, 0]) 66 | linear_extrude(height = eps) 67 | square(size = [bevelR, bevelW]); 68 | } 69 | } 70 | } 71 | 72 | 73 | module stackConnectorPlug() { 74 | 75 | difference() { 76 | stackConnectorBase(connectorRectPlugSlack); 77 | 78 | translate(v=[connectorRectWidth/2,connectorRectDepth/2,connectorBottomToScrew]) 79 | rotate(a=[0,0,90]) 80 | rotate(a=[90,0,0]) 81 | hexNutPocket_N(rackFrameScrewType, openSide=false); 82 | } 83 | } 84 | 85 | module stackConnectorBottom() { 86 | 87 | height = 2; // space between bottom and floor 88 | 89 | stackConnectorPlug(); 90 | translate(v=[0,0,-height]) 91 | cube(size=[connectorRectWidth+connectorRectPlugSlack, connectorRectDepth+connectorRectPlugSlack, height]); 92 | } 93 | -------------------------------------------------------------------------------- /rack/connector/transformations.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | 5 | // Y-bar 6 | yBarMirrorOtherCornerTrans = translate(v = [0, yBarDepth, 0]) * mirror(v = [0, 1, 0]); 7 | 8 | yBarBasePlateConnectorTrans = translate(v = [yBarWidth-yBarBasePlateConnectorWidth, joinCornerDepth, 0]); 9 | 10 | yBarStackConnectorTrans = translate(v = [connectorXEdgeToYBarXEdge, connectorYEdgeToYBarYEdge, 0]); 11 | 12 | yBarSideModuleConnectorTrans = translate(v = [ 13 | yBarWidth-(railTotalWidth+railSlotToInnerYEdge+railSlotToSideWallSlot+sideWallConnectorSlotWidth), 14 | sideWallSlotToXZ, 15 | yBarHeight 16 | ]); 17 | 18 | yBarMainRailConnectorTrans = translate(v = [ 19 | yBarWidth-(railTotalWidth+railSlotToInnerYEdge), 20 | railSlotToXZ, 21 | yBarHeight-railFootThickness 22 | ]); 23 | 24 | yBarXBarConnectorTrans = translate(v = [yBarWidth+eps, 0, 0]); 25 | 26 | 27 | // X-bar 28 | xBarYBarConnectorTrans = rotate(a=[0,0,-90]); 29 | xBarMirrorOtherCornerTrans = translate(v = [0, xBarX, 0]) * mirror(v = [0, 1, 0]); 30 | 31 | 32 | // Main rail 33 | mirrorMainRailOtherSideTrans = translate(v = [0, 0, railTotalHeight]) * mirror(v=[0,0,1]); 34 | 35 | -------------------------------------------------------------------------------- /rack/connector/xBarYBarConnectors.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | 5 | // On xBar 6 | module onXBarToYBarNegative() { 7 | y = 27; 8 | z = 8; 9 | slack = xBarYBarDovetailSlack; 10 | 11 | translate(v=[-1,14,0]) 12 | mirror(v=[1,0,0]) 13 | rotate(a=[0,0,-90]) 14 | dovetail( 15 | topWidth = 15+slack, 16 | bottomWidth = 12+slack, 17 | height = 2, 18 | length = yBarHeight, 19 | headExtension = 1+slack, 20 | baseExtension = 2, 21 | frontFaceLength = 0.5, // elephant foot compensation 22 | frontFaceScale = 1.05, 23 | backFaceLength = 5, 24 | backFaceScale = 1.2 25 | ); 26 | 27 | // TODO clean this up 28 | translate(v = [-xBarSideThickness, y, z]) 29 | rotate(a = [0, -90, 0]) 30 | counterSunkHead_N(rackFrameScrewType, screwExtension=inf10, headExtension=inf10); 31 | 32 | // lugs for snap fit and hold in place 33 | translate(v=[-0.1,26,13.5]) 34 | lug(); 35 | } 36 | 37 | module onXBarToYBarPositive() { 38 | // lugs for snap fit and hold in place 39 | translate(v=[-0.1,26,2]) 40 | lug(); 41 | } 42 | 43 | 44 | // On yBar 45 | module onYBarToXBarNegative() { 46 | y = 27; 47 | z = 8; 48 | translate(v = [-5, y, z]) 49 | rotate(a = [180, 0, 0]) 50 | rotate(a = [0, 90, 0]) 51 | hexNutPocket_N("m3", openSide=false, backSpace=5); 52 | 53 | // lugs for snap fit and hold in place 54 | translate(v=[-0.1,26,2]) 55 | lug(); 56 | } 57 | 58 | module onYBarToXBarPositive() { 59 | 60 | translate(v=[1, 14,0]) // TODO: variable for the 14 61 | rotate(a=[0,0,-90]) 62 | dovetail( 63 | topWidth = 15-xySlack, // figure out why we need this 64 | bottomWidth = 12, 65 | height = 2, 66 | length = yBarHeight, 67 | headExtension = 1, 68 | baseExtension = 2, 69 | frontFaceLength = 2, 70 | frontFaceScale = 0.95, // elephant foot compensation 71 | backFaceLength = 5, 72 | backFaceScale = 1.2 73 | ); 74 | 75 | // lugs for snap fit and hold in place 76 | translate(v=[-0.1,26,13.5]) 77 | lug(); 78 | } 79 | 80 | 81 | xBarConnectorToYBarConnectorTrans = mirror(v=[1,0,0]); 82 | yBarConnectorToXBarConnectorTrans = mirror(v=[-1,0,0]); 83 | 84 | 85 | module lug() { 86 | hull() { 87 | sphere(r=0.5); 88 | 89 | translate(v=[0,2,0]) 90 | sphere(r=0.5); 91 | } 92 | } -------------------------------------------------------------------------------- /rack/mainRail.scad: -------------------------------------------------------------------------------- 1 | include <../helper/common.scad> 2 | include <../config/common.scad> 3 | include <./sharedVariables.scad> 4 | include <./connector/connectors.scad> 5 | 6 | mainRail(); 7 | 8 | module mainRail() { 9 | 10 | applyBevels() 11 | applyConnector(on="mainRail", to="yBar", trans=yBarConnectorTrans) 12 | applyConnector(on="mainRail", to="yBar", trans=mirrorMainRailOtherSideTrans * yBarConnectorTrans) 13 | mainRailBase(); 14 | 15 | module mainRailBase() { 16 | 17 | difference() { 18 | union() { 19 | frontRailSegment(); 20 | 21 | translate(v = [railSideMountThickness, railFrontThickness, 0]) 22 | rotate(a = [0, 0, 90]) 23 | sideSupportSegment(); 24 | } 25 | } 26 | 27 | module frontRailSegment() { 28 | difference() { 29 | cube(size = [frontFaceWidth, railFrontThickness, railTotalHeight]); 30 | 31 | for (i = [1:numRailScrews]) { 32 | translate(v = [railScrewHoleToOuterEdge, railFrontThickness/2, i*screwDiff+railFootThickness]) 33 | rotate(a = [90, 0, 0]) 34 | hexNutPocket_N(mainRailScrewType); 35 | } 36 | } 37 | } 38 | 39 | module sideSupportSegment() { 40 | difference() { 41 | cube(size = [sideSupportDepth, railSideMountThickness, railTotalHeight]); 42 | 43 | for (i = [1:numRailScrews]) { 44 | translate(v = [frontScrewSpacing, railFrontThickness/2, i*screwDiff+railFootThickness]) 45 | rotate(a = [90, 0, 0]) 46 | cylinder(r = screwRadiusSlacked(mainRailSideMountScrewType), h = inf10, $fn = 32); 47 | } 48 | } 49 | } 50 | 51 | } 52 | 53 | yBarConnectorTrans = identity; 54 | 55 | module applyBevels() { 56 | b = 0.5; // bevel value 57 | apply_n() { 58 | union() { 59 | halfspace(vpos = [-1, -1, 0], p = [b, b, 0]); 60 | halfspace(vpos = [-1, 0, -1], p = [b, 0, b]); 61 | halfspace(vpos = [-1, 0, 1], p = [b, 0, railTotalHeight-b]); 62 | 63 | cylindricalFiletNegative(p0=[frontFaceWidth, 0, 0], p1=[frontFaceWidth, 0, railTotalHeight], n=[1,-1,0], r=1); 64 | cylindricalFiletNegative(p0=[frontFaceWidth, railFrontThickness, railFootThickness+4], p1=[frontFaceWidth, railFrontThickness, railTotalHeight-(railFootThickness+4)], n=[1,1,0], r=1); 65 | } 66 | 67 | children(0); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rack/print/eval_P.scad: -------------------------------------------------------------------------------- 1 | include <../sharedVariables.scad> 2 | use <../xBar.scad> 3 | use <../yBar.scad> 4 | use <../mainRail.scad> 5 | 6 | // Evaluation print for slack config, please see rackstack/config/print.scad to configure tolerances 7 | // Too tight -> increase slacks values. Too loose -> decrease values 8 | 9 | intersection() { 10 | yBar(); 11 | halfspace(vpos=[0,-1,0], p=[0,joinCornerDepth,0]); 12 | } 13 | 14 | translate(v=[35,0,0]) 15 | intersection() { 16 | translate(v=[0,-xBarX + xBarSideThickness + 5,0]) 17 | xBar(); 18 | halfspace(vpos=[0,1,0], p=[0,0,0]); 19 | } 20 | 21 | translate(v=[35,18,0]) 22 | rotate(a=[0,0,90]) 23 | intersection() { 24 | mirror(v=[0,1,0]) 25 | multmatrix(mainRailPrintOrientation) 26 | mainRail(); 27 | halfspace(vpos=[-1,0,0], p=[19,0,0]); 28 | 29 | mainRailPrintOrientation = [ 30 | [cos(-90), 0, sin(-90), railTotalHeight], 31 | [0, 1, 0, 0], 32 | [-sin(-90), 0, cos(-90), 0], 33 | [0, 0, 0, 1] 34 | ]; 35 | } -------------------------------------------------------------------------------- /rack/print/hingeModule.scad: -------------------------------------------------------------------------------- 1 | use <../side/hingeModule.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required (? need to print to test) 5 | hingeModule(); -------------------------------------------------------------------------------- /rack/print/magnetModuleLeft_P.scad: -------------------------------------------------------------------------------- 1 | use <../side/magnetModule.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required (? need to print to test) 5 | mirror(v=[1,0,0]) 6 | magnetModule(); -------------------------------------------------------------------------------- /rack/print/magnetModuleRight_P.scad: -------------------------------------------------------------------------------- 1 | use <../side/magnetModule.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required (? need to print to test) 5 | magnetModule(); -------------------------------------------------------------------------------- /rack/print/mainRail_P.scad: -------------------------------------------------------------------------------- 1 | include <../sharedVariables.scad> 2 | 3 | use <../mainRail.scad> 4 | 5 | // Oriented for 3d printing. 6 | // Supports not required. 7 | multmatrix(mainRailPrintOrientation) 8 | mainRail(); 9 | 10 | mainRailPrintOrientation = [ 11 | [cos(-90), 0, sin(-90), railTotalHeight], 12 | [0, 1, 0, 0 ], 13 | [-sin(-90), 0, cos(-90), 0 ], 14 | [0, 0, 0, 1 ] 15 | ]; -------------------------------------------------------------------------------- /rack/print/rackFeet_P.scad: -------------------------------------------------------------------------------- 1 | include <../sharedVariables.scad> 2 | use <../stackEnds.scad> 3 | 4 | 5 | // Oriented for 3d printing. No supports required. 6 | rotate(a=[90-feetProtrusionAngle,0,0]) 7 | rackFeet(); -------------------------------------------------------------------------------- /rack/print/rackJoiner_P.scad: -------------------------------------------------------------------------------- 1 | use <../stackEnds.scad> 2 | 3 | // Oriented for 3d printing. No supports required, but it's recommended to print this with a brim. 4 | rackJoiner(); -------------------------------------------------------------------------------- /rack/print/sideWallLeft_P.scad: -------------------------------------------------------------------------------- 1 | use <../side/sideWallLeft.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required (? need to print to test), but a brim is recommended 5 | // Depending on the type of filament you use, you could also print this with the outer wall side facing down. 6 | sideWallLeft(); -------------------------------------------------------------------------------- /rack/print/sideWallRight_P.scad: -------------------------------------------------------------------------------- 1 | use <../side/sideWallRight.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required (? need to print to test), but a brim is recommended 5 | // Depending on the type of filament you use, you could also print this with the outer wall side facing down. 6 | sideWallRight(); -------------------------------------------------------------------------------- /rack/print/stackConnectorBottom_P.scad: -------------------------------------------------------------------------------- 1 | use <../stackEnds.scad> 2 | 3 | // Oriented for 3d printing. No supports required. 4 | stackConnectorBottom(); -------------------------------------------------------------------------------- /rack/print/xBar_P.scad: -------------------------------------------------------------------------------- 1 | use <../xBar.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required: depending on roundness. 5 | xBar(); -------------------------------------------------------------------------------- /rack/print/xyPlate_P.scad: -------------------------------------------------------------------------------- 1 | use <../xyPlate.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports generally not required. 5 | xyPlate(); -------------------------------------------------------------------------------- /rack/print/yBarLeft_P.scad: -------------------------------------------------------------------------------- 1 | use <../yBar.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports required at XY wall connections, and depending on roundness 5 | yBar(); 6 | -------------------------------------------------------------------------------- /rack/print/yBarRight_P.scad: -------------------------------------------------------------------------------- 1 | use <../yBar.scad> 2 | 3 | // Oriented for 3d printing. 4 | // Supports required at XY wall connections, and depending on roundness 5 | 6 | mirror(v=[1,0,0]) 7 | yBar(); 8 | -------------------------------------------------------------------------------- /rack/sharedVariables.scad: -------------------------------------------------------------------------------- 1 | include <../config/common.scad> 2 | 3 | // TODO: This rather unweildy config file is the result of multiple lazy and forgetful refactors. Clean it up. 4 | 5 | // Main rail variables: 6 | 7 | /* Small horizontal planes at the top and bottom of the main rails. Used so we can fasten the rail to the frame 8 | Note that this value is also used for a depression at the bottom/top of the frame for aligning the rail */ 9 | railFootThickness = 3; 10 | 11 | railTotalHeight = screwDiff * (numRailScrews + 1) + 2 * railFootThickness; 12 | 13 | railFrontThickness = 8; // Make sure that the nuts for the chosen screw type can slot within the front face 14 | railSideMountThickness = 2.5; 15 | 16 | // Distance between the middle of a screw mount and the rail's vertical edges 17 | railScrewHoleToInnerEdge = 5; 18 | railScrewHoleToOuterEdge = 7; 19 | 20 | // Distance between the midpoint of the rail screw holes. 21 | rackMountScrewWidth = maxUnitWidth + 2 * railScrewHoleToInnerEdge; 22 | 23 | // Extra spacing for screws. 24 | frontScrewSpacing = 15; 25 | 26 | sideSupportScrewHoleToBackEdge = 4; 27 | sideSupportDepth = sideSupportScrewHoleToBackEdge + frontScrewSpacing; 28 | 29 | frontFaceWidth = railScrewHoleToInnerEdge + railScrewHoleToOuterEdge; 30 | 31 | railTotalWidth = frontFaceWidth; 32 | railTotalDepth = railFrontThickness+sideSupportDepth; 33 | 34 | // Side Wall variables, cannot put in sideWallVariables due to dependency by ybar 35 | sideWallThickness = 2.5; 36 | 37 | sideWallSlotToOuterYEdge = 3; 38 | sideWallSlotToOuterXEdge = 3; // TODO rename to variables found in ybar 39 | sideWallConnectorSlotWidth = 7; 40 | 41 | // Y Bar variables: 42 | railSlotToXZ = 3; 43 | sideWallSlotToXZ = 3; 44 | 45 | railSlotToInnerYEdge = 2; 46 | railSlotToSideWallSlot = 2; 47 | 48 | yBarWidth = railSlotToInnerYEdge + railTotalWidth+ railSlotToSideWallSlot 49 | + sideWallSlotToOuterYEdge + sideWallConnectorSlotWidth; 50 | yBarDepth = maxUnitDepth + 2*railSlotToInnerYEdge; 51 | yBarHeight = 15; 52 | yBarWallThickness = 3; 53 | yBarRoundness = baseRoundness; 54 | 55 | joinCornerDepth = 32; 56 | 57 | // It's actually the railSlotToInnerYEdge of the yBar, it'll be nice to be able to refer to it like yBar.railSlotToInnerYEdge 58 | xBarX = maxUnitWidth - 2*railSlotToInnerYEdge; 59 | xBarY = 32; 60 | xBarHeight = 15; 61 | 62 | xBarWallThickness = 3; 63 | xBarSideThickness = 8; 64 | xBarRoundness = baseRoundness; 65 | 66 | rackTotalWidth = 2*yBarWidth + xBarX; 67 | rackTotalDepth = yBarDepth; 68 | 69 | 70 | // Dimensions for the connector block, applied to y-bar 71 | yBarXYPlateBlockX = 12; 72 | yBarXYPlateBlockY = 14; 73 | yBarXYPlateBlockZ = 10; 74 | 75 | // Needed for y bar to align this connector to its inner Y edge 76 | yBarBasePlateConnectorWidth = yBarXYPlateBlockX; 77 | 78 | // x and y faces of the yBarBasePlateMount_P block 79 | plateBlockInnerXFaceToScrew = 6; 80 | plateBlockInnerYFaceToScrew = 8; 81 | plateBlockBaseConnRecession = 3; 82 | plateBlockBaseConnY = 8; 83 | 84 | basePlateYBarSlideNutDx = yBarXYPlateBlockX - plateBlockInnerXFaceToScrew; 85 | basePlateYBarSlideNutDy = yBarXYPlateBlockY - plateBlockInnerYFaceToScrew; 86 | 87 | basePlateScrewMountToYBarXZFace = basePlateYBarSlideNutDy + joinCornerDepth; // Distance to the nearest YBar XZ face 88 | basePlateScrewMountToYBarYZFace = (yBarWidth+basePlateYBarSlideNutDx) - yBarBasePlateConnectorWidth; 89 | 90 | basePlateConnYBarCornerDx = yBarWidth; // distance from a plate body corner and the nearest yBar corner 91 | basePlateConnYBarCornerDy = xBarY; // distance from a plate body corner and the nearest yBar corner 92 | 93 | basePlateConnPosX = basePlateScrewMountToYBarYZFace - basePlateConnYBarCornerDx; // distance between plateBody corner at (0,0,0) and the related corner 94 | basePlateConnPosY = basePlateScrewMountToYBarXZFace - basePlateConnYBarCornerDy; 95 | 96 | xyPlateConnDx = xBarX + 2*basePlateYBarSlideNutDx; // X distance between connectors 97 | xyPlateConnDy = yBarDepth - 2*basePlateScrewMountToYBarXZFace; // Y distance between connectors 98 | plateGap = 1; // distance between edge of xy plate and other parts 99 | assert(plateGap >= xySlack); 100 | 101 | 102 | connectorYEdgeToYBarYEdge = 5; 103 | connectorXEdgeToYBarXEdge = 5; 104 | 105 | connectorRectWidth = 10; 106 | connectorRectDepth = 10; 107 | connectorTotalHeight = 10; 108 | 109 | connectorSocketMagnetExtrudeHeight = 1; 110 | connectorTaperStartHeight = 3; 111 | connectorTopR = 3; 112 | connectorRectPlugSlack = -0.2; 113 | connectorRectSocketSlack = 0.2; 114 | connectorBottomToScrew = 6; 115 | // Distance from midpoint of stack connectors to each other 116 | stackConnectorDx = rackTotalWidth - 2*(connectorXEdgeToYBarXEdge + connectorRectWidth/2); 117 | stackConnectorDy = rackTotalDepth - 2*(connectorYEdgeToYBarYEdge + connectorRectDepth/2); 118 | stackConnectorDualSpacing = 0.5; 119 | 120 | feetProtrusionAngle = 40; -------------------------------------------------------------------------------- /rack/side/hingeModule.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <./sideWallVariables.scad> 5 | 6 | 7 | module hingeModule(fixed=false) { 8 | 9 | applyHingePole() 10 | applyYBarScrewMount(fixed) 11 | base(); 12 | 13 | module base() { 14 | 15 | connW = sideWallConnW; 16 | connD = sideWallConnD; 17 | 18 | intersection() { 19 | 20 | union() { 21 | cube(size = [connW, connD, sideWallConnLugDepression]); 22 | 23 | // Riser to enforce side wall hinge clearance 24 | translate(v = [0, 0, sideWallConnLugDepression]) 25 | cube(size = [connW, connD - 12, sideWallZGapClearance]); 26 | } 27 | 28 | union() { 29 | // don't bevel the part around the dowel pin hole 30 | *cube(size = [connW, 2*(hingePoleR+radiusXYSlack) , sideWallConnLugDepression + sideWallZGapClearance]); 31 | 32 | // TODO: pattern for this? beef up mirror4XY? 33 | intersection() { 34 | cVal = 0.25; 35 | halfspace(p = [0, cVal, 0], vpos = [0, 1, 1]); 36 | halfspace(p = [cVal, 0, 0], vpos = [1, 0, 1]); 37 | halfspace(p = [connW-cVal, 0, 0], vpos = [-1, 0, 1]); 38 | halfspace(p = [0, connD-cVal, 0], vpos = [0, -1, 1]); 39 | } 40 | } 41 | } 42 | } 43 | 44 | module applyHingePole() { 45 | apply_n() { 46 | 47 | union() { 48 | translate(v = [sideWallConnW/2.0, hingePoleR + radiusXYSlack, 0]) 49 | cylinder(r = hingePoleR + radiusXYSlack, h = inf50, $fn=64); 50 | 51 | translate(v = [sideWallConnW/2.0, 0 , 0]) 52 | cube(size=[2, 5, inf50], center=true); 53 | } 54 | 55 | children(0); 56 | } 57 | } 58 | 59 | 60 | module applyYBarScrewMount(fixed=false) { 61 | apply_n() { 62 | 63 | if (!fixed) { 64 | translate(v = [yBarScrewHoleToOuterYEdge, yBarScrewHoleToFrontXEdge, sideWallConnLugDepression]) 65 | counterSunkHead_N(rackFrameScrewType, headExtension = eps, screwExtension = inf10); 66 | } 67 | 68 | children(0); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rack/side/magnetModule.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <./sideWallVariables.scad> 5 | 6 | 7 | module magnetModule(fixed=false) { 8 | 9 | applyYBarScrewMount(fixed) 10 | applyMagnetMount() 11 | base(); 12 | 13 | module base() { 14 | 15 | connW = sideWallConnW; 16 | connD = sideWallConnD; 17 | 18 | intersection() { 19 | 20 | cube(size = [connW, connD, sideWallConnLugDepression]); 21 | 22 | // TODO: pattern for this? beef up mirror4XY? 23 | cVal = 0.25; 24 | halfspace(p=[0,cVal,0], vpos=[0,1,1]); 25 | halfspace(p=[cVal,0,0], vpos=[1,0,1]); 26 | halfspace(p=[connW-cVal,0,0], vpos=[-1,0,1]); 27 | halfspace(p=[0,connD-cVal,0], vpos=[0,-1,1]); 28 | } 29 | } 30 | 31 | module applyYBarScrewMount(fixed=false) { 32 | 33 | apply_n() { 34 | 35 | if (!fixed) { 36 | translate(v = [yBarScrewHoleToOuterYEdge, yBarScrewHoleToFrontXEdge, sideWallConnLugDepression]) 37 | counterSunkHead_N(rackFrameScrewType, headExtension = eps, screwExtension = inf10); 38 | } 39 | 40 | children(0); 41 | } 42 | } 43 | 44 | module applyMagnetMount() { 45 | apply_pn() { 46 | magnetMountShell(); 47 | magnetMountHole(); 48 | children(0); 49 | } 50 | 51 | module magnetMountShell() { 52 | hull() { 53 | translate(v = [0, magnetModuleMagnetMountDy, magnetModuleMagnetMountDz]) 54 | rotate(a = [0, 90, 0]) 55 | cylinder(r = magnetMountShellRadius, h = sideWallConnW-magnetFaceToSideWallConnOuterYEdge); 56 | 57 | translate(v = [0, 2, sideWallConnH]) 58 | cube(size = [sideWallConnW-magnetFaceToSideWallConnOuterYEdge, 2*magnetMountShellRadius, eps]); 59 | } 60 | } 61 | 62 | module magnetMountHole() { 63 | translate(v = [sideWallConnW-(magnetFaceToSideWallConnOuterYEdge+magnetHSlacked), 64 | magnetModuleMagnetMountDy, 65 | magnetModuleMagnetMountDz]) 66 | rotate(a = [0, 90, 0]) 67 | cylinder(r = magnetRSlacked, h = magnetHSlacked); 68 | } 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /rack/side/sideWallBase.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <./sideWallMagnetMount.scad> 5 | include <./sideWallVariables.scad> 6 | use <./magnetModule.scad> 7 | use <./hingeModule.scad> 8 | 9 | //translate(v = [hingePoleDx ,hingePoleDy, 10]) 10 | //rotate(a=[0,0,-120]) 11 | //translate(v = [-hingePoleDx ,-hingePoleDy, 0]) 12 | *sideWallBase(); 13 | 14 | module sideWallBase() { 15 | 16 | applyHingeConnector() 17 | applySideWallBracing(numVerticalRibs=2) 18 | applyMagnetConnector() 19 | applyHandle() 20 | sideWallBase(); 21 | 22 | module sideWallBase() { 23 | 24 | module sideWallShellHelper(x, y, z, r) { 25 | translate(v = [r, r, 0]) 26 | minkowski() { 27 | cube(size = [x-r, y-2*r, z]); 28 | 29 | if (r > 0) { 30 | sphere(r = r); 31 | } 32 | } 33 | } 34 | 35 | intersection() { 36 | difference() { 37 | sideWallShellHelper(sideWallX, sideWallY, sideWallZ, baseRoundness); 38 | translate(v = [sideWallThickness, sideWallThickness, 0]) 39 | sideWallShellHelper(sideWallX, sideWallY-2*sideWallThickness, sideWallZ, max(0,baseRoundness-sideWallThickness)); 40 | } 41 | halfspace(vpos = [-1, 0, 0], p = [sideWallX, 0, 0]); 42 | halfspace(vpos = [0, 0, -1], p = [0, 0, sideWallZ]); 43 | halfspace(vpos = [0, 0, 1], p = [0, 0, 0]); 44 | halfspace(vpos = [1, 0, 0], p = [0, 0, 0]); 45 | } 46 | } 47 | 48 | module applyHingeConnector() { 49 | 50 | hingeHoleShellR = hingePoleR+1; 51 | 52 | hingeHoleH = hingePoleH-sideWallConnLugDepression; 53 | assert(hingeHoleH > 1); 54 | 55 | apply_pn() { 56 | hull() { 57 | hingeShell(); 58 | hingeSideProjectionPlane(); 59 | } 60 | 61 | union() { 62 | hingeHole(extraZ=1); 63 | 64 | translate(v=[0,0,sideWallZ]) 65 | mirror(v=[0,0,1]) 66 | hingeHole(extraZ=1); 67 | 68 | // Trim parts of the wall for rotational clearance 69 | halfspace(p=[0, sideWallY-2.5, 0], vpos=[-0.5,1,0]); // flat area to limit rotation against the main rail 70 | halfspace(p=[sideWallX-10, sideWallY, 0], vpos=[1,2.5,0]); 71 | } 72 | 73 | children(0); 74 | } 75 | 76 | module hingeShell() { 77 | translate(v = [hingePoleDx, hingePoleDy, 0]) 78 | cylinder(r = hingeHoleShellR, h = sideWallZ); 79 | } 80 | 81 | // XZ plane in line with the back of the case. Project the hinge pole shell onto this to fill any weird 82 | // geometries from the curves of the side wall 83 | module hingeSideProjectionPlane() { 84 | translate(v=[sideWallThickness, hingePoleDy - hingeHoleShellR, 0]) 85 | cube(size=[eps, 2*hingeHoleShellR, sideWallZ]); 86 | } 87 | 88 | module hingeHole(extraZ) { 89 | translate(v = [hingePoleDx, hingePoleDy, 0]) 90 | cylinder(r = hingePoleR+radiusXYSlack, h = hingeHoleH+extraZ); 91 | } 92 | } 93 | 94 | // TODO: add correct magnet translations, also remove random variables 95 | module applyMagnetConnector() { 96 | apply_p() { 97 | union() { 98 | translate(v = [sideWallThickness, magnetMountToYBarFront, magnetMountToYBarTop - sideWallZHingeTotalClearance]) 99 | sideWallMagnetMountRotated(); 100 | 101 | translate(v = [sideWallThickness, magnetMountToYBarFront, sideWallZ - (magnetMountToYBarTop- sideWallZHingeTotalClearance)]) 102 | sideWallMagnetMountRotated(); 103 | } 104 | 105 | children(0); 106 | } 107 | 108 | module sideWallMagnetMountRotated() { 109 | rotate(a=[0,90,0]) 110 | sideWallMagnetMount(); 111 | } 112 | } 113 | 114 | module applyHandle() { 115 | 116 | handleWidth = 8; 117 | handleLength = 60; 118 | handleRoundness = 7; 119 | widthOffset = 3; 120 | 121 | apply_n() { 122 | 123 | minkowski() { 124 | sphere(r=handleRoundness); 125 | 126 | translate(v = [sideWallX -(handleWidth-handleRoundness) + widthOffset, 0, (sideWallZ-handleLength)/2]) 127 | cube(size = [handleWidth-handleRoundness, sideWallThickness, handleLength-handleRoundness]); 128 | } 129 | 130 | children(0); 131 | } 132 | } 133 | } 134 | 135 | module applySideWallDefaultVentilation(numVents) { 136 | 137 | r = 2; // vent roundness 138 | ventLength = sideWallY - 2*sideWallDefaultVentilationToZEdge; 139 | ventZDiff = numVents == 1 140 | ? 0 // TODO kinda ugly 141 | : (sideWallZ - 2*sideWallDefaultVentilationToYEdge)/(numVents-1); 142 | 143 | apply_n() { 144 | if (numVents > 0) { 145 | for (i = [0:numVents-1]) { 146 | translate(v = [0, sideWallDefaultVentilationToZEdge, i*ventZDiff+sideWallDefaultVentilationToYEdge]) 147 | vent(); 148 | } 149 | } 150 | 151 | children(0); 152 | } 153 | 154 | module vent() { 155 | translate(v=[-inf/2,r,-sideWallDefaultVentilationWidth/2]) 156 | minkowski() { 157 | rotate(a=[0,90,0]) 158 | cylinder(r=r,h=inf); 159 | cube(size = [inf, max(eps, ventLength-2*r), max(eps,sideWallDefaultVentilationWidth-2*r)]); 160 | } 161 | } 162 | } 163 | 164 | module applySideWallBracing(numVerticalRibs) { 165 | 166 | apply_p() { 167 | // TODO add horizontal bracing 168 | union() { 169 | sideWallVerticalBracing(numRibs = numVerticalRibs); 170 | sideWallHorizontalBracing(); 171 | } 172 | children(0); 173 | } 174 | 175 | module sideWallVerticalBracing(numRibs) { 176 | 177 | ribYDiff = sideWallY - 2*sideWallDefaultVerticalBracingToZEdge; 178 | 179 | translate(v=[0,sideWallDefaultVerticalBracingToZEdge,0]) 180 | intersection() { 181 | for (i = [0:numRibs-1]) { 182 | translate(v = [0, i*ribYDiff, 0]) 183 | verticalRib(); 184 | } 185 | 186 | halfspace(vpos=[1,0,0], p=[0,0,0]); 187 | } 188 | 189 | module verticalRib(height=4, thickness=3, rampLength=0) { 190 | 191 | translate(v=[sideWallThickness-eps,-thickness/2,0]) 192 | hull() { 193 | cube(size = [eps, thickness, eps]); 194 | 195 | translate(v = [0, 0, rampLength]) 196 | cube(size = [height, thickness, sideWallZ-2*rampLength]); 197 | 198 | translate(v = [0, 0, sideWallZ]) 199 | cube(size = [eps, thickness, eps]); 200 | } 201 | } 202 | } 203 | 204 | module sideWallHorizontalBracing() { 205 | 206 | ribThickness = 2.5; 207 | 208 | horizontalRib(thickness=ribThickness); 209 | 210 | translate(v=[0,0,sideWallZ-ribThickness]) 211 | horizontalRib(thickness=ribThickness); 212 | 213 | module horizontalRib(height=4, thickness=3, rampLength=5) { 214 | 215 | ribLength = sideWallY + rampLength - sideWallDefaultHorizontalBracingToZEdge; 216 | 217 | translate(v = [sideWallThickness-eps, sideWallY-ribLength, 0]) 218 | hull() { 219 | cube(size = [height, ribLength, thickness]); 220 | 221 | translate(v=[0,-rampLength,0]) 222 | cube(size=[eps, eps, thickness]); 223 | } 224 | } 225 | } 226 | } 227 | 228 | -------------------------------------------------------------------------------- /rack/side/sideWallLeft.scad: -------------------------------------------------------------------------------- 1 | include <./sideWallBase.scad> 2 | 3 | sideWallLeft(); 4 | 5 | module sideWallLeft() { 6 | 7 | numVentsCustom = sideWallVentilation? ceil((sideWallZ - 2*sideWallDefaultVentilationToYEdge)/10): 0; 8 | 9 | applySideWallDefaultVentilation(numVents=numVentsCustom) 10 | sideWallBase(); 11 | } 12 | -------------------------------------------------------------------------------- /rack/side/sideWallMagnetMount.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | include <../sharedVariables.scad> 4 | include <./sideWallVariables.scad> 5 | 6 | module sideWallMagnetMount() { 7 | // oriented so that the xy face is the side wall's inner face 8 | difference() { 9 | cylinder(r1 = magnetMountShellRadius+1, r2 = magnetMountShellRadius, h = innerSideWallToYBarMagnetConn); 10 | 11 | translate(v=[0, 0, innerSideWallToYBarMagnetConn-magnetHSlacked]) 12 | cylinder(r = magnetRSlacked, h = magnetHSlacked); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /rack/side/sideWallRight.scad: -------------------------------------------------------------------------------- 1 | include <./sideWallBase.scad> 2 | 3 | sideWallRight(); 4 | 5 | module sideWallRight() { 6 | 7 | numVentsCustom = sideWallVentilation? ceil((sideWallZ - 2*sideWallDefaultVentilationToYEdge)/10): 0; 8 | 9 | mirror(v=[1,0,0]) 10 | applySideWallDefaultVentilation(numVents=numVentsCustom) 11 | sideWallBase(); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /rack/side/sideWallVariables.scad: -------------------------------------------------------------------------------- 1 | include <../../helper/common.scad> 2 | include <../../config/common.scad> 3 | 4 | 5 | sideWallConnW = 7; 6 | sideWallConnD = 20; 7 | sideWallConnH = 3; 8 | sideWallConnLugDepression = sideWallConnH; 9 | 10 | yBarScrewHoleToOuterYEdge = 3.5; 11 | yBarScrewHoleToFrontXEdge = 16; 12 | 13 | magnetFaceToSideWallConnOuterYEdge = 2; 14 | magnetMountShellRadius = magnetRSlacked + 1; 15 | 16 | innerSideWallToYBarMagnetConn = magnetFaceToSideWallConnOuterYEdge + sideWallSlotToOuterYEdge - sideWallThickness; 17 | 18 | hingePoleR = dowelPinR; 19 | hingePoleH = dowelPinH; 20 | 21 | sideWallZHingeSlack = 0.3; 22 | sideWallZGapClearance = 0.5; 23 | sideWallZHingeTotalClearance = sideWallZHingeSlack + sideWallZGapClearance; // only for one edge 24 | sideWallZ = (railTotalHeight - 2*railFootThickness) - 2*sideWallZHingeTotalClearance; 25 | sideWallY = yBarDepth; 26 | 27 | magnetMountToYBarTop = magnetMountShellRadius + sideWallZHingeTotalClearance + 1; 28 | magnetMountToYBarFront = magnetMountShellRadius + sideWallSlotToXZ + 2; 29 | 30 | sideWallXGapClearance = 0.2; 31 | sideWallX = (yBarWidth-(railTotalWidth+railSlotToInnerYEdge)) - sideWallXGapClearance; 32 | 33 | hingePoleDx = sideWallSlotToOuterYEdge + sideWallConnW/2.0; 34 | hingePoleDy = sideWallY - (sideWallSlotToOuterXEdge + (hingePoleR+radiusXYSlack)); 35 | 36 | sideWallDefaultVerticalBracingToZEdge = 30; 37 | sideWallDefaultHorizontalBracingToZEdge = sideWallDefaultVerticalBracingToZEdge; 38 | sideWallDefaultVentilationToZEdge = 40; 39 | sideWallDefaultVentilationToYEdge = 25; 40 | sideWallDefaultVentilationWidth = 6; 41 | 42 | magnetModuleMagnetMountDy = magnetMountToYBarFront - sideWallSlotToXZ; 43 | magnetModuleMagnetMountDz = magnetMountToYBarTop + sideWallConnLugDepression; 44 | -------------------------------------------------------------------------------- /rack/stackEnds.scad: -------------------------------------------------------------------------------- 1 | include <../helper/common.scad> 2 | include <./sharedVariables.scad> 3 | include <./connector/connectors.scad> 4 | 5 | module rackJoiner() { 6 | translate(v=[0,0,stackConnectorDualSpacing/2]) 7 | stackConnectorPlug(); 8 | 9 | mirror(v=[0,0,1]) 10 | translate(v=[0,0,stackConnectorDualSpacing/2]) 11 | stackConnectorPlug(); 12 | 13 | translate(v=[0,0,-stackConnectorDualSpacing/2]) 14 | cube(size=[connectorRectWidth+connectorRectPlugSlack, connectorRectDepth+connectorRectPlugSlack, stackConnectorDualSpacing]); 15 | } 16 | 17 | module rackFeet() { 18 | 19 | bandThickness = 2; 20 | height = 18; 21 | 22 | translate(v = [stackConnectorDx/2, 0, 2]) 23 | mirror(v=[0,0,1]) { 24 | // stack connectors along rack x axis 25 | translate(v = [-(stackConnectorDx+connectorRectWidth)/2, -connectorRectDepth/2, 2-overhangSlack]) 26 | mirror(v = [0, 0, 1]) { 27 | translate(v = [stackConnectorDx, 0, 0]) 28 | stackConnectorBottom(); 29 | 30 | stackConnectorBottom(); 31 | } 32 | band(); 33 | } 34 | 35 | module band() { 36 | intersection() { 37 | translate(v=[0,0,2]) 38 | difference() { 39 | roundedCube(rackTotalWidth, inf50, height, 5, center = true); 40 | 41 | translate(v=[0,0,3]) 42 | roundedCube(rackTotalWidth-6, inf50, height-6, 3, center = true); 43 | } 44 | 45 | halfspace(vpos=[0,1,-tan(feetProtrusionAngle)],p=[0,-8,2]); 46 | halfspace(vpos=[0,-1, tan(feetProtrusionAngle)],p=[0,6,2]); 47 | 48 | // TODO make these edge deburrings more parametric 49 | halfspace(vpos=[0,-1,0],p=[0,16,2]); 50 | halfspace(vpos=[0,1,0],p=[0,-5,2]); 51 | } 52 | 53 | } 54 | 55 | module roundedCube(x,y,z,r, center=false) { 56 | translate(v=[0,0,z/2]) 57 | minkowski() { 58 | cube(size=[x-2*r,y,z-2*r], center=center); 59 | 60 | rotate(a=[90,0,0]) 61 | cylinder(r=r, h=eps); 62 | } 63 | } 64 | } 65 | 66 | rackTopHandle(); 67 | 68 | module rackTopHandle() { 69 | 70 | 71 | handleWidth = 20; 72 | handleHeight = 50; 73 | 74 | handleTopThickness = 10; 75 | handleBottomThickness = 10; 76 | handleSideThickness = 10; 77 | 78 | handleR = baseRoundness; 79 | 80 | handleRing(); 81 | 82 | module handleRing() { 83 | 84 | w = handleWidth - 2*handleR; 85 | st = max(eps,handleSideThickness - 2*handleR); 86 | bt = max(eps,handleBottomThickness - 2*handleR); 87 | tt = max(eps,handleTopThickness - 2*handleR); 88 | 89 | y =100; 90 | 91 | minkowski() { 92 | 93 | sphere(r=handleR); 94 | 95 | ringFourHull() { 96 | cube(size = [w, st, bt]); 97 | 98 | translate(v = [0, y-handleSideThickness, 0]) 99 | cube(size = [w, st, bt]); 100 | 101 | translate(v = [0, y-handleSideThickness, handleHeight-handleTopThickness]) 102 | cube(size = [w, st, tt]); 103 | 104 | translate(v = [0, 0, handleHeight-handleTopThickness]) 105 | cube(size = [w, st, tt]); 106 | } 107 | } 108 | 109 | } 110 | 111 | 112 | module ringFourHull() { 113 | union() { 114 | hull() {children(0); children(1);} 115 | hull() {children(1); children(2);} 116 | hull() {children(2); children(3);} 117 | hull() {children(3); children(0);} 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /rack/xBar.scad: -------------------------------------------------------------------------------- 1 | include <../helper/common.scad> 2 | include <../config/common.scad> 3 | include <./sharedVariables.scad> 4 | include <./connector/connectors.scad> 5 | 6 | xBar(); 7 | 8 | module xBar() { 9 | 10 | applyConnector(on="xBar", to="yBar", trans=xBarMirrorOtherCornerTrans * xBarYBarConnectorTrans) 11 | applyConnector(on="xBar", to="yBar", trans=xBarYBarConnectorTrans) 12 | xBarBase(); 13 | 14 | module xBarBase() { 15 | intersection() { 16 | 17 | difference() { 18 | cylindricalFiletEdge(xBarY, xBarX, xBarHeight, xBarRoundness); 19 | 20 | union() { 21 | translate(v = [xBarWallThickness, xBarSideThickness, xBarWallThickness]) 22 | cylindricalFiletEdge(xBarY, xBarX-2*xBarSideThickness, xBarHeight, xBarRoundness-xBarWallThickness); 23 | 24 | connectorWallFiletNegative(); 25 | 26 | multmatrix(xBarMirrorOtherCornerTrans) 27 | connectorWallFiletNegative(); 28 | } 29 | } 30 | 31 | // Shave off bottom corners to reduce elephant's foot at where xBar and YBar join 32 | halfspace(vpos = [0, 1, 1], p = [0, 0.75, 0]); 33 | halfspace(vpos = [0, -1, 1], p = [0, xBarX-0.75, 0]); 34 | } 35 | 36 | 37 | module connectorWallFiletNegative() { 38 | cylindricalFiletNegative( 39 | p0=[xBarWallThickness, xBarSideThickness, xBarHeight], 40 | p1=[xBarY, xBarSideThickness, xBarHeight], 41 | n=[0, 1, 1], r=2 42 | ); 43 | } 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /rack/xyPlate.scad: -------------------------------------------------------------------------------- 1 | include <../helper/common.scad> 2 | include <./connector/connectors.scad> 3 | 4 | xyPlate(); 5 | 6 | module xyPlate() { 7 | 8 | translate(v=-[basePlateConnPosX,basePlateConnPosY,0]) // center around one of the YBarConnector holes 9 | applyVentilation() 10 | applyConnector(on="basePlate", to="yBar") 11 | plateBody(); 12 | 13 | module plateBody() { 14 | plateBodyX = xBarX - 2*plateGap; 15 | plateBodyY = (yBarDepth - 2*xBarY) - 2*plateGap; 16 | plateBodyH = xBarWallThickness; 17 | 18 | translate(v=[plateGap, plateGap, 0]) { 19 | cube(size = [plateBodyX, plateBodyY, plateBodyH]); 20 | 21 | // bracing 22 | braceThickness = 3; 23 | braceHeight = 2; 24 | translate(v = [0, 0, plateBodyH]) 25 | difference() { 26 | cube(size = [plateBodyX, plateBodyY, braceHeight]); 27 | translate(v=[braceThickness, braceThickness,0]) 28 | cube(size=[plateBodyX-2*braceThickness, plateBodyY-2*braceThickness, braceHeight]); 29 | } 30 | 31 | } 32 | } 33 | 34 | module applyVentilation() { 35 | 36 | apply_n() { 37 | numSlits = 5; 38 | edgePadding = 30; 39 | diff = (xBarX-2*edgePadding)/(numSlits-1); 40 | slitWidth = 4; 41 | slitLength = (yBarDepth - 2*xBarY)-2*edgePadding; 42 | 43 | for(i=[0:numSlits-1]) { 44 | translate(v=[edgePadding+diff*i-slitWidth/2, edgePadding,0]) 45 | minkowski() { 46 | cylinder(h=1,r=2); 47 | cube(size=[slitWidth,slitLength,inf]); 48 | } 49 | } 50 | children(0); 51 | } 52 | } 53 | 54 | } -------------------------------------------------------------------------------- /rack/yBar.scad: -------------------------------------------------------------------------------- 1 | include <../helper/common.scad> 2 | include <../config/common.scad> 3 | include <./connector/connectors.scad> 4 | include <./sharedVariables.scad> 5 | 6 | yBar(); 7 | 8 | module yBar() { 9 | 10 | applyOnYBarBothCorners(to="basePlate", trans=yBarBasePlateConnectorTrans) 11 | applyOnYBarBothCorners(to="stackConnector", trans=yBarStackConnectorTrans) 12 | applySideModules() 13 | applyOnYBarBothCorners(to="mainRail", trans=yBarMainRailConnectorTrans) 14 | applyOnYBarBothCorners(to="xBar", trans=yBarXBarConnectorTrans) 15 | yBarBase(); 16 | 17 | module yBarBase() { 18 | intersection() { 19 | difference() { 20 | sphericalFiletEdge(yBarWidth, yBarDepth, yBarHeight, yBarRoundness); 21 | 22 | translate(v = [yBarWallThickness, joinCornerDepth, yBarWallThickness]) 23 | cylindricalFiletEdge(yBarWidth, yBarDepth-2*joinCornerDepth, yBarHeight, yBarRoundness-yBarWallThickness); 24 | } 25 | 26 | halfspace(vpos=[-1, 0, 1], p=[yBarWidth-1, 0,0]); 27 | } 28 | } 29 | 30 | // Helper module to apply connectors to both corners 31 | module applyOnYBarBothCorners(to, trans) { 32 | applyConnector(on="yBar", to=to, trans=trans) 33 | applyConnector(on="yBar", to=to, trans=yBarMirrorOtherCornerTrans * trans) 34 | children(0); 35 | } 36 | 37 | 38 | module applySideModules() { 39 | 40 | if (!fixedSideModules) { 41 | applyOnYBarBothCorners(to="sideModule", trans=yBarSideModuleConnectorTrans) 42 | children(0); 43 | 44 | } else { 45 | 46 | applyConnector(on="yBar", to="magnetModule", trans=yBarSideModuleConnectorTrans) 47 | applyConnector(on="yBar", to="hingeModule", trans=yBarMirrorOtherCornerTrans * yBarSideModuleConnectorTrans) 48 | children(0); 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /stl/micro/rack/eval_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/eval_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/hingeModule.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/hingeModule.stl -------------------------------------------------------------------------------- /stl/micro/rack/magnetModuleLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/magnetModuleLeft_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/magnetModuleRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/magnetModuleRight_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/mainRail_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/mainRail_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/rackFeet_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/rackFeet_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/rackJoiner_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/rackJoiner_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/sideWallLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/sideWallLeft_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/sideWallRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/sideWallRight_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/stackConnectorBottom_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/stackConnectorBottom_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/xBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/xBar_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/xyPlate_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/xyPlate_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/yBarLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/yBarLeft_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/yBarRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/yBarRight_P.stl -------------------------------------------------------------------------------- /stl/micro/rack/yBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/micro/rack/yBar_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/eval_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/eval_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/hingeModule.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/hingeModule.stl -------------------------------------------------------------------------------- /stl/mini/rack/magnetModuleLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/magnetModuleLeft_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/magnetModuleRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/magnetModuleRight_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/mainRail_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/mainRail_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/rackFeet_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/rackFeet_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/rackJoiner_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/rackJoiner_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/sideWallLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/sideWallLeft_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/sideWallRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/sideWallRight_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/stackConnectorBottom_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/stackConnectorBottom_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/xBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/xBar_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/xyPlate_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/xyPlate_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/yBarLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/yBarLeft_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/yBarRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/yBarRight_P.stl -------------------------------------------------------------------------------- /stl/mini/rack/yBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/mini/rack/yBar_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/eval_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/eval_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/hingeModule.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/hingeModule.stl -------------------------------------------------------------------------------- /stl/nano/rack/magnetModuleLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/magnetModuleLeft_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/magnetModuleRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/magnetModuleRight_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/mainRail_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/mainRail_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/rackFeet_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/rackFeet_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/rackJoiner_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/rackJoiner_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/sideWallLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/sideWallLeft_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/sideWallRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/sideWallRight_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/stackConnectorBottom_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/stackConnectorBottom_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/xBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/xBar_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/xyPlate_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/xyPlate_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/yBarLeft_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/yBarLeft_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/yBarRight_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/yBarRight_P.stl -------------------------------------------------------------------------------- /stl/nano/rack/yBar_P.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jazwa/rackstack/5bfb76a4ed850f7d02c33f651f40bd123d1f5208/stl/nano/rack/yBar_P.stl --------------------------------------------------------------------------------