feat: design improvements

This commit is contained in:
2024-02-29 15:56:03 +01:00
parent 6b1f73d765
commit c83b8ae89c
20 changed files with 197 additions and 188 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
*.stl
/build/
/.direnv/

131
README.md
View File

@@ -1,121 +1,38 @@
# Brick Monorail
# create-svelte
Parametric Lego-compatible monorail tracks, designed specifically for 3d printing.
Two orders of magnitude cheaper than buying used.
Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
Since the parts are made parametric, you can have any length or curve radius you want,
but these are the original tracks you can replicate
## Creating a project
As an overview, or why it's worth a try: They print rapidly on modern printers, and cost barely anything.
If you're seeing this, you've probably already done this step. Congrats!
| Preset | Cost | Time | Image |
| ----------------------------- | ----- | ---- | -------------------------------------------------------------------------------- |
| C15 | ~10ct | 45m | ![C15](./assets/generated/C15.png) |
| C7 | ~5ct | 15m | ![C7](./assets/generated/C7.png) |
| S25 | ~12ct | 1h | ![S25](./assets/generated/S25.png) |
| S10 | ~5ct | 15m | ![S10](./assets/generated/S10.png) |
| S5 | ~5ct | 15m | ![S5](./assets/generated/S5.png) |
| S4 | ~5ct | 15m | ![S4](./assets/generated/S4.png) |
| Classic Full Curve (R28 90°) | ~25ct | 2h | ![Classic Full Curve](./assets/generated/Classic%20Full%20Curve.png) |
| Classic Half Curve (R28 45°) | ~12ct | 1h | ![Classic Half Curve](./assets/generated/Classic%20Half%20Curve.png) |
| Classic Full Straight (L32) | ~20ct | 1.5h | ![Classic Full Straight](./assets/generated/Classic%20Full%20Straight.png) |
| Classic Half Straight (L16) | ~10ct | 45m | ![Classic Half Straight](./assets/generated/Classic%20Half%20Straight.png) |
| Classic Quarter Straight (L8) | ~5ct | 15m | ![Classic Quarter Straight](./assets/generated/Classic%20Quarter%20Straight.png) |
```bash
# create a new project in the current directory
npm create svelte@latest
You can also generate your own custom rails with any length, radius or angle.
# create a new project in my-app
npm create svelte@latest my-app
```
Todo: ramps, switches, train assembly
## Developing
## Design differences
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
Injection molding has vastly different requirements to 3d printing.
Due to this the original rails print absolutely horribly due to the copious amounts of supports needed.
Surfaces printed on supports just never look good.
```bash
npm run dev
Because of that, I decided to instead of having a support part on the bottom, I'd carve out space for
a 1x2 plate, which you can fit there for the same effect.
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
The monorail tracks are also solid now, which is not something you can do in injection molding but leaves
a really nice surface finish at the bottom of the rail for us.
## Building
For now, I also use non-baseplate aligned joints for curves. While this means you can't just snap the rails
on a baseplate, it enables you to use straight rails at non-90 degree angles which I think is an absolute
win over the original design since the 45 degree curves are useless outside of joining them with switches.
To create a production version of your app:
_These are 3d printing optimized, compatible rails, not replicas. Replicas print horribly due to support._
```bash
npm run build
```
### Optional Differences
You can preview the production build with `npm run preview`.
#### Operating on r25 instead of r28
What difference do these three studs make?
With this radius we can take advantage of the pythagorean triples `3/4/5` and `7/24/25` to stay on the stud
grid with turntables.
- Curve C15
- Curve C7
- Straight S21
- Straight S7
- Straight S6
- Incline I4
How it fits
- Two C15 and one C7 make exactly a 90 degree turn, where every part stays on the stud grid.
- An s-curve with C15 moves over 20 studs, diagonals can be done with any length divisible by 5
- An s-curve with C7 moves two studs, but diagonals require a full S25 to land cleanly again.
![](./assets/r25.svg)
## Printing
To be compatible with standard bricks, the following print settings are strongly advised
- Nozzle: **0.4**, this is equivalent to 1 LDU.
- Layer height: **0.2**, less than that is barely better and just causes more problems than it solves.
If you want to go lower, it's advisable to use heights that satisfy `0.4 % h == 0` such as 0.1.
- **Make sure your initial layer height is either 0.2 or 0.4!**
- Supports: **yes**, normal, don't use tree supports.
- Set your "Initial Layer Horizontal Expansion" to a value that works for you, for example -0.2mm to compensate for elephant's foot.
Alternatively, you can trim the extra plastic off by hand.
My prints were done on a modified SecKit SK-Go2 running Klipper, with 10k acceleration and 150mm/s print speed for a good
balance of speed and quality. A short rail will take about an hour to print.
### Inserts
_This is optional and increases difficulty and print time, but I highly recommened it._
Studs and anti-studs print rather inconsistently at the orientation of the rail.
To combat that, I added the option to pre-print stud and anti-stud inserts at perfect orientation and insert them mid-print.
Because the layer is immediately covered up, the plastic will adhere like it would if it was printed in one go.
This also allows you to pre-select the best inserts without having to print a full rail only to have one bad stud.
- In Cura, insert a "Pause at layer" at the last layer where the insert spots are not covered yet.
- If you use Klipper, use "Search and replace" with `; Layer 32` to `; Layer 32\nPAUSE`.
### Filament
| Color | RAL | PLA Supplier | ABS Supplier |
| ----------------- | ---------- | ------------ | ------------ |
| Light Bluish Gray | `RAL 7040` | dasfilament | |
| Light Gray | `RAL 7005` | | |
- None of these colors will be an exact match, just the texture of 3d printing it can make a huge difference, but usually fall close enough in the range.
- The original rails will be in _Light Gray_, not _Light Bluish Gray_, but since I barely own any pre- 2004 color change bricks I chose to match my other bricks instead.
_Notes for newcomers:_
- Most bricks are injection molded from ABS, however I find PLA to be much more easy and safe to handle.
- PLA starts to deform at 60°C, so don't leave it in the sun or in your car.
- PLA is brittle. While ABS deforms with force applied to it, PLA will just snap.
- ABS is harder to print, an enclosure is a must, and warping can be difficult to handle.
#### Printing Safety
In case you are new to 3D printing:
- Most filaments (including PLA) can release toxic gases when _burned_
- FDM printing can cause fine particle emission
- ABS when heated to normal printing temperatures can release styrene fumes
> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 731 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 849 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 658 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 493 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 569 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 712 B

After

Width:  |  Height:  |  Size: 0 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 627 B

After

Width:  |  Height:  |  Size: 0 B

View File

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1709126324,
"narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "d465f4819400de7c8d874d50b982301f28a84605",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1709101946,
"narHash": "sha256-TsySgcWm/GlbYdL3AEva49ceeI2BdPQ7muwfYNr1fwo=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "d53c2037394da6fe98decca417fc8fda64bf2443",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

23
flake.nix Normal file
View File

@@ -0,0 +1,23 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = {
self,
nixpkgs,
flake-utils,
}:
flake-utils.lib.eachDefaultSystem (system: let
pkgs = import nixpkgs {inherit system;};
packages =
(with pkgs; [
nodejs_18
nodePackages.pnpm
]);
in {
devShell = pkgs.mkShell {
buildInputs = packages;
};
});
}

View File

@@ -1,74 +1,77 @@
{
"fileFormatVersion": "1",
"parameterSets": {
"C15": {
"Angle": "1",
"Length": "15",
"Radius": "25"
},
"C7": {
"Angle": "1",
"Length": "7",
"Radius": "25"
},
"S25": {
"Angle": "0",
"Length": "25",
"Radius": "25"
},
"S10": {
"Angle": "0",
"Length": "10",
"Radius": "25"
},
"S5": {
"Angle": "0",
"Length": "5",
"Radius": "25"
},
"S4": {
"Angle": "0",
"Length": "4",
"Radius": "25"
},
"Classic Full Straight": {
"Angle": "0",
"Length": "32",
"Radius": "28"
},
"Classic Half Straight": {
"Angle": "0",
"Length": "16",
"Radius": "28"
},
"Classic Quarter Straight": {
"Angle": "0",
"Length": "8",
"Radius": "28"
},
"Classic Full Curve": {
"Angle": "90",
"Length": "8",
"Radius": "28"
},
"Classic Half Curve": {
"Angle": "45",
"Length": "8",
"Radius": "28"
},
"StudInsert": {
"Type": "studs"
},
"AntiStudInsert": {
"Type": "antistuds"
},
"New set 1": {
"Angle": "15",
"AntiStudInserts": "false",
"Length": "20",
"Radius": "25",
"StudInserts": "false",
"Type": "switch"
}
"fileFormatVersion": "1",
"parameterSets": {
"C15": {
"Angle": "15",
"Radius": "25",
"AngleIsLength": true
},
"C7": {
"Angle": "7",
"Radius": "25",
"AngleIsLength": true
},
"S25": {
"Angle": "0",
"Length": "25",
"Radius": "25"
},
"S10": {
"Angle": "0",
"Length": "10",
"Radius": "25"
},
"S5": {
"Angle": "0",
"Length": "5",
"Radius": "25"
},
"S4": {
"Angle": "0",
"Length": "4",
"Radius": "25"
},
"Classic Full Straight": {
"Angle": "0",
"Length": "32",
"Radius": "28"
},
"Classic Half Straight": {
"Angle": "0",
"Length": "16",
"Radius": "28"
},
"Classic Quarter Straight": {
"Angle": "0",
"Length": "8",
"Radius": "28"
},
"Classic Full Curve": {
"Angle": "90",
"Length": "8",
"Radius": "28"
},
"Classic Half Curve": {
"Angle": "45",
"Length": "8",
"Radius": "28"
},
"StudInsert": {
"Type": "studs"
},
"AntiStudInsert": {
"Type": "antistuds"
},
"Switch R25": {
"Angle": "15",
"AngleIsLength": "true",
"AntiStudInserts": "false",
"Length": "20",
"Radius": "25",
"StudInserts": "false",
"SwitchFrontLength": "4",
"SwitchSupportCount": "3",
"Type": "switch"
}
}
}

View File

@@ -1,19 +1,19 @@
include <BOSL2/std.scad>;
include <BOSL2/beziers.scad>;
/* [Print Settings] */
Tolerance = 0.2;
// Mid-print stud inserts allowing the studs to be printed facing up seperately
StudInserts = true;
// Mid-print slot inserts eliminating the need for supports
AntiStudInserts = true;
// Part to generate
Type = "switch"; // [rail,switch,studs,antistuds]
Type = "rail"; // [rail,switch,studs,antistuds]
/* [Model Settings] */
Length = 8; // [4:1:56]
Length = 20; // [4:1:56]
Radius = 25; // [4:1:36]
// The angle the track takes
Angle = 0.0;
Angle = 15;
// Useful when working with Pythagorean Triples
AngleIsLength = true;
SwitchSupportCount = 3;
@@ -45,6 +45,7 @@ $teethRailWidth = 10 * $LDU;
$teethWidth = $tile / $teeth;
$teethDepth = 3 * $LDU;
module tooth() {
$height = $teethWidth - $teethTolerance;
$rail = $teethRailWidth / 2;
@@ -284,7 +285,7 @@ module monorailSwitch() {
translate([0, $tile * SwitchFrontLength, $tile / 2]) cube([$tile * 20, $teethTolerance, $plate], anchor=BOTTOM);
translate([0, $tile * (SwitchFrontLength + midLength), $tile / 2]) cube([$tile * 20, $teethTolerance, $plate], anchor=BOTTOM);
difference() {
translate([0, $tile * SwitchFrontLength, $tile / 2]) cube([$tile * 20, midLength * $tile, $LDU], anchor=BOTTOM+FRONT);
translate([0, $tile * SwitchFrontLength, $tile / 2]) cube([$tile * 20, midLength * $tile, Tolerance], anchor=BOTTOM+FRONT);
for (i = [0:SwitchSupportCount - 1]) {
y = SwitchFrontLength + 1 + ((midLength - 2) / (SwitchSupportCount - 1) * i);
@@ -307,13 +308,13 @@ module monorailSwitch() {
wl = 2 * $tile + travelDistance + $teethRailWidth;
wr = (1 - cos(asin((y - SwitchFrontLength) / Radius))) * Radius * $tile + travelDistance + 2 * $tile + $teethRailWidth;
translate([-wl, $tile * y, 0]) group() {
cube([wl + wr, strength + $LDU * 2, $tile], anchor=LEFT);
rotate([45, 0, 0]) cube([wl + wr, strength * 2 + $LDU * 2, strength * 2 + $LDU * 2], anchor=LEFT);
cube([wl + wr, strength + Tolerance * 2, $tile], anchor=LEFT);
rotate([45, 0, 0]) cube([wl + wr, strength * 2 + Tolerance * 2, strength * 2 + Tolerance * 2], anchor=LEFT);
};
}
translate([0, $tile * (SwitchFrontLength + midLength / 2), -$tile / 2])
cyl(d=$tile * (4 + travelDistance / 2) + $LDU * 2, h=$tile / 2, chamfer=$LDU * 4, $fn=64, anchor=BOTTOM);
//translate([0, $tile * (SwitchFrontLength + midLength / 2), -$tile / 2])
// cyl(d=$tile * (4 + travelDistance / 2) + $LDU * 2, h=$tile / 2, chamfer=$LDU * 4, $fn=64, anchor=BOTTOM);
}
if (SwitchFrontLength > 2) {
@@ -351,7 +352,10 @@ if (Type == "rail") {
rotate([180, 0, 180]) antiStudInsert();
} else if (Type == "switch") {
//guiderailChainLink();
monorailSwitch();
intersection() {
monorailSwitch();
translate([0, 55, 0]) cube([100, 60, 100], FRONT);
}
}