Compare commits
17 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de92bfc408 | ||
|
|
839ea944e9 | ||
|
|
ae9b343b40 | ||
|
|
51aa349be5 | ||
|
|
44ed1746f9 | ||
|
|
a5bad40082 | ||
|
|
bbb742f271 | ||
|
|
9dd9add861 | ||
|
|
bc80d1e65f | ||
|
|
34cd659177 | ||
|
|
3a7f1ebb1b | ||
|
|
631ef6e837 | ||
|
|
91a43c8259 | ||
|
|
e91dffa6cc | ||
|
|
1ad8a68308 | ||
|
|
9ed1685b80 | ||
|
|
3e935d4f3d |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
*.3mf
|
*.3mf
|
||||||
*.stl
|
*.stl
|
||||||
|
.vscode
|
||||||
|
|||||||
11
.lint/pylint/.pylintrc
Normal file
11
.lint/pylint/.pylintrc
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[BASIC]
|
||||||
|
|
||||||
|
good-names=i,j,k,ex,Run,_,pk,x,y
|
||||||
|
|
||||||
|
[FORMAT]
|
||||||
|
|
||||||
|
max-line-length=121
|
||||||
|
|
||||||
|
[MESSAGES CONTROL]
|
||||||
|
|
||||||
|
disable=import-error,logging-fstring-interpolation,missing-module-docstring,missing-function-docstring,missing-class-docstring,duplicate-code
|
||||||
@@ -14,4 +14,12 @@ repos:
|
|||||||
- id: renovate-config-validator
|
- id: renovate-config-validator
|
||||||
args: ["--strict"]
|
args: ["--strict"]
|
||||||
language_version: 20.18.0 # workaround till https://github.com/renovatebot/pre-commit-hooks/issues/2460 is fixed
|
language_version: 20.18.0 # workaround till https://github.com/renovatebot/pre-commit-hooks/issues/2460 is fixed
|
||||||
|
- repo: https://github.com/PyCQA/pylint
|
||||||
|
rev: v3.3.6
|
||||||
|
hooks:
|
||||||
|
- id: pylint
|
||||||
|
args:
|
||||||
|
[
|
||||||
|
"--rcfile=.lint/pylint/.pylintrc"
|
||||||
|
]
|
||||||
...
|
...
|
||||||
|
|||||||
98
README.md
98
README.md
@@ -1,4 +1,4 @@
|
|||||||
[📚 What's it for?](#use-cases) | [⚙️ How does it work?](#how-it-works) | [🌐 Free & OpenSource](#open-standard)
|
[📚 What's it for?](#-use-cases) | [⚙️ How does it work?](#-features) | [🌐 Free & OpenSource](#-open-specs)
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@@ -8,27 +8,27 @@ You can find all parametric and non-parametric models, as well as the `.f3d` fil
|
|||||||
|
|
||||||
The parametric models are available in the [HomeRacker GitHub Repository](https://github.com/kellervater/homeracker/tree/main/models).
|
The parametric models are available in the [HomeRacker GitHub Repository](https://github.com/kellervater/homeracker/tree/main/models).
|
||||||
|
|
||||||
> [!NOTE]
|
> 💡 **Note**
|
||||||
> The basic HomeRacker system is also referred to as `HomeRacker - Core`. Free for everyone to use, remix, and reshare.
|
> The basic HomeRacker system is also referred to as `HomeRacker - Core`. Free for everyone to use, remix, and reshare.
|
||||||
|
|
||||||
# 📑 Table of Contents
|
# 📑 Table of Contents
|
||||||
- [🔧 Use Cases](#use-cases)
|
- [🔧 Use Cases](#-use-cases)
|
||||||
- [✨ Features](#features)
|
- [✨ Features](#-features)
|
||||||
- [⚙️ How it works](#how-it-works)
|
- [⚙️ How it works](#%EF%B8%8F-how-it-works)
|
||||||
- [🛠️ Assembly Basics](#assembly-basics)
|
- [🛠️ Assembly Basics](#%EF%B8%8F-assembly-basics)
|
||||||
- [💡 Assembly Tips](#assembly-tips)
|
- [💡 Assembly Tips](#-assembly-tips)
|
||||||
- [🖨️ Printing Tips](#printing-tips)
|
- [🖨️ Printing Tips](#%EF%B8%8F-printing-tips)
|
||||||
- [📐 Tech Specs](#tech-specs)
|
- [📐 Tech Specs](#-tech-specs)
|
||||||
- [🧱 Supports](#supports)
|
- [🧱 Supports](#-supports)
|
||||||
- [🔗 Connectors](#connectors)
|
- [🔗 Connectors](#-connectors)
|
||||||
- [📏 Lock Pins](#lock-pins)
|
- [📏 Lock Pins](#-lock-pins)
|
||||||
- [🌍 Open Specs](#open-specs)
|
- [🌍 Open Specs](#-open-specs)
|
||||||
- [❓ Why the name?](#why-the-name)
|
- [❓ Why the name?](#-why-the-name)
|
||||||
- [📜 Licensing](#-licensing)
|
- [📜 Licensing](#-licensing)
|
||||||
- [🧪 Tests](#tests)
|
- [🧪 Tests](#-tests)
|
||||||
- [⚠️ Disclaimer](#disclaimer)
|
- [⚠️ Disclaimer](#%EF%B8%8F-disclaimer)
|
||||||
- [🔬 How I tested](#how-i-tested)
|
- [🔬 How I tested](#-how-i-tested)
|
||||||
- [📋 Todos](#todos)
|
- [📋 Todos](#-todos)
|
||||||
|
|
||||||
# 🔧 Use Cases
|
# 🔧 Use Cases
|
||||||
I created HomeRacker because I was dissatisfied with the existing solutions available online.
|
I created HomeRacker because I was dissatisfied with the existing solutions available online.
|
||||||
@@ -44,15 +44,15 @@ To give you an idea of how this may look (10" rack, half-constructed Pi mini-rac
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Aside from the basic [building blocks](#basic-building-blocks), the rack above also contains the following parts:
|
Aside from the basic [building blocks](#-tech-specs), the rack above also contains the following parts:
|
||||||
|
|
||||||
* **10" Rack**
|
* **10" Rack**
|
||||||
* HomeRacker - 10" Rackmount Kit (Todo: link to Makerworld model) for standard-height units
|
* [HomeRacker - 10" Rackmount Kit](https://makerworld.com/en/models/1353730-modular-10-server-rack#profileId-1396904) for standard-height units
|
||||||
* Raspi 5 Mount Kit:
|
* [Raspi 5 Mount Kit](https://makerworld.com/en/models/1324096-pi-5-snapcase-for-homeracker#profileId-1360937):
|
||||||
* Vertical Mount Adapter for HomeRacker
|
* Vertical Mount Adapter for HomeRacker
|
||||||
* Front panel for 10" racks
|
* Front panel for 10" racks
|
||||||
* Rackmount ears for the switch. These are fully customizable rackmount ears I created as an [OpenSCAD file](./models/rackmount_ears/rackmount_ears.scad). You can customize it directly [here](https://makerworld.com/en/models/1259227-fully-customizable-rackmount-ears#profileId-1283271).
|
* Rackmount ears for the switch. These are fully customizable rackmount ears I created as an [OpenSCAD file](https://github.com/kellervater/homeracker/blob/main/models/rackmount_ears/rackmount_ears.scad). You can customize it directly [here](https://makerworld.com/en/models/1259227-fully-customizable-rackmount-ears#profileId-1283271).
|
||||||
* HomeRacker Airflow Kit (Todo: link to Makerworld model), which consists of:
|
* [HomeRacker Airflow Kit](https://makerworld.com/en/models/1353730-modular-10-server-rack#profileId-1396904) (currently part of the 10" rack model), which consists of:
|
||||||
* Front/back panels
|
* Front/back panels
|
||||||
* Side panels
|
* Side panels
|
||||||
* Bottom/top panels with air intake/exhaust grids and bores for standard fans (80/92/120mm)
|
* Bottom/top panels with air intake/exhaust grids and bores for standard fans (80/92/120mm)
|
||||||
@@ -66,7 +66,7 @@ The `HomeRacker - Core` features:
|
|||||||
* **Fully modular** – Thanks to the support-connector system, you can scale in any direction. The only limits are material strength—and how much money, space, and time you have.
|
* **Fully modular** – Thanks to the support-connector system, you can scale in any direction. The only limits are material strength—and how much money, space, and time you have.
|
||||||
* **3D-printable** – The entire core system is printable, and no tools are required for assembly.
|
* **3D-printable** – The entire core system is printable, and no tools are required for assembly.
|
||||||
* **No supports needed** – Not a single part of the core system needs printed supports.
|
* **No supports needed** – Not a single part of the core system needs printed supports.
|
||||||
* **OpenSource** – Build your own adapters and use the system in personal or commercial projects (see [Open Standard](#open-standard) and [Licensing](#-licensing) for details).
|
* **OpenSource** – Build your own adapters and use the system in personal or commercial projects (see [🌍 Open Specs](#-open-specs) and [📜 Licensing](#-licensing) for details).
|
||||||
|
|
||||||
## ⚙️ How it works
|
## ⚙️ How it works
|
||||||
> **tl;dr** Think of a rack shape you want to build, download the model (insert link), print it, assemble it, and add your own mounts/adapters/whatever-you-like.
|
> **tl;dr** Think of a rack shape you want to build, download the model (insert link), print it, assemble it, and add your own mounts/adapters/whatever-you-like.
|
||||||
@@ -86,8 +86,7 @@ Assembly is straightforward and requires no tools:
|
|||||||
|
|
||||||
### 💡 Assembly Tips
|
### 💡 Assembly Tips
|
||||||
|
|
||||||
> [!NOTE]
|
> 💡 **Pro Tip**: I created a sample 10" Cyberpunk-themed 3D model on MakerWorld (❗Todo: insert link) for inspiration.
|
||||||
> **Pro Tip**: I created a sample 10" Cyberpunk-themed 3D model on MakerWorld (❗Todo: insert link) for inspiration.
|
|
||||||
|
|
||||||
1. Plan ahead! Otherwise, you'll end up with an army of unused parts like me:
|
1. Plan ahead! Otherwise, you'll end up with an army of unused parts like me:
|
||||||
|
|
||||||
@@ -106,13 +105,11 @@ Assembly is straightforward and requires no tools:
|
|||||||
1. If bed adhesion is sketchy: Add a brim to the supports. The small contact surface on connectors can cause print failures.
|
1. If bed adhesion is sketchy: Add a brim to the supports. The small contact surface on connectors can cause print failures.
|
||||||
2. Prevent warping: Keep the print bed clean and oil-free. Even a fingerprint can cause issues.
|
2. Prevent warping: Keep the print bed clean and oil-free. Even a fingerprint can cause issues.
|
||||||
3. When printing new filament: Make sure to calibrate the material flow!
|
3. When printing new filament: Make sure to calibrate the material flow!
|
||||||
> [!NOTE]
|
> 💡 **Pro Tip**: If you need to disassemble and the pin is stuck, push it from the other side with another pin to release it.
|
||||||
> **Pro Tip**: If you need to disassemble and the pin is stuck, push it from the other side with another pin to release it.
|
|
||||||
|
|
||||||
## 📐 Tech Specs
|
## 📐 Tech Specs
|
||||||
|
|
||||||
> [!NOTE]
|
> 💡 **Note** - For actual dimensions, check out the original Fusion `.f3d` files on Makerworld. All designs are fully parameterized for easy scaling.
|
||||||
> For actual dimensions, check out the original Fusion `.f3d` files on Makerworld. All designs are fully parameterized for easy scaling.
|
|
||||||
|
|
||||||
The system is based on 4 core measurements:
|
The system is based on 4 core measurements:
|
||||||
|
|
||||||
@@ -160,7 +157,7 @@ Types:
|
|||||||
* **Pull-Through** – Open center for complex builds (e.g. 10" rack).
|
* **Pull-Through** – Open center for complex builds (e.g. 10" rack).
|
||||||
* **Feet** – Solid end pieces; used as rack feet.
|
* **Feet** – Solid end pieces; used as rack feet.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> ❗ **Important**
|
||||||
> Connector centers are always 1 `base_unit` in height. No offsets, no fluff.
|
> Connector centers are always 1 `base_unit` in height. No offsets, no fluff.
|
||||||
> So: 2 × 3-unit supports + 1 connector = exactly 7 base units (105mm).
|
> So: 2 × 3-unit supports + 1 connector = exactly 7 base units (105mm).
|
||||||
|
|
||||||
@@ -187,6 +184,7 @@ Schematics:
|
|||||||
|
|
||||||
**Side View**
|
**Side View**
|
||||||
> Don’t ask why the height is 3.791mm—it works. I left it as-is.
|
> Don’t ask why the height is 3.791mm—it works. I left it as-is.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> I may tweak the pin grip in future versions for to make it easier to pull-out again. But the base dimensions will remain the same for compatibility.
|
> I may tweak the pin grip in future versions for to make it easier to pull-out again. But the base dimensions will remain the same for compatibility.
|
||||||
@@ -194,7 +192,7 @@ Schematics:
|
|||||||
## 🌍 Open Specs
|
## 🌍 Open Specs
|
||||||
|
|
||||||
I created `HomeRacker - Core` to be an open spec that any maker can build on, with (almost) no strings attached.
|
I created `HomeRacker - Core` to be an open spec that any maker can build on, with (almost) no strings attached.
|
||||||
(See [Licensing](#-licensing) for more.)
|
(See [📜 Licensing](#-licensing) for more.)
|
||||||
|
|
||||||
I encourage you to make your own models based on HomeRacker!
|
I encourage you to make your own models based on HomeRacker!
|
||||||
Let me know, and I’ll feature your work on this page and cross-link it on Makerworld (subject to my "very objective" approval 😄).
|
Let me know, and I’ll feature your work on this page and cross-link it on Makerworld (subject to my "very objective" approval 😄).
|
||||||
@@ -208,15 +206,15 @@ So, "HomeRacker" was born—part practical, part tongue-in-cheek. It fits the ho
|
|||||||
|
|
||||||
# 📜 Licensing
|
# 📜 Licensing
|
||||||
|
|
||||||
> [!NOTE]
|
> 💡 **tl;dr**
|
||||||
> tl;dr – Use it for ANY purpose (even commercial), but credit me and share alike!
|
> – Use it for ANY purpose (even commercial), but credit me and share alike!
|
||||||
|
|
||||||
* Source code: `MIT License` ([LICENSE](./LICENSE))
|
* Source code: `MIT License` ([LICENSE](https://github.com/kellervater/homeracker/blob/main/LICENSE))
|
||||||
* 3D models & creative assets (`/models/`): `CC BY 4.0 License` ([/models/LICENSE](./models/LICENSE))
|
* 3D models & creative assets (`/models/`): `CC BY 4.0 License` ([/models/LICENSE](https://github.com/kellervater/homeracker/blob/main/models/LICENSE))
|
||||||
|
|
||||||
These licenses apply to the `HomeRacker - Core` system and customizable rackmount ears.
|
These licenses apply to the `HomeRacker - Core` system and customizable rackmount ears.
|
||||||
|
|
||||||
> [!IMPORTANT]
|
> ❗ **Important**
|
||||||
> Other models I publish may have more restrictive licenses. This will be stated clearly on Makerworld.
|
> Other models I publish may have more restrictive licenses. This will be stated clearly on Makerworld.
|
||||||
|
|
||||||
HomeRacker is an unregistered trademark of Patrick Pötz (kellervater), first used publicly on 12.04.2025.
|
HomeRacker is an unregistered trademark of Patrick Pötz (kellervater), first used publicly on 12.04.2025.
|
||||||
@@ -229,7 +227,7 @@ Look at all the prototypes:
|
|||||||
|
|
||||||
## ⚠️ Disclaimer
|
## ⚠️ Disclaimer
|
||||||
|
|
||||||
> [!WARNING]
|
> ⚠️ **Warning**
|
||||||
> This project is provided “as is,” without any warranty. Use at your own risk. I’m not responsible for damage, injury, or loss caused by using this system or its parts.
|
> This project is provided “as is,” without any warranty. Use at your own risk. I’m not responsible for damage, injury, or loss caused by using this system or its parts.
|
||||||
|
|
||||||
|
|
||||||
@@ -256,14 +254,22 @@ E.g.: you could print a connector in ABS, a support in PLA Matte and a Lock Pin
|
|||||||
> 🛠️ **Btw:** I am not affiliated with Bambu in any way besides uploading my models to MakerWorld and occasionally making use of their Exclusive Model program. But they don't pay me for naming their products anywhere else (I wish 😉).
|
> 🛠️ **Btw:** I am not affiliated with Bambu in any way besides uploading my models to MakerWorld and occasionally making use of their Exclusive Model program. But they don't pay me for naming their products anywhere else (I wish 😉).
|
||||||
|
|
||||||
# 📋 Todos
|
# 📋 Todos
|
||||||
* [ ] Rename Building blocks in f3d (did bad translations from german to english there)
|
* [x] Rename Building blocks in f3d (did bad translations from german to english there)
|
||||||
* [ ] Release models on MakerLab
|
* [x] Release models on MakerLab
|
||||||
* [ ] HomeRacker - Core (under above's license, non-exclusive)
|
* [x] HomeRacker - Core (under above's license, non-exclusive)
|
||||||
* [ ] HomeRacker - 10" Rackmount Kit (exclusive)
|
* [x] HomeRacker - 10" Rackmount Kit (exclusive)
|
||||||
* [ ] HomeRacker - Pi5 Mount Kit (exclusive)
|
* [x] HomeRacker - Pi5 Mount Kit (exclusive)
|
||||||
* [x] Customizable Rackmount Ears
|
* [x] Customizable Rackmount Ears
|
||||||
* [ ] HomeRacker - Airflow Kit (exclusive)
|
* [x] HomeRacker - Airflow Kit (exclusive) -> under 10" Rack
|
||||||
* [ ] HomeRacker - Shelf
|
* [x] HomeRacker - Shelf
|
||||||
* [ ] Can we even call it a standard yet?
|
|
||||||
* [ ] Contributing.md stub?
|
* [ ] Contributing.md stub?
|
||||||
* [ ] Quickstart Guide
|
* [ ] Quickstart Guide
|
||||||
|
* [ ] Parts Catalog
|
||||||
|
* [ ] Link (maybe via table) example models
|
||||||
|
|
||||||
|
# Logo
|
||||||
|
I asked ChatGPT to create a logo, and I think it turned out great — so we’ll stick with it for now. This logo will also be used as an overlay image for the thumbnails of all my 3D models that are compatible with HomeRacker.
|
||||||
|
|
||||||
|
I encourage you to do the same if you create models for HomeRacker. That way, it’ll be immediately visible to users that a model is designed to be mounted on a HomeRacker system.
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
BIN
favicon.ico
Normal file
BIN
favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
img/homeracker_favicon_transparent.xcf
Normal file
BIN
img/homeracker_favicon_transparent.xcf
Normal file
Binary file not shown.
BIN
img/homeracker_icon_yellow.png
Normal file
BIN
img/homeracker_icon_yellow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 152 KiB |
BIN
img/homeracker_logo.png
Normal file
BIN
img/homeracker_logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 481 KiB |
BIN
img/homeracker_logo.xcf
Normal file
BIN
img/homeracker_logo.xcf
Normal file
Binary file not shown.
BIN
img/homeracker_logo_transparent.png
Normal file
BIN
img/homeracker_logo_transparent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 485 KiB |
5
models/flexmount/README.md
Normal file
5
models/flexmount/README.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
Fully customizable mount to be used with HomeRacker.
|
||||||
|
It's intended for non-standard devices you want to mount on HomeRacker.
|
||||||
|
Non-standard being devices which aren't originally meant to be mounted in 10 or 19" racks.
|
||||||
|
Devices might be PSU's for Raspberry Pis or small smart switches like on these pictures:
|
||||||
|
<TODO: insert pics here>
|
||||||
204
models/flexmount/flexmount.scad
Normal file
204
models/flexmount/flexmount.scad
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
// import BOSL2
|
||||||
|
include <BOSL2/std.scad>
|
||||||
|
|
||||||
|
$fn=100;
|
||||||
|
|
||||||
|
/* [Hidden] */
|
||||||
|
// constants which shouldn't be changed
|
||||||
|
|
||||||
|
// This is the HomeRacker base unit. don't change this!
|
||||||
|
BASE_UNIT=15; // mm
|
||||||
|
// Standard tolerance for the mount. This is a sane default.
|
||||||
|
TOLERANCE=0.2; // mm
|
||||||
|
// Base strength. This is a sane default.
|
||||||
|
BASE_STRENGTH=2; // mm
|
||||||
|
// Chamfer size. This is a sane default.
|
||||||
|
CHAMFER=1; // mm
|
||||||
|
// Lock Pin side length
|
||||||
|
LOCK_PIN_SIDE=4; // mm
|
||||||
|
// lock pin edge distance
|
||||||
|
LOCK_PIN_EDGE_DISTANCE=5.5; // mm
|
||||||
|
|
||||||
|
/* [Base] */
|
||||||
|
// If set to true, the mount will be fitted to align with the 10/19 inch HomRacker rackmount kit.
|
||||||
|
//fit_to_rack=true;
|
||||||
|
|
||||||
|
/* [Device Measurements] */
|
||||||
|
// Width of the device in mm. Will determine how far apart the actual mounts are in width.
|
||||||
|
device_width=100 // [20:0.1:500]
|
||||||
|
; // TODO test for zero cube
|
||||||
|
// Depth of the device in mm. Will determine how far apart the actual mounts are in depth.
|
||||||
|
device_depth=99; // [54:0.1:500]
|
||||||
|
// Height of the device in mm. Will determine how far apart the actual mounts are in height.
|
||||||
|
device_height=25.5; // [15:0.1:500]
|
||||||
|
|
||||||
|
/* [Bracket] */
|
||||||
|
// Defines the bracket thickness on top of the device.
|
||||||
|
bracket_strength_top=7.5; // [4:0.1:20]
|
||||||
|
bracket_strength_sides=7.5; // [4:0.1:20]
|
||||||
|
|
||||||
|
// diff_width resembles the gap between the device and the mount. This gap will be filled with a cuboid
|
||||||
|
modulo_width=(BASE_UNIT - ( device_width + TOLERANCE ) % BASE_UNIT);
|
||||||
|
WIDTH_DIFF = modulo_width==15 ? 0 : modulo_width;
|
||||||
|
echo("Diff Width: ", WIDTH_DIFF);
|
||||||
|
mount_gap_filler_start=(device_width+TOLERANCE)/2;
|
||||||
|
// echo("mount_gap_filler_start: ", mount_gap_filler_start);
|
||||||
|
// echo("effective mount distance: ", device_width+WIDTH_DIFF+TOLERANCE);
|
||||||
|
|
||||||
|
|
||||||
|
DEPTH_DIFF=device_depth % BASE_UNIT;
|
||||||
|
echo("Diff Depth: ", DEPTH_DIFF);
|
||||||
|
mount_offset_depth=(device_depth-DEPTH_DIFF-BASE_UNIT)/2;
|
||||||
|
echo("mount_offset_depth*2: ", mount_offset_depth*2);
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
// creates a bracket around the device with TOLERANCE as additional space and BASE_STRENGTH as the strength of the bracket
|
||||||
|
module bracket(width,depth,height) {
|
||||||
|
// Bracket
|
||||||
|
outer_width=width+BASE_STRENGTH*2+TOLERANCE;
|
||||||
|
outer_depth=depth+BASE_STRENGTH*2+TOLERANCE;
|
||||||
|
outer_height=height+BASE_STRENGTH;
|
||||||
|
intersection(){
|
||||||
|
difference() {
|
||||||
|
// Outer
|
||||||
|
cuboid(
|
||||||
|
size=[outer_width,outer_depth,outer_height],
|
||||||
|
anchor=BOTTOM,
|
||||||
|
chamfer=CHAMFER, edges=[TOP,FRONT,BACK,LEFT,RIGHT], except=[BOTTOM]
|
||||||
|
);
|
||||||
|
// Top Skeleton
|
||||||
|
cuboid(
|
||||||
|
size=[width-bracket_strength_top,depth-bracket_strength_top,outer_height],
|
||||||
|
anchor=BOTTOM,
|
||||||
|
chamfer=-CHAMFER, edges=[TOP]
|
||||||
|
);
|
||||||
|
// Bottom Recess
|
||||||
|
cuboid(
|
||||||
|
size=[outer_width,outer_depth,height-bracket_strength_sides],
|
||||||
|
anchor=BOTTOM
|
||||||
|
);
|
||||||
|
}
|
||||||
|
translate([0,0,outer_height])
|
||||||
|
cuboid(
|
||||||
|
size=[outer_width,outer_depth,bracket_strength_sides],
|
||||||
|
anchor=TOP,
|
||||||
|
chamfer=CHAMFER, edges=[BOTTOM]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// device module adds TOLERANCE to the device size and creates a cuboid with the device size
|
||||||
|
// this is used to create the device in the mount and add TOLERANCE to the device size for spacing
|
||||||
|
module device(width,depth,height) {
|
||||||
|
// Device
|
||||||
|
cuboid(
|
||||||
|
size=[width+TOLERANCE,depth+TOLERANCE,height+TOLERANCE],
|
||||||
|
anchor=BOTTOM
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mount
|
||||||
|
module mount(){
|
||||||
|
depth=BASE_UNIT+BASE_STRENGTH*2+TOLERANCE;
|
||||||
|
gap_filler_width=(WIDTH_DIFF)/2;
|
||||||
|
|
||||||
|
// depth translation
|
||||||
|
translate([0,mount_offset_depth,0])
|
||||||
|
|
||||||
|
union(){
|
||||||
|
|
||||||
|
translate([mount_gap_filler_start,0,0])
|
||||||
|
// Mount
|
||||||
|
union(){
|
||||||
|
top_width=BASE_STRENGTH;
|
||||||
|
bottom_width=BASE_UNIT+gap_filler_width;
|
||||||
|
// Bridge
|
||||||
|
difference(){
|
||||||
|
cuboid_width = bottom_width;
|
||||||
|
cuboid_chamfer = bottom_width;
|
||||||
|
prismoid(
|
||||||
|
size1 = [bottom_width, depth], // Bottom face: width 30, depth 60
|
||||||
|
size2 = [top_width, depth], // Top face: becomes a line segment (width 0)
|
||||||
|
shift = [(-bottom_width+top_width)/2, 0], // Shift top edge center to X=+15 (aligns with right edge of base)
|
||||||
|
chamfer=CHAMFER,
|
||||||
|
h = device_height, // Height
|
||||||
|
//chamfer=CHAMFER/2,
|
||||||
|
anchor = BOTTOM+LEFT // Anchor bottom left
|
||||||
|
);
|
||||||
|
translate([0,0,BASE_STRENGTH])
|
||||||
|
cuboid(
|
||||||
|
size=[cuboid_width,BASE_UNIT,device_height],
|
||||||
|
anchor=BOTTOM+LEFT,
|
||||||
|
chamfer=BASE_UNIT/2, edges=[BOTTOM], except=[RIGHT]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depth enforcement
|
||||||
|
depth_enforcement_x = BASE_STRENGTH;
|
||||||
|
depth_enforcemnt_y2 = BASE_UNIT*2;
|
||||||
|
prismoid(
|
||||||
|
size1 = [depth_enforcement_x, depth],
|
||||||
|
size2 = [depth_enforcement_x, depth_enforcemnt_y2],
|
||||||
|
shift = [0, (depth-depth_enforcemnt_y2)/2],
|
||||||
|
h = device_height,
|
||||||
|
chamfer=[CHAMFER,0,0,CHAMFER],
|
||||||
|
anchor = BOTTOM+LEFT
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mount Rail with holes
|
||||||
|
difference(){
|
||||||
|
// Mount Rail
|
||||||
|
difference(){
|
||||||
|
height=BASE_UNIT+TOLERANCE/2;
|
||||||
|
// outer
|
||||||
|
cuboid(
|
||||||
|
size=[bottom_width,depth,height],
|
||||||
|
anchor=TOP+LEFT,
|
||||||
|
chamfer=CHAMFER, edges=[BOTTOM,RIGHT], except=[TOP]
|
||||||
|
);
|
||||||
|
// support recess
|
||||||
|
cuboid(
|
||||||
|
size=[bottom_width,BASE_UNIT+TOLERANCE,height],
|
||||||
|
anchor=TOP+LEFT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// holes
|
||||||
|
translate([bottom_width-LOCK_PIN_EDGE_DISTANCE,0,-LOCK_PIN_EDGE_DISTANCE-TOLERANCE/2])
|
||||||
|
cuboid(
|
||||||
|
size=[LOCK_PIN_SIDE,depth,LOCK_PIN_SIDE],
|
||||||
|
anchor=TOP+RIGHT
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module mirror_mount_x_plane(){
|
||||||
|
mount();
|
||||||
|
x_mirror_plane = [1,0,0];
|
||||||
|
mirror(x_mirror_plane) {
|
||||||
|
mount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assembly
|
||||||
|
rotate([180,0,0])
|
||||||
|
union() {
|
||||||
|
|
||||||
|
|
||||||
|
difference() {
|
||||||
|
bracket(device_width,device_depth,device_height);
|
||||||
|
device(device_width,device_depth,device_height);
|
||||||
|
}
|
||||||
|
// Mount Right
|
||||||
|
mirror_mount_x_plane();
|
||||||
|
|
||||||
|
y_mirror_plane = [0,1,0];
|
||||||
|
mirror(y_mirror_plane) {
|
||||||
|
mirror_mount_x_plane();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
2
scripts/.gitignore
vendored
Normal file
2
scripts/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.env
|
||||||
|
.vscode
|
||||||
12
scripts/ExportParametricShelf/ExportParametricShelf.manifest
Normal file
12
scripts/ExportParametricShelf/ExportParametricShelf.manifest
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"autodeskProduct": "Fusion",
|
||||||
|
"type": "script",
|
||||||
|
"author": "kellervater (Patrick Pötz)",
|
||||||
|
"description": {
|
||||||
|
"": "Exports all common dimensions of the shelf."
|
||||||
|
},
|
||||||
|
"version": "",
|
||||||
|
"supportedOS": "windows|mac",
|
||||||
|
"editEnabled": true,
|
||||||
|
"iconFilename": "ScriptIcon.svg"
|
||||||
|
}
|
||||||
176
scripts/ExportParametricShelf/ExportParametricShelf.py
Normal file
176
scripts/ExportParametricShelf/ExportParametricShelf.py
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
#Fusion360API Python Script
|
||||||
|
#Description: Exports STEP files. Exports "Support" once per depth,
|
||||||
|
# and "Shelf" for each width/depth combination.
|
||||||
|
|
||||||
|
import adsk.core, adsk.fusion, adsk.cam, traceback, os
|
||||||
|
|
||||||
|
def run(context):
|
||||||
|
ui = None
|
||||||
|
try:
|
||||||
|
# --- Basic Fusion 360 setup ---
|
||||||
|
app = adsk.core.Application.get()
|
||||||
|
ui = app.userInterface
|
||||||
|
design = app.activeProduct
|
||||||
|
if not design:
|
||||||
|
ui.messageBox('No active Fusion 360 design', 'Export Script Error')
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Get Document Version ---
|
||||||
|
doc = app.activeDocument
|
||||||
|
version_str = "v_unknown" # Default in case of error
|
||||||
|
try:
|
||||||
|
if doc.dataFile:
|
||||||
|
version_number = doc.dataFile.versionNumber
|
||||||
|
version_str = f"v{version_number}"
|
||||||
|
else:
|
||||||
|
version_str = "v_unsaved"
|
||||||
|
ui.messageBox('The current document has not been saved to the cloud yet.\n'
|
||||||
|
'Filenames will use "v_unsaved" instead of a version number.',
|
||||||
|
'Unsaved Document Warning')
|
||||||
|
except Exception as e:
|
||||||
|
ui.messageBox(f'Could not retrieve document version:\n{e}\n'
|
||||||
|
f'Filenames will use "{version_str}".', 'Version Error')
|
||||||
|
# --- End Get Document Version ---
|
||||||
|
|
||||||
|
# --- Configuration
|
||||||
|
widthParamName = "shelf_width_units"
|
||||||
|
depthParamName = "shelf_depth_units"
|
||||||
|
minWidth = 4
|
||||||
|
maxWidth = 15
|
||||||
|
minDepth = 7
|
||||||
|
maxDepth = 15
|
||||||
|
baseFileName = "homeracker_shelf"
|
||||||
|
shelfBodyName = "Shelf"
|
||||||
|
supportBodyName = "Support"
|
||||||
|
# --- End Configuration ---
|
||||||
|
|
||||||
|
# Get the root component
|
||||||
|
rootComp = design.rootComponent
|
||||||
|
|
||||||
|
# Get User Parameters collection
|
||||||
|
userParams = design.userParameters
|
||||||
|
if not userParams:
|
||||||
|
ui.messageBox('Could not access user parameters.', 'Export Script Error')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the specific parameters
|
||||||
|
widthParam = userParams.itemByName(widthParamName)
|
||||||
|
depthParam = userParams.itemByName(depthParamName)
|
||||||
|
if not widthParam or not depthParam:
|
||||||
|
ui.messageBox(f'Could not find parameters: "{widthParamName}" or "{depthParamName}". Check names.', 'Param Error')
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Find the bodies to control visibility (once before loops) ---
|
||||||
|
shelfBody = None
|
||||||
|
supportBody = None
|
||||||
|
try:
|
||||||
|
shelfBody = rootComp.bRepBodies.itemByName(shelfBodyName)
|
||||||
|
supportBody = rootComp.bRepBodies.itemByName(supportBodyName)
|
||||||
|
if not shelfBody:
|
||||||
|
ui.messageBox(f'Body "{shelfBodyName}" not found. Aborting...', 'Body Not Found Warning')
|
||||||
|
return
|
||||||
|
if not supportBody:
|
||||||
|
ui.messageBox(f'Body "{supportBodyName}" not found. Aborting...', 'Body Not Found Warning')
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
ui.messageBox(f'Error finding bodies:\n{traceback.format_exc()}', 'Error Finding Bodies')
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Get Export Directory from User ---
|
||||||
|
folderDialog = ui.createFolderDialog()
|
||||||
|
folderDialog.title = "Select Folder to Save STEP Files"
|
||||||
|
dialogResult = folderDialog.showDialog()
|
||||||
|
if dialogResult == adsk.core.DialogResults.DialogOK:
|
||||||
|
exportFolder = folderDialog.folder
|
||||||
|
else:
|
||||||
|
ui.messageBox('Export cancelled by user.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- Get Export Manager ---
|
||||||
|
exportMgr = design.exportManager
|
||||||
|
|
||||||
|
# --- Progress Bar Setup ---
|
||||||
|
num_depths = (maxDepth - minDepth + 1)
|
||||||
|
num_widths = (maxWidth - minWidth + 1)
|
||||||
|
# Total exports = one support per depth + one shelf per width*depth
|
||||||
|
total_exports = num_depths + (num_depths * num_widths)
|
||||||
|
progressDialog = ui.createProgressDialog()
|
||||||
|
progressDialog.isCancelEnabled = False
|
||||||
|
progressDialog.show(f'Exporting {baseFileName}', 'Initializing...', 0, total_exports)
|
||||||
|
exported_count = 0
|
||||||
|
|
||||||
|
# --- Loop through parameter combinations ---
|
||||||
|
for d in range(minDepth, maxDepth + 1):
|
||||||
|
try:
|
||||||
|
# --- Set Depth Parameter ---
|
||||||
|
depthParam.value = float(d)
|
||||||
|
adsk.doEvents() # Allow update
|
||||||
|
|
||||||
|
progressDialog.progressValue = exported_count
|
||||||
|
progressDialog.message = f'Exporting Support: D={d} ({exported_count + 1}/{total_exports})'
|
||||||
|
adsk.doEvents()
|
||||||
|
|
||||||
|
# Set visibility: Support ONLY
|
||||||
|
supportBody.isLightBulbOn = True
|
||||||
|
shelfBody.isLightBulbOn = False # Hide shelf if it exists
|
||||||
|
adsk.doEvents() # Allow visibility change
|
||||||
|
|
||||||
|
# Construct filename & path for Support
|
||||||
|
supportFileName = f"{baseFileName}_Support_D{d}_{version_str}"
|
||||||
|
supportFullPath = os.path.join(exportFolder, supportFileName + ".step")
|
||||||
|
|
||||||
|
# Export Support
|
||||||
|
supportStepOptions = exportMgr.createSTEPExportOptions(supportFullPath, rootComp)
|
||||||
|
exportMgr.execute(supportStepOptions)
|
||||||
|
exported_count += 1
|
||||||
|
# --- End Support Export ---
|
||||||
|
|
||||||
|
except Exception:
|
||||||
|
progressDialog.hide() # Hide progress on error
|
||||||
|
ui.messageBox(f'Failed during processing or Support export for Depth={d}:\n{traceback.format_exc()}', 'Outer Loop Error')
|
||||||
|
return
|
||||||
|
|
||||||
|
# --- INNER LOOP: Width (for Shelf Export) ---
|
||||||
|
for w in range(minWidth, maxWidth + 1):
|
||||||
|
try:
|
||||||
|
progressDialog.progressValue = exported_count
|
||||||
|
progressDialog.message = f'Exporting Shelf: W={w}, D={d} ({exported_count + 1}/{total_exports})'
|
||||||
|
adsk.doEvents()
|
||||||
|
|
||||||
|
# --- Set Width Parameter ---
|
||||||
|
widthParam.value = float(w)
|
||||||
|
adsk.doEvents() # Allow update
|
||||||
|
|
||||||
|
# --- Set Visibility for Shelf Export: Shelf ONLY ---
|
||||||
|
shelfBody.isLightBulbOn = True
|
||||||
|
supportBody.isLightBulbOn = False # Hide support if it exists
|
||||||
|
adsk.doEvents() # Allow visibility change
|
||||||
|
|
||||||
|
# --- Construct Filename & Path for Shelf ---
|
||||||
|
shelfFileName = f"{baseFileName}_Shelf_W{w}_D{d}_{version_str}"
|
||||||
|
shelfFullPath = os.path.join(exportFolder, shelfFileName + ".step")
|
||||||
|
|
||||||
|
# --- Export Shelf ---
|
||||||
|
shelfStepOptions = exportMgr.createSTEPExportOptions(shelfFullPath, rootComp)
|
||||||
|
exportMgr.execute(shelfStepOptions)
|
||||||
|
exported_count += 1
|
||||||
|
|
||||||
|
except Exception as e_inner:
|
||||||
|
# Log error for this specific shelf, but continue loop
|
||||||
|
ui.messageBox(f'Failed exporting Shelf W={w}, D={d}:\n{traceback.format_exc()}\nSkipping this combination.', 'Inner Loop Error')
|
||||||
|
# Optionally add logging here instead of message box spam
|
||||||
|
continue # Continue to next width value
|
||||||
|
|
||||||
|
# --- Clean up ---
|
||||||
|
progressDialog.hide()
|
||||||
|
|
||||||
|
if exported_count > 0:
|
||||||
|
ui.messageBox(f'Successfully exported {exported_count} STEP files to:\n{exportFolder}', 'Export Complete')
|
||||||
|
else:
|
||||||
|
ui.messageBox('No files were exported. Check script configuration or logs for errors.', 'Export Result')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if ui:
|
||||||
|
ui.messageBox('Script Failed Unexpectedly:\n{}'.format(traceback.format_exc()))
|
||||||
|
if 'progressDialog' in locals() and progressDialog.isShowing:
|
||||||
|
progressDialog.hide()
|
||||||
6
scripts/ExportParametricShelf/README.md
Normal file
6
scripts/ExportParametricShelf/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
This script is intended to be used with the Fusion360 file `HomeRacker - Shelf`.
|
||||||
|
It exports all sane dimensions of the shelf to avoid tedious manual work.
|
||||||
|
It is ready to go and just needs to be run from within Fusion360 (`Shift + S`).
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Since I bootstrapped the script from within Fusion, idk how to import it. Maybe you need to create a new one and just import the contents. Just try it and let me know how it works.
|
||||||
2569
scripts/ExportParametricShelf/ScriptIcon.svg
Normal file
2569
scripts/ExportParametricShelf/ScriptIcon.svg
Normal file
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 85 KiB |
Reference in New Issue
Block a user