feat: design improvements
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
*.stl
|
||||
/build/
|
||||
/.direnv/
|
||||
|
||||
131
README.md
@@ -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 |  |
|
||||
| C7 | ~5ct | 15m |  |
|
||||
| S25 | ~12ct | 1h |  |
|
||||
| S10 | ~5ct | 15m |  |
|
||||
| S5 | ~5ct | 15m |  |
|
||||
| S4 | ~5ct | 15m |  |
|
||||
| Classic Full Curve (R28 90°) | ~25ct | 2h |  |
|
||||
| Classic Half Curve (R28 45°) | ~12ct | 1h |  |
|
||||
| Classic Full Straight (L32) | ~20ct | 1.5h |  |
|
||||
| Classic Half Straight (L16) | ~10ct | 45m |  |
|
||||
| Classic Quarter Straight (L8) | ~5ct | 15m |  |
|
||||
```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.
|
||||
|
||||

|
||||
|
||||
## 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.
|
||||
|
||||
|
Before Width: | Height: | Size: 588 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 731 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 849 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 472 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 384 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 493 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 644 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 569 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 700 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 0 B |
|
Before Width: | Height: | Size: 627 B After Width: | Height: | Size: 0 B |
0
assets/generated/Switch R25.png
Normal file
61
flake.lock
generated
Normal 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
@@ -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;
|
||||
};
|
||||
});
|
||||
}
|
||||
145
track.json
@@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
track.scad
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||