test: Vendor kcl-samples and add simulation tests for them (#5460)

* Change to unzip

* Download kcl-samples as zip to public dir

* Fix fetch:samples, e2e electron still not working

* Change error message to be clearer

* Refactor so that input and output directories of sim tests can be different

* Add kcl samples test implementation

* Update output since adding kcl_samples tests

* Update kcl-samples branch

* Fix git-ignore pattern to only apply to the root

* Fix yarn install and yarn fetch:samples to work the first time

* Remove unneeded exists check

* Change to use kcl-samples in public directory

* Add kcl-samples

* Update output since updating kcl-samples

* Update output files

* Change to not fetch samples during yarn install

* Update output after merge

* Ignore kcl-samples in codespell

* WIP: Don't run e2e if only kcl-samples changed

* Conditionally run cargo tests

* Fix to round floating point values in program memory arrays

* Update output since merge and rounding numbers in memory

* Fix memory redaction for floating point to find more values

* Fix float redaction pattern

* Update output since rounding floating point numbers

* Add center to floating point pattern

* Fix trigger to use picomatch syntax

* Update output since rounding center

* Remove kcl-samples github workflows

* Enable Rust backtrace

* Update output after re-running

* Update output after changing order of post-extrude commands

* Fix to have deterministic order of commands

* Update output after reverting ordering changes

* Update kcl-samples

* Update output after updating samples

* Fix error messages to show the names of all samples that failed

* Change cargo test command to match current one

* Update kcl-samples

* Update output since updating kcl-samples

* Add generate manifest workflow and yarn script

* Fix error check to actually work

* Change util function to be what we actually need

* Move new files after merge

* Fix paths since directory move

* Add dependabot updates for kcl-samples

* Add GitHub workflow to make PR to kcl-samples repo

* Add GitHub workflow to check kcl-samples header comments

* Fix worfklow to change to the right directory

* Add auto-commit simulation test output changes

* Add permissions to workflows

* Fix to run git commit step

* Install just if needed

* Fix directory of justfile

* Add installation of cargo-insta

* Fix to use underscore

* Fix to allow just command failure

* Change to always install CLI tools and cache them

* Trying to fix overwrite failing

* Combine commands

* Change reviewer

* Change to PR targeting the next branch

* Change git commands to not do unnecessary fetch

* Comment out trigger for creating a PR

* Update kcl-samples from next branch

* Update outputs after kcl-samples change

* Fix to use bash pipefail

* Add rust backtrace

* Print full env from sim tests

* Change command to use long option name

* Fix to use ci profile even when calling through just

* Add INSTA_UPDATE=always

* Fix git push by using an app token on checkout

* Add comments

* Fix to use bash options

* Change to echo when no changes are found

* Fix so that kcl-samples updates don't trigger full run

* Fix paths to reflect new crate location

* Fix path detection

* Fix e2e job to ignore kcl_samples simulation test output

* Fix the fetch logic for the KCL samples after vendoring (#5661)

Fixes the last 2 E2E tests for #5460.

---------

Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
This commit is contained in:
Jonathan Tran
2025-03-06 18:01:24 -05:00
committed by GitHub
parent 200a9af61f
commit 69553fded7
347 changed files with 794355 additions and 88 deletions

7
public/kcl-samples/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
.vscode
.idea
.DS_Store
screenshots/main.kcl
step/main.kcl
/project.toml
thumbnail.png

View File

@ -0,0 +1,136 @@
// 3D Boaty Function Library
// The following file describes various functions to build the 3D boaty. The name of this file is a bit of misnomer, the shape of the object is a typical park bench.
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
export dividerThickness = 4
fn dividerSketch(plane) {
sketch000 = startSketchOn(plane)
|> startProfileAt([-16.82, 21.2], %)
|> line(end = [-0.13, -1.27])
|> tangentialArcTo([-15.94, profileStartY(%) - 7.73], %)
|> tangentialArcTo([-16.6, profileStartY(%) - 15.52], %)
|> tangentialArcTo([-18.38, profileStartY(%) - 18.63], %)
|> line(end = [-1.25, -2.6])
|> xLine(6.04, %)
|> line(end = [6.68, 7.87])
|> tangentialArcTo([10.06, profileStartY(%) - 12.69], %)
|> line(end = [7.28, -8.47])
|> xLine(5.98, %)
|> line(end = [-1.3, 3.01])
|> tangentialArcTo([22.45, profileStartY(%) - 2.84], %)
|> tangentialArcTo([25.08, profileStartY(%) + 6.42], %)
|> line(end = [2.35, 16.36])
|> line(end = [1.78, 1.15])
|> tangentialArcTo([23.93, profileStartY(%) + 27.29], %)
|> line(end = [-1.92, 0.21])
|> line(end = [-3.74, -26.54])
|> tangentialArcTo([15.13, profileStartY(%) - 1.72], %)
|> tangentialArcTo(profileStart(%), %)
|> close()
return sketch000
}
export fn divider(plane) {
right = dividerSketch(plane)
|> extrude(length = dividerThickness / 2)
left = dividerSketch(plane)
|> extrude(length = -dividerThickness / 2)
shell(right, thickness = 1.5, faces = ["end"])
shell(left, thickness = 1.5, faces = ["start"])
return 0
}
fn connectorSketch(plane, start) {
sketch001 = startSketchOn(plane)
|> startProfileAt(start, %)
|> polygon({
radius = 1.2,
numSides = 6,
center = profileStart(%),
inscribed = false
}, %)
return sketch001
}
export fn connector(plane, length) {
connectorSketch(plane, [-12, 8])
|> extrude(length = length)
connectorSketch(plane, [16, 8])
|> extrude(length = length)
return 0
}
fn seatSlatSketch(plane) {
sketch003 = startSketchOn(plane)
|> startProfileAt([-7, 19], %)
|> line(end = [-10, 0.5])
|> line(end = [0.2, 2.5])
|> line(end = [1.5, 1.5])
|> line(end = [6.9, -0.5])
|> line(end = [1.5, -1.5])
|> line(endAbsolute = profileStart(%))
|> close()
|> patternLinear2d(instances = 3, distance = 11, axis = [1, -0.05])
return sketch003
}
export fn seatSlats(plane, length) {
seatSlatSketch(plane)
|> extrude(length = length)
return 0
}
fn backSlatsSketch(plane) {
sketch004 = startSketchOn(plane)
|> startProfileAt([22, 38.5], %)
|> angledLine([173, 2], %)
|> line(end = [-1.74, 2.03])
|> angledLine([82, 6.6], %)
|> line(end = [2.23, 1.42])
|> angledLine([-7, 2], %)
|> line(endAbsolute = profileStart(%))
|> close()
|> patternLinear2d(instances = 2, distance = 11, axis = [-0.137, -1])
return sketch004
}
export fn backSlats(plane, length) {
b = backSlatsSketch(plane)
|> extrude(length = length)
return b
}
fn armRestPath(plane) {
sketch005 = startSketchOn(plane)
|> startProfileAt([20, 33], %)
|> xLine(-20, %)
|> arc({
angleStart = 90,
angleEnd = 180,
radius = 10
}, %)
return sketch005
}
fn armRestProfile(plane, offset) {
sketch006 = startSketchOn(plane)
|> startProfileAt([offset, 32.4], %)
|> xLine(1.3, %)
|> line(end = [0.3, 0.6])
|> line(end = [-0.3, 0.6])
|> xLine(-2.6, %)
|> line(end = [-0.3, -0.6])
|> line(end = [0.3, -0.6])
|> close()
return sketch006
}
export fn armRest(plane, offset) {
path = armRestPath( offsetPlane(plane, offset = offset))
profile = armRestProfile( offsetPlane("-XZ", offset = 20), offset)
sweep(profile, path = path)
return 0
}

View File

@ -0,0 +1,34 @@
// 3D Boaty
// This is a slight remix of Depep1's original 3D Boaty (https://www.printables.com/model/1141963-3d-boaty). This is a tool used for benchmarking 3D FDM printers for bed adhesion, overhangs, bridging and top surface quality. The name of this file is a bit of misnomer, the shape of the object is a typical park bench.
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
// Define the bench length
benchLength = 56
// Import various constants and functions from our library
import dividerThickness from "boat-parts.kcl"
import divider from "boat-parts.kcl"
import connector from "boat-parts.kcl"
import seatSlats from "boat-parts.kcl"
import backSlats from "boat-parts.kcl"
import armRest from "boat-parts.kcl"
// Create the dividers, these hold the seat and back slats
divider("YZ")
divider(offsetPlane("-YZ", offset = benchLength / 2))
divider(offsetPlane("YZ", offset = benchLength / 2))
// Create the connectors to join the dividers
connector(offsetPlane("YZ", offset = -benchLength / 2), benchLength)
// Create the seat slats
seatSlats(offsetPlane("YZ", offset = -benchLength / 2 - dividerThickness / 2), benchLength + dividerThickness)
// Create the back slats
backSlats(offsetPlane("YZ", offset = -benchLength / 2 - dividerThickness / 2), benchLength + dividerThickness)
// Create the arm rests
armRest("-YZ", benchLength / 2)
armRest("-YZ", -benchLength / 2)

View File

@ -0,0 +1,246 @@
// 80/20 Rail
// An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position
// Set units
@settings(defaultLengthUnit = in)
// Define function
fn rail8020(originStart, railHeight, railLength) {
// Sketch side 1 of profile
sketch001 = startSketchOn('-XZ')
|> startProfileAt([
originStart[0],
0.1 * railHeight + originStart[1]
], %)
|> arc({
angleStart = 180,
angleEnd = 270,
radius = 0.1 * railHeight
}, %)
|> arc({
angleStart = 180,
angleEnd = 0,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(0.1 * railHeight, %)
|> arc({
angleStart = 180,
angleEnd = 0,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(0.06 * railHeight, %, $edge1)
|> yLine(0.087 * railHeight, %, $edge2)
|> xLine(-0.183 * railHeight, %, $edge3)
|> angledLineToY({
angle = 45,
to = (1 - 0.356) / 2 * railHeight + originStart[1]
}, %, $edge4)
|> xLine(0.232 * railHeight, %, $edge5)
|> angledLineToY({
angle = -45,
to = 0.087 * railHeight + originStart[1]
}, %, $edge6)
|> xLine(-0.183 * railHeight, %, $edge7)
|> yLine(-0.087 * railHeight, %, $edge8)
|> xLine(0.06 * railHeight, %)
|> arc({
angleStart = 180,
angleEnd = 0,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(0.1 * railHeight, %)
|> arc({
angleStart = 180,
angleEnd = 0,
radius = 0.072 / 4 * railHeight
}, %)
|> arc({
angleStart = -90,
angleEnd = 0,
radius = 0.1 * railHeight
}, %)
// Sketch side 2 of profile
|> arc({
angleStart = 270,
angleEnd = 90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(0.1 * railHeight, %)
|> arc({
angleStart = 270,
angleEnd = 90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(0.06 * railHeight, %, $edge9)
|> xLine(-0.087 * railHeight, %, $edge10)
|> yLine(-0.183 * railHeight, %, $edge11) // edge11
|> angledLineToX({
angle = 135,
to = ((1 - 0.356) / 2 + 0.356) * railHeight + originStart[0]
}, %, $edge12) // edge12
|> yLine(0.232 * railHeight, %, $edge13) // 13
|> angledLineToX({
angle = 45,
to = (1 - 0.087) * railHeight + originStart[0]
}, %, $edge14) // 14
|> yLine(-0.183 * railHeight, %, $edge15) // 15
|> xLine(0.087 * railHeight, %, $edge16)
|> yLine(0.06 * railHeight, %)
|> arc({
angleStart = 270,
angleEnd = 90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(0.1 * railHeight, %)
|> arc({
angleStart = 270,
angleEnd = 90,
radius = 0.072 / 4 * railHeight
}, %)
// Sketch side 3 of profile
|> arc({
angleStart = 0,
angleEnd = 90,
radius = 0.1 * railHeight
}, %)
|> arc({
angleStart = 0,
angleEnd = -180,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(-0.1 * railHeight, %)
|> arc({
angleStart = 0,
angleEnd = -180,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(-0.06 * railHeight, %, $edge17)
|> yLine(-0.087 * railHeight, %, $edge18)
|> xLine(0.183 * railHeight, %, $edge19)
|> angledLineToY({
angle = 45,
to = ((1 - 0.356) / 2 + 0.356) * railHeight + originStart[1]
}, %, $edge20)
|> xLine(-0.232 * railHeight, %, $edge21)
|> angledLineToY({
angle = 135,
to = (1 - 0.087) * railHeight + originStart[1]
}, %, $edge22)
|> xLine(0.183 * railHeight, %, $edge23)
|> yLine(0.087 * railHeight, %, $edge24)
|> xLine(-0.06 * railHeight, %)
|> arc({
angleStart = 0,
angleEnd = -180,
radius = 0.072 / 4 * railHeight
}, %)
|> xLine(-0.1 * railHeight, %)
|> arc({
angleStart = 0,
angleEnd = -180,
radius = 0.072 / 4 * railHeight
}, %)
|> arc({
angleStart = 90,
angleEnd = 180,
radius = 0.1 * railHeight
}, %)
// Sketch side 4 of profile
|> arc({
angleStart = 90,
angleEnd = -90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(-0.1 * railHeight, %)
|> arc({
angleStart = 90,
angleEnd = -90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(-0.06 * railHeight, %, $edge25)
|> xLine(0.087 * railHeight, %, $edge26)
|> yLine(0.183 * railHeight, %, $edge27)
|> angledLineToX({
angle = 135,
to = (1 - 0.356) / 2 * railHeight + originStart[0]
}, %, $edge28)
|> yLine(-0.232 * railHeight, %, $edge29)
|> angledLineToX({
angle = 45,
to = 0.087 * railHeight + originStart[0]
}, %, $edge30)
|> yLine(0.183 * railHeight, %, $edge31)
|> xLine(-0.087 * railHeight, %, $edge32)
|> yLine(-0.06 * railHeight, %)
|> arc({
angleStart = 90,
angleEnd = -90,
radius = 0.072 / 4 * railHeight
}, %)
|> yLine(-0.1 * railHeight, %)
|> arc({
angleStart = 90,
angleEnd = -90,
radius = 0.072 / 4 * railHeight
}, %)
|> close()
// Sketch center hole of profile
|> hole(circle(
center = [
.5 * railHeight + originStart[0],
.5 * railHeight + originStart[1]
],
radius = .205 * railHeight / 2
), %)
|> extrude(length = railLength)
|> fillet(
radius = 0.06,
tags = [
getNextAdjacentEdge(edge3),
getNextAdjacentEdge(edge4),
getNextAdjacentEdge(edge5),
getNextAdjacentEdge(edge6),
getNextAdjacentEdge(edge11),
getNextAdjacentEdge(edge12),
getNextAdjacentEdge(edge13),
getNextAdjacentEdge(edge14),
getNextAdjacentEdge(edge19),
getNextAdjacentEdge(edge20),
getNextAdjacentEdge(edge21),
getNextAdjacentEdge(edge22),
getNextAdjacentEdge(edge27),
getNextAdjacentEdge(edge28),
getNextAdjacentEdge(edge29),
getNextAdjacentEdge(edge30)
]
)
|> fillet(
radius = 0.03,
tags = [
getNextAdjacentEdge(edge1),
getNextAdjacentEdge(edge2),
getNextAdjacentEdge(edge7),
getNextAdjacentEdge(edge8),
getNextAdjacentEdge(edge9),
getNextAdjacentEdge(edge10),
getNextAdjacentEdge(edge15),
getNextAdjacentEdge(edge16),
getNextAdjacentEdge(edge17),
getNextAdjacentEdge(edge18),
getNextAdjacentEdge(edge23),
getNextAdjacentEdge(edge24),
getNextAdjacentEdge(edge25),
getNextAdjacentEdge(edge26),
getNextAdjacentEdge(edge31),
getNextAdjacentEdge(edge32)
]
)
return sketch001
}
// Generate one adjustable rail of 80/20
rail8020([0, 0], 1.5, 48)

View File

@ -0,0 +1,103 @@
# kcl-samples
KittyCAD Language (KCL) is our language for defining geometry and working with our Geometry Engine efficiently.
This repository includes a mixture of simple and complex models demonstrating the features and syntax of KCL.
The samples can be browsed in our documentation at <https://zoo.dev/docs/kcl-samples>.
## Guidelines for adding samples
Merge PRs to the `next` branch, not main. When we release Modeling App, we will merge this repo's `next` into `main`. This way, `main` is always compatible with the latest ZMA release.
KCL samples conform to a set of style guidelines to ensure consistency and readability.
1. **File Naming:** Name your KCL files descriptively and concisely, using hyphens to separate words (e.g., flange.kcl, ball-bearing.kcl).
2. **File Header:** Include a title comment at the top of each file, followed by a brief description explaining what the model is and its typical use cases.
3. **Inline Comments:** Use inline comments to explain non-obvious parts of the code. Each major section should have a comment describing its purpose.
4. **Constants:** Define constants at the beginning of your KCL files for any values that might change or need to be reused (e.g., dimensions, angles).
## Snapshot and export
When you submit a PR to add or modify KCL samples, images and STEP files will be generated and added to the repository automatically.
---
#### [3d-boaty](3d-boaty/main.kcl) ([step](step/3d-boaty.step)) ([screenshot](screenshots/3d-boaty.png))
[![3d-boaty](screenshots/3d-boaty.png)](3d-boaty/main.kcl)
#### [80-20-rail](80-20-rail/main.kcl) ([step](step/80-20-rail.step)) ([screenshot](screenshots/80-20-rail.png))
[![80-20-rail](screenshots/80-20-rail.png)](80-20-rail/main.kcl)
#### [a-parametric-bearing-pillow-block](a-parametric-bearing-pillow-block/main.kcl) ([step](step/a-parametric-bearing-pillow-block.step)) ([screenshot](screenshots/a-parametric-bearing-pillow-block.png))
[![a-parametric-bearing-pillow-block](screenshots/a-parametric-bearing-pillow-block.png)](a-parametric-bearing-pillow-block/main.kcl)
#### [ball-bearing](ball-bearing/main.kcl) ([step](step/ball-bearing.step)) ([screenshot](screenshots/ball-bearing.png))
[![ball-bearing](screenshots/ball-bearing.png)](ball-bearing/main.kcl)
#### [bracket](bracket/main.kcl) ([step](step/bracket.step)) ([screenshot](screenshots/bracket.png))
[![bracket](screenshots/bracket.png)](bracket/main.kcl)
#### [car-wheel-assembly](car-wheel-assembly/main.kcl) ([step](step/car-wheel-assembly.step)) ([screenshot](screenshots/car-wheel-assembly.png))
[![car-wheel-assembly](screenshots/car-wheel-assembly.png)](car-wheel-assembly/main.kcl)
#### [color-cube](color-cube/main.kcl) ([step](step/color-cube.step)) ([screenshot](screenshots/color-cube.png))
[![color-cube](screenshots/color-cube.png)](color-cube/main.kcl)
#### [cycloidal-gear](cycloidal-gear/main.kcl) ([step](step/cycloidal-gear.step)) ([screenshot](screenshots/cycloidal-gear.png))
[![cycloidal-gear](screenshots/cycloidal-gear.png)](cycloidal-gear/main.kcl)
#### [dodecahedron](dodecahedron/main.kcl) ([step](step/dodecahedron.step)) ([screenshot](screenshots/dodecahedron.png))
[![dodecahedron](screenshots/dodecahedron.png)](dodecahedron/main.kcl)
#### [enclosure](enclosure/main.kcl) ([step](step/enclosure.step)) ([screenshot](screenshots/enclosure.png))
[![enclosure](screenshots/enclosure.png)](enclosure/main.kcl)
#### [exhaust-manifold](exhaust-manifold/main.kcl) ([step](step/exhaust-manifold.step)) ([screenshot](screenshots/exhaust-manifold.png))
[![exhaust-manifold](screenshots/exhaust-manifold.png)](exhaust-manifold/main.kcl)
#### [flange-with-patterns](flange-with-patterns/main.kcl) ([step](step/flange-with-patterns.step)) ([screenshot](screenshots/flange-with-patterns.png))
[![flange-with-patterns](screenshots/flange-with-patterns.png)](flange-with-patterns/main.kcl)
#### [flange-xy](flange-xy/main.kcl) ([step](step/flange-xy.step)) ([screenshot](screenshots/flange-xy.png))
[![flange-xy](screenshots/flange-xy.png)](flange-xy/main.kcl)
#### [focusrite-scarlett-mounting-bracket](focusrite-scarlett-mounting-bracket/main.kcl) ([step](step/focusrite-scarlett-mounting-bracket.step)) ([screenshot](screenshots/focusrite-scarlett-mounting-bracket.png))
[![focusrite-scarlett-mounting-bracket](screenshots/focusrite-scarlett-mounting-bracket.png)](focusrite-scarlett-mounting-bracket/main.kcl)
#### [food-service-spatula](food-service-spatula/main.kcl) ([step](step/food-service-spatula.step)) ([screenshot](screenshots/food-service-spatula.png))
[![food-service-spatula](screenshots/food-service-spatula.png)](food-service-spatula/main.kcl)
#### [french-press](french-press/main.kcl) ([step](step/french-press.step)) ([screenshot](screenshots/french-press.png))
[![french-press](screenshots/french-press.png)](french-press/main.kcl)
#### [gear-rack](gear-rack/main.kcl) ([step](step/gear-rack.step)) ([screenshot](screenshots/gear-rack.png))
[![gear-rack](screenshots/gear-rack.png)](gear-rack/main.kcl)
#### [gear](gear/main.kcl) ([step](step/gear.step)) ([screenshot](screenshots/gear.png))
[![gear](screenshots/gear.png)](gear/main.kcl)
#### [gridfinity-baseplate-magnets](gridfinity-baseplate-magnets/main.kcl) ([step](step/gridfinity-baseplate-magnets.step)) ([screenshot](screenshots/gridfinity-baseplate-magnets.png))
[![gridfinity-baseplate-magnets](screenshots/gridfinity-baseplate-magnets.png)](gridfinity-baseplate-magnets/main.kcl)
#### [gridfinity-baseplate](gridfinity-baseplate/main.kcl) ([step](step/gridfinity-baseplate.step)) ([screenshot](screenshots/gridfinity-baseplate.png))
[![gridfinity-baseplate](screenshots/gridfinity-baseplate.png)](gridfinity-baseplate/main.kcl)
#### [gridfinity-bins-stacking-lip](gridfinity-bins-stacking-lip/main.kcl) ([step](step/gridfinity-bins-stacking-lip.step)) ([screenshot](screenshots/gridfinity-bins-stacking-lip.png))
[![gridfinity-bins-stacking-lip](screenshots/gridfinity-bins-stacking-lip.png)](gridfinity-bins-stacking-lip/main.kcl)
#### [gridfinity-bins](gridfinity-bins/main.kcl) ([step](step/gridfinity-bins.step)) ([screenshot](screenshots/gridfinity-bins.png))
[![gridfinity-bins](screenshots/gridfinity-bins.png)](gridfinity-bins/main.kcl)
#### [hex-nut](hex-nut/main.kcl) ([step](step/hex-nut.step)) ([screenshot](screenshots/hex-nut.png))
[![hex-nut](screenshots/hex-nut.png)](hex-nut/main.kcl)
#### [i-beam](i-beam/main.kcl) ([step](step/i-beam.step)) ([screenshot](screenshots/i-beam.png))
[![i-beam](screenshots/i-beam.png)](i-beam/main.kcl)
#### [kitt](kitt/main.kcl) ([step](step/kitt.step)) ([screenshot](screenshots/kitt.png))
[![kitt](screenshots/kitt.png)](kitt/main.kcl)
#### [lego](lego/main.kcl) ([step](step/lego.step)) ([screenshot](screenshots/lego.png))
[![lego](screenshots/lego.png)](lego/main.kcl)
#### [mounting-plate](mounting-plate/main.kcl) ([step](step/mounting-plate.step)) ([screenshot](screenshots/mounting-plate.png))
[![mounting-plate](screenshots/mounting-plate.png)](mounting-plate/main.kcl)
#### [multi-axis-robot](multi-axis-robot/main.kcl) ([step](step/multi-axis-robot.step)) ([screenshot](screenshots/multi-axis-robot.png))
[![multi-axis-robot](screenshots/multi-axis-robot.png)](multi-axis-robot/main.kcl)
#### [pipe-flange-assembly](pipe-flange-assembly/main.kcl) ([step](step/pipe-flange-assembly.step)) ([screenshot](screenshots/pipe-flange-assembly.png))
[![pipe-flange-assembly](screenshots/pipe-flange-assembly.png)](pipe-flange-assembly/main.kcl)
#### [pipe-with-bend](pipe-with-bend/main.kcl) ([step](step/pipe-with-bend.step)) ([screenshot](screenshots/pipe-with-bend.png))
[![pipe-with-bend](screenshots/pipe-with-bend.png)](pipe-with-bend/main.kcl)
#### [pipe](pipe/main.kcl) ([step](step/pipe.step)) ([screenshot](screenshots/pipe.png))
[![pipe](screenshots/pipe.png)](pipe/main.kcl)
#### [poopy-shoe](poopy-shoe/main.kcl) ([step](step/poopy-shoe.step)) ([screenshot](screenshots/poopy-shoe.png))
[![poopy-shoe](screenshots/poopy-shoe.png)](poopy-shoe/main.kcl)
#### [router-template-cross-bar](router-template-cross-bar/main.kcl) ([step](step/router-template-cross-bar.step)) ([screenshot](screenshots/router-template-cross-bar.png))
[![router-template-cross-bar](screenshots/router-template-cross-bar.png)](router-template-cross-bar/main.kcl)
#### [router-template-slate](router-template-slate/main.kcl) ([step](step/router-template-slate.step)) ([screenshot](screenshots/router-template-slate.png))
[![router-template-slate](screenshots/router-template-slate.png)](router-template-slate/main.kcl)
#### [sheet-metal-bracket](sheet-metal-bracket/main.kcl) ([step](step/sheet-metal-bracket.step)) ([screenshot](screenshots/sheet-metal-bracket.png))
[![sheet-metal-bracket](screenshots/sheet-metal-bracket.png)](sheet-metal-bracket/main.kcl)
#### [socket-head-cap-screw](socket-head-cap-screw/main.kcl) ([step](step/socket-head-cap-screw.step)) ([screenshot](screenshots/socket-head-cap-screw.png))
[![socket-head-cap-screw](screenshots/socket-head-cap-screw.png)](socket-head-cap-screw/main.kcl)
#### [walkie-talkie](walkie-talkie/main.kcl) ([step](step/walkie-talkie.step)) ([screenshot](screenshots/walkie-talkie.png))
[![walkie-talkie](screenshots/walkie-talkie.png)](walkie-talkie/main.kcl)
#### [washer](washer/main.kcl) ([step](step/washer.step)) ([screenshot](screenshots/washer.png))
[![washer](screenshots/washer.png)](washer/main.kcl)

View File

@ -0,0 +1,104 @@
// A Parametric Bearing Pillow Block
// A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads.
// Set units
@settings(defaultLengthUnit = in)
// Define constants such as length, width, height, counter-bore depth and diameter, bearing diameter, hole location padding, and more
length = 6
width = 4
height = 1
cbDepth = .25
cbDia = .7
holeDia = .375
padding = 1.5
bearingDia = 3
// (Needs to be updated). Sketch the block and extrude up to where the counterbore diameter starts.
block = startSketchOn('XY')
|> startProfileAt([-width / 2, -length / 2], %)
|> line(endAbsolute = [width / 2, -length / 2])
|> line(endAbsolute = [width / 2, length / 2])
|> line(endAbsolute = [-width / 2, length / 2])
|> close()
|> hole(circle(
center = [
-(width / 2 - (padding / 2)),
-(length / 2 - (padding / 2))
],
radius = holeDia / 2
), %)
|> hole(circle(
center = [
-(width / 2 - (padding / 2)),
length / 2 - (padding / 2)
],
radius = holeDia / 2
), %)
|> hole(circle(
center = [
width / 2 - (padding / 2),
length / 2 - (padding / 2)
],
radius = holeDia / 2
), %)
|> hole(circle(
center = [
width / 2 - (padding / 2),
-(length / 2 - (padding / 2))
],
radius = holeDia / 2
), %)
|> hole(circle(
center = [0, 0],
radius = bearingDia / 2
), %)
|> extrude(length = height - cbDepth)
// Create a second sketch that creates the counterbore diameters and extrude the rest of the way to get the total height. Note: You cannot use startSketchOn(block, 'end'). The extrude lives outside the bounds, and the engine will not execute. This is a known issue.
secondHalf = startSketchOn({
plane = {
origin = { x = 0, y = 0, z = height - cbDepth },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
})
|> startProfileAt([-width / 2, -length / 2], %)
|> line(endAbsolute = [width / 2, -length / 2])
|> line(endAbsolute = [width / 2, length / 2])
|> line(endAbsolute = [-width / 2, length / 2])
|> close()
|> hole(circle(
center = [
-(width / 2 - (padding / 2)),
-(length / 2 - (padding / 2))
],
radius = cbDia / 2
), %)
|> hole(circle(
center = [
-(width / 2 - (padding / 2)),
length / 2 - (padding / 2)
],
radius = cbDia / 2
), %)
|> hole(circle(
center = [
width / 2 - (padding / 2),
length / 2 - (padding / 2)
],
radius = cbDia / 2
), %)
|> hole(circle(
center = [
width / 2 - (padding / 2),
-(length / 2 - (padding / 2))
],
radius = cbDia / 2
), %)
|> hole(circle(
center = [0, 0],
radius = bearingDia / 2
), %)
|> extrude(length = cbDepth)

View File

@ -0,0 +1,124 @@
// Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Set units
@settings(defaultLengthUnit = in)
// Define constants like ball diameter, inside diamter, overhange length, and thickness
outsideDiameter = 1.625
sphereDia = 0.25
shaftDia = 0.75
overallThickness = 0.313
wallThickness = 0.100
overHangLength = .3
nBalls = 10
chainWidth = sphereDia / 2
chainThickness = sphereDia / 8
linkDiameter = sphereDia / 4
customPlane = {
plane = {
origin = {
x = 0,
y = 0,
z = -overallThickness / 2
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Sketch the inside bearing piece
insideWallSketch = startSketchOn(customPlane)
|> circle(
center = [0, 0],
radius = shaftDia / 2 + wallThickness
)
|> hole(circle(
center = [0, 0],
radius = shaftDia / 2
), %)
// Extrude the inside bearing piece
insideWall = extrude(insideWallSketch, length = overallThickness)
// Create the sketch of one of the balls
ballsSketch = startSketchOn("XY")
|> startProfileAt([shaftDia / 2 + wallThickness, 0.001], %)
|> arc({
angleEnd = 0,
angleStart = 180,
radius = sphereDia / 2
}, %)
|> close()
// Revolve the ball to make a sphere and pattern around the inside wall
balls = revolve({ axis = "X" }, ballsSketch)
|> patternCircular3d(
arcDegrees = 360,
axis = [0, 0, 1],
center = [0, 0, 0],
instances = nBalls,
rotateDuplicates = true
)
// Create the sketch for the chain around the balls
chainSketch = startSketchOn("XY")
|> startProfileAt([
shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2),
0.125 * sin(toRadians(60))
], %)
|> arc({
angleEnd = 60,
angleStart = 120,
radius = sphereDia / 2
}, %)
|> line(end = [0, chainThickness])
|> line(end = [-chainWidth, 0])
|> close()
// Revolve the chain sketch
chainHead = revolve({ axis = "X" }, chainSketch)
|> patternCircular3d(
arcDegrees = 360,
axis = [0, 0, 1],
center = [0, 0, 0],
instances = nBalls,
rotateDuplicates = true
)
// Create the sketch for the links in between the chains
linkSketch = startSketchOn("XZ")
|> circle(
center = [
shaftDia / 2 + wallThickness + sphereDia / 2,
0
],
radius = linkDiameter / 2
)
// Revolve the link sketch
linkRevolve = revolve({ axis = 'Y', angle = 360 / nBalls }, linkSketch)
|> patternCircular3d(
arcDegrees = 360,
axis = [0, 0, 1],
center = [0, 0, 0],
instances = nBalls,
rotateDuplicates = true
)
// Create the sketch for the outside walls
outsideWallSketch = startSketchOn(customPlane)
|> circle(
center = [0, 0],
radius = outsideDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = shaftDia / 2 + wallThickness + sphereDia
), %)
outsideWall = extrude(outsideWallSketch, length = overallThickness)
// https://www.mcmaster.com/60355K185/

View File

@ -0,0 +1,113 @@
// Shelf Bracket
// This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
sigmaAllow = 35000 // psi (6061-T6 aluminum)
width = 6
p = 300 // Force on shelf - lbs
factorOfSafety = 1.2 // FOS of 1.2
shelfMountL = 5
wallMountL = 2
shelfDepth = 12 // Shelf is 12 inches in depth from the wall
moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in)
filletRadius = .375
extFilletRadius = .25
mountingHoleDiameter = 0.5
// Calculate required thickness of bracket
thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
// Sketch the bracket body and fillet the inner and outer edges of the bend
bracketLeg1Sketch = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(end = [shelfMountL - filletRadius, 0], tag = $fillet1)
|> line(end = [0, width], tag = $fillet2)
|> line(end = [-shelfMountL + filletRadius, 0])
|> close()
|> hole(circle(
center = [1, 1],
radius = mountingHoleDiameter / 2
), %)
|> hole(circle(
center = [shelfMountL - 1.5, width - 1],
radius = mountingHoleDiameter / 2
), %)
|> hole(circle(
center = [1, width - 1],
radius = mountingHoleDiameter / 2
), %)
|> hole(circle(
center = [shelfMountL - 1.5, 1],
radius = mountingHoleDiameter / 2
), %)
// Extrude the leg 2 bracket sketch
bracketLeg1Extrude = extrude(bracketLeg1Sketch, length = thickness)
|> fillet(
radius = extFilletRadius,
tags = [
getNextAdjacentEdge(fillet1),
getNextAdjacentEdge(fillet2)
]
)
// Sketch the fillet arc
filletSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line(end = [0, thickness])
|> arc({
angleEnd = 180,
angleStart = 90,
radius = filletRadius + thickness
}, %)
|> line(end = [thickness, 0])
|> arc({
angleEnd = 90,
angleStart = 180,
radius = filletRadius
}, %)
// Sketch the bend
filletExtrude = extrude(filletSketch, length = -width)
// Create a custom plane for the leg that sits on the wall
customPlane = {
plane = {
origin = { x = -filletRadius, y = 0, z = 0 },
xAxis = { x = 0, y = 1, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 1, y = 0, z = 0 }
}
}
// Create a sketch for the second leg
bracketLeg2Sketch = startSketchOn(customPlane)
|> startProfileAt([0, -filletRadius], %)
|> line(end = [width, 0])
|> line(end = [0, -wallMountL], tag = $fillet3)
|> line(end = [-width, 0], tag = $fillet4)
|> close()
|> hole(circle(
center = [1, -1.5],
radius = mountingHoleDiameter / 2
), %)
|> hole(circle(
center = [5, -1.5],
radius = mountingHoleDiameter / 2
), %)
// Extrude the second leg
bracketLeg2Extrude = extrude(bracketLeg2Sketch, length = -thickness)
|> fillet(
radius = extFilletRadius,
tags = [
getNextAdjacentEdge(fillet3),
getNextAdjacentEdge(fillet4)
]
)

View File

@ -0,0 +1,96 @@
// Brake Caliper
// Brake calipers are used to squeeze the brake pads against the rotor, causing larger and larger amounts of friction depending on how hard the brakes are pressed.
// Set units
@settings(defaultLengthUnit = in)
// Import Constants
import caliperTolerance, caliperPadLength, caliperThickness, caliperOuterEdgeRadius, caliperInnerEdgeRadius, rotorDiameter, rotorTotalThickness, yAxisOffset from "globals.kcl"
// Create the plane for the brake caliper. This is so it can match up with the rotor model.
brakeCaliperPlane = {
plane = {
origin = { x = 0, y = yAxisOffset, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Sketch the brake caliper profile
brakeCaliperSketch = startSketchOn(brakeCaliperPlane)
|> startProfileAt([
rotorDiameter / 2 + caliperTolerance,
0
], %)
|> line(end = [
0,
rotorTotalThickness + caliperTolerance - caliperInnerEdgeRadius
])
|> tangentialArc({
offset = 90,
radius = caliperInnerEdgeRadius
}, %)
|> line(end = [
-caliperPadLength + 2 * caliperInnerEdgeRadius,
0
])
|> tangentialArc({
offset = -90,
radius = caliperInnerEdgeRadius
}, %)
|> line(end = [
0,
caliperThickness - (caliperInnerEdgeRadius * 2)
])
|> tangentialArc({
offset = -90,
radius = caliperInnerEdgeRadius
}, %)
|> line(end = [
caliperPadLength + caliperThickness - caliperOuterEdgeRadius - caliperInnerEdgeRadius,
0
])
|> tangentialArc({
offset = -90,
radius = caliperOuterEdgeRadius
}, %)
|> line(end = [
0,
-2 * caliperTolerance - (2 * caliperThickness) - rotorTotalThickness + 2 * caliperOuterEdgeRadius
])
|> tangentialArc({
offset = -90,
radius = caliperOuterEdgeRadius
}, %)
|> line(end = [
-caliperPadLength - caliperThickness + caliperOuterEdgeRadius + caliperInnerEdgeRadius,
0
])
|> tangentialArc({
offset = -90,
radius = caliperInnerEdgeRadius
}, %)
|> line(end = [
0,
caliperThickness - (2 * caliperInnerEdgeRadius)
])
|> tangentialArc({
offset = -90,
radius = caliperInnerEdgeRadius
}, %)
|> line(end = [
caliperPadLength - (2 * caliperInnerEdgeRadius),
0
])
|> tangentialArc({
offset = 90,
radius = caliperInnerEdgeRadius
}, %)
|> close()
// Revolve the brake caliper sketch
revolve({ axis = "Y", angle = -70 }, brakeCaliperSketch)
|> appearance(color = "#c82d2d", metalness = 90, roughness = 90)

View File

@ -0,0 +1,111 @@
// Wheel rotor
// A component of a disc brake system. It provides a surface for brake pads to press against, generating the friction needed to slow or stop the vehicle.
// Set units
@settings(defaultLengthUnit = in)
// Import Constants
import rotorDiameter, rotorInnerDiameter, rotorSinglePlateThickness, rotorInnerDiameterThickness, lugHolePatternDia, lugSpacing, rotorTotalThickness, spacerPatternDiameter, spacerDiameter, spacerLength, spacerCount, wheelDiameter, lugCount, yAxisOffset, drillAndSlotCount from "globals.kcl"
rotorPlane = {
plane = {
origin = { x = 0, y = yAxisOffset, z = 0 },
xAxis = { x = -1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
fn lugPattern(plane) {
lugHolePattern = circle(
plane,
center = [-lugSpacing / 2, 0],
radius = 0.315
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = lugCount,
rotateDuplicates = true
)
return lugHolePattern
}
rotorSketch = startSketchOn(rotorPlane)
|> circle(
center = [0, 0],
radius = rotorDiameter / 2
)
|> hole(lugPattern(%), %)
rotor = extrude(rotorSketch, length = rotorSinglePlateThickness)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
rotorBumpSketch = startSketchOn(rotorPlane)
|> circle(
center = [0, 0],
radius = rotorInnerDiameter / 2
)
|> hole(lugPattern(%), %)
rotorBump = extrude(rotorBumpSketch, length = -rotorInnerDiameterThickness)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
rotorSecondaryPlatePlane = {
plane = {
origin = {
x = 0,
y = yAxisOffset + rotorTotalThickness * 0.75,
z = 0
},
xAxis = { x = -1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
secondaryRotorSketch = startSketchOn(rotorSecondaryPlatePlane)
|> circle(
center = [0, 0],
radius = rotorDiameter / 2
)
|> hole(lugPattern(%), %)
secondRotor = extrude(secondaryRotorSketch, length = rotorSinglePlateThickness)
spacerSketch = startSketchOn(rotorSecondaryPlatePlane)
|> circle(
center = [spacerPatternDiameter / 2, 0],
radius = spacerDiameter
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = spacerCount,
rotateDuplicates = true
)
spacers = extrude(spacerSketch, length = -spacerLength)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
rotorSlottedSketch = startSketchOn(rotor, 'START')
|> startProfileAt([2.17, 2.56], %)
|> xLine(0.12, %)
|> yLine(2.56, %)
|> xLine(-0.12, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternCircular2d(
center = [0, 0],
instances = drillAndSlotCount,
arcDegrees = 360,
rotateDuplicates = true
)
rotorSlotted = extrude(rotorSlottedSketch, length = -rotorSinglePlateThickness / 2)
secondRotorSlottedSketch = startSketchOn(secondRotor, 'END')
|> startProfileAt([-2.17, 2.56], %)
|> xLine(-0.12, %)
|> yLine(2.56, %)
|> xLine(0.12, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternCircular2d(
center = [0, 0],
instances = drillAndSlotCount,
arcDegrees = 360,
rotateDuplicates = true
)
secondRotorSlotted = extrude(secondRotorSlottedSketch, length = -rotorSinglePlateThickness / 2)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)

View File

@ -0,0 +1,44 @@
// Tire
// A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities.
// Set units
@settings(defaultLengthUnit = in)
// Import Constants
import tireInnerDiameter, tireOuterDiameter, tireDepth, bendRadius, tireTreadWidth, tireTreadDepth, tireTreadOffset from "globals.kcl"
// Create the sketch of the tire
tireSketch = startSketchOn("XY")
|> startProfileAt([tireInnerDiameter / 2, tireDepth / 2], %)
|> line(endAbsolute = [
tireOuterDiameter / 2 - bendRadius,
tireDepth / 2
], tag = $edge1)
|> tangentialArc({ offset = -90, radius = bendRadius }, %)
|> line(endAbsolute = [
tireOuterDiameter / 2,
tireDepth / 2 - tireTreadOffset
])
|> line(end = [-tireTreadDepth, 0])
|> line(end = [0, -tireTreadWidth])
|> line(end = [tireTreadDepth, 0])
|> line(endAbsolute = [
tireOuterDiameter / 2,
-tireDepth / 2 + tireTreadOffset + tireTreadWidth
])
|> line(end = [-tireTreadDepth, 0])
|> line(end = [0, -tireTreadWidth])
|> line(end = [tireTreadDepth, 0])
|> line(endAbsolute = [
tireOuterDiameter / 2,
-tireDepth / 2 + bendRadius
])
|> tangentialArc({ offset = -90, radius = bendRadius }, %)
|> line(endAbsolute = [tireInnerDiameter / 2, -tireDepth / 2], tag = $edge2)
|> close()
// Revolve the sketch to create the tire
revolve({ axis = "Y" }, tireSketch)
|> appearance(color = "#0f0f0f", roughness = 80)

View File

@ -0,0 +1,197 @@
// Car Wheel
// A sports car wheel with a circular lug pattern and spokes.
// Set units
@settings(defaultLengthUnit = in)
// Import Constants
import lugCount, lugSpacing, offset, backSpacing, wheelWidth, wheelDiameter, spokeCount, spokeGap, spokeAngle, spokeThickness from "globals.kcl"
// Create the wheel center
lugBase = startSketchOn('XZ')
|> circle(
center = [0, 0],
radius = (lugSpacing + 1.5) / 2
)
|> hole(circle(
center = [0, 0],
radius = (lugSpacing - 1.5) / 2
), %)
|> extrude(length = wheelWidth / 20)
// Extend the wheel center and bore holes to accomidate the lug heads
lugExtrusion = startSketchOn(lugBase, 'END')
|> circle(
center = [0, 0],
radius = (lugSpacing + 1.5) / 2
)
|> hole(circle(
center = [0, 0],
radius = (lugSpacing - 1.5) / 2
), %)
|> extrude(length = wheelWidth / 10)
// Create the circular pattern for the lugs
lugClearance = startSketchOn(lugExtrusion, 'END')
|> circle(
center = [lugSpacing / 2, 0],
radius = 1.2 / 2
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = lugCount,
rotateDuplicates = true
)
|> extrude(length = -wheelWidth / 10)
// Create the circular pattern for the lug holes
lugHoles = startSketchOn(lugBase, 'END')
|> circle(
center = [lugSpacing / 2, 0],
radius = 16 * mm() / 2
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = lugCount,
rotateDuplicates = true
)
|> extrude(length = -wheelWidth / 20)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
// Add detail to the wheel center by revolving curved edge profiles
wheelCenterInner = startSketchOn('XY')
|> startProfileAt([(lugSpacing - 1.5) / 2, 0], %)
|> yLine(-wheelWidth / 10 - (wheelWidth / 20), %)
|> bezierCurve({
to = [-0.4, 0.3],
control1 = [-0.3, 0],
control2 = [0, 0.3]
}, %)
|> yLineTo(0, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ axis = 'y' }, %)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
wheelCenterOuter = startSketchOn('XY')
|> startProfileAt([(lugSpacing + 1.5) / 2, 0], %)
|> yLine(-wheelWidth / 10 - (wheelWidth / 20), %)
|> bezierCurve({
to = [0.4, -0.1],
control1 = [0.3, 0],
control2 = [0.2, -0.3]
}, %)
|> yLineTo(-wheelWidth / 20, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ axis = 'y' }, %)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
// Write a function that defines the spoke geometry, patterns and extrudes it
fn spoke(spokeGap, spokeAngle, spokeThickness) {
// Seperating the spoke base planes
plane001 = {
plane = {
origin = [0.0, 0.0, spokeGap / 2],
xAxis = [1.0, 0.0, spokeAngle],
yAxis = [0.0, 1.0, 0.0],
zAxis = [0.0, 0.0, 1.0]
}
}
// Spoke cross sections
spokeProfile = startSketchOn(plane001)
|> startProfileAt([(lugSpacing + 2) / 2, -0.7], %)
|> bezierCurve({
to = [
(wheelDiameter - lugSpacing - 2.9) / 2,
offset
],
control1 = [
(wheelDiameter - lugSpacing - 2.9) / 3.5,
offset / 7
],
control2 = [
(wheelDiameter - lugSpacing - 2.9) / 4,
offset / 1.5
]
}, %)
|> yLine(-wheelWidth / 15, %)
|> bezierCurve({
to = [
-(wheelDiameter - lugSpacing - 2.9) / 2,
-offset
],
control1 = [
-(wheelDiameter - lugSpacing - 2.9) / 5,
-offset / 7
],
control2 = [
-(wheelDiameter - lugSpacing - 2.9) / 5,
-offset / 1.5
]
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
// Circular pattern spokes
spokePattern = extrude(spokeProfile, length = spokeThickness)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, -2000, 0],
instances = spokeCount,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
return spokePattern
}
spoke(spokeGap, spokeAngle, spokeThickness)
spoke(-spokeGap, -spokeAngle, -spokeThickness)
// Define and revolve wheel exterior
startSketchOn('XY')
|> startProfileAt([
wheelDiameter / 2,
-wheelWidth + backSpacing + offset
], %)
|> yLine(wheelWidth * 0.25, %)
|> line(end = [-wheelWidth * 0.02, wheelWidth * 0.02])
|> yLine(wheelWidth * 0.25, %)
|> line(end = [wheelWidth * 0.02, wheelWidth * 0.02])
|> yLineTo(backSpacing + offset, %)
|> line(end = [wheelWidth * 0.05, wheelWidth * .01])
|> yLine(wheelWidth * 0.05, %)
|> xLine(-wheelWidth * 0.03, %)
|> yLine(-wheelWidth * 0.02, %)
|> line(end = [-wheelWidth * 0.05, -wheelWidth * 0.01])
|> yLine(-backSpacing * 0.7, %)
|> line(end = [
-wheelDiameter * 0.01,
-wheelWidth * 0.02
])
|> yLineTo(offset - 0.2, %)
|> line(end = [
-wheelDiameter * 0.03,
-wheelWidth * 0.02
])
|> yLine(-wheelWidth * 0.02, %)
|> line(end = [
wheelDiameter * 0.03,
-wheelWidth * 0.1
])
|> yLine(-wheelWidth * 0.05, %)
|> line(end = [wheelWidth * 0.02, -wheelWidth * 0.02])
|> yLineTo(-wheelWidth + backSpacing + offset - 0.28, %)
|> line(end = [wheelWidth * 0.05, -wheelWidth * 0.01])
|> yLine(-wheelWidth * 0.02, %)
|> xLine(wheelWidth * 0.03, %)
|> yLine(wheelWidth * 0.05, %)
|> close()
|> revolve({ axis = 'y' }, %)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)

View File

@ -0,0 +1,53 @@
// Car wheel assembly global constants
// Set units
@settings(defaultLengthUnit = in)
// Car Wheel
export lugCount = 5
export lugSpacing = 114.3 * mm()
export offset = -35 * mm()
export backSpacing = 6.38
export wheelWidth = 9.5
export wheelDiameter = 19
export spokeCount = 6
export spokeGap = 0.2
export spokeAngle = 0.02
export spokeThickness = 0.95
// Lug Nut
export lugDiameter = 24 * mm()
export lugHeadLength = lugDiameter * .5
export lugThreadDiameter = lugDiameter / 2 * .85
export lugLength = 30 * mm()
export lugThreadDepth = lugLength - 12.7 * mm()
// Car Rotor
export rotorDiameter = 12
export rotorInnerDiameter = 6
export rotorSinglePlateThickness = 0.25
export rotorInnerDiameterThickness = 0.5
export lugHolePatternDia = 3
export rotorTotalThickness = 1
export spacerPatternDiameter = 11
export spacerDiameter = 0.25
export spacerLength = rotorTotalThickness - (2 * rotorSinglePlateThickness)
export spacerCount = 16
export yAxisOffset = 0.5
export drillAndSlotCount = 5
// Car Tire
export tireInnerDiameter = 19
export tireOuterDiameter = 24
export tireDepth = 11.02
export bendRadius = 1.6
export tireTreadWidth = 0.39
export tireTreadDepth = 0.39
export tireTreadOffset = 3.15
// Brake Caliper
export caliperTolerance = 0.050
export caliperPadLength = 1.6
export caliperThickness = 0.39
export caliperOuterEdgeRadius = 0.39
export caliperInnerEdgeRadius = 0.12

View File

@ -0,0 +1,42 @@
// Lug Nut
// lug Nuts are essential components used to create secure connections, whether for electrical purposes, like terminating wires or grounding, or for mechanical purposes, such as providing mounting points or reinforcing structural joints.
// Set units
@settings(defaultLengthUnit = in)
// Import Constants
import lugDiameter, lugHeadLength, lugThreadDiameter, lugLength, lugThreadDepth, lugSpacing from "globals.kcl"
customPlane = {
plane = {
origin = {
x = lugSpacing / 2,
y = -30 * mm(),
z = 0
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = -1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
fn lug(plane, length, diameter) {
lugSketch = startSketchOn(customPlane)
|> startProfileAt([0 + diameter / 2, 0], %)
|> angledLineOfYLength({ angle = 70, length = lugHeadLength }, %)
|> xLineTo(lugDiameter / 2, %)
|> yLineTo(lugLength, %)
|> tangentialArc({ offset = 90, radius = 3 * mm() }, %)
|> xLineTo(0 + .001, %, $c1)
|> yLineTo(lugThreadDepth, %)
|> xLineTo(lugThreadDiameter, %)
|> yLineTo(0, %)
|> close()
|> revolve({ axis = "Y" }, %)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
return lugSketch
}
lug(customPlane, lugLength, lugDiameter)

View File

@ -0,0 +1,25 @@
// Car Wheel Assembly
// A car wheel assembly with a rotor, tire, and lug nuts.
// Set units
@settings(defaultLengthUnit = in)
import 'car-wheel.kcl' as carWheel
import 'car-rotor.kcl' as carRotor
import "brake-caliper.kcl" as brakeCaliper
import 'lug-nut.kcl' as lugNut
import 'car-tire.kcl' as carTire
import lugCount from 'globals.kcl'
carRotor
carWheel
lugNut
|> patternCircular3d(
arcDegrees = 360,
axis = [0, 1, 0],
center = [0, 0, 0],
instances = lugCount,
rotateDuplicates = false
)
brakeCaliper
carTire

View File

@ -0,0 +1,47 @@
// Color Cube
// This is a color cube centered about the origin. It is used to help determine orientation in the scene.
// Set unit
@settings(defaultLengthUnit = mm)
// Globals referenced in drawRectangle
size = 100
halfSize = size/2
extrudeLength = 1.0
metalConstant = 50
roughnessConstant = 50
// Create planes for 6 sides of a cube
bluePlane = offsetPlane('XY', offset = halfSize)
yellowPlane = offsetPlane('XY', offset = -halfSize)
greenPlane = offsetPlane('XZ', offset = -halfSize)
purplePlane = offsetPlane('-XZ', offset = -halfSize)
redPlane = offsetPlane('YZ', offset = halfSize - extrudeLength)
tealPlane = offsetPlane('YZ', offset = -halfSize)
// Sketch a rectangle centered at the origin of the profile
fn sketchRectangle (profile, color) {
return profile
|> startProfileAt([-halfSize, halfSize], %)
|> angledLine([0, size], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
size
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(%, length = extrudeLength)
|> appearance(color = color, metalness = metalConstant, roughness = roughnessConstant)
}
// Sketch each side of the cube
sketchRectangle(bluePlane,'#0000FF')
sketchRectangle(yellowPlane,'#FFFF00')
sketchRectangle(greenPlane,'#00FF00')
sketchRectangle(redPlane,'#FF0000')
sketchRectangle(tealPlane,'#00FFFF')
sketchRectangle(purplePlane,'#FF00FF')

View File

@ -0,0 +1,51 @@
// Cycloidal Gear
// A cycloidal gear is a gear with a continuous, curved tooth profile. They are used in watchmaking and high precision robotics actuation
// Set Units
@settings(defaultLengthUnit = in)
fn cycloidalGear(gearPitch, gearHeight, holeDiameter, helixAngle) {
// Create a function to draw the gear profile as a sketch. Rotate each profile about the gear's axis by an helix angle proportional to the total gear height
fn gearSketch(gHeight) {
helixAngleP = helixAngle * gHeight / gearHeight
gearProfile = startSketchOn(offsetPlane("XY", offset = gHeight))
|> startProfileAt([
gearPitch * 1.55 * cos(toRadians(helixAngleP)) + gearPitch * sin(toRadians(-helixAngleP)),
gearPitch * 1.55 * sin(toRadians(helixAngleP)) + gearPitch * cos(toRadians(-helixAngleP))
], %)
|> arc({
angleStart = 90 + helixAngleP,
angleEnd = -90 + helixAngleP,
radius = gearPitch
}, %)
|> tangentialArc({
radius = gearPitch * 1.67,
offset = 60
}, %)
|> tangentialArc({ radius = gearPitch, offset = -180 }, %)
|> tangentialArc({
radius = gearPitch * 1.67,
offset = 60
}, %)
|> tangentialArc({ radius = gearPitch, offset = -180 }, %)
|> tangentialArcTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> hole(circle(
center = [0, 0],
radius = holeDiameter / 2
), %)
return gearProfile
}
// Draw sketches of the gear profile along the gear height and loft them together
gearLoft = loft([
gearSketch(0),
gearSketch(gearHeight / 2),
gearSketch(gearHeight)
])
return gearLoft
}
cycloidalGear(.3, 1.5, 0.297, -80)

View File

@ -0,0 +1,90 @@
// Hollow Dodecahedron
// A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards.
// Set units
@settings(defaultLengthUnit = in)
// Input parameters
// circumscribed radius
circR = 25
// Calculated parameters
// thickness of the dodecahedron
wallThickness = circR * 0.2
// angle between faces in radians
dihedral = acos(-(sqrt(5) / 5))
// inscribed radius
inscR = circR / 15 * sqrt(75 + 30 * sqrt(5))
// pentagon edge length
edgeL = 4 * circR / (sqrt(3) * (1 + sqrt(5)))
// pentagon radius
pentR = edgeL / 2 / sin(toRadians(36))
// Define a plane for the bottom angled face
plane = {
plane = {
origin = [
-inscR * cos(toRadians(toDegrees(dihedral) - 90)),
0,
inscR - (inscR * sin(toRadians(toDegrees(dihedral) - 90)))
],
xAxis = [cos(dihedral), 0.0, sin(dihedral)],
yAxis = [0, 1, 0],
zAxis = [sin(dihedral), 0, -cos(dihedral)]
}
}
// Create a regular pentagon inscribed in a circle of radius pentR
bottomFace = startSketchOn('XY')
|> polygon({
radius = pentR,
numSides = 5,
center = [0, 0],
inscribed = true
}, %)
bottomSideFace = startSketchOn(plane)
|> polygon({
radius = pentR,
numSides = 5,
center = [0, 0],
inscribed = true
}, %)
// Extrude the faces in each plane
bottom = extrude(bottomFace, length = wallThickness)
bottomSide = extrude(bottomSideFace, length = wallThickness)
// Pattern the sides so we have a full dodecahedron
bottomBowl = patternCircular3d(
bottomSide,
instances = 5,
axis = [0, 0, 1],
center = [0, 0, 0],
arcDegrees = 360,
rotateDuplicates = true
)
// pattern the bottom to create the top face
patternCircular3d(
bottom,
instances = 2,
axis = [0, 1, 0],
center = [0, 0, inscR],
arcDegrees = 360,
rotateDuplicates = true
)
// pattern the bottom angled faces to create the top
patternCircular3d(
bottomBowl,
instances = 2,
axis = [0, 1, 0],
center = [0, 0, inscR],
arcDegrees = 360,
rotateDuplicates = true
)

View File

@ -0,0 +1,196 @@
// Enclosure
// An enclosure body and sealing lid for storing items
// Set units
@settings(defaultLengthUnit = mm)
length = 175
width = 125
height = 70
wallThickness = 3
holeDia = 4
// Model a box with base enclosure dimensions
sketch001 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> angledLine([0, width], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) + 90,
length
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001)
|> close()
extrude001 = extrude(sketch001, length = height)
|> fillet(
radius = wallThickness * 4,
tags = [
getNextAdjacentEdge(rectangleSegmentA001),
getNextAdjacentEdge(rectangleSegmentB001),
getNextAdjacentEdge(rectangleSegmentC001),
getNextAdjacentEdge(rectangleSegmentD001)
]
)
// Apply a shell to the enclosure base to create the internal storage
|> shell(
faces = ["end"],
thickness = wallThickness
)
// Define a function to create the internal structure to secure a fastener at each corner
fn function001(originStart) {
// Create a plane to sketch on shell interior
plane001 = {
plane = {
origin = [0.0, 0.0, wallThickness],
xAxis = [1.0, 0.0, 0.0],
yAxis = [0.0, 1.0, 0.0],
zAxis = [0.0, 0.0, 1.0]
}
}
// Create a pillar with a fasterner hole at the center
sketch002 = startSketchOn(plane001)
|> circle(
center = [originStart[0], originStart[1]],
radius = holeDia + wallThickness
)
|> hole(circle(
center = [originStart[0], originStart[1]],
radius = holeDia
), %)
extrude002 = extrude(sketch002, length = height - wallThickness)
return extrude002
}
// Place the internal pillar at each corner
function001([
wallThickness * 3 + holeDia,
wallThickness * 3 + holeDia
])
function001([
wallThickness * 3 + holeDia,
length - (wallThickness * 3 + holeDia)
])
function001([
width - (wallThickness * 3 + holeDia),
wallThickness * 3 + holeDia
])
function001([
width - (wallThickness * 3 + holeDia),
length - (wallThickness * 3 + holeDia)
])
// Define lid position and outer surface
sketch003 = startSketchOn('XY')
|> startProfileAt([width * 1.2, 0], %)
|> angledLine([0, width], %, $rectangleSegmentA002)
|> angledLine([
segAng(rectangleSegmentA001) + 90,
length
], %, $rectangleSegmentB002)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC002)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD002)
|> close()
|> hole(circle(
center = [
width * 1.2 + wallThickness * 3 + holeDia,
wallThickness * 3 + holeDia
],
radius = holeDia
), %)
|> hole(circle(
center = [
width * 1.2 + wallThickness * 3 + holeDia,
length - (wallThickness * 3 + holeDia)
],
radius = holeDia
), %)
|> hole(circle(
center = [
width * 2.2 - (wallThickness * 3 + holeDia),
wallThickness * 3 + holeDia
],
radius = holeDia
), %)
|> hole(circle(
center = [
width * 2.2 - (wallThickness * 3 + holeDia),
length - (wallThickness * 3 + holeDia)
],
radius = holeDia
), %)
extrude003 = extrude(sketch003, length = wallThickness)
|> fillet(
radius = wallThickness * 4,
tags = [
getNextAdjacentEdge(rectangleSegmentA002),
getNextAdjacentEdge(rectangleSegmentB002),
getNextAdjacentEdge(rectangleSegmentC002),
getNextAdjacentEdge(rectangleSegmentD002)
]
)
// Define lid inner and sealing surfaces
sketch004 = startSketchOn(extrude003, 'END')
|> startProfileAt([
width * 1.2 + wallThickness,
wallThickness
], %)
|> angledLine([0, width - (2 * wallThickness)], %, $rectangleSegmentA003)
|> angledLine([
segAng(rectangleSegmentA003) + 90,
length - (2 * wallThickness)
], %, $rectangleSegmentB003)
|> angledLine([
segAng(rectangleSegmentA003),
-segLen(rectangleSegmentA003)
], %, $rectangleSegmentC003)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD003)
|> close()
|> hole(circle(
center = [
width * 1.2 + wallThickness * 3 + holeDia,
wallThickness * 3 + holeDia
],
radius = holeDia + wallThickness
), %)
|> hole(circle(
center = [
width * 1.2 + wallThickness * 3 + holeDia,
length - (wallThickness * 3 + holeDia)
],
radius = holeDia + wallThickness
), %)
|> hole(circle(
center = [
width * 2.2 - (wallThickness * 3 + holeDia),
wallThickness * 3 + holeDia
],
radius = holeDia + wallThickness
), %)
|> hole(circle(
center = [
width * 2.2 - (wallThickness * 3 + holeDia),
length - (wallThickness * 3 + holeDia)
],
radius = holeDia + wallThickness
), %)
extrude004 = extrude(sketch004, length = wallThickness)
|> fillet(
radius = wallThickness * 3,
tags = [
getNextAdjacentEdge(rectangleSegmentA003),
getNextAdjacentEdge(rectangleSegmentB003),
getNextAdjacentEdge(rectangleSegmentC003),
getNextAdjacentEdge(rectangleSegmentD003)
]
)

View File

@ -0,0 +1,152 @@
// Exhaust Manifold
// A welded exhaust header for an inline 4-cylinder engine
// Set Units
@settings(defaultLengthUnit = in)
// Define Constants
primaryTubeDiameter = 1.625
wallThickness = 0.080
plateHeight = 0.125
bendRadius = 3
// Create a function to draw each primary tube with specified lengths and angles
fn primaryTube(n, angle001, length001, length002, length003) {
// Create an index for the function
pos001 = n * 2
// Define a plane for each sweep path defined by an angle
sweepPlane = {
plane = {
origin = [pos001, 0.0, 0],
xAxis = [
sin(toRadians(-angle001)),
cos(toRadians(-angle001)),
0.0
],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}
// Draw a path for each sweep
sweepPath = startSketchOn(sweepPlane)
|> startProfileAt([0, plateHeight], %)
|> line(end = [0, length001])
|> tangentialArc({ offset = -80, radius = bendRadius }, %, $arc01)
|> angledLine({
angle = tangentToEnd(arc01),
length = length002
}, %)
|> tangentialArc({ offset = 85, radius = bendRadius }, %, $arc02)
|> angledLine({
angle = tangentToEnd(arc02),
length = length003
}, %)
// Create the cross section of each tube and sweep them
sweepProfile = startSketchOn('XY')
|> circle(
center = [pos001, 0],
radius = primaryTubeDiameter / 2
)
|> hole(circle(
center = [pos001, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
|> sweep(path = sweepPath)
return { }
}
// Draw a primary tube for each cylinder with specified lengths and angles
primaryTube(0, 0, 3, 6, 5)
primaryTube(1, 1, 3, 6, 5)
primaryTube(2, 24.3, 5, 5, 3)
primaryTube(3, 25.2, 5, 5, 3)
// Create the mounting flange for the header
flangeSketch = startSketchOn('XY')
|> startProfileAt([3 + 1.3, -1.25], %)
|> xLine(-2.6, %, $seg01)
|> tangentialArc({ radius = .3, offset = -40 }, %)
|> tangentialArc({ radius = .9, offset = 80 }, %)
|> tangentialArc({ radius = .3, offset = -40 }, %)
|> xLine(-1.4, %, $seg03)
|> yLine(segLen(seg01), %, $seg04)
|> xLine(3.1, %, $seg05)
|> tangentialArc({ radius = .3, offset = -40 }, %)
|> tangentialArc({ radius = 1.5, offset = 80 }, %)
|> tangentialArc({ radius = .3, offset = -40 }, %)
|> xLine(segLen(seg05), %, $seg07)
|> yLineTo(profileStartY(%), %, $seg08)
|> xLine(-segLen(seg03), %, $seg09)
|> tangentialArc({ radius = .3, offset = -40 }, %)
|> tangentialArc({ radius = .9, offset = 80 }, %)
|> tangentialArcTo([profileStartX(%), profileStartY(%)], %)
|> close()
// Create openings in the flange to accommodate each tube
|> hole(circle(
center = [0, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
|> hole(circle(
center = [2, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
|> hole(circle(
center = [4, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
|> hole(circle(
center = [6, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
// Add mounting holes to the flange
|> hole(circle(
center = [
-primaryTubeDiameter * .6,
-primaryTubeDiameter * .6
],
radius = 0.25 / 2
), %)
|> hole(circle(
center = [
primaryTubeDiameter * .6,
primaryTubeDiameter * .6
],
radius = 0.25 / 2
), %)
|> hole(circle(
center = [
3 * 2 - (primaryTubeDiameter * .6),
primaryTubeDiameter * .6
],
radius = 0.25 / 2
), %)
|> hole(circle(
center = [
3 * 2 + primaryTubeDiameter * .6,
-primaryTubeDiameter * .6
],
radius = 0.25 / 2
), %)
// Extrude the flange and fillet the edges
|> extrude(length = plateHeight)
|> fillet(
radius = 1.5,
tags = [
getNextAdjacentEdge(seg04),
getNextAdjacentEdge(seg07)
]
)
|> fillet(
radius = .25,
tags = [
getNextAdjacentEdge(seg03),
getNextAdjacentEdge(seg08)
]
)

View File

@ -0,0 +1,83 @@
// Flange
// A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
mountingHoleDia = .625
baseDia = 4.625
pipeDia = 1.25
thickness = .625
totalThickness = 0.813
topTotalDiameter = 2.313
bottomThickness = 0.06
bottomTotalDiameter = 2.5
mountingHolePlacementDiameter = 3.5
baseThickness = .625
topTotalThickness = totalThickness - (bottomThickness + baseThickness)
holeLocator = baseDia - 8
nHoles = 4
// Add assertion so nHoles are always greater than 1
assertGreaterThan(nHoles, 1, "nHoles must be greater than 1")
// Create the circular pattern for the mounting holes
circles = startSketchOn('XY')
|> circle(
center = [mountingHolePlacementDiameter / 2, 0],
radius = mountingHoleDia / 2
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = nHoles,
rotateDuplicates = true
)
// Create the base of the flange and add the mounting holes
flangeBase = startSketchOn('XY')
|> circle(
center = [0, 0],
radius = baseDia / 2
)
|> hole(circles, %)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = baseThickness)
// Plane for top face
topFacePlane = {
plane = {
origin = { x = 0, y = 0, z = baseThickness },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Create the extrusion on the top of the flange base
topExtrusion = startSketchOn(topFacePlane)
|> circle(
center = [0, 0],
radius = topTotalDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = topTotalThickness)
// Create the extrusion on the bottom of the flange base
bottomExtrusion = startSketchOn("XY")
|> circle(
center = [0, 0],
radius = bottomTotalDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = -bottomThickness)

View File

@ -0,0 +1,87 @@
// Flange with XY coordinates
// A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
mountingHoleDia = .625
baseDia = 4.625
pipeDia = 1.25
thickness = .625
totalThickness = 0.813
topTotalDiameter = 2.313
bottomThickness = 0.06
bottomTotalDiameter = 2.5
mountingHolePlacementDiameter = 3.5
baseThickness = .625
topTotalThickness = totalThickness - (bottomThickness + baseThickness)
holeLocator = baseDia - 8
nHoles = 4
// Add assertion so nHoles are always greater than 1
assertGreaterThan(nHoles, 1, "nHoles must be greater than 1")
// Create the flange base and the six mounting holes
flangeBase = startSketchOn('XY')
|> circle(
center = [0, 0],
radius = baseDia / 2
)
|> hole(circle(
center = [mountingHolePlacementDiameter / 2, 0],
radius = mountingHoleDia / 2
), %)
|> hole(circle(
center = [0, mountingHolePlacementDiameter / 2],
radius = mountingHoleDia / 2
), %)
|> hole(circle(
center = [-mountingHolePlacementDiameter / 2, 0],
radius = mountingHoleDia / 2
), %)
|> hole(circle(
center = [0, -mountingHolePlacementDiameter / 2],
radius = mountingHoleDia / 2
), %)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = baseThickness)
// Plane for top face
topFacePlane = {
plane = {
origin = { x = 0, y = 0, z = baseThickness },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Create the extrusion on the top of the flange base
topExtrusion = startSketchOn(topFacePlane, 'end')
|> circle(
center = [0, 0],
radius = topTotalDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = topTotalThickness)
// Create the extrusion on the bottom of the flange base
bottomExtrusion = startSketchOn("XY")
|> circle(
center = [0, 0],
radius = bottomTotalDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = pipeDia / 2
), %)
|> extrude(length = -bottomThickness)
// https://www.mcmaster.com/44685K193/

View File

@ -0,0 +1,159 @@
// A mounting bracket for the Focusrite Scarlett Solo audio interface
// This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material
// Set units
@settings(defaultLengthUnit = mm)
// define constants in mm
radius = 6.0
width = 144.0
length = 80.0
depth = 45.0
thk = 4
holeDiam = 5
tabLength = 25
tabWidth = 12
tabThk = 4
// define a rectangular shape func
fn rectShape(pos, w, l) {
rr = startSketchOn('xy')
|> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)
|> line(endAbsolute = [pos[0] + w / 2, pos[1] - (l / 2)], tag = $edge01)
|> line(endAbsolute = [pos[0] + w / 2, pos[1] + l / 2], tag = $edge02)
|> line(endAbsolute = [pos[0] - (w / 2), pos[1] + l / 2], tag = $edge03)
|> close(tag = $edge04)
return rr
}
// define the bracket plane
bracketPlane = {
plane = {
origin = { x = 0, y = length / 2 + thk, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = -1, z = 0 }
}
}
// build the bracket sketch around the body
fn bracketSketch(w, d, t) {
s = startSketchOn(bracketPlane)
|> startProfileAt([-w / 2 - t, d + t], %)
|> line(endAbsolute = [-w / 2 - t, -t], tag = $edge1)
|> line(endAbsolute = [w / 2 + t, -t], tag = $edge2)
|> line(endAbsolute = [w / 2 + t, d + t], tag = $edge3)
|> line(endAbsolute = [w / 2, d + t], tag = $edge4)
|> line(endAbsolute = [w / 2, 0], tag = $edge5)
|> line(endAbsolute = [-w / 2, 0], tag = $edge6)
|> line(endAbsolute = [-w / 2, d + t], tag = $edge7)
|> close(tag = $edge8)
return s
}
// build the body of the bracket
bs = bracketSketch(width, depth, thk)
bracketBody = bs
|> extrude(length = length + 2 * thk)
|> fillet(
radius = radius,
tags = [
getPreviousAdjacentEdge(bs.tags.edge7),
getPreviousAdjacentEdge(bs.tags.edge2),
getPreviousAdjacentEdge(bs.tags.edge3),
getPreviousAdjacentEdge(bs.tags.edge6)
]
)
// define the tab plane
tabPlane = {
plane = {
origin = { x = 0, y = 0, z = depth + thk },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// build the tabs of the mounting bracket (right side)
tabsR = startSketchOn(tabPlane)
|> startProfileAt([width / 2 + thk, length / 2 + thk], %)
|> line(end = [tabWidth, -tabLength / 3], tag = $edge11)
|> line(end = [0, -tabLength / 3 * 2], tag = $edge12)
|> line(end = [-tabWidth, -tabLength / 3], tag = $edge13)
|> close(tag = $edge14)
|> hole(circle(
center = [
width / 2 + thk + tabWidth / 2,
length / 2 + thk - (tabLength / (3 / 2))
],
radius = holeDiam / 2
), %)
|> extrude(length = -tabThk)
|> fillet(
radius = holeDiam / 2,
tags = [
getNextAdjacentEdge(edge11),
getNextAdjacentEdge(edge12)
]
)
|> patternLinear3d(
axis = [0, -1, 0],
instances = 2,
distance = length + 2 * thk - (tabLength * 4 / 3)
)
// build the tabs of the mounting bracket (left side)
tabsL = startSketchOn(tabPlane)
|> startProfileAt([-width / 2 - thk, length / 2 + thk], %)
|> line(end = [-tabWidth, -tabLength / 3], tag = $edge21)
|> line(end = [0, -tabLength / 3 * 2], tag = $edge22)
|> line(end = [tabWidth, -tabLength / 3], tag = $edge23)
|> close(tag = $edge24)
|> hole(circle(
center = [
-width / 2 - thk - (tabWidth / 2),
length / 2 + thk - (tabLength / (3 / 2))
],
radius = holeDiam / 2
), %)
|> extrude(length = -tabThk)
|> fillet(
radius = holeDiam / 2,
tags = [
getNextAdjacentEdge(edge21),
getNextAdjacentEdge(edge22)
]
)
|> patternLinear3d(
axis = [0, -1, 0],
instances = 2,
distance = length + 2 * thk - (tabLength * 4 / 3)
)
// define a plane for retention bumps
retPlane = {
plane = {
origin = { x = -width / 2 + 20, y = 0, z = 0 },
xAxis = { x = 0, y = 1, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 1, y = 0, z = 0 }
}
}
// build the retention bump in the front
retFront = startSketchOn(retPlane)
|> startProfileAt([-length / 2 - thk, 0], %)
|> line(end = [0, thk])
|> line(end = [thk, -thk])
|> close()
|> extrude(length = width - 40)
// build the retention bump in the back
retBack = startSketchOn(retPlane)
|> startProfileAt([length / 2 + thk, 0], %)
|> line(end = [0, thk])
|> line(end = [-thk, 0])
|> line(end = [0, -thk])
|> close()
|> extrude(length = width - 40)

View File

@ -0,0 +1,166 @@
// Food Service Spatula
// Use these spatulas for mixing, flipping, and scraping.
// Set units
@settings(defaultLengthUnit = mm)
// Define constants in millimeters (mm)
flipperThickness = 3.5
flipperLength = 70.0
handleWidth = 15.0
gripLength = 150.0
flipperFilletRadius = 5.0
flipperSlotWidth = 10.0
gripWidth = 10.0
gripHeight = 20.0
gripFilletRadius = 3.0
gripSlotWidth = 8.0
// function for drawing slots on a sketch given the start and end points as well as a width
fn slot(sketch1, start, end, width) {
angle = if start[0] == end[0] {
if end[1] > start[1] {
90
} else {
270
}
} else {
if end[0] < start[0] {
toDegrees(atan((end[1] - start[1]) / (end[0] - start[0]))) + 180
} else {
toDegrees( atan((end[1] - start[1]) / (end[0] - start[0])))
}
}
dist = sqrt(pow(end[1] - start[1], 2) + pow(end[0] - start[0], 2))
xstart = width / 2 * cos(toRadians(angle - 90)) + start[0]
ystart = width / 2 * sin(toRadians(angle - 90)) + start[1]
slotSketch = startProfileAt([xstart, ystart], sketch1)
|> angledLine({ angle = angle, length = dist }, %, $line000)
|> tangentialArc({ radius = width / 2, offset = 180 }, %, $arc000)
|> angledLine({ angle = angle, length = -dist }, %, $line001)
|> tangentialArcTo([profileStartX(%), profileStartY(%)], %, $arc001)
|> close()
return slotSketch
}
// create a sketch on the "XY" plane
sketch000 = startSketchOn('XY')
// create a profile of the flipper
flipperProfile = startProfileAt([-flipperLength, -32.0], sketch000)
|> line(end = [flipperLength, 2.0])
|> yLine(60.0, %, $backEdge)
|> line(end = [-flipperLength, 2.0])
|> arc({
angleEnd = 196.912390,
angleStart = 163.087610,
radius = 110.0
}, %)
|> close()
// create a profile of the middle
slotProfile000 = slot(sketch000, [-25, 0], [-55, 0], flipperSlotWidth)
// create a profile of the top slot
slotProfile001 = slot(sketch000, [-25, 18], [-55, 19], flipperSlotWidth)
// create a profile of the bottom slot
slotProfile002 = slot(sketch000, [-25, -18], [-55, -19], flipperSlotWidth)
// create a profile with slots for the spatula
spatulaProfile = flipperProfile
|> hole(slotProfile000, %)
|> hole(slotProfile001, %)
|> hole(slotProfile002, %)
// extrude the profile to create the spatula flipper
flipper = extrude(spatulaProfile, length = flipperThickness)
// fillet the edges of the flipper
fillet(
flipper,
radius = flipperFilletRadius,
tags = [
getNextAdjacentEdge(backEdge),
getPreviousAdjacentEdge(backEdge)
]
)
// create a sketch on the "XZ" plane offset by half the thickness
sketch001 = startSketchOn(offsetPlane("XZ", offset = -handleWidth / 2))
// create a profile of the spatula handle
handleProfile = startProfileAt([0.0, flipperThickness], sketch001)
|> line(end = [31.819805, 31.819805], tag = $handleBottomEdge)
|> line(end = [140.953893, 51.303021])
|> line(end = [-1.710101, 4.698463])
|> line(end = [-141.995517, -51.682142], tag = $handleTopEdge)
|> line(end = [-36.139148, -36.139148])
|> xLine(7.071068, %)
|> close()
// create an extrusion extrude001
handle = extrude(handleProfile, length = handleWidth)
// fillet the bend of the spatula handle
fillet(
handle,
radius = 4,
tags = [
getNextAdjacentEdge(handleBottomEdge),
getNextAdjacentEdge(handleTopEdge)
]
)
// define a plane which is at the end of the handle
handlePlane = {
plane = {
origin = [208.593833, 0.0, 75.921946],
xAxis = [0.342020, -0.0, -0.939693],
yAxis = [0.0, 1.0, 0.0],
zAxis = [0.939693, -0.0, 0.342020]
}
}
// create a sketch on the handle plane
sketch002 = startSketchOn(handlePlane)
// create a profile of the grip
gripProfile = startProfileAt([-26.806746, -10.0], sketch002)
|> xLine(gripWidth - (2 * gripFilletRadius), %)
|> arc({
angleStart = -90.0,
angleEnd = 0.0,
radius = gripFilletRadius
}, %)
|> yLine(gripHeight - (2 * gripFilletRadius), %)
|> arc({
angleStart = 0.0,
angleEnd = 90.0,
radius = gripFilletRadius
}, %)
|> xLine(-(gripWidth - (2 * gripFilletRadius)), %)
|> arc({
angleStart = 90.0,
angleEnd = 180.0,
radius = gripFilletRadius
}, %)
|> yLine(-(gripHeight - (2 * gripFilletRadius)), %, $gripEdgeTop)
|> arc({
angleStart = 180.0,
angleEnd = 270.0,
radius = gripFilletRadius
}, %)
|> close()
// extrude the grip profile to create the grip
grip = extrude(gripProfile, length = -gripLength)
// create a sketch on the grip for the hole
sketch003 = startSketchOn(grip, gripEdgeTop)
// create a profile for the grip hole
gripHoleProfile = slot(sketch003, [0, 200], [0, 210], gripSlotWidth)
// cut a hole in the grip
extrude(gripHoleProfile, length = -gripWidth-20)

View File

@ -0,0 +1,231 @@
// French Press
// A french press immersion coffee maker
// Set units
@settings(defaultLengthUnit = in)
// Define constants
carafeDiameter = 4.41
carafeHeight = 7.32
handleThickness = 0.65
// Upper ring of the metal structure
sketch001 = startSketchOn('XZ')
|> startProfileAt([carafeDiameter / 2, 5.7], %)
|> angledLine([0, 0.1], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
-0.75
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ angle = 360, axis = 'Y' }, %)
// Create an angled plane to sketch the supports
plane001 = {
plane = {
origin = [-0.26, 0.26, 0.0],
xAxis = [1, 1, 0.0],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}
// Cross section of the metal supports
sketch002 = startSketchOn(plane001)
|> startProfileAt([carafeDiameter / 2, 5.7], %)
|> xLine(0.1, %)
|> yLine(-5.2, %, $edge1)
|> arc({
angleStart = 180,
angleEnd = 205,
radius = 0.3
}, %)
|> angledLine({ angle = -60, length = 0.6 }, %, $edge2)
|> arc({
angleStart = 30,
angleEnd = -120,
radius = 0.6
}, %)
|> angledLineToY({ angle = 150, to = -0.2 }, %, $edge3)
|> arc({
angleStart = 60,
angleEnd = 90,
radius = 0.5
}, %)
|> xLineTo(0.1, %, $edgeLen)
|> yLine(0.1, %)
|> xLine(segLen(edgeLen) + 0.035, %, $edge4)
|> arc({
angleStart = 90,
angleEnd = 60,
radius = 0.6
}, %)
|> angledLine({
angle = 150,
length = -segLen(edge3) + 0.035
}, %, $edge5)
|> arc({
angleStart = -120,
angleEnd = 30,
radius = 0.5
}, %)
|> angledLine({
angle = -60,
length = -segLen(edge2) + 0.035
}, %, $edge6)
|> arc({
angleStart = 205,
angleEnd = 180,
radius = 0.6
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = 0.75)
|> patternCircular3d(
axis = [0, 0, 1],
center = [0, 0, 0],
instances = 4,
arcDegrees = 360,
rotateDuplicates = true
)
// Cross plate
sketch003 = startSketchOn(offsetPlane('XY', offset = 1))
|> circle(
center = [0, 0],
radius = carafeDiameter / 2 - 0.15
)
extrude001 = extrude(sketch003, length = 0.050)
sketch004 = startSketchOn(extrude001, 'END')
|> startProfileAt([0.3, 0.17], %)
|> yLine(1.2, %)
|> arc({
angleStart = 90,
angleEnd = -30,
radius = 1.2
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternCircular2d(
center = [0, 0],
instances = 3,
arcDegrees = 360,
rotateDuplicates = true
)
extrude002 = extrude(sketch004, length = -0.050)
// Filter screen
sketch005 = startSketchOn('XZ')
|> startProfileAt([0.15, 1.11], %)
|> xLineTo(carafeDiameter / 2 - 0.2, %)
|> angledLineToX({
angle = 30,
to = carafeDiameter / 2 - 0.07
}, %, $seg1)
|> angledLine({ angle = -60, length = 0.050 }, %)
|> angledLine({ angle = 30, length = -segLen(seg1) }, %)
|> xLineTo(0.15, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ axis = 'y' }, %)
// Plunger and stem
sketch006 = startSketchOn('XZ')
|> startProfileAt([0.1, 1], %)
|> line(end = [0.1, 0])
|> angledLineToX({ angle = 10, to = 0.05 }, %)
|> yLine(10, %)
|> line(end = [0.6, 0])
|> yLine(-.05, %)
|> tangentialArc({ radius = 0.6, offset = -90 }, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ axis = 'y' }, %)
// Spiral plate
sketch007 = startSketchOn(offsetPlane('XY', offset = 1.12))
|> circle(
center = [0, 0],
radius = carafeDiameter / 2 - 0.24
)
|> hole(circle(center = [0, 0], radius = .15), %)
extrude003 = extrude(sketch007, length = 0.050)
// Pattern holes in the spiral plate
sketch008 = startSketchOn(extrude003, 'END')
|> circle(center = [1.4, 0], radius = .3)
|> patternCircular2d(
center = [0, 0],
instances = 8,
arcDegrees = 360,
rotateDuplicates = true
)
extrude004 = extrude(sketch008, length = -0.050)
// Pattern holes in the spiral plate
sketch009 = startSketchOn(extrude003, 'END')
|> circle(center = [0.6, 0], radius = .2)
|> patternCircular2d(
center = [0, 0],
instances = 4,
arcDegrees = 360,
rotateDuplicates = true
)
extrude005 = extrude(sketch009, length = -0.050)
// Extrude a glass carafe body
sketch010 = startSketchOn("XY")
|> circle(
center = [0, 0],
radius = carafeDiameter / 2
)
// Perform a shell operation to hollow the carafe body with the top face removed
extrude006 = extrude(sketch010, length = carafeHeight)
|> shell(faces = ["end"], thickness = .07)
// Draw and revolve the lid
sketch011 = startSketchOn('XZ')
|> startProfileAt([0.2, carafeHeight - 0.7], %)
|> xLine(carafeDiameter / 2 - 0.3, %)
|> yLine(0.7, %)
|> xLine(0.3, %)
|> yLine(0.4, %)
|> line(end = [-0.02, 0.02])
|> bezierCurve({
to = [-carafeDiameter / 2 - 0.1, 1],
control1 = [-0.3, 0],
control2 = [carafeDiameter / 10, 1]
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> revolve({ axis = 'y' }, %)
// Draw and extrude handle
sketch012 = startSketchOn(offsetPlane('XZ', offset = handleThickness / 2))
|> startProfileAt([2.3, 6.4], %)
|> line(end = [0.56, 0])
|> tangentialArcTo([4.1, 5.26], %)
|> tangentialArcTo([4.17, 1.6], %)
|> tangentialArcTo([3.13, 0.61], %)
|> line(end = [-1.09, 0])
|> line(end = [0, 0.43])
|> line(end = [0.99, -0.02])
|> tangentialArcTo([3.63, 1.6], %)
|> tangentialArcTo([3.56, 5.15], %)
|> tangentialArcTo([2.72, 5.88], %)
|> line(end = [-0.4, 0])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude007 = extrude(sketch012, length = -handleThickness)

View File

@ -0,0 +1,63 @@
// 100mm Gear Rack
// A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate.
// Set Units
@settings(defaultLengthUnit = mm)
// Define constants
length = 100
pitchHeight = 11.5
width = 5
height = 12
minHeight = 10.875
// Create the body of the rack
rackBody = startSketchOn('XY')
|> startProfileAt([-length / 2, 0], %)
|> line(end = [length, 0])
|> line(end = [0, minHeight])
|> line(end = [-length, 0])
|> close()
|> extrude(length = width)
// Create a function for sketch of a single tooth
fn tooth() {
toothSketch = startSketchOn('XY')
|> startProfileAt([-length / 2 + 0.567672, minHeight], %)
|> tangentialArcToRelative([0.157636, 0.110378], %)
|> line(end = [0.329118, 0.904244])
|> tangentialArcToRelative([0.157636, 0.110378], %)
|> line(end = [0.186505, 0])
|> tangentialArcToRelative([0.157636, -0.110378], %)
|> line(end = [0.329118, -0.904244])
|> tangentialArcToRelative([0.157636, -0.110378], %)
|> close()
|> extrude(length = width)
return toothSketch
}
// Pattern the single tooth over the length of the rack body
teeth = tooth()
|> patternLinear3d(
axis = [10, 0, 0],
distance = 1.570796,
instances = 63
)
// Sketch and extrude the first end cap. This is a partial tooth
endCapTooth = startSketchOn('XY')
|> startProfileAt([-length / 2, 11.849525], %)
|> line(end = [0.314524, -0.864147])
|> tangentialArcToRelative([0.157636, -0.110378], %)
|> line(endAbsolute = [-length / 2, minHeight])
|> close()
|> extrude(length = width)
// Sketch and extrude the second end cap. This is a partial tooth
endCapTooth2 = startSketchOn('XY')
|> startProfileAt([length / 2, 11.849525], %)
|> line(end = [-0.314524, -0.864147])
|> tangentialArcToRelative([-0.157636, -0.110378], %)
|> line(endAbsolute = [length / 2, minHeight])
|> close()
|> extrude(length = width)

View File

@ -0,0 +1,112 @@
// Spur Gear
// A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants
nTeeth = 21
module = 0.5
pitchDiameter = module * nTeeth
pressureAngle = 20
addendum = module
deddendum = 1.25 * module
baseDiameter = pitchDiameter * cos(toRadians(pressureAngle))
tipDiameter = pitchDiameter + 2 * module
gearHeight = 3
// Interpolate points along the involute curve
cmo = 101
rs = map([0..cmo], fn (i) {
return baseDiameter / 2 + i / cmo * (tipDiameter - baseDiameter) / 2
})
// Calculate operating pressure angle
angles = map(rs, fn (r) {
return toDegrees( acos(baseDiameter / 2 / r))
})
// Calculate the involute function
invas = map(angles, fn (a) {
return tan(toRadians(a)) - toRadians(a)
})
// Map the involute curve
xs = map([0..cmo], fn (i) {
return rs[i] * cos(invas[i])
})
ys = map([0..cmo], fn (i) {
return rs[i] * sin(invas[i])
})
// Extrude the gear body
body = startSketchOn('XY')
|> circle(
center = [0, 0],
radius = baseDiameter / 2
)
|> extrude(length = gearHeight)
toothAngle = 360 / nTeeth / 1.5
// Plot the involute curve
fn leftInvolute(i, sg) {
j = 100 - i // iterate backwards
return line(sg, endAbsolute = [xs[j], ys[j]])
}
fn rightInvolute(i, sg) {
x = rs[i] * cos(toRadians(-toothAngle + toDegrees(atan(ys[i] / xs[i]))))
y = -rs[i] * sin(toRadians(-toothAngle + toDegrees(atan(ys[i] / xs[i]))))
return line(sg, endAbsolute = [x, y])
}
// Draw gear teeth
start = startSketchOn('XY')
|> startProfileAt([xs[101], ys[101]], %)
teeth = reduce([0..100], start, leftInvolute)
|> arc({
angleStart = 0,
angleEnd = toothAngle,
radius = baseDiameter / 2
}, %)
|> reduce([1..101], %, rightInvolute)
|> close()
|> extrude(length = gearHeight)
|> patternCircular3d(
axis = [0, 0, 1],
center = [0, 0, 0],
instances = nTeeth,
arcDegrees = 360,
rotateDuplicates = true
)
// Define the constants of the keyway and the bore hole
keywayWidth = 0.250
keywayDepth = keywayWidth / 2
holeDiam = 2
holeRadius = 1
startAngle = asin(keywayWidth / 2 / holeRadius)
// Sketch the keyway and center hole and extrude
keyWay = startSketchOn(body, 'END')
|> startProfileAt([
holeRadius * cos(startAngle),
holeRadius * sin(startAngle)
], %)
|> xLine(keywayDepth, %)
|> yLine(-keywayWidth, %)
|> xLine(-keywayDepth, %)
|> arc({
angleEnd = 180,
angleStart = -1 * 180 / PI * startAngle + 360,
radius = holeRadius
}, %)
|> arc({
angleEnd = 180 / PI * startAngle,
angleStart = 180,
radius = holeRadius
}, %)
|> close()
|> extrude(length = -gearHeight)

View File

@ -0,0 +1,66 @@
const fs = require("fs");
const path = require("path");
const FILE_EXTENSION = ".kcl";
const MANIFEST_FILE = "manifest.json";
const COMMENT_PREFIX = "//";
// Function to read and parse .kcl files
const getKclMetadata = (projectPath, files) => {
const primaryKclFile = files.find((file) => file.includes("main.kcl")) ?? files.sort()[0];
const fullPathToPrimaryKcl = path.join(projectPath, primaryKclFile);
const content = fs.readFileSync(fullPathToPrimaryKcl, "utf-8");
const lines = content.split("\n");
if (lines.length < 2) {
return null;
}
const title = lines[0].replace(COMMENT_PREFIX, "").trim();
const description = lines[1].replace(COMMENT_PREFIX, "").trim();
return {
file: primaryKclFile,
// Assumed to ALWAYS be 1 level deep. That's the current practice.
pathFromProjectDirectoryToFirstFile: fullPathToPrimaryKcl.split('/').splice(-2).join('/'),
// This was added so that multiple file project samples do not load in
// the web app through the manifest.
multipleFiles: files.length > 1,
title,
description,
};
};
// Function to scan the directory and generate the manifest.json
const generateManifest = (dir) => {
const projectDirectories = fs.readdirSync(dir);
const manifest = [];
projectDirectories.forEach((file) => {
const projectPath = path.join(dir, file);
const stattedDir = fs.statSync(projectPath);
if (stattedDir.isDirectory()) {
const files = fs
.readdirSync(projectPath)
.filter((f) => f.endsWith(FILE_EXTENSION));
if (files.length === 0) {
return;
}
const metadata = getKclMetadata(projectPath, files);
if (metadata) {
manifest.push(metadata);
}
}
});
// Write the manifest.json
const outputPath = path.join(dir, MANIFEST_FILE);
fs.writeFileSync(outputPath, JSON.stringify(manifest, null, 2));
console.log(`Manifest of ${manifest.length} items written to ${outputPath}`);
};
// Run the script
console.log(`Generating ${MANIFEST_FILE}...`);
const projectDir = path.resolve(__dirname); // Set project root directory
generateManifest(projectDir);

View File

@ -0,0 +1,225 @@
// Gridfinity Baseplate With Magnets
// Gridfinity is a system to help you work more efficiently. This is a system invented by Zack Freedman. There are two main components the baseplate and the bins. The components are comprised of a matrix of squares. Allowing easy stacking and expansion. This baseplate version includes holes for magnet placement
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
// Define constants
binLength = 42.0
cornerRadius = 4.0
firstStep = 0.7
secondStep = 1.8
thirdStep = 2.15
magOuterDiam = 6.5
magOffset = 4.8
magDepth = 2.4
// Number of bins in each direction
countBinWidth = 2
countBinLength = 3
// The total height of the baseplate is a summation of the vertical heights of the baseplate steps
height = firstStep + secondStep + thirdStep
// define a function which builds the profile of the baseplate bin
fn face(plane) {
faceSketch = startSketchOn(plane)
|> startProfileAt([0, 0], %)
|> yLine(height, %)
|> angledLineOfYLength({ angle = -45, length = thirdStep }, %)
|> yLine(-secondStep, %)
|> angledLineOfYLength({ angle = -45, length = firstStep }, %)
|> close()
return faceSketch
}
// extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius)), length = binLength - (cornerRadius * 2), )
// create the other sides of the bin by using a circular pattern
sides = patternCircular3d(
singleSide,
arcDegrees = 360,
axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0],
instances = 4,
rotateDuplicates = true
)
// define an axis axis000
axis000 = {
custom = {
axis = [0.0, 1.0],
origin = [cornerRadius, cornerRadius]
}
}
// create a single corner of the bin
singleCorner = revolve({ angle = -90, axis = axis000 }, face(offsetPlane("YZ", offset = cornerRadius)))
// create the corners of the bin
corners = patternCircular3d(
singleCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0],
instances = 4,
rotateDuplicates = true
)
// create the baseplate by patterning sides
basePlateSides = patternLinear3d(
sides,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)
// create the corners of the baseplate by patterning the corners
basePlateCorners = patternLinear3d(
corners,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)
// create the center cutout for the magnet profile
fn magnetCenterCutout(plane) {
magnetSketch = startSketchOn(plane)
|> startProfileAt([
firstStep + thirdStep,
2 * magOuterDiam
], %)
|> xLine(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2), %)
|> arc({
angleStart = 90.0,
angleEnd = 0.0,
radius = magOuterDiam / 2
}, %)
|> yLine(-(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2)), %)
|> xLine(binLength - (4 * magOuterDiam), %)
|> yLine(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2), %)
|> arc({
angleStart = 180.0,
angleEnd = 90.0,
radius = magOuterDiam / 2
}, %)
|> xLine(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2), %)
|> yLine(binLength - (4 * magOuterDiam), %)
|> xLine(-(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2)), %)
|> arc({
angleStart = 270.0,
angleEnd = 180.0,
radius = magOuterDiam / 2
}, %)
|> yLine(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2), %)
|> xLine(-(binLength - (4 * magOuterDiam)), %, $line012)
|> yLine(-(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2)), %)
|> arc({
angleStart = 360.0,
angleEnd = 270.0,
radius = magOuterDiam / 2
}, %)
|> xLine(-(2 * magOuterDiam - (firstStep + thirdStep) - (magOuterDiam / 2)), %)
|> yLine(-(binLength - (4 * magOuterDiam)), %)
|> close()
return magnetSketch
}
// create the outside profile of the magnets
fn magnetBase(plane) {
magnetBaseSketch = startSketchOn(plane)
|> startProfileAt([0, 0], %)
|> xLine(binLength, %, $line001)
|> yLine(binLength, %, $line002)
|> xLineTo(profileStartX(%), %, $line003)
|> close(tag = $line004)
|> hole(magnetCenterCutout(plane), %)
return magnetBaseSketch
}
// create sketch profile sketch000Profile002
magnetsSketch = startSketchOn('XY')
|> circle(
center = [cornerRadius * 2, cornerRadius * 2],
radius = magOuterDiam / 2
)
|> patternCircular2d(
center = [binLength / 2, binLength / 2],
instances = 4,
arcDegrees = 360,
rotateDuplicates = true
)
// create a profile with holes for the magnets
magnetProfile = magnetBase("XY")
|> hole(magnetsSketch, %)
// create an extrusion of the magnet cutout with holes
magnetHolesExtrude = extrude(magnetProfile, length = -magDepth)
// add a fillet to the extrusion
magnetHolesExtrudeFillets = fillet(
magnetHolesExtrude,
radius = cornerRadius,
tags = [
getNextAdjacentEdge(magnetHolesExtrude.sketch.tags.line001),
getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line001),
getNextAdjacentEdge(magnetHolesExtrude.sketch.tags.line003),
getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line003)
]
)
// create a profile without the holes for the magnets
magnetProfileNoMagnets = magnetBase(offsetPlane("XY", offset = -magDepth))
// create an extrusion of the magnet cutout without holes
magnetCutoutExtrude = extrude(magnetProfileNoMagnets, length = -magDepth)
// add a fillet to the extrusion
magnetCutoutExtrudeFillets = fillet(
magnetCutoutExtrude,
radius = cornerRadius,
tags = [
getNextAdjacentEdge(magnetCutoutExtrude.sketch.tags.line001),
getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line001),
getNextAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003),
getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003)
]
)
// pattern the magnet cutouts with holes
patternLinear3d(
magnetHolesExtrudeFillets,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)
// pattern the magnet cutouts without holes
patternLinear3d(
magnetCutoutExtrudeFillets,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)

View File

@ -0,0 +1,91 @@
// Gridfinity Baseplate
// Gridfinity is a system to help you work more efficiently. This is a system invented by Zack Freedman. There are two main components the baseplate and the bins. The components are comprised of a matrix of squares. Allowing easy stacking and expansion
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
// Define constants
binLength = 42.0
cornerRadius = 4.0
firstStep = 0.7
secondStep = 1.8
thirdStep = 2.15
// Number of bins in each direction
countBinWidth = 2
countBinLength = 3
// The total height of the baseplate is a summation of the vertical heights of the baseplate steps
height = firstStep + secondStep + thirdStep
// define a function which builds the profile of the baseplate bin
fn face(plane) {
faceSketch = startSketchOn(plane)
|> startProfileAt([0, 0], %)
|> yLine(height, %)
|> angledLineOfYLength({ angle = -45, length = thirdStep }, %)
|> yLine(-secondStep, %)
|> angledLineOfYLength({ angle = -45, length = firstStep }, %)
|> close()
return faceSketch
}
// extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern
sides = patternCircular3d(
singleSide,
arcDegrees = 360,
axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0],
instances = 4,
rotateDuplicates = true
)
// define an axis axis000
axis000 = {
custom = {
axis = [0.0, 1.0],
origin = [cornerRadius, cornerRadius]
}
}
// create a single corner of the bin
singleCorner = revolve({ angle = -90, axis = axis000 }, face(offsetPlane("YZ", offset = cornerRadius)))
// create the corners of the bin
corners = patternCircular3d(
singleCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0],
instances = 4,
rotateDuplicates = true
)
// create the baseplate by patterning sides
basePlateSides = patternLinear3d(
sides,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)
// create the corners of the baseplate by patterning the corners
basePlateCorners = patternLinear3d(
corners,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
)

View File

@ -0,0 +1,325 @@
// Gridfinity Bins With A Stacking Lip
// Gridfinity is a system to help you work more efficiently. This is a system invented by Zack Freedman. There are two main components the baseplate and the bins. The components are comprised of a matrix of squares. Allowing easy stacking and expansion. This Gridfinity bins version includes a lip to allowable stacking Gridfinity bins
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
// Define constants
binLength = 41.5
binHeight = 7.0
binBaseLength = 2.95
binTol = 0.25
binThk = 1.2
cornerRadius = 3.75
firstStep = 0.8
secondStep = 1.8
thirdStep = 2.15
magOuterDiam = 6.5
magOffset = 4.8
magDepth = 2.4
lipRadius = 0.5
lipStep1 = 1.4
lipStep2 = 1.2
lipStep3 = 0.7
lipStep4 = 1.8
lipStep5 = 1.9
// Number of bins in each direction
countBinWidth = 2
countBinLength = 3
countBinHeight = 1
// The total height of the baseplate is a summation of the vertical heights of the baseplate steps
height = firstStep + secondStep + thirdStep
lipHeight = lipStep1 + lipStep2 + lipStep3 + lipStep4 + lipStep5
// define a function which builds the profile of the baseplate bin
fn face(plane) {
faceSketch = startSketchOn(plane)
|> startProfileAt([binBaseLength + binTol, 0], %)
|> yLine(height, %)
|> xLine(-binBaseLength, %)
|> angledLineOfYLength({ angle = -45, length = thirdStep }, %)
|> yLine(-secondStep, %)
|> angledLineOfYLength({ angle = -45, length = firstStep }, %)
|> close()
return faceSketch
}
// extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern
sides = patternCircular3d(
singleSide,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2,
0
],
instances = 4,
rotateDuplicates = true
)
// define an axis axis000
axis000 = {
custom = {
axis = [0.0, 1.0],
origin = [
cornerRadius + binTol,
cornerRadius + binTol
]
}
}
// create a single corner of the bin
singleCorner = revolve({ angle = -90, axis = axis000 }, face(offsetPlane("YZ", offset = cornerRadius + binTol)))
// create the corners of the bin
corners = patternCircular3d(
singleCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2,
0
],
instances = 4,
rotateDuplicates = true
)
singleBinFill = startSketchOn("XY")
|> startProfileAt([
binBaseLength + binTol,
binBaseLength + binTol
], %)
|> line(end = [binLength - (binBaseLength * 2), 0], tag = $line000)
|> line(end = [0, binLength - (binBaseLength * 2)], tag = $line001)
|> xLineTo(profileStartX(%), %, $line002)
|> close(tag = $line003)
|> extrude(length = height)
|> fillet(
radius = firstStep,
tags = [
getNextAdjacentEdge(line000),
getPreviousAdjacentEdge(line000),
getNextAdjacentEdge(line002),
getPreviousAdjacentEdge(line002)
]
)
magCutout000 = startSketchOn(singleBinFill, "start")
|> circle(
center = [
-magOffset - binBaseLength - binTol,
magOffset + binBaseLength + binTol
],
radius = magOuterDiam / 2
)
|> patternCircular2d(
arcDegrees = 360,
center = [
(-binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2
],
instances = 4,
rotateDuplicates = true
)
|> extrude(length = -magDepth)
// create the baseplate by patterning sides
binSides = patternLinear3d(
sides,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
// create the corners of the baseplate by patterning the corners
binCorners = patternLinear3d(
corners,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
// create the fill of the bin by patterning the corners
binFill = patternLinear3d(
singleBinFill,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
//
binTop = startSketchOn(offsetPlane("XY", offset = height))
|> startProfileAt([0, 0], %)
|> xLine((binLength + 2 * binTol) * countBinWidth, %, $line010)
|> yLine((binLength + 2 * binTol) * countBinLength, %, $line011)
|> xLineTo(profileStartX(%), %, $line012)
|> close(tag = $line013)
|> extrude(length = binHeight * countBinHeight)
|> fillet(
radius = cornerRadius,
tags = [
getNextAdjacentEdge(line010),
getPreviousAdjacentEdge(line010),
getNextAdjacentEdge(line012),
getPreviousAdjacentEdge(line012)
]
)
|> shell(faces = ["end"], thickness = binThk)
// define a function which builds the profile of the baseplate bin
fn lipFace(plane) {
faceSketch = startSketchOn(plane)
|> startProfileAt([0, 0], %)
// |> yLine(lipHeight, %, $line100)
|> line(end = [0.0, 5.792893], tag = $line000)
|> arc({
angleStart = 180.0,
angleEnd = 45.0,
radius = 0.500000
}, %, $arc000)
// |> angledLineOfYLength({ angle: -45, length: lipStep5 }, %)
|> line(end = [1.046447, -1.046447], tag = $line001)
|> yLine(-lipStep4, %)
|> angledLineOfYLength({ angle = -45, length = lipStep3 }, %)
|> yLine(-lipStep2, %)
|> angledLineOfYLength({ angle = -135, length = lipStep1 }, %)
|> close()
return faceSketch
}
plane000 = {
plane = {
origin = [
cornerRadius,
0.0,
height + binHeight * countBinHeight
],
xAxis = [0.0, 1.0, 0.0],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}
plane001 = {
plane = {
origin = [
0.0,
cornerRadius,
height + binHeight * countBinHeight
],
xAxis = [1.0, 0.0, 0.0],
yAxis = [0.0, 0.0, 1.0],
zAxis = [0.0, 1.0, 0.0]
}
}
plane002 = {
plane = {
origin = [
countBinWidth * (binLength + 2 * binTol) - cornerRadius,
0.0,
height + binHeight * countBinHeight
],
xAxis = [0.0, 1.0, 0.0],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}
// extrude a single side of the lip of the bin
lipSingleLength = extrude(lipFace(plane000), length = binLength * countBinWidth - (2 * cornerRadius) + 2 * binTol * countBinWidth)
// extrude a single side of the lip of the bin
lipSingleWidth = extrude(lipFace(plane001), length = binLength * countBinLength - (2 * cornerRadius) + 2 * binTol * countBinLength)
// create the other sides of the lips by using a circular pattern
lipLengths = patternCircular3d(
lipSingleLength,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2 * countBinWidth,
(binLength + 2 * binTol) / 2 * countBinLength,
0
],
instances = 2,
rotateDuplicates = true
)
// create the other sides of the lips by using a circular pattern
lipWidths = patternCircular3d(
lipSingleWidth,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2 * countBinWidth,
(binLength + 2 * binTol) / 2 * countBinLength,
0
],
instances = 2,
rotateDuplicates = true
)
// define an axis axis000
axis001 = {
custom = {
axis = [0.0, 1.0],
origin = [cornerRadius, cornerRadius]
}
}
// create a single corner of the bin
lipSingleLengthCorner = revolve({ angle = -90, axis = axis001 }, lipFace(plane000))
// create a single corner of the bin
lipSingleWidthCorner = revolve({ angle = 90, axis = axis001 }, lipFace(plane002))
// create the corners of the bin
lipCorners000 = patternCircular3d(
lipSingleLengthCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2 * countBinWidth,
(binLength + 2 * binTol) / 2 * countBinLength,
0
],
instances = 2,
rotateDuplicates = true
)
// create the corners of the bin
lipCorners001 = patternCircular3d(
lipSingleWidthCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2 * countBinWidth,
(binLength + 2 * binTol) / 2 * countBinLength,
0
],
instances = 2,
rotateDuplicates = true
)

View File

@ -0,0 +1,182 @@
// Gridfinity Bins
// Gridfinity is a system to help you work more efficiently. This is a system invented by Zack Freedman. There are two main components the baseplate and the bins. The components are comprised of a matrix of squares. Allowing easy stacking and expansion
// Set units in millimeters (mm)
@settings(defaultLengthUnit = mm)
// Define constants
binLength = 41.5
binHeight = 7.0
binBaseLength = 2.95
binTol = 0.25
binThk = 1.2
cornerRadius = 3.75
firstStep = 0.8
secondStep = 1.8
thirdStep = 2.15
magOuterDiam = 6.5
magOffset = 4.8
magDepth = 2.4
// Number of bins in each direction
countBinWidth = 2
countBinLength = 3
countBinHeight = 2
// The total height of the baseplate is a summation of the vertical heights of the baseplate steps
height = firstStep + secondStep + thirdStep
// define a function which builds the profile of the baseplate bin
fn face(plane) {
faceSketch = startSketchOn(plane)
|> startProfileAt([binBaseLength + binTol, 0], %)
|> yLine(height, %)
|> xLine(-binBaseLength, %)
|> angledLineOfYLength({ angle = -45, length = thirdStep }, %)
|> yLine(-secondStep, %)
|> angledLineOfYLength({ angle = -45, length = firstStep }, %)
|> close()
return faceSketch
}
// extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2), )
// create the other sides of the bin by using a circular pattern
sides = patternCircular3d(
singleSide,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2,
0
],
instances = 4,
rotateDuplicates = true
)
// define an axis axis000
axis000 = {
custom = {
axis = [0.0, 1.0],
origin = [
cornerRadius + binTol,
cornerRadius + binTol
]
}
}
// create a single corner of the bin
singleCorner = revolve({ angle = -90, axis = axis000 }, face(offsetPlane("YZ", offset = cornerRadius + binTol)))
// create the corners of the bin
corners = patternCircular3d(
singleCorner,
arcDegrees = 360,
axis = [0, 0, 1],
center = [
(binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2,
0
],
instances = 4,
rotateDuplicates = true
)
singleBinFill = startSketchOn("XY")
|> startProfileAt([
binBaseLength + binTol,
binBaseLength + binTol
], %)
|> line(end = [binLength - (binBaseLength * 2), 0], tag = $line000)
|> line(end = [0, binLength - (binBaseLength * 2)], tag = $line001)
|> xLineTo(profileStartX(%), %, $line002)
|> close(tag = $line003)
|> extrude(length = height)
|> fillet(
radius = firstStep,
tags = [
getNextAdjacentEdge(line000),
getPreviousAdjacentEdge(line000),
getNextAdjacentEdge(line002),
getPreviousAdjacentEdge(line002)
]
)
magCutout000 = startSketchOn(singleBinFill, "start")
|> circle(
center = [
-magOffset - binBaseLength - binTol,
magOffset + binBaseLength + binTol
],
radius = magOuterDiam / 2
)
|> patternCircular2d(
arcDegrees = 360,
center = [
(-binLength + 2 * binTol) / 2,
(binLength + 2 * binTol) / 2
],
instances = 4,
rotateDuplicates = true
)
|> extrude(length = -magDepth)
// create the baseplate by patterning sides
binSides = patternLinear3d(
sides,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
// create the corners of the baseplate by patterning the corners
binCorners = patternLinear3d(
corners,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
// create the fill of the bin by patterning the corners
binFill = patternLinear3d(
singleBinFill,
axis = [1.0, 0.0, 0.0],
instances = countBinWidth,
distance = binLength + binTol * 2
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
)
// create the top of the bin
binTop = startSketchOn(offsetPlane("XY", offset = height))
|> startProfileAt([0, 0], %)
|> xLine((binLength + 2 * binTol) * countBinWidth, %, $line010)
|> yLine((binLength + 2 * binTol) * countBinLength, %, $line011)
|> xLineTo(profileStartX(%), %, $line012)
|> close(tag = $line013)
|> extrude(length = binHeight * countBinHeight)
|> fillet(
radius = cornerRadius,
tags = [
getNextAdjacentEdge(line010),
getPreviousAdjacentEdge(line010),
getNextAdjacentEdge(line012),
getPreviousAdjacentEdge(line012)
]
)
|> shell(faces = ["end"], thickness = binThk)

View File

@ -0,0 +1,31 @@
// Hex nut
// A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants (5/16" - 24 thread size)
wallToWallLength = 0.5
thickness = 0.266
diameter = 0.3125
// Define a function for the hex nut
fn hexNut(start, thk, innerDia) {
hexNutSketch = startSketchOn('-XZ')
|> startProfileAt([start[0] + innerDia, start[1]], %)
|> angledLine({ angle = 240, length = innerDia }, %)
|> angledLine({ angle = 180, length = innerDia }, %)
|> angledLine({ angle = 120, length = innerDia }, %)
|> angledLine({ angle = 60, length = innerDia }, %)
|> angledLine({ angle = 0, length = innerDia * .90 }, %)
|> close()
|> hole(circle(
center = [start[0], start[1]],
radius = innerDia / 2
), %)
|> extrude(length = thk)
return hexNutSketch
}
// Create a hex nut
hexNut([0, 0], thickness, diameter)

View File

@ -0,0 +1,22 @@
// I-beam
// A structural metal beam with an I shaped cross section. Often used in construction
// Set Units
@settings(defaultLengthUnit = in)
//Define Beam Dimensions
beamLength = 24
beamWidth = 2.663
beamHeight = 4
wallThickness = 0.293
// Sketch a quadrant of the beam cross section, then mirror for symmetry across each axis. Extrude to the appropriate length
sketch001 = startSketchOn('-XZ')
|> startProfileAt([0, beamHeight/2], %)
|> xLine(beamWidth/2, %)
|> yLine(-wallThickness, %)
|> xLineTo(wallThickness/2, %)
|> yLineTo(0, %)
|> mirror2d({ axis = 'X' }, %)
|> mirror2d({ axis = 'Y' }, %)
|> extrude(length = beamLength)

View File

@ -0,0 +1,256 @@
// Kitt
// The beloved KittyCAD mascot in a voxelized style.
// pixel box function
fn pixelBox(kitExtrude, extrudeTag, positionY, positionZ, width, height, depth) {
pixelBoxBody = startSketchOn(kitExtrude, extrudeTag)
|> startProfileAt([positionY, positionZ], %)
|> line(end = [0, height])
|> line(end = [width, 0])
|> line(end = [0, -height])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = depth)
return pixelBoxBody
}
// 1. Kitty Body
kitBodyElevation = 6
kitBodyWidth = 26
kitBodyHeight = 25
kitBodyDepth = 18
kitBody = startSketchOn('XZ')
|> startProfileAt([-kitBodyWidth / 2, kitBodyElevation], %)
|> line(end = [0, kitBodyHeight])
|> line(end = [kitBodyWidth, 0], tag = $seg01)
|> line(end = [0, -kitBodyHeight], tag = $seg02)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = kitBodyDepth)
// 2. Kitty Head (Frame of display)
kitHeadOffset = 1
kitHeadHeight = 16
kitHeadElevation = kitBodyElevation + kitBodyHeight - kitHeadOffset - kitHeadHeight
kitHeadWidth = kitBodyWidth - (kitHeadOffset * 2)
kitHeadDepth = 3
kitHead = pixelBox(kitBody, 'END', -kitHeadWidth / 2, kitHeadElevation, kitHeadWidth, kitHeadHeight, kitHeadDepth)
kitFaceElevation = kitHeadElevation + 2
// 3. Kitty Face
kitFaceWidth = kitHeadWidth - 4
kitFaceHeight = kitHeadElevation + kitHeadHeight - kitFaceElevation - 3
kitFaceDepth = 2
kitFace = startSketchOn(kitHead, 'END')
|> startProfileAt([-kitFaceWidth / 2, kitFaceElevation], %)
|> line(end = [0, 1]) // left lower corner up
|> line(end = [-1, 0]) // left lower corner left
|> line(end = [0, kitFaceHeight]) // left side up
|> line(end = [1, 0]) // left upper corner right
|> line(end = [0, 1]) // left upper corner up
|> line(end = [kitFaceWidth, 0]) // upper side right
|> line(end = [0, -1]) // right upper corner down
|> line(end = [1, 0]) // right upper corner right
|> line(end = [0, -kitFaceHeight]) // right side down
|> line(end = [-1, 0]) // right lower corner left
|> line(end = [0, -1]) // right lower corner down
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = -kitFaceDepth)
// Kitty Face Features:
// 3.1 Kitty Eyes
// 3.1.1 Kitty Left Eye
kitEyeDepth = 0.5
kitEyeHeihgt = kitFaceElevation + 7
kitEyeOffset = 7
// 3.1.2 Kitty Right Eye
kitLeftEye1 = pixelBox(kitFace, 'START', -kitEyeOffset, kitEyeHeihgt, 1, 1, kitEyeDepth)
// 3.2 Kitty Nose
kitLeftEye2 = pixelBox(kitFace, 'START', -kitEyeOffset + 1, kitEyeHeihgt + 1, 3, 1, kitEyeDepth)
kitLeftEye3 = pixelBox(kitFace, 'START', -kitEyeOffset + 4, kitEyeHeihgt, 1, 1, kitEyeDepth)
kitRightEye = pixelBox(kitFace, 'START', kitEyeOffset - 3, kitEyeHeihgt - 1, 2, 4, kitEyeDepth)
kitNoseElevation = kitEyeHeihgt - 5
kitNose = startSketchOn(kitFace, 'START')
|> startProfileAt([-2, kitNoseElevation], %) // H V
|> line(end = [0, 1]) // lower-left up
|> line(end = [2, 0]) // lower-left right
|> line(end = [0, 2]) // mid-left up
|> line(end = [-1, 0]) // upper-left left
|> line(end = [0, 1]) // upper-left up
|> line(end = [3, 0]) // upper-mid right
|> line(end = [0, -1]) // upper-right down
|> line(end = [-1, 0]) // upper-right left
|> line(end = [0, -2]) // mid-left down
|> line(end = [2, 0]) // lower-right right
|> line(end = [0, -1]) // lower-right down
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = kitEyeDepth)
// 3.3 Kitty Mouth
kitMouthOffset = 4
kitMouthHeight = kitEyeHeihgt - 3
kitMouthUpLeft = pixelBox(kitFace, 'START', -kitMouthOffset, kitMouthHeight, 1, 1, kitEyeDepth)
// 4. Kitty Belly
kitMouthDownLeft = pixelBox(kitFace, 'START', -kitMouthOffset + 1, kitMouthHeight - 1, 1, 1, kitEyeDepth)
kitMouthUpRight = pixelBox(kitFace, 'START', kitMouthOffset, kitMouthHeight, 1, 1, kitEyeDepth)
kitMouthDownRight = pixelBox(kitFace, 'START', kitMouthOffset - 1, kitMouthHeight - 1, 1, 1, kitEyeDepth)
kitBellyElevation = kitBodyElevation + 1
kitBellyHeight = kitHeadElevation - kitBellyElevation - 1
// 4.1 Kitty VHS
kitBellyWidth = kitHeadWidth
kitBellyDepth = kitHeadDepth
kitBelly = pixelBox(kitBody, 'END', -kitBellyWidth / 2, kitBellyElevation, kitBellyWidth, kitBellyHeight, kitBellyDepth)
kitVHSelevation = kitBellyElevation + 1
kitVHSheight = 2
// 4.2 Kitty Floppy
kitVHSwidth = 8
kitVHSdepth = 1
kitVHS = pixelBox(kitBelly, 'END', -kitVHSwidth / 2, kitVHSelevation, kitVHSwidth, kitVHSheight, kitVHSdepth)
kitFloppyElevation = kitBellyElevation + 1
kitFloppyHeight = 1
kitFloppyWidth = 5
kitFloppyOffset = kitBellyWidth / 2 - 1
kitFloppyDepth = 2
// 4.3 Kitty Belly Button
kitFloppy1 = pixelBox(kitBelly, 'END', -kitFloppyOffset, kitFloppyElevation, kitFloppyWidth, kitFloppyHeight, -kitFloppyDepth)
kitFloppy2 = pixelBox(kitBelly, 'END', -kitFloppyOffset, kitFloppyElevation + 2, kitFloppyWidth, kitFloppyHeight, -kitFloppyDepth)
kitFloppy3 = pixelBox(kitBelly, 'END', kitFloppyOffset, kitFloppyElevation, -kitFloppyWidth, kitFloppyHeight, -kitFloppyDepth)
kitBellyButtonOffset = kitHeadWidth / 2 - 3
kitBellyButtonElevation = kitHeadElevation - 1
kitBellyButtonWidth = 2
// 4.4 Kitty Buttons
kitBellyButtonHeight = 1
kitBellyButtonDepth = kitHeadDepth + 1
kitBellyButton = pixelBox(kitBody, 'END', -kitBellyButtonOffset, kitBellyButtonElevation, kitBellyButtonWidth, kitBellyButtonHeight, kitBellyButtonDepth)
kitButtonWidth = 1
kitButtonHeight = 2
kitButtonDepth = kitFloppyDepth
kitButtonElevation = kitFloppyElevation + 2
kitButton1 = pixelBox(kitBelly, 'END', kitFloppyOffset, kitFloppyElevation + 2, -kitButtonWidth, kitButtonHeight, -kitButtonDepth)
// 5. Kitty Legs
kitButton2 = pixelBox(kitBelly, 'END', kitFloppyOffset - kitButtonWidth - 1, kitFloppyElevation + 2, -kitButtonWidth, kitButtonHeight, -kitButtonDepth)
kitButton3 = pixelBox(kitBelly, 'END', kitFloppyOffset - (2 * (kitButtonWidth + 1)), kitFloppyElevation + 2, -kitButtonWidth, kitButtonHeight, -kitButtonDepth)
kitShoeWidth = 7
kitShoeLength = 10
kitShoeHeight = 3
fn kitLeg(offsetFront, offsetSide) {
kitShoeOffsetFront = kitShoeLength / 2 - (kitBodyDepth / 2) - offsetFront
kitFootPrint = startSketchOn('XY')
|> startProfileAt([offsetSide, kitShoeOffsetFront], %)
|> line(end = [kitShoeWidth, 0])
|> line(end = [0, -kitShoeLength])
|> line(end = [-kitShoeWidth, 0])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
kitShoe = extrude(kitFootPrint, length = kitShoeHeight)
kitPantsOffsetSide = offsetSide + 1
kitPantsOffsetFront = 2 * kitShoeOffsetFront - 2
kitPantsWidth = kitShoeWidth - 2
kitPantsFrontWidth = kitPantsWidth
kitPantsHeight = kitBodyElevation - kitShoeHeight
kitPants = pixelBox(kitShoe, 'END', kitPantsOffsetSide, kitPantsOffsetFront, kitPantsFrontWidth, kitPantsWidth, kitPantsHeight)
return kitShoe
}
kitLegOffset = 3
kitRightLeg = kitLeg(0, kitLegOffset)
kitLeftLeg = kitLeg(0, -kitLegOffset - kitShoeWidth)
// 6. Kitty Ears
kitEarWidth = 8
kitEarDepth = 8
kitEarHeight = 2
fn kitEar(earOffsetFront, earOffsetSide) {
kitNewEarOffsetFront = kitBodyDepth - earOffsetFront
kitNewEarOffsetSide = -(kitBodyWidth / 2 - earOffsetSide)
baseVolume = pixelBox(kitBody, seg01, kitNewEarOffsetSide, kitNewEarOffsetFront, kitEarWidth, -kitEarDepth, kitEarHeight)
secondOffset = 1
secondLevel = pixelBox(baseVolume, 'END', kitNewEarOffsetSide + secondOffset, kitNewEarOffsetFront - 0.01, kitEarWidth - (secondOffset * 2), -kitEarDepth + secondOffset * 2, kitEarHeight)
thirdOffset = 2
thirdLevel = pixelBox(secondLevel, 'END', kitNewEarOffsetSide + thirdOffset, kitNewEarOffsetFront - 0.02, kitEarWidth - (thirdOffset * 2), -kitEarDepth + thirdOffset * 2, kitEarHeight)
fourthOffset = 3
fourthLevel = pixelBox(thirdLevel, 'END', kitNewEarOffsetSide + fourthOffset, kitNewEarOffsetFront - 0.03, kitEarWidth - (fourthOffset * 2), -kitEarDepth + fourthOffset * 2, kitEarHeight)
return baseVolume
}
kitEarOffsetFront = 4
kitEarOffsetSide = 1
kitRightEar = kitEar(kitEarOffsetFront, kitEarOffsetSide)
kitLeftEar = kitEar(kitEarOffsetFront, kitBodyWidth - kitEarWidth - kitEarOffsetSide)
// 7. Kitty Side
// 7.1 Grill
grillOffset = 4
grillRowA = kitBodyElevation + kitBodyHeight - grillOffset
grillRowB = grillRowA - 2
grillRowC = grillRowA - 4
grillColumnA = kitBodyDepth - grillOffset
grillColumnB = grillColumnA - 1
grillColumnC = grillColumnA - 2
grillColumnD = grillColumnA - 3
grillColumnE = grillColumnA - 4
grillHoleSize = 1
grillHoleDepth = -2
grillHoleAB = pixelBox(kitBody, seg02, grillRowA, grillColumnB, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleAD = pixelBox(kitBody, seg02, grillRowA, grillColumnD, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleBA = pixelBox(kitBody, seg02, grillRowB, grillColumnA, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleBC = pixelBox(kitBody, seg02, grillRowB, grillColumnC, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleBE = pixelBox(kitBody, seg02, grillRowB, grillColumnE, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleCB = pixelBox(kitBody, seg02, grillRowC, grillColumnB, grillHoleSize, grillHoleSize, grillHoleDepth)
grillHoleCD = pixelBox(kitBody, seg02, grillRowC, grillColumnD, grillHoleSize, grillHoleSize, grillHoleDepth)
// 7.2 Kitty Vent
kitVentElevation = kitBodyElevation + 1
kitVentOffset = 1
kitVentHoleWidth = 1
kitVentHoleHeight = 4
kitVentHoleDepth = grillHoleDepth
kitVentA = pixelBox(kitBody, seg02, kitVentElevation, kitVentOffset, kitVentHoleHeight, kitVentHoleWidth, kitVentHoleDepth)
kitVentB = pixelBox(kitBody, seg02, kitVentElevation, kitVentOffset + 2, kitVentHoleHeight, kitVentHoleWidth, kitVentHoleDepth)
kitVentC = pixelBox(kitBody, seg02, kitVentElevation, kitVentOffset + 4, kitVentHoleHeight, kitVentHoleWidth, kitVentHoleDepth)

View File

@ -0,0 +1,109 @@
// Lego Brick
// A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants
lbumps = 5 // number of bumps long
wbumps = 3 // number of bumps wide
pitch = 8.0
clearance = 0.1
bumpDiam = 4.8
bumpHeight = 1.8
height = 3.2
t = (pitch - (2 * clearance) - bumpDiam) / 2.0
postDiam = pitch - t // works out to 6.5
totalLength = lbumps * pitch - (2.0 * clearance)
totalWidth = wbumps * pitch - (2.0 * clearance)
// Calculate the number of segments in the length and width
lSegments = totalLength / lbumps
wSegments = totalWidth / wbumps
// Add assertions to ensure that the number of bumps are greater than 1
assertGreaterThan(lbumps, 1, "lbumps must be greater than 1")
assertGreaterThan(wbumps, 1, "wbumps must be greater than 1")
// Create the plane for the pegs. This is a hack so that the pegs can be patterned along the face of the lego base.
pegFace = {
plane = {
origin = { x = 0, y = 0, z = height },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Create the plane for the tubes underneath the lego. This is a hack so that the tubes can be patterned underneath the lego.
tubeFace = {
plane = {
origin = { x = 0, y = 0, z = height - t },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Make the base
s = startSketchOn('XY')
|> startProfileAt([-totalWidth / 2, -totalLength / 2], %)
|> line(end = [totalWidth, 0])
|> line(end = [0, totalLength])
|> line(end = [-totalWidth, 0])
|> close()
|> extrude(length = height)
// Sketch and extrude a rectangular shape to create the shell underneath the lego. This is a hack until we have a shell function.
shellExtrude = startSketchOn(s, "start")
|> startProfileAt([
-(totalWidth / 2 - t),
-(totalLength / 2 - t)
], %)
|> line(end = [totalWidth - (2 * t), 0])
|> line(end = [0, totalLength - (2 * t)])
|> line(end = [-(totalWidth - (2 * t)), 0])
|> close()
|> extrude(length = -(height - t))
// Create the pegs on the top of the base
peg = startSketchOn(s, 'end')
|> circle(
center = [
-(pitch * (wbumps - 1) / 2),
-(pitch * (lbumps - 1) / 2)
],
radius = bumpDiam / 2
)
|> patternLinear2d(
axis = [1, 0],
instances = wbumps,
distance = pitch
)
|> patternLinear2d(
axis = [0, 1],
instances = lbumps,
distance = pitch
)
|> extrude(length = bumpHeight)
// Create the pegs on the bottom of the base
tubePattern = startSketchOn(tubeFace)
|> circle(
center = [
-(pitch * (wbumps - 1) / 2 - (pitch / 2)),
-(pitch * (lbumps - 1) / 2 - (pitch / 2))
],
radius = bumpDiam / 2
)
|> patternLinear2d(
axis = [1, 0],
instances = wbumps - 1,
distance = pitch
)
|> patternLinear2d(
axis = [0, 1],
instances = lbumps - 1,
distance = pitch
)
|> extrude(length = -bumpHeight)

View File

@ -0,0 +1,73 @@
// Mounting Plate
// A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants
plateLength = 10
plateWidth = 6
filletRadius = 0.5
plateThickness = .5
centerHoleDiameter = 2
// Create a function that defines the body width and length of the mounting plate. Tag the corners so they can be passed through the fillet function.
fn rectShape(pos, w, l) {
rr = startSketchOn('XY')
|> startProfileAt([pos[0] - (w / 2), pos[1] - (l / 2)], %)
|> line(endAbsolute = [pos[0] + w / 2, pos[1] - (l / 2)], tag = $edge1)
|> line(endAbsolute = [pos[0] + w / 2, pos[1] + l / 2], tag = $edge2)
|> line(endAbsolute = [pos[0] - (w / 2), pos[1] + l / 2], tag = $edge3)
|> close(tag = $edge4)
return rr
}
// Define the hole radius and x, y location constants
holeRadius = .25
holeIndex = .75
// Create the mounting plate extrusion, holes, and fillets
rs = rectShape([0, 0], plateWidth, plateLength)
part = rs
|> hole(circle(
center = [
-plateWidth / 2 + holeIndex,
plateLength / 2 - holeIndex
],
radius = holeRadius
), %)
|> hole(circle(
center = [
plateWidth / 2 - holeIndex,
plateLength / 2 - holeIndex
],
radius = holeRadius
), %)
|> hole(circle(
center = [
-plateWidth / 2 + holeIndex,
-plateLength / 2 + holeIndex
],
radius = holeRadius
), %)
|> hole(circle(
center = [
plateWidth / 2 - holeIndex,
-plateLength / 2 + holeIndex
],
radius = holeRadius
), %)
|> hole(circle(
center = [0, 0],
radius = centerHoleDiameter
), %)
|> extrude(length = plateThickness)
|> fillet(
radius = filletRadius,
tags = [
getPreviousAdjacentEdge(rs.tags.edge1),
getPreviousAdjacentEdge(rs.tags.edge2),
getPreviousAdjacentEdge(rs.tags.edge3),
getPreviousAdjacentEdge(rs.tags.edge4)
]
)

View File

@ -0,0 +1,64 @@
// Global constants for the multi-axis robot
// Set Units
@settings(defaultLengthUnit = in)
// Axis Angles
export axisJ4 = 25
export axisJ3 = 60
export axisJ2 = 110
export axisJ1 = 80
// Robot Arm Base
export basePlateRadius = 5
export basePlateThickness = 0.5
export baseChamfer = 2
export baseHeight = 5
// J2 Axis for Robot Arm
export axisJ2ArmLength = 31
export axisJ2ArmWidth = 4
export axisJ2ArmThickness = 2.5
// J3 Axis for Robot Arm
export axisJ3C = axisJ3 - 180 + axisJ2
export axisJ3CArmLength = 20
export axisJ3CArmWidth = 3.75
export axisJ3CArmThickness = 2.5
// Planes
export plane001 = {
plane = {
origin = [0.0, 0.0, baseHeight - 1.5 + 0.1],
xAxis = [1.0, 0.0, 0.0],
yAxis = [0.0, 1.0, 0.0],
zAxis = [0.0, 0.0, 1.0]
}
}
export plane002 = {
plane = {
origin = [0.0, 0.0, 0.0],
xAxis = [
sin(toRadians(axisJ1)),
cos(toRadians(axisJ1)),
0.0
],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}
// Define Plane to Move J2 Axis Robot Arm
export plane003 = {
plane = {
origin = [-0.1, 0.0, 0.0],
xAxis = [
sin(toRadians(axisJ1)),
cos(toRadians(axisJ1)),
0.0
],
yAxis = [0.0, 0.0, 1.0],
zAxis = [1.0, 0.0, 0.0]
}
}

View File

@ -0,0 +1,15 @@
// Robot Arm
// A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes
// Set Units
@settings(defaultLengthUnit = in)
import 'robot-arm-base.kcl' as robotArmBase
import 'robot-rotating-base.kcl' as rotatingBase
import 'robot-arm-j2.kcl' as j2RobotArm
import 'robot-arm-j3.kcl' as j3RobotArm
robotArmBase
rotatingBase
j2RobotArm
j3RobotArm

View File

@ -0,0 +1,82 @@
// Robot Arm Base
// Set Units
@settings(defaultLengthUnit = in)
// Import Constants
import basePlateRadius, basePlateThickness, baseChamfer, baseHeight from "globals.kcl"
// Base Plate
sketch001 = startSketchOn('XY')
|> startProfileAt([-basePlateRadius, -basePlateRadius], %)
|> angledLine([0, 2 * basePlateRadius], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) + 90,
2 * basePlateRadius
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001)
|> close()
extrude001 = extrude(sketch001, length = basePlateThickness)
|> chamfer(
length = baseChamfer,
tags = [
getNextAdjacentEdge(rectangleSegmentA001),
getNextAdjacentEdge(rectangleSegmentB001),
getNextAdjacentEdge(rectangleSegmentC001),
getNextAdjacentEdge(rectangleSegmentD001)
]
)
// Base Motor for actuating first joint
sketch002 = startSketchOn(extrude001, 'END')
|> circle(center = [0, 0], radius = 4, tag = $referenceEdge)
extrude002 = extrude(sketch002, length = baseHeight - basePlateThickness - 1.5)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge)]
)
sketch003 = startSketchOn(extrude002, 'END')
|> circle(center = [0, 0], radius = 0.5)
extrude003 = extrude(sketch003, length = 1)
// Pattern M8 mounting bolts in base
sketch4A = startSketchOn(extrude001, 'END')
|> circle(
center = [
-basePlateRadius + 1,
-basePlateRadius + baseChamfer + 0.5
],
radius = 0.4
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = 4,
rotateDuplicates = true
)
extrude4A = extrude(sketch4A, length = -basePlateThickness)
sketch4B = startSketchOn(extrude001, 'END')
|> circle(
center = [
-basePlateRadius + 0.5 + baseChamfer,
-basePlateRadius + 1
],
radius = 0.4
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = 4,
rotateDuplicates = true
)
extrude(sketch4B, length = -basePlateThickness)
|> appearance(color = "#754110", roughness = 90, metalness = 90)

View File

@ -0,0 +1,99 @@
// J2 Axis for Robot Arm
// Set Units
@settings(defaultLengthUnit = in)
import axisJ1, axisJ2, axisJ2ArmWidth, axisJ2ArmLength, axisJ2ArmThickness, plane003 from "globals.kcl"
// Create Body of J2 Robot Arm
sketch011 = startSketchOn(plane003)
|> startProfileAt([
1.75 - (axisJ2ArmWidth / 2 * sin(toRadians(axisJ2))),
8 + axisJ2ArmWidth / 2 * cos(toRadians(axisJ2))
], %)
|> arc({
angleStart = 90 + axisJ2,
angleEnd = 270 + axisJ2,
radius = axisJ2ArmWidth / 2
}, %)
|> angledLine({
angle = axisJ2,
length = axisJ2ArmLength
}, %)
|> arc({
angleStart = -90 + axisJ2,
angleEnd = 90 + axisJ2,
radius = axisJ2ArmWidth / 2
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude011 = extrude(sketch011, length = -axisJ2ArmThickness)
sketch012 = startSketchOn(extrude011, 'START')
|> circle(center = [-1.75, 8], radius = 1.9, tag = $referenceEdge4)
extrude012 = extrude(sketch012, length = 0.15)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge4)]
)
sketch013 = startSketchOn(extrude011, 'START')
|> circle(
center = [
-1.75 - (axisJ2ArmLength * cos(toRadians(axisJ2))),
8 + axisJ2ArmLength * sin(toRadians(axisJ2))
],
radius = 1.9,
tag = $referenceEdge5)
extrude013 = extrude(sketch013, length = 1)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge5)]
)
// Draw Bolt Patterns on J2 Robot Arm
sketch014 = startSketchOn(extrude012, 'END')
|> circle(center = [-1.75, 6.75], radius = 0.2)
|> patternCircular2d(
center = [-1.75, 8],
instances = 8,
arcDegrees = 360,
rotateDuplicates = true
)
extrude014 = extrude(sketch014, length = 0.15)
sketch015 = startSketchOn(extrude013, 'END')
|> circle(
center = [
-1.75 - ((axisJ2ArmLength - 1) * cos(toRadians(axisJ2))),
8 + (axisJ2ArmLength - 1.5) * sin(toRadians(axisJ2))
],
radius = 0.2
)
|> patternCircular2d(
center = [
-1.75 - (axisJ2ArmLength * cos(toRadians(axisJ2))),
8 + axisJ2ArmLength * sin(toRadians(axisJ2))
],
instances = 4,
arcDegrees = 360,
rotateDuplicates = true
)
extrude015 = extrude(sketch015, length = 0.15)
sketch016 = startSketchOn(extrude011, 'END')
|> circle(
center = [
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2))
],
radius = 0.3
)
extrude(sketch016, length = 1)
|> appearance(color = "#454545", metalness = 90, roughness = 90)

View File

@ -0,0 +1,171 @@
// J3 Robot Arm
// Set Units
@settings(defaultLengthUnit = in)
import plane002, axisJ2, axisJ3C, axisJ4, axisJ2ArmLength, axisJ3CArmLength, axisJ3CArmWidth, axisJ3CArmThickness from "globals.kcl"
// Create Body of J3 Robot Arm
sketch017 = startSketchOn(plane002)
|> startProfileAt([
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)) - (axisJ3CArmWidth / 2 * sin(toRadians(axisJ3C))),
8 + axisJ2ArmLength * sin(toRadians(axisJ2)) + axisJ3CArmWidth / 2 * cos(toRadians(axisJ3C))
], %)
|> arc({
angleStart = 90 + axisJ3C,
angleEnd = 270 + axisJ3C,
radius = axisJ3CArmWidth / 2
}, %)
|> angledLine({
angle = axisJ3C,
length = axisJ3CArmLength
}, %)
|> arc({
angleStart = 270 + axisJ3C,
angleEnd = 90 + axisJ3C,
radius = axisJ3CArmWidth / 2
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg01)
|> close()
extrude017 = extrude(sketch017, length = axisJ3CArmThickness)
sketch018 = startSketchOn(extrude017, 'END')
|> circle(
center = [
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2))
],
radius = 3.7 / 2,
tag = $referenceEdge6)
extrude018 = extrude(sketch018, length = 0.15)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge6)]
)
// Draw Bolt Pattern on J3 Robot Arm
sketch019 = startSketchOn(extrude018, 'END')
|> circle(
center = [
1.75 + (axisJ2ArmLength - 1) * cos(toRadians(axisJ2)),
8 + (axisJ2ArmLength - 1.5) * sin(toRadians(axisJ2))
],
radius = 0.2
)
|> patternCircular2d(
center = [
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2))
],
instances = 8,
arcDegrees = 360,
rotateDuplicates = true
)
extrude019 = extrude(sketch019, length = 0.15)
// On the J3 Robot Arm Body, Create Mounting Clevis for Grabber Claw
sketch020 = startSketchOn(extrude017, 'START')
|> circle(
center = [
-1.75 - (axisJ2ArmLength * cos(toRadians(axisJ2))) - (axisJ3CArmLength * cos(toRadians(axisJ3C))),
8 + axisJ2ArmLength * sin(toRadians(axisJ2)) + axisJ3CArmLength * sin(toRadians(axisJ3C))
],
radius = axisJ3CArmWidth / 2
)
extrude020 = extrude(sketch020, length = -0.5)
sketch021 = startSketchOn(extrude017, 'END')
|> circle(
center = [
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)) + axisJ3CArmLength * cos(toRadians(axisJ3C)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2)) + axisJ3CArmLength * sin(toRadians(axisJ3C))
],
radius = axisJ3CArmWidth / 2.01
)
extrude021 = extrude(sketch021, length = -0.5)
// Define Grabber Claw Constants
grabberLength = 7
sketch022 = startSketchOn(extrude021, 'START')
|> circle(center = [0, 0], radius = 0.10)
extrude022 = extrude(sketch022, length = -0.01)
// Build Upper Claw Finger
sketch023 = startSketchOn(extrude022, 'START')
|> startProfileAt([
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)) + axisJ3CArmLength * cos(toRadians(axisJ3C)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2)) + axisJ3CArmLength * sin(toRadians(axisJ3C))
], %)
|> angledLine({
angle = axisJ3C + axisJ4 / 2,
length = grabberLength / 4
}, %)
|> arc({
angleStart = 150 + axisJ3C + axisJ4 / 2,
angleEnd = 30 + axisJ3C + axisJ4 / 2,
radius = grabberLength / 3
}, %)
|> angledLine({
angle = axisJ3C + axisJ4 / 2,
length = grabberLength / 6
}, %)
|> angledLine({
angle = axisJ3C + axisJ4 / 2 + 132,
length = grabberLength / 3.5
}, %)
|> angledLine({
angle = axisJ3C + axisJ4 / 2 + 160,
length = grabberLength / 3.5
}, %)
|> angledLine({
angle = axisJ3C + axisJ4 / 2 + 200,
length = grabberLength / 3
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude023 = extrude(sketch023, length = -1.5)
// Build Lower Claw Finger
sketch024 = startSketchOn(extrude022, 'START')
|> startProfileAt([
1.75 + axisJ2ArmLength * cos(toRadians(axisJ2)) + axisJ3CArmLength * cos(toRadians(axisJ3C)),
8 + axisJ2ArmLength * sin(toRadians(axisJ2)) + axisJ3CArmLength * sin(toRadians(axisJ3C))
], %)
|> angledLine({
angle = axisJ3C - (axisJ4 / 2),
length = grabberLength / 4
}, %)
|> arc({
angleStart = 210 + axisJ3C - (axisJ4 / 2),
angleEnd = 330 + axisJ3C - (axisJ4 / 2),
radius = grabberLength / 3
}, %)
|> angledLine({
angle = axisJ3C - (axisJ4 / 2),
length = grabberLength / 6
}, %)
|> angledLine({
angle = axisJ3C - (axisJ4 / 2) - 132,
length = grabberLength / 3.5
}, %)
|> angledLine({
angle = axisJ3C - (axisJ4 / 2) - 160,
length = grabberLength / 3.5
}, %)
|> angledLine({
angle = axisJ3C - (axisJ4 / 2) - 200,
length = grabberLength / 3
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude(sketch024, length = -1.5)
|> appearance(color = "#6572b3", metalness = 90, roughness = 90)

View File

@ -0,0 +1,86 @@
// Robot Rotating Base
// Set Units
@settings(defaultLengthUnit = in)
import axisJ1, baseHeight, plane001, plane002 from "globals.kcl"
// Create Rotating Base
sketch005 = startSketchOn(plane001)
|> circle(center = [0, 0], radius = 3.9, tag = $referenceEdge1)
extrude005 = extrude(sketch005, length = 1.5 - 0.1)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge1)]
)
|> appearance(color = "#4f7d54", metalness = 90, roughness = 90)
sketch006 = startSketchOn(plane002)
|> startProfileAt([3.5, baseHeight], %)
|> angledLine({ angle = 60, length = 1.75 }, %)
|> arc({
angleStart = -30,
angleEnd = -30 + 180,
radius = 3
}, %)
|> angledLineToY({ angle = 60, to = baseHeight }, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude006 = extrude(sketch006, length = 1)
sketch007 = startSketchOn(extrude006, 'END')
|> circle(
center = [
1.75 * cos(toRadians(axisJ1)) / abs(cos(toRadians(axisJ1))),
8
],
radius = 2.75,
tag = $referenceEdge2)
extrude007 = extrude(sketch007, length = 1.5)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge2)]
)
// Draw Bolt Pattern on Rotating Base
sketch008 = startSketchOn(extrude007, 'END')
|> circle(
center = [
1.75 * cos(toRadians(axisJ1)) / abs(cos(toRadians(axisJ1))),
6.75
],
radius = 0.2
)
|> patternCircular2d(
center = [
1.75 * cos(toRadians(axisJ1)) / abs(cos(toRadians(axisJ1))),
8
],
instances = 4,
arcDegrees = 360,
rotateDuplicates = true
)
extrude008 = extrude(sketch008, length = 0.2)
sketch009 = startSketchOn(extrude007, 'END')
|> circle(
center = [
1.75 * cos(toRadians(axisJ1)) / abs(cos(toRadians(axisJ1))),
8
],
radius = 0.5,
tag = $referenceEdge3)
extrude009 = extrude(sketch009, length = 0.15)
|> fillet(
radius = 0.1,
tags = [getOppositeEdge(referenceEdge3)]
)
|> appearance(color = "#4f7d54", metalness = 90, roughness = 90)
sketch010 = startSketchOn(plane002)
|> circle(center = [1.75, 8], radius = 0.3)
extrude(sketch010, length = -1)

View File

@ -0,0 +1,196 @@
import asyncio
import os
import re
from concurrent.futures import ProcessPoolExecutor
from io import BytesIO
from operator import itemgetter
from pathlib import Path
import kcl
import requests
from PIL import Image
RETRIES = 5
def export_step(kcl_path: Path, save_path: Path) -> bool:
# determine the current directory
try:
export_response = asyncio.run(
kcl.execute_and_export(str(kcl_path.parent), kcl.FileExportFormat.Step)
)
stl_path = save_path.with_suffix(".step")
with open(stl_path, "wb") as out:
out.write(bytes(export_response[0].contents))
return True
except Exception as e:
print(e)
return False
def find_files(
path: str | Path, valid_suffixes: list[str], name_pattern: str | None = None
) -> list[Path]:
"""
Recursively find files in a folder by a list of provided suffixes or file naming pattern
Args:
path: str | Path
Root folder to search
valid_suffixes: Container[str]
List of valid suffixes to find files by (e.g. ".stp", ".step")
name_pattern: str
Name pattern to additionally filter files by (e.g. "_component")
Returns:
list[Path]
"""
path = Path(path)
valid_suffixes = [i.lower() for i in valid_suffixes]
return sorted(
file for file in path.rglob("*")
if file.suffix.lower() in valid_suffixes and
(name_pattern is None or re.match(name_pattern, file.name))
)
def snapshot(kcl_path: Path, save_path: Path) -> bool:
try:
snapshot_response = asyncio.run(
kcl.execute_and_snapshot(str(kcl_path.parent), kcl.ImageFormat.Png)
)
image = Image.open(BytesIO(bytearray(snapshot_response)))
im_path = save_path.with_suffix(".png")
image.save(im_path)
return True
except Exception as e:
print(e)
return False
def update_step_file_dates(step_file_path: Path) -> None:
# https://github.com/KittyCAD/cli/blob/main/src/cmd_kcl.rs#L1092
regex = r"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+\+\d{2}:\d{2}"
subst = r"1970-01-01T00:00:00.0+00:00"
with open(step_file_path, "r") as inp:
contents = inp.read()
contents = re.sub(regex, subst, contents)
with open(step_file_path, "w") as out:
out.write(contents)
def process_single_kcl(kcl_path: Path) -> dict:
# The part name is the parent folder since each file is main.kcl
part_name = kcl_path.parent.name
print(f"Processing {part_name}")
# determine the root dir, which is where this python script
root_dir = Path(__file__).parent
# step and screenshots for the part are based on the root dir
step_path = root_dir / "step" / part_name
screenshots_path = root_dir / "screenshots" / part_name
# attempt step export
export_status = export_step(kcl_path=kcl_path, save_path=step_path)
count = 1
while not export_status and count < RETRIES:
export_status = export_step(kcl_path=kcl_path, save_path=step_path)
count += 1
# attempt screenshot
snapshot_status = snapshot(kcl_path=kcl_path, save_path=screenshots_path)
count = 1
while not snapshot_status and count < RETRIES:
snapshot_status = snapshot(kcl_path=kcl_path, save_path=screenshots_path)
count += 1
# find relative paths, used for building the README.md
kcl_rel_path = kcl_path.relative_to(Path(__file__).parent)
step_rel_path = step_path.relative_to(Path(__file__).parent).with_suffix(".step")
screenshot_rel_path = screenshots_path.relative_to(Path(__file__).parent).with_suffix(".png")
# readme string for the part
readme_entry = (
f"#### [{part_name}]({kcl_rel_path}) ([step]({step_rel_path})) ([screenshot]({screenshot_rel_path}))\n"
f"[![{part_name}]({screenshot_rel_path})]({kcl_rel_path})"
)
return {"filename": f"{kcl_rel_path}", "export_status": export_status, "snapshot_status": snapshot_status,
"readme_entry": readme_entry}
def update_readme(new_content: str, search_string: str = '---\n') -> None:
with open("README.md", 'r', encoding='utf-8') as file:
lines = file.readlines()
# Find the line containing the search string
found_index = -1
for i, line in enumerate(lines):
if search_string in line:
found_index = i
break
new_lines = lines[:found_index + 1]
new_lines.append(new_content)
# Write the modified content back to the file
with open("README.md", 'w', encoding='utf-8') as file:
file.writelines(new_lines)
file.write("\n")
def main():
kcl_files = find_files(path=Path(__file__).parent, valid_suffixes=[".kcl"], name_pattern="main")
# run concurrently
with ProcessPoolExecutor(max_workers=5) as executor:
futures = [executor.submit(process_single_kcl, kcl_file) for kcl_file in kcl_files]
results = [future.result() for future in futures]
results = sorted(results, key=itemgetter('filename'))
step_files = find_files(path=Path(__file__).parent, valid_suffixes=[".step"])
with ProcessPoolExecutor(max_workers=5) as executor:
_ = [executor.submit(update_step_file_dates, step_file) for step_file in step_files]
if False in [i["export_status"] for i in results]:
comment_body = "The following files failed to export to STEP format:\n"
for i in results:
if not i["export_status"]:
comment_body += f"{i['filename']}\n"
url = f"https://api.github.com/repos/{os.getenv('GH_REPO')}/issues/{os.getenv('GH_PR')}/comments"
headers = {
'Authorization': f'token {os.getenv("GH_TOKEN")}',
}
json_data = {
'body': comment_body,
}
requests.post(url, headers=headers, json=json_data, timeout=60)
new_readme_links = []
for result in results:
if result["export_status"] and result["snapshot_status"]:
new_readme_links.append(result["readme_entry"])
new_readme_str = "\n".join(new_readme_links)
update_readme(new_readme_str)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,304 @@
// Pipe and Flange Assembly
// A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
flangeThickness = .125
flangeBaseDia = 2
boreHeight = 1
flangePipeDia = 1
mountingHoleDia = 0.425
screwDia = 0.375
tol = 0.010
hexNutScale = 0.90
wallThickness = 0.5
screwLength = 1.125
washerThickness = 0.0625
screwStart = [
0,
flangeThickness + washerThickness,
1.375
]
capRatio = .190 / .313 // Ratio grabbed from another screw
hexRatio = 5 / 32 / .190 // Ratio grabbed from another screw
hexStartingAngle = 210 // first angle of hex pattern (degrees)
hexInteriorAngle = 120 // degrees
hexChangeAngle = 180 - hexInteriorAngle // degrees
screwPlane = {
plane = {
origin = {
x = screwStart[0],
y = screwStart[1],
z = screwStart[2]
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
fn capScrew(start, length, dia) {
headLength = dia // inch
wallToWallLength = hexRatio * dia
headDia = dia / capRatio
hexWallLength = wallToWallLength / 2 * 1 / cos(toRadians(30)) // inch
// Length of Cap Head is always equal to diameter
capHeadLength = dia
// Create the head of the cap screw
screwHeadSketch = startSketchOn(screwPlane)
|> circle(
center = [0, 0],
radius = headDia / 2
)
// Extrude the screw head sketch
screwHead = extrude(screwHeadSketch, length = dia)
// Define the sketch of the hex pattern on the screw head
hexPatternSketch = startSketchOn(screwHead, 'end')
|> startProfileAt([
-start[0] + wallToWallLength / 2,
start[2]
], %)
|> yLine(-hexWallLength / 2, %)
|> angledLine({
angle = hexStartingAngle,
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - hexChangeAngle,
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (2 * hexChangeAngle),
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (3 * hexChangeAngle),
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (4 * hexChangeAngle),
length = hexWallLength
}, %)
|> close()
hexPattern = extrude(hexPatternSketch, length = -headLength * 0.75)
return hexPattern
}
workingPlane = {
plane = {
origin = { x = 0, y = flangeThickness, z = 0 },
xAxis = { x = 0, y = 0, z = 1 },
yAxis = { x = 1, y = 0, z = 0 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
// Washer function
fn washer(plane, start, thk, innerDia, outerDia) {
washerSketch = startSketchOn(plane)
|> circle(
center = [start[0], start[1]],
radius = outerDia / 2
)
|> hole(circle(
center = [start[0], start[1]],
radius = innerDia / 2
), %)
|> extrude(length = thk)
return washerSketch
}
// Hex nut function
fn hexNut(start, thk, innerDia) {
hexNutSketch = startSketchOn({
plane = {
origin = {
x = start[0],
y = -wallThickness - washerThickness,
z = start[2]
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = 1, z = 0 }
}
})
|> startProfileAt([0 + innerDia * hexNutScale, 0], %)
|> angledLine({
angle = 240,
length = innerDia * hexNutScale
}, %)
|> angledLine({
angle = 180,
length = innerDia * hexNutScale
}, %)
|> angledLine({
angle = 120,
length = innerDia * hexNutScale
}, %)
|> angledLine({
angle = 60,
length = innerDia * hexNutScale
}, %)
|> angledLine({ angle = 0, length = innerDia * .90 }, %)
|> close()
|> hole(circle(
center = [0, 0],
radius = innerDia / 2
), %)
|> extrude(length = -thk)
return hexNutSketch
}
// Mounting holes pattern
mountingHolePattern = startSketchOn('XZ')
|> circle(
center = [screwStart[0], screwStart[2]],
radius = screwDia / 2 + tol
)
|> patternCircular2d(
arcDegrees = 360,
center = [0, 0],
instances = 7,
rotateDuplicates = true
)
// Sketch and revolve the pipe
pipe = startSketchOn('XY')
|> startProfileAt([flangePipeDia / 2 - tol, 0], %)
|> line(end = [0, -2])
|> angledLine({ angle = -60, length = .5 }, %)
|> line(end = [0, -1])
|> line(end = [-flangeThickness, 0])
|> line(end = [0, 1])
|> angledLine({ angle = -240, length = .5 }, %)
|> line(end = [0, 5])
|> angledLine({ angle = 60, length = .5 }, %)
|> line(end = [0, 1])
|> line(end = [flangeThickness, 0])
|> line(end = [0, -1])
|> angledLine({ angle = 240, length = .5 }, %)
|> close()
|> revolve({ axis = 'y' }, %)
|> appearance(color = "#7b79d7")
// Sketch and extrude the wall
wall = startSketchOn('XZ')
|> startProfileAt([-4, -4], %)
|> line(end = [0, 8])
|> line(end = [8, 0])
|> line(end = [0, -8])
|> close()
|> hole(mountingHolePattern, %)
|> hole(circle(
center = [0, 0],
radius = flangePipeDia / 2
), %)
|> extrude(length = wallThickness)
|> appearance(color = "#c7aa8f")
// Sketch and revolve the flange
flangeBase = startSketchOn('XZ')
|> circle(
center = [0, 0],
radius = flangeBaseDia
)
|> hole(mountingHolePattern, %)
|> hole(circle(
center = [0, 0],
radius = flangePipeDia / 2
), %)
|> extrude(length = -flangeThickness)
|> appearance(color = "#9b9797")
// Create the washer and pattern around the flange
washer(workingPlane, [screwStart[2], screwStart[0]], 0.0625, screwDia + tol, 0.625)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, 0, 0],
instances = 7,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#d8da5d")
// Create the cap screw and pattern around the flange
capScrew([
0,
flangeThickness + washerThickness,
1.375
], screwLength, screwDia)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, 0, 0],
instances = 7,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#4cd411")
screwBodySketch = startSketchOn(screwPlane)
|> circle(
center = [0, 0],
radius = screwDia / 2
)
screwBody = extrude(screwBodySketch, length = -screwLength)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, 0, 0],
instances = 7,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#4cd411")
// Create a plane for the washers on the back side of the wall
backSideWasherPlane = {
plane = {
origin = {
x = 0,
y = -wallThickness - washerThickness,
z = 0
},
xAxis = { x = 0, y = 0, z = 1 },
yAxis = { x = 1, y = 0, z = 0 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
// Create the washers on the backside of the wall
washer(backSideWasherPlane, [screwStart[2], screwStart[0]], 0.0625, screwDia + tol, 0.625)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, 0, 0],
instances = 7,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#e8ec09")
// Create the hex nut and pattern around the flange
hexNut([
screwStart[0],
screwStart[1],
screwStart[2]
], .25, screwDia + tol)
|> patternCircular3d(
axis = [0, 1, 0],
center = [0, 0, 0],
instances = 7,
arcDegrees = 360,
rotateDuplicates = true
)
|> appearance(color = "#bc3434")

View File

@ -0,0 +1,35 @@
// Pipe with bend
// A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
innerDiameter = 10
outerDiameter = 20
bendRadius = 30
bendAngle = 90
// create a sketch in the 'XZ' plane
sketch000 = startSketchOn("XZ")
// create a profile for the outer diameter
outerProfile = circle(
sketch000,
center = [bendRadius, 0],
radius = outerDiameter / 2
)
// create a profile for the inner diameter
innerProfile = circle(
sketch000,
center = [bendRadius, 0],
radius = innerDiameter / 2
)
// create the profile of the pipe
pipeProfile = outerProfile
|> hole(innerProfile, %)
// revolve the pipe profile at the desired angle
pipe = revolve({ axis = "Y", angle = bendAngle }, pipeProfile)

View File

@ -0,0 +1,36 @@
// Pipe
// A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants
pipeTotalLength = 20
pipeLargeDiaLength = 1
pipeLargeDia = 1
pipeSmallDia = .75
thickness = 0.125
pipeTransitionAngle = 60
pipeTransitionLength = 0.5
pipeSmallDiaLength = pipeTotalLength - pipeTransitionLength - pipeLargeDiaLength
// Create the sketch to be revolved around the y-axis. Use the small diameter, large diameter, length, and thickness to define the sketch.
pipeSketch = startSketchOn('XY')
|> startProfileAt([pipeSmallDia - (thickness / 2), 38], %)
|> line(end = [thickness, 0])
|> line(end = [0, -pipeSmallDiaLength])
|> angledLineOfYLength({
angle = -60,
length = pipeTransitionLength
}, %)
|> line(end = [0, -pipeLargeDiaLength])
|> xLine(-thickness, %)
|> line(end = [0, pipeLargeDiaLength])
|> angledLineToX({
angle = -pipeTransitionAngle + 180,
to = pipeSmallDia - (thickness / 2)
}, %)
|> close()
// Revolve the sketch to create the pipe
pipe = revolve({ axis = 'y' }, pipeSketch)

View File

@ -0,0 +1,164 @@
// Poopy Shoe
// poop shute for bambu labs printer - optimized for printing.
// Set units
@settings(defaultLengthUnit = in)
wallThickness = 0.125
wallsWidth = 3
height = 5.125
filletRadius = 0.050
backLength = 6
exitHeight = 1
frontLength = 7
sketch001 = startSketchOn("-YZ")
|> startProfileAt([wallsWidth / 2, 0], %)
|> xLine(wallThickness / 2, %)
|> angledLineToX({ angle = 60, to = wallsWidth }, %, $seg01)
|> yLineTo(height, %)
|> xLine(-wallThickness, %)
|> yLineTo(segEndY(seg01), %)
|> angledLineToX({
angle = 60,
to = wallsWidth / 2 + wallThickness / 2
}, %)
|> xLine(-wallThickness, %)
|> angledLineToX({ angle = 180 - 60, to = wallThickness }, %)
|> yLineTo(height, %)
|> xLineTo(0, %)
|> yLineTo(segEndY(seg01), %)
|> angledLineToY({ angle = 180 - 60, to = 0 }, %)
|> close()
part001 = revolve({
angle = 90,
axis = {
custom = {
axis = [1.0, 0.0],
origin = [0.0, height + .0001]
}
}
}, sketch001)
sketch002 = startSketchOn('-YZ')
|> startProfileAt([wallsWidth / 2, 0], %)
|> xLine(wallThickness / 2, %)
|> angledLineToX({ angle = 60, to = wallsWidth }, %, $seg02)
|> yLineTo(height, %)
|> xLine(-wallThickness, %)
|> yLineTo(segEndY(seg01), %)
|> angledLineToX({
angle = 60,
to = wallsWidth / 2 + wallThickness / 2
}, %)
|> xLine(-wallThickness, %)
|> angledLineToX({ angle = 180 - 60, to = wallThickness }, %)
|> yLineTo(height, %)
|> xLineTo(0, %)
|> yLineTo(segEndY(seg02), %)
|> angledLineToY({ angle = 180 - 60, to = 0 }, %)
|> close()
|> extrude(length = backLength - height)
customPlane = {
plane = {
origin = {
x = 0,
y = -(wallsWidth / 2 - (wallThickness / 2)),
z = 0
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = -1, z = 0 }
}
}
sketch003 = startSketchOn(customPlane)
|> startProfileAt([0, 0], %)
|> tangentialArc({ offset = 60, radius = height }, %)
|> angledLineToY({ angle = 60, to = 0 }, %)
|> close()
|> extrude(length = wallThickness)
sketch004 = startSketchOn(sketch002, 'END')
|> startProfileAt([0, 0], %)
|> yLineTo(height, %)
|> xLineTo(wallThickness, %)
|> yLineTo(segEndY(seg01), %)
|> angledLineToX({
angle = 180 - 60,
to = wallsWidth / 2 - (wallThickness / 2)
}, %)
|> xLine(wallThickness, %)
|> angledLineToY({ angle = 60, to = segEndY(seg01) }, %)
|> yLineTo(height, %)
|> xLine(wallThickness, %)
|> tangentialArcTo([
(frontLength - wallsWidth) / 2 + wallsWidth,
height - ((height - exitHeight) / 2)
], %)
|> tangentialArcTo([frontLength, exitHeight], %)
|> yLineTo(0, %)
|> close(tag = $seg04)
|> extrude(length = wallThickness)
customPlane2 = {
plane = {
origin = {
x = -1 * (backLength - height + wallsWidth),
y = 0,
z = 0
},
xAxis = { x = 0, y = -1, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 1, y = 0, z = 0 }
}
}
sketch005 = startSketchOn(customPlane2)
|> startProfileAt([0, 0], %)
|> yLineTo(height, %)
|> xLineTo(wallsWidth, %)
|> tangentialArcTo([
(frontLength - wallsWidth) / 2 + wallsWidth,
height - ((height - exitHeight) / 2)
], %)
|> tangentialArcTo([frontLength, exitHeight], %)
|> yLineTo(0, %, $seg03)
|> close()
|> extrude(length = wallThickness)
sketch006 = startSketchOn(sketch005, seg03)
|> startProfileAt([0, -1 * (backLength - height)], %)
|> xLineTo(-exitHeight, %)
|> yLine(-wallsWidth, %)
|> xLineTo(0, %)
|> close()
|> extrude(length = wallThickness)
sketch007 = startSketchOn(sketch004, 'END')
|> startProfileAt([0, 0], %)
|> xLineTo(wallThickness, %)
|> yLineTo(height, %)
|> xLineTo(0, %)
|> close()
|> extrude(length = wallsWidth - (2 * wallThickness))
customPlane3 = {
plane = {
origin = {
x = -1 * (backLength - height + wallsWidth),
y = 0,
z = wallThickness
},
xAxis = { x = 0, y = -1, z = 0 },
yAxis = { x = 1, y = 0, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
sketch008 = startSketchOn(customPlane3)
|> startProfileAt([wallThickness, wallThickness], %)
|> xLineTo(frontLength, %)
|> yLine(wallsWidth - (2 * wallThickness), %)
|> xLineTo(wallThickness, %)
|> close()
|> extrude(length = -wallThickness)

View File

@ -0,0 +1,2 @@
[settings.app]
themeColor = "255"

View File

@ -0,0 +1,3 @@
pillow==11.1.0
requests==2.32.3
zoo-kcl==0.3.45

View File

@ -0,0 +1,93 @@
// Router template for a cross bar
// A guide for routing a notch into a cross bar.
// Set Units
@settings(defaultLengthUnit = mm)
routerDiameter = 12.7
templateDiameter = 11 / 16 * inch()
templateGap = (templateDiameter - routerDiameter) / 2 - 0.5
slateWidthHalf = 41.5 / 2
minClampingDistance = 50 + 30
templateThickness = 10
radius = 10
depth = 30
distanceToInsideEdge = slateWidthHalf + templateThickness + templateGap
sketch001 = startSketchOn('XZ')
|> startProfileAt([ZERO, depth + templateGap], %)
|> xLine(slateWidthHalf - radius, %, $seg01)
|> arc({
angleEnd = 0,
angleStart = 90,
radius = 10 + templateGap
}, %, $seg09)
|> yLineTo(-templateThickness, %, $seg03)
|> xLine(templateThickness, %, $seg07)
|> yLineTo((segEndY(seg01) + templateThickness) / 2 - templateThickness, %, $seg02)
|> xLineTo(segEndX(seg03) + minClampingDistance, %, $seg06)
|> yLine(templateThickness * 2, %, $seg08)
|> xLineTo(segEndX(seg02) + 0, %, $seg05)
|> yLineTo(segEndY(seg01) + templateThickness, %, $seg10)
|> xLineTo(ZERO, %, $seg04)
|> xLine(-segLen(seg04), %)
|> yLine(-segLen(seg10), %)
|> xLine(-segLen(seg05), %)
|> yLine(-segLen(seg08), %)
|> xLine(segLen(seg06), %)
|> yLine(-segLen(seg02), %)
|> xLine(segLen(seg07), %)
|> yLine(segLen(seg03), %)
|> arc({
angleEnd = 90,
angleStart = 180,
radius = 10 + templateGap
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude001 = extrude(sketch001, length = 5)
sketch003 = startSketchOn(extrude001, 'START')
|> startProfileAt([distanceToInsideEdge, 0], %)
|> angledLine([180, templateThickness], %, $rectangleSegmentA002)
|> angledLine([
segAng(rectangleSegmentA002) + 90,
templateThickness
], %, $rectangleSegmentB002)
|> angledLine([
segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002)
], %, $rectangleSegmentC002)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude003 = extrude(sketch003, length = 13)
sketch002 = startSketchOn(extrude001, 'START')
|> startProfileAt([-distanceToInsideEdge, 0], %)
|> angledLine([0, templateThickness], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
templateThickness
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude002 = extrude(sketch002, length = 13)
sketch004 = startSketchOn(extrude002, 'END')
|> startProfileAt([-distanceToInsideEdge, 0], %)
|> angledLine([0, distanceToInsideEdge * 2], %, $rectangleSegmentA003)
|> angledLine([
segAng(rectangleSegmentA003) - 90,
templateThickness
], %, $rectangleSegmentB003)
|> angledLine([
segAng(rectangleSegmentA003),
-segLen(rectangleSegmentA003)
], %, $rectangleSegmentC003)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude004 = extrude(sketch004, length = 4)

View File

@ -0,0 +1,88 @@
// Router template for a slate
// A guide for routing a slate for a cross bar.
// Set Units
@settings(defaultLengthUnit = mm)
// Define constants
routerDiameter = 12.7
templateDiameter = 11 / 16 * inch()
templateGap = (templateDiameter - routerDiameter) / 2 - 0.5
slateWidthHalf = 41.5 / 2
minClampingDistance = 50 + 30
templateThickness = 10
radius = 10
depth = 30
length001 = slateWidthHalf - radius
length002 = depth + minClampingDistance
// Create the first sketch
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, depth - templateGap], %)
|> xLine(length001, %, $seg01)
|> arc({
angleEnd = 0,
angleStart = 90,
radius = radius - templateGap
}, %)
|> yLineTo(-templateGap * 2 - (templateDiameter / 2), %, $seg05)
|> xLineTo(slateWidthHalf + templateThickness, %, $seg04)
|> yLine(-length002, %, $seg03)
|> xLineTo(ZERO, %, $seg02)
// |> line(end = [7.78, 11.16])
|> xLine(-segLen(seg02), %)
|> yLine(segLen(seg03), %)
|> xLine(segLen(seg04), %)
|> yLine(segLen(seg05), %)
|> arc({
angleEnd = 90,
angleStart = 180,
radius = radius - templateGap
}, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
// Extrude the first sketch
extrude001 = extrude(sketch001, length = 5)
// Create the second sketch
sketch002 = startSketchOn(extrude001, 'START')
|> startProfileAt([
-slateWidthHalf,
-templateGap * 2 - (templateDiameter / 2)
], %)
|> xLine(-7, %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) + 90,
minClampingDistance
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
// Extrude the second sketch
extrude002 = extrude(sketch002, length = 7.5)
// Create the third sketch
sketch003 = startSketchOn(extrude001, 'START')
|> startProfileAt([
slateWidthHalf,
-templateGap * 2 - (templateDiameter / 2)
], %)
|> xLine(7, %, $rectangleSegmentA002)
|> angledLine([
segAng(rectangleSegmentA002) - 90,
minClampingDistance
], %)
|> angledLine([
segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002)
], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
// Extrude the third Sketch
extrude003 = extrude(sketch003, length = 7.5)

View File

@ -0,0 +1,111 @@
// Sheet Metal Bracket
// A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly.
// Set Units
@settings(defaultLengthUnit = in)
// Define constants such as sheet metal thickness, bend radius, flange length, bolt diameter size, etc.
thickness = 0.090
bendRad = 0.08
outsideBendRad = bendRad + thickness
flangeLength = 0.5
hatHeight = 3
hatWidth = 5
boltSize = 0.25
flangeWidth = 1.5
// Sketch and extrude the base shape and fillet the inside and outside edges.
baseExtrusion = startSketchOn('-XZ')
|> startProfileAt([0, 0], %)
|> line(end = [0, thickness], tag = $e1)
|> line(end = [flangeLength, 0], tag = $e2)
|> line(end = [0, hatHeight], tag = $e3)
|> line(end = [hatWidth, 0], tag = $e4)
|> line(end = [0, -hatHeight], tag = $e5)
|> line(end = [flangeLength, 0], tag = $e6)
|> line(end = [0, -thickness], tag = $e7)
|> line(end = [-flangeLength - thickness, 0], tag = $e8)
|> line(end = [0, hatHeight], tag = $e9)
|> line(end = [-hatWidth + 2 * thickness, 0], tag = $e10)
|> line(end = [0, -hatHeight], tag = $e11)
|> close(tag = $e12)
|> extrude(length = hatWidth)
|> fillet(
radius = bendRad,
tags = [getNextAdjacentEdge(e2)]
)
|> fillet(
radius = outsideBendRad,
tags = [getNextAdjacentEdge(e3)]
)
|> fillet(
radius = outsideBendRad,
tags = [getNextAdjacentEdge(e4)]
)
|> fillet(
radius = bendRad,
tags = [getNextAdjacentEdge(e5)]
)
|> fillet(
radius = outsideBendRad,
tags = [getNextAdjacentEdge(e8)]
)
|> fillet(
radius = bendRad,
tags = [getNextAdjacentEdge(e9)]
)
|> fillet(
radius = bendRad,
tags = [getNextAdjacentEdge(e10)]
)
|> fillet(
radius = outsideBendRad,
tags = [getNextAdjacentEdge(e11)]
)
// Define the flanges and place the bolt holes
flange1 = startSketchOn('XY')
|> startProfileAt([0, 0], %)
|> line(end = [0, hatWidth])
|> line(end = [flangeWidth, 0], tag = $e13)
|> line(end = [0, -hatWidth], tag = $e14)
|> close()
|> hole(circle(
center = [0.75, 1],
radius = boltSize
), %)
|> hole(circle(
center = [0.75, 4],
radius = boltSize
), %)
|> extrude(length = thickness)
|> fillet(
radius = 0.5,
tags = [
getNextAdjacentEdge(e13),
getNextAdjacentEdge(e14)
]
)
flange2 = startSketchOn('XY')
|> startProfileAt([-6, 0], %)
|> line(end = [0, hatWidth])
|> line(end = [-flangeWidth, 0], tag = $e15)
|> line(end = [0, -hatWidth], tag = $e16)
|> close()
|> hole(circle(
center = [-6.75, 1],
radius = boltSize
), %)
|> hole(circle(
center = [-6.75, 4],
radius = boltSize
), %)
|> extrude(length = thickness)
|> fillet(
radius = 0.25,
tags = [
getNextAdjacentEdge(e15),
getNextAdjacentEdge(e16)
]
)

View File

@ -0,0 +1,68 @@
// Socket Head Cap Screw
// This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key.
// Set units
@settings(defaultLengthUnit = in)
// Define constants
screwLength = 1.0
screwDiameter = .190
headDiameter = .313
headLength = screwDiameter
hexWallToWall = 5 / 32
capRatio = screwDiameter / headDiameter
hexRatio = hexWallToWall / headDiameter
hexWallLength = hexWallToWall / 2 * 1 / cos(toRadians(30))
hexStartingAngle = 210 // first angle of hex pattern
hexInteriorAngle = 120
hexChangeAngle = 180 - hexInteriorAngle
// Write a function that defines the Socket Head Cap Screw
fn capScrew(start, length, dia, capHeadLength) {
// Create the head of the cap screw
screwHeadSketch = startSketchOn('XZ')
|> circle(
center = [start[0], start[1]],
radius = dia / capRatio / 2
)
// Extrude the screw head sketch
screwHead = extrude(screwHeadSketch, length = capHeadLength)
// Define the sketch of the hex pattern on the screw head
hexPatternSketch = startSketchOn(screwHead, 'end')
|> startProfileAt([hexWallToWall / 2, 0], %)
|> yLine(-hexWallLength / 2, %)
|> angledLine({
angle = hexStartingAngle,
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - hexChangeAngle,
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (2 * hexChangeAngle),
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (3 * hexChangeAngle),
length = hexWallLength
}, %)
|> angledLine({
angle = hexStartingAngle - (4 * hexChangeAngle),
length = hexWallLength
}, %)
|> close()
hexPattern = extrude(hexPatternSketch, length = -headLength * 0.75)
screwBodySketch = startSketchOn(screwHead, "start")
|> circle(
center = [start[0], start[1]],
radius = dia / 2
)
screwBody = extrude(screwBodySketch, length = length)
return screwBody
}
capScrew([0, 0], screwLength, screwDiameter, screwDiameter)

View File

@ -0,0 +1,50 @@
// Antenna
// Set units
@settings(defaultLengthUnit = in)
// import constants
import height, width, antennaBaseWidth, antennaBaseHeight, antennaTopWidth, antennaTopHeight from "globals.kcl"
// Calculate the origin
origin = [-width / 2 + .45, -0.10]
// Create the antenna
antennaX = origin[0]
antennaY = origin[1]
antennaPlane = {
plane = {
origin = { x = 0, y = 0, z = height / 2 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
}
// Create the antenna base sketch
sketch001 = startSketchOn(antennaPlane)
|> startProfileAt([origin[0], origin[1]], %)
|> line(end = [antennaBaseWidth, 0])
|> line(end = [0, -antennaBaseHeight])
|> line(end = [-antennaBaseWidth, 0])
|> close()
// Create the antenna top sketch
loftPlane = offsetPlane('XY', offset = height / 2 + 3)
sketch002 = startSketchOn(loftPlane)
|> startProfileAt([
origin[0] + (antennaBaseWidth - antennaTopWidth) / 2,
origin[1] - ((antennaBaseHeight - antennaTopHeight) / 2)
], %)
|> xLine(antennaTopWidth, %)
|> yLine(-antennaTopHeight, %)
|> xLine(-antennaTopWidth, %)
|> close()
// Create the antenna using a loft
loft([sketch001, sketch002])
|> appearance(color = "#000000")

View File

@ -0,0 +1,80 @@
// Walkie talkie body
// Set units
@settings(defaultLengthUnit = in)
// Import constants
import height, width, thickness, chamferLength, offset, screenWidth, screenHeight, screenYPosition, screenDepth, speakerBoxWidth, speakerBoxHeight from "globals.kcl"
bodySketch = startSketchOn('XZ')
|> startProfileAt([-width / 2, height / 2], %)
|> xLine(width, %, $chamfer1)
|> yLine(-height, %, $chamfer2)
|> xLine(-width, %, $chamfer3)
|> close(tag = $chamfer4)
bodyExtrude = extrude(bodySketch, length = thickness)
|> chamfer(
length = chamferLength,
tags = [
getNextAdjacentEdge(chamfer1),
getNextAdjacentEdge(chamfer2),
getNextAdjacentEdge(chamfer3),
getNextAdjacentEdge(chamfer4)
]
)
// Define the offset for the indentation
sketch002 = startSketchOn(bodyExtrude, 'END')
|> startProfileAt([
-width / 2 + offset,
height / 2 - (chamferLength + offset / 2 * cos(toRadians(45)))
], %)
|> angledLineToY({ angle = 45, to = height / 2 - offset }, %)
|> line(endAbsolute = [
width / 2 - (chamferLength + offset / 2 * cos(toRadians(45))),
height / 2 - offset
])
|> angledLineToX({ angle = -45, to = width / 2 - offset }, %)
|> line(endAbsolute = [
width / 2 - offset,
-(height / 2 - (chamferLength + offset / 2 * cos(toRadians(45))))
])
|> angledLineToY({
angle = -135,
to = -height / 2 + offset
}, %)
|> line(endAbsolute = [
-(width / 2 - (chamferLength + offset / 2 * cos(toRadians(45)))),
-height / 2 + offset
])
|> angledLineToX({
angle = -225,
to = -width / 2 + offset
}, %)
|> close()
extrude002 = extrude(sketch002, length = -0.0625)
// Create the pocket for the screen
sketch003 = startSketchOn(extrude002, 'start')
|> startProfileAt([-screenWidth / 2, screenYPosition], %)
|> xLine(screenWidth, %, $seg01)
|> yLine(-screenHeight, %)
|> xLine(-segLen(seg01), %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
extrude003 = extrude(sketch003, length = screenDepth)
// Create the speaker box
sketch004 = startSketchOn(extrude002, 'start')
|> startProfileAt([-1.25 / 2, -.125], %)
|> xLine(speakerBoxWidth, %)
|> yLine(-speakerBoxHeight, %)
|> xLine(-speakerBoxWidth, %)
|> close()
extrude(sketch004, length = -.5)
|> appearance(
color = "#277bb0",
)

View File

@ -0,0 +1,38 @@
// Walkie Talkie button
// Set units
@settings(defaultLengthUnit = in)
// Import constants
import screenHeight, buttonWidth, tolerance, buttonHeight, buttonThickness from 'globals.kcl'
// Create a function for the button
export fn button(origin, rotation, plane) {
buttonSketch = startSketchOn(plane)
|> startProfileAt([origin[0], origin[1]], %)
|> angledLine({
angle = 180 + rotation,
length = buttonWidth
}, %, $tag1)
|> angledLine({
angle = 270 + rotation,
length = buttonHeight
}, %, $tag2)
|> angledLine({
angle = 0 + rotation,
length = buttonWidth
}, %)
|> close()
buttonExtrude = extrude(buttonSketch, length = buttonThickness)
|> chamfer(
length = .050,
tags = [
getNextAdjacentEdge(tag1),
getNextAdjacentEdge(tag2)
]
)
|> appearance(color = "#ff0000")
return buttonExtrude
}

View File

@ -0,0 +1,85 @@
// Walkie talkie case
// Set units
@settings(defaultLengthUnit = in)
// Import constants and Zoo logo
import width, height, chamferLength, offset, screenWidth, screenHeight, screenYPosition, screenDepth, speakerBoxWidth, speakerBoxHeight, squareHoleSideLength, caseTolerance from "globals.kcl"
import zLogo, oLogo, oLogo2 from "zoo-logo.kcl"
plane = offsetPlane("XZ", offset = 1)
fn screenHole(sketchStart) {
sketch006 = startSketchOn(sketchStart)
|> startProfileAt([-screenWidth / 2, screenYPosition], %)
|> xLine(screenWidth, %)
|> yLine(-screenHeight, %)
|> xLine(-screenWidth, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
return sketch006
}
fn squareHolePattern(plane, x, y) {
fn transformX(i) {
return { translate = [.125 * i, 0] }
}
fn transformY(i) {
return { translate = [0, -.125 * i] }
}
squareHolePatternSketch = startSketchOn(plane)
|> startProfileAt([-x, -y], %)
|> line(end = [squareHoleSideLength / 2, 0])
|> line(end = [0, -squareHoleSideLength / 2])
|> line(end = [-squareHoleSideLength / 2, 0])
|> close()
|> patternTransform2d(instances = 13, transform = transformX)
|> patternTransform2d(instances = 11, transform = transformY)
return squareHolePatternSketch
}
sketch005 = startSketchOn(offsetPlane("XZ", offset = 1))
|> startProfileAt([
-width / 2 + offset + caseTolerance,
height / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45)))
], %)
|> angledLineToY({
angle = 45,
to = height / 2 - (offset + caseTolerance)
}, %)
|> line(endAbsolute = [
width / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45))),
height / 2 - (offset + caseTolerance)
])
|> angledLineToX({
angle = -45,
to = width / 2 - (offset + caseTolerance)
}, %)
|> line(endAbsolute = [
width / 2 - (offset + caseTolerance),
-(height / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45))))
])
|> angledLineToY({
angle = -135,
to = -height / 2 + offset + caseTolerance
}, %)
|> line(endAbsolute = [
-(width / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45)))),
-height / 2 + offset + caseTolerance
])
|> angledLineToX({
angle = -225,
to = -width / 2 + offset + caseTolerance
}, %)
|> close()
|> hole(screenHole(plane), %)
|> hole(squareHolePattern(plane, .75, .125), %)
|> hole(zLogo(plane, [-.30, -1.825], .20), %)
|> hole(oLogo(plane, [-.075, -1.825], .20), %)
|> hole(oLogo2(plane, [-.075, -1.825], .20), %)
|> hole(oLogo(plane, [.175, -1.825], .20), %)
|> hole(oLogo2(plane, [.175, -1.825], .20), %)
extrude(sketch005, length = -0.0625)
|> appearance(color = '#D0FF01', metalness = 0, roughness = 50)

View File

@ -0,0 +1,42 @@
// Global constants for the walkie talkie
// Set units
@settings(defaultLengthUnit = in)
// body
export height = 4
export width = 2.5
export thickness = 1
export chamferLength = .325
export offset = .125
export screenWidth = 1.75
export screenHeight = 1
export screenYPosition = height / 2 - 0.75
export screenDepth = -.0625
export speakerBoxWidth = 1.25
export speakerBoxHeight = 1.25
// antenna
export antennaBaseWidth = .5
export antennaBaseHeight = .25
export antennaTopWidth = .30
export antennaTopHeight = .05
// button
export buttonWidth = .15
export tolerance = 0.020
export buttonHeight = screenHeight / 2 - tolerance
export buttonThickness = .040
// case
export squareHoleSideLength = 0.0625
export caseTolerance = 0.010
// knob
export knobDiameter = .5
export knobHeight = .25
export knobRadius = 0.050
// talk-button
export talkButtonSideLength = 0.5
export talkButtonHeight = 0.050

View File

@ -0,0 +1,38 @@
// Walkie talkie knob
// Set units
@settings(defaultLengthUnit = in)
// Import constants
import width, thickness, height, knobDiameter, knobHeight, knobRadius from "globals.kcl"
// Define the plane for the knob
knobPlane = {
plane = {
origin = {
x = width / 2 - 0.70,
y = -thickness / 2,
z = height / 2
},
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 0, y = 1, z = 0 }
}
}
// Create the knob sketch and revolve
startSketchOn(knobPlane)
|> startProfileAt([0.0001, 0], %)
|> xLine(knobDiameter / 2, %)
|> yLine(knobHeight - 0.05, %)
|> arc({
angleStart = 0,
angleEnd = 90,
radius = .05
}, %)
|> xLineTo(0.0001, %)
|> close()
|> revolve({ axis = "Y" }, %)
|> appearance(color = '#D0FF01', metalness = 90, roughness = 50)

View File

@ -0,0 +1,50 @@
// Walkie Talkie
// A portable, handheld two-way radio device that allows users to communicate wirelessly over short to medium distances. It operates on specific radio frequencies and features a push-to-talk button for transmitting messages, making it ideal for quick and reliable communication in outdoor, work, or emergency settings.
// Set units
@settings(defaultLengthUnit = in)
// Import parts and constants
import 'body.kcl'
import 'antenna.kcl'
import 'case.kcl'
import 'talk-button.kcl' as talkButton
import 'knob.kcl'
import button from "button.kcl"
import width, height, thickness, screenWidth, screenHeight, screenYPosition, tolerance from "globals.kcl"
// Import the body
body
// Import the case
case
// Import the antenna
antenna
// Import the buttons
button([
-(screenWidth / 2 + tolerance),
screenYPosition
], 0, offsetPlane("XZ", offset = thickness))
button([
-(screenWidth / 2 + tolerance),
screenYPosition - (screenHeight / 2)
], 0, offsetPlane("XZ", offset = thickness))
button([
screenWidth / 2 + tolerance,
screenYPosition - screenHeight
], 180, offsetPlane("XZ", offset = thickness))
button([
screenWidth / 2 + tolerance,
screenYPosition - (screenHeight / 2)
], 180, offsetPlane("XZ", offset = thickness))
// Import the talk button
talkButton
// Import the frequency knob
knob

View File

@ -0,0 +1,46 @@
// Walkie talkie talk button
// Set units
@settings(defaultLengthUnit = in)
// Import constants
import width, thickness, talkButtonSideLength, talkButtonHeight from "globals.kcl"
talkButtonPlane = {
plane = {
origin = {
x = width / 2,
y = -thickness / 2,
z = .5
},
xAxis = { x = 0, y = 1, z = 0 },
yAxis = { x = 0, y = 0, z = 1 },
zAxis = { x = 1, y = 0, z = 0 }
}
}
// Create the talk button sketch
talkButtonSketch = startSketchOn(talkButtonPlane)
|> startProfileAt([
-talkButtonSideLength / 2,
talkButtonSideLength / 2
], %)
|> xLine(talkButtonSideLength, %, $tag1)
|> yLine(-talkButtonSideLength, %, $tag2)
|> xLine(-talkButtonSideLength, %, $tag3)
|> close(tag = $tag4)
// Create the talk button and apply fillets
extrude(talkButtonSketch, length = talkButtonHeight)
|> fillet(
radius = 0.050,
tags = [
getNextAdjacentEdge(tag1),
getNextAdjacentEdge(tag2),
getNextAdjacentEdge(tag3),
getNextAdjacentEdge(tag4)
]
)
|> appearance(color = '#D0FF01', metalness = 90, roughness = 90)

View File

@ -0,0 +1,83 @@
// Zoo logo
// Define a function to draw the ZOO "Z"
export fn zLogo(surface, origin, scale) {
zSketch = surface
|> startProfileAt([
0 + origin[0],
0.15 * scale + origin[1]
], %)
|> yLine(-0.15 * scale, %)
|> xLine(0.15 * scale, %)
|> angledLineToX({
angle = 47.15,
to = 0.3 * scale + origin[0]
}, %, $seg1)
|> yLineTo(0 + origin[1], %, $seg3)
|> xLine(0.63 * scale, %)
|> yLine(0.225 * scale, %)
|> xLine(-0.57 * scale, %)
|> angledLineToX({
angle = 47.15,
to = 0.93 * scale + origin[0]
}, %)
|> yLine(0.15 * scale, %)
|> xLine(-0.15 * scale, %)
|> angledLine({
angle = 47.15,
length = -segLen(seg1)
}, %, $seg2)
|> yLine(segLen(seg3), %)
|> xLineTo(0 + origin[0], %)
|> yLine(-0.225 * scale, %)
|> angledLineThatIntersects({
angle = 0,
intersectTag = seg2,
offset = 0
}, %)
|> close()
return zSketch
}
// Define a function to draw the ZOO "O"
export fn oLogo(surface, origin, scale) {
oSketch001 = surface
|> startProfileAt([
.788 * scale + origin[0],
.921 * scale + origin[1]
], %)
|> arc({
angleStart = 47.15 + 6,
angleEnd = 47.15 - 6 + 180,
radius = .525 * scale
}, %)
|> angledLine({ angle = 47.15, length = .24 * scale }, %)
|> arc({
angleStart = 47.15 - 11 + 180,
angleEnd = 47.15 + 11,
radius = .288 * scale
}, %)
|> close()
return oSketch001
}
export fn oLogo2(surface, origin, scale) {
oSketch002 = surface
|> startProfileAt([
.16 * scale + origin[0],
.079 * scale + origin[1]
], %)
|> arc({
angleStart = 47.15 + 6 - 180,
angleEnd = 47.15 - 6,
radius = .525 * scale
}, %)
|> angledLine({ angle = 47.15, length = -.24 * scale }, %)
|> arc({
angleStart = 47.15 - 11,
angleEnd = 47.15 + 11 - 180,
radius = .288 * scale
}, %)
|> close()
return oSketch002
}

View File

@ -0,0 +1,30 @@
// Washer
// A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time.
// Set units
@settings(defaultLengthUnit = in)
// Define constants in inches (in)
innerDiameter = 0.203
outerDiameter = 0.438
thicknessMax = 0.038
thicknessMin = 0.024
// Write a function that defines the washer and extrude it.
fn washer(plane, innerDia, outerDia, thk) {
// Define the sketch of the washer
washerSketch = startSketchOn(plane)
|> circle(
center = [0, 0],
radius = outerDia / 2
)
|> hole(circle(
center = [0, 0],
radius = innerDia / 2
), %)
washer = extrude(washerSketch, length = thk)
return washer
}
washer('XY', innerDiameter, outerDiameter, thicknessMax)