Compare commits

...

25 Commits

Author SHA1 Message Date
87dfda28a9 Move axes to std constants; move helix, revolve, and mirror2d to be declated in KCL
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-04-02 17:05:28 +13:00
42f44e11f5 Add Edge type to std
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-03-28 11:18:10 +08:00
16ad7ff77a Move turns to a submodule of std
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-03-28 11:10:51 +08:00
71b9e40bd9 Fix to not use cursed empty object type and add lint (#6033) 2025-03-27 22:08:57 +00:00
4f35197a96 Add a loading state to CommandBarKclInput while calculating (#6025)
* Add a loading state to CommandBarKclInput while calculating

This is so that we can more reliably await for the calculation to be
completed before advancing the command bar in Playwright E2E tests.

* Make sure the spinner shows on first frame, before `isExecuting` is set too
2025-03-27 16:22:04 -04:00
40b0cf5fd3 Create a new commit to preserve history from main (#6019) 2025-03-27 15:49:38 +00:00
355e6acf0d Make tests fail when there are console errors (#6015)
* Add a test to confirm console errors fail tests

* Check for console errors on all browsers

* Ignore error impacting lots of tests

* Add more detected errors to the allowlist for now
2025-03-27 15:41:25 +00:00
4ff38e7f44 Add dual-sink and makeup mirror to KCL samples (#6023)
* add makeup mirror

* m -> M

* add metal sink unit

* Update kcl-samples simulation test output

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-27 10:57:01 -04:00
1dcd3b84b7 Populate all environment variables from files (#6014) 2025-03-27 10:38:24 -04:00
2957216bd3 Fix kitt kcl-sample expected output (#6022) 2025-03-27 14:31:12 +00:00
11160f0b40 Point-and-click Helix from cylinders (#5979) 2025-03-26 17:57:30 -04:00
4b2c745db5 Fix error: unknown sketch function circle (#6012) 2025-03-26 14:04:59 -04:00
bb983021b1 Feature: Traditional menu actions in desktop application (#5892)
* chore: skeleton for building and creating menus. Need electron to renderer interface to dynamically set the menu

* chore: skeleton typing for communication between nodes and web side

* chore: more skeleton for the different roles within the menu options, need more type safety

* chore: adding more skeleton and templates of what the menus could be

* chore: implemented first pass for helpRole links

* fix: syntax issue stopped the build step

* feature: loading different menus based on your page

* feature: Home page file role implemented

* chore: handling the build workflow for the signin page

* fix: moving edit actionst to the edit menu

* chore: adding preferences to the file role

* chore: redoing help roles based on the question mark widget

* fix: auto fmt

* chore: examples of accelerator strings for Menu.MenuItems keyboard shortcuts!

* chore: oddly specific toggle API for disabling MenuItems from JS. No rules!

* fix: do not implement a custom label disable thingy, use id on menu and use the native APIga

* fix: auto fmt

* fix: adding some typechecks and auto fmt fixes

* fix: trying to fix custom type?

* fix: nvm we back, the lsp on my editor borked for a second

* fix: adding one more level to the custom type for the labels

* chore: cleaning up type definitions to read easier

* fix: resolving yarn lint errors

* chore: adding file sign out

* chore: adding more file bar actions

* chore: ready for PR draft

* fix: preemptive GC collectoin bug fix if somehow a user interacts with a menu while it is being GCed

* fix: linking the OG source

* fix: set application menu to null to avoid default electron menu

* chore: trying to add more typescript

* chore: BIG workflow changes... better typing, less IPC junk

* fix: remapping the icp functions to the cb option select...

* chore: all og events are rehooked up with new workflow pattern

* feat: adding more options to the native bar!

* fix: todo

* chore: cleaning up some menus and adding more

* fix: desktop vs browser and lint errors

* fix: typescript did not like sample electorn JS code for the basic templates with isMac conditionals...

* fix: PR clean up

* fix: more PR cleanup

* A snapshot a day keeps the bugs away! 📷🐛

* fix: added the new help menu to the default sign in and modeling page

* fix: disabled two menu actions within sign in page since they will not do anything.

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* fix: mergining main, auto fixes

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* fix: fixed ipc renderer off/remove listener bug

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* fix: report a bug to refresha and report a bug

* fix: new type for webContents send payload that does not brick TS

* fix: removing import file from url since it is not working in the command palette for manual user input

* fix: removing old comment

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* chore: adding some E2E tests.

* chore: added E2E tests for each file menu

* fix: auto fixes

* chore: adding more edit role E2E tests

* chore: e2e test

* chore: adding help role e2e test

* A snapshot a day keeps the bugs away! 📷🐛

* chore: e2e test for all the menu options you can interact with in the frontend

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-26 13:03:44 -05:00
d27b8871bc Set bot user info to amend commits (#6011) 2025-03-26 17:12:20 +00:00
1753047d87 Feature: Release named views to all users (#5814)
* chore: cleanup to get named views released!

* fix: fixed gizmo, client side camera sync and remove DEV flag

* yarp

* chore: implementing E2E tests for creating a named view

* fix: cleaning up and commenting E2E tests for named views

* fix: we did it bois, the skip ceral i zation bricked my E2E test :(

* fix: auto formatter

* fix: snapshot uuid matching because rust will randomly generate thme

* fix: auto fmt

* fix: trying to resolve typescript issues

* fix: handling NamedView vs CameraViewState type checking

* fix: no idea I just mapped export to 3d export because we have no 2d export yet...

* fix: random file I wrote because my editor was too slow

* fix: git merge did not do what I wanted

* A snapshot a day keeps the bugs away! 📷🐛

* fix: linter errors

* A snapshot a day keeps the bugs away! 📷🐛

---------

Co-authored-by: 49fl <ircsurfer33@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
2025-03-26 16:12:35 +00:00
fa16fcedff update all kcl-samples w/ format (#5999) 2025-03-26 11:53:34 -04:00
0677474097 Bump KCL in prep for release (#6010) 2025-03-26 15:52:32 +00:00
0c4826cdd5 Prevent overwriting the commit status on main (#6009) 2025-03-26 10:41:41 -04:00
b6fe660b84 Add edit flow for point-and-click Chamfer and Fillet (#5946)
* WIP: Add edit flow for Fillet
Fixes #5521

* Support sweepedge fillet and add edit tests

* A snapshot a day keeps the bugs away! 📷🐛

* Lint and cleanup

* Add edit flow for Chamfer
Fixes #5950

* Change to shared prepareToEdit function

* Clean up

* Lint

* Clean up of types and use of getEdgeCutConsumedCodeRef

* Find pipeIndex instead of hardcode

* Add error for non-pipe fillet and test it

* A snapshot a day keeps the bugs away! 📷🐛

* Fix lint

* A snapshot a day keeps the bugs away! 📷🐛

* Utility function to reduce code reuse across fillet and chamfer

* Clean up test diff

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Lint

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Remove change not needed

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Fix typo in toast

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Remove ['segment', 'sweepEdge'] const as it was causing some sort of circ dep

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-26 11:57:08 +00:00
c53fa421ad Give a warning when using per-project default units (#5956)
* Give a warning when using per-project default units

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Factor non-settings out of MetaSettings

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Fix formatting

* Fix code pane e2e test

* Fix callstack blowup in edit flow

* Avoid dumb timeout issue with command registration in test

* Use a safer way to wait for modeling command registration in test

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-03-26 18:59:43 +13:00
736533a482 Ignore unit tests when running Playwright Electron (#5996)
* Ignore unit tests when running Playwright Electron

* Skip a couple more Windows tests for now
2025-03-25 20:59:07 -04:00
a15565682d Update main.kcl (#5989)
* Update main.kcl

Improving parameter naming in I beam

* Update kcl-samples simulation test output

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Josh Gomez <114548659+jgomez720@users.noreply.github.com>
2025-03-25 23:36:53 +00:00
58861cd24a ci: Fix e2e workflow to always use condition (#5994)
Fix e2e workflow to always use condition
2025-03-25 23:18:57 +00:00
c0cdcb1b98 Disable automated snapshot commits (#6002) 2025-03-25 22:27:30 +00:00
41f45afb3c [bug] Fix edit parameter flow and test (#5997)
* Fix callstack blowup in edit flow

* Avoid dumb timeout issue with command registration in test

* A snapshot a day keeps the bugs away! 📷🐛

* Use a safer way to wait for modeling command registration in test

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* De-flake create parameter test step

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-03-25 22:10:52 +00:00
414 changed files with 79845 additions and 48763 deletions

View File

@ -1,5 +1,6 @@
NODE_ENV=development NODE_ENV=development
DEV=true DEV=true
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
@ -8,3 +9,5 @@ VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000 VITE_KC_CONNECTION_TIMEOUT_MS=5000
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence! # ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local" #VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
FAIL_ON_CONSOLE_ERRORS=true

10
.envrc
View File

@ -1,3 +1,13 @@
# Load optional shared environment variables
source_up_if_exists source_up_if_exists
# Load default development environment variables
dotenv .env.development
# Load optional environment variables overrides
dotenv_if_exists .env.development.local
# Load optional testing environment variables
dotenv_if_exists e2e/playwright/playwright-secrets.env
use flake . use flake .

View File

@ -20,6 +20,7 @@
"plugin:react-hooks/recommended" "plugin:react-hooks/recommended"
], ],
"rules": { "rules": {
"@typescript-eslint/no-empty-object-type": "error",
"@typescript-eslint/no-floating-promises": "error", "@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-misused-promises": "error", "@typescript-eslint/no-misused-promises": "error",
"@typescript-eslint/no-unused-vars": ["error", { "@typescript-eslint/no-unused-vars": ["error", {

View File

@ -257,7 +257,8 @@ jobs:
fi fi
- name: Commit changes, if any - name: Commit changes, if any
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }} # TODO: find a more reliable way to detect visual changes
if: ${{ false && needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
shell: bash shell: bash
run: | run: |
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
@ -343,7 +344,7 @@ jobs:
run: yarn tronb:vite:dev run: yarn tronb:vite:dev
- name: Install vector - name: Install vector
if: contains(matrix.os, 'ubuntu') if: ${{ needs.conditions.outputs.should-run == 'true' && contains(matrix.os, 'ubuntu') }}
shell: bash shell: bash
run: | run: |
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh

View File

@ -28,12 +28,18 @@ jobs:
- name: Sync with main - name: Sync with main
run: | run: |
# checkout our branch # Create the branch
git checkout all-e2e || git checkout -b all-e2e git checkout all-e2e || git checkout -b all-e2e
# fetch origin
# Reset to main
git fetch origin git fetch origin
# reset to main
git reset --hard origin/main git reset --hard origin/main
# force push it
# Get a new SHA to prevent overwriting the commit status on main
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit --allow-empty --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
# Overwrite the branch
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
git push --force origin all-e2e git push --force origin all-e2e

View File

@ -54,7 +54,7 @@ example = extrude(exampleSketch, length = 5)
// Add color to a revolved solid. // Add color to a revolved solid.
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
|> circle(center = [15, 0], radius = 5) |> circle(center = [15, 0], radius = 5)
|> revolve(angle = 360, axis = 'y') |> revolve(angle = 360, axis = Y)
|> appearance(color = '#ff0000', metalness = 90, roughness = 90) |> appearance(color = '#ff0000', metalness = 90, roughness = 90)
``` ```

View File

@ -9,13 +9,12 @@ layout: manual
### `std` ### `std`
- [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN) - [`X`](/docs/kcl/consts/std-X)
- [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN)
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN)
- [`XY`](/docs/kcl/consts/std-XY) - [`XY`](/docs/kcl/consts/std-XY)
- [`XZ`](/docs/kcl/consts/std-XZ) - [`XZ`](/docs/kcl/consts/std-XZ)
- [`Y`](/docs/kcl/consts/std-Y)
- [`YZ`](/docs/kcl/consts/std-YZ) - [`YZ`](/docs/kcl/consts/std-YZ)
- [`ZERO`](/docs/kcl/consts/std-ZERO) - [`Z`](/docs/kcl/consts/std-Z)
### `std::math` ### `std::math`
@ -23,3 +22,10 @@ layout: manual
- [`PI`](/docs/kcl/consts/std-math-PI) - [`PI`](/docs/kcl/consts/std-math-PI)
- [`TAU`](/docs/kcl/consts/std-math-TAU) - [`TAU`](/docs/kcl/consts/std-math-TAU)
### `std::turns`
- [`HALF_TURN`](/docs/kcl/consts/std-turns-HALF_TURN)
- [`QUARTER_TURN`](/docs/kcl/consts/std-turns-QUARTER_TURN)
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-turns-THREE_QUARTER_TURN)
- [`ZERO`](/docs/kcl/consts/std-turns-ZERO)

View File

@ -1,15 +0,0 @@
---
title: "std::HALF_TURN"
excerpt: ""
layout: manual
---
```js
std::HALF_TURN: number(deg) = 180deg
```

View File

@ -1,15 +0,0 @@
---
title: "std::QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::QUARTER_TURN: number(deg) = 90deg
```

View File

@ -1,15 +0,0 @@
---
title: "std::THREE_QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::THREE_QUARTER_TURN: number(deg) = 270deg
```

View File

@ -1,5 +1,5 @@
--- ---
title: "std::ZERO" title: "std::X"
excerpt: "" excerpt: ""
layout: manual layout: manual
--- ---
@ -9,7 +9,7 @@ layout: manual
```js ```js
std::ZERO: number = 0 std::X
``` ```

15
docs/kcl/consts/std-Y.md Normal file
View File

@ -0,0 +1,15 @@
---
title: "std::Y"
excerpt: ""
layout: manual
---
```js
std::Y
```

15
docs/kcl/consts/std-Z.md Normal file
View File

@ -0,0 +1,15 @@
---
title: "std::Z"
excerpt: ""
layout: manual
---
```js
std::Z
```

View File

@ -0,0 +1,15 @@
---
title: "std::turns::HALF_TURN"
excerpt: ""
layout: manual
---
```js
std::turns::HALF_TURN: number(deg) = 180deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::turns::QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::turns::QUARTER_TURN: number(deg) = 90deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::turns::THREE_QUARTER_TURN"
excerpt: ""
layout: manual
---
```js
std::turns::THREE_QUARTER_TURN: number(deg) = 270deg
```

View File

@ -0,0 +1,15 @@
---
title: "std::turns::ZERO"
excerpt: ""
layout: manual
---
```js
std::turns::ZERO: number = 0
```

File diff suppressed because one or more lines are too long

View File

@ -22,20 +22,22 @@ layout: manual
* [`string`](kcl/types/string) * [`string`](kcl/types/string)
* [`tag`](kcl/types/tag) * [`tag`](kcl/types/tag)
* **std** * **std**
* [`Axis2d`](kcl/types/Axis2d)
* [`Axis3d`](kcl/types/Axis3d)
* [`Edge`](kcl/types/Edge)
* [`Face`](kcl/types/Face) * [`Face`](kcl/types/Face)
* [`HALF_TURN`](kcl/consts/std-HALF_TURN)
* [`Helix`](kcl/types/Helix) * [`Helix`](kcl/types/Helix)
* [`Plane`](kcl/types/Plane) * [`Plane`](kcl/types/Plane)
* [`Point2d`](kcl/types/Point2d) * [`Point2d`](kcl/types/Point2d)
* [`Point3d`](kcl/types/Point3d) * [`Point3d`](kcl/types/Point3d)
* [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
* [`Sketch`](kcl/types/Sketch) * [`Sketch`](kcl/types/Sketch)
* [`Solid`](kcl/types/Solid) * [`Solid`](kcl/types/Solid)
* [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN) * [`X`](kcl/consts/std-X)
* [`XY`](kcl/consts/std-XY) * [`XY`](kcl/consts/std-XY)
* [`XZ`](kcl/consts/std-XZ) * [`XZ`](kcl/consts/std-XZ)
* [`Y`](kcl/consts/std-Y)
* [`YZ`](kcl/consts/std-YZ) * [`YZ`](kcl/consts/std-YZ)
* [`ZERO`](kcl/consts/std-ZERO) * [`Z`](kcl/consts/std-Z)
* [`abs`](kcl/abs) * [`abs`](kcl/abs)
* [`acos`](kcl/acos) * [`acos`](kcl/acos)
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX) * [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
@ -72,7 +74,7 @@ layout: manual
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge) * [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
* [`getOppositeEdge`](kcl/getOppositeEdge) * [`getOppositeEdge`](kcl/getOppositeEdge)
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge) * [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
* [`helix`](kcl/helix) * [`helix`](kcl/std-helix)
* [`hole`](kcl/hole) * [`hole`](kcl/hole)
* [`hollow`](kcl/hollow) * [`hollow`](kcl/hollow)
* [`inch`](kcl/inch) * [`inch`](kcl/inch)
@ -91,7 +93,6 @@ layout: manual
* [`map`](kcl/map) * [`map`](kcl/map)
* [`max`](kcl/max) * [`max`](kcl/max)
* [`min`](kcl/min) * [`min`](kcl/min)
* [`mirror2d`](kcl/mirror2d)
* [`mm`](kcl/mm) * [`mm`](kcl/mm)
* [`offsetPlane`](kcl/offsetPlane) * [`offsetPlane`](kcl/offsetPlane)
* [`patternCircular2d`](kcl/patternCircular2d) * [`patternCircular2d`](kcl/patternCircular2d)
@ -110,7 +111,7 @@ layout: manual
* [`push`](kcl/push) * [`push`](kcl/push)
* [`reduce`](kcl/reduce) * [`reduce`](kcl/reduce)
* [`rem`](kcl/rem) * [`rem`](kcl/rem)
* [`revolve`](kcl/revolve) * [`revolve`](kcl/std-revolve)
* [`rotate`](kcl/rotate) * [`rotate`](kcl/rotate)
* [`round`](kcl/round) * [`round`](kcl/round)
* [`scale`](kcl/scale) * [`scale`](kcl/scale)
@ -146,3 +147,9 @@ layout: manual
* [`tan`](kcl/std-math-tan) * [`tan`](kcl/std-math-tan)
* **std::sketch** * **std::sketch**
* [`circle`](kcl/std-sketch-circle) * [`circle`](kcl/std-sketch-circle)
* [`mirror2d`](kcl/std-sketch-mirror2d)
* **std::turns**
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -146,7 +146,7 @@ exampleSketch = startSketchOn(XY)
|> line(end = [-2, 0]) |> line(end = [-2, 0])
|> close() |> close()
example = revolve(exampleSketch, axis = 'y', angle = 180) example = revolve(exampleSketch, axis = Y, angle = 180)
exampleSketch002 = startSketchOn(example, 'end') exampleSketch002 = startSketchOn(example, 'end')
|> startProfileAt([4.5, -5], %) |> startProfileAt([4.5, -5], %)
@ -177,7 +177,7 @@ exampleSketch = startSketchOn(XY)
example = revolve( example = revolve(
exampleSketch, exampleSketch,
axis = 'y', axis = Y,
angle = 180, angle = 180,
tagEnd = $end01, tagEnd = $end01,
) )

116
docs/kcl/std-helix.md Normal file

File diff suppressed because one or more lines are too long

246
docs/kcl/std-revolve.md Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -74,7 +74,7 @@ helixPath = helix(
revolutions = 4, revolutions = 4,
length = 10, length = 10,
radius = 5, radius = 5,
axis = 'Z', axis = Z,
) )
// Create a spring by sweeping around the helix path. // Create a spring by sweeping around the helix path.

12
docs/kcl/types/Axis2d.md Normal file
View File

@ -0,0 +1,12 @@
---
title: "std::Axis2d"
excerpt: "An infinte line in 2d space."
layout: manual
---
An infinte line in 2d space.

12
docs/kcl/types/Axis3d.md Normal file
View File

@ -0,0 +1,12 @@
---
title: "std::Axis3d"
excerpt: "An infinte line in 3d space."
layout: manual
---
An infinte line in 3d space.

12
docs/kcl/types/Edge.md Normal file
View File

@ -0,0 +1,12 @@
---
title: "std::Edge"
excerpt: "The edge of a solid."
layout: manual
---
The edge of a solid.

View File

@ -21,14 +21,15 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
await page.addInitScript(() => { await page.addInitScript(() => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`// Extruded Triangle `@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(XZ) // Extruded Triangle
|> startProfileAt([0, 0], %) sketch001 = startSketchOn(XZ)
|> line(end = [10, 0]) |> startProfileAt([0, 0], %)
|> line(end = [-5, 10]) |> line(end = [10, 0])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(end = [-5, 10])
|> close() |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
extrude001 = extrude(sketch001, length = 5)` |> close()
extrude001 = extrude(sketch001, length = 5)`
) )
}) })

View File

@ -513,7 +513,8 @@ c = 3 + a`
await homePage.openProject(projectName) await homePage.openProject(projectName)
// TODO: you probably shouldn't need an engine connection to add a parameter, // TODO: you probably shouldn't need an engine connection to add a parameter,
// but you do because all modeling commands have that requirement // but you do because all modeling commands have that requirement
await scene.settled(cmdBar) // Don't use scene.settled here
await expect(scene.startEditSketchBtn).toBeEnabled({ timeout: 15_000 })
await test.step(`Create a parameter via command bar`, async () => { await test.step(`Create a parameter via command bar`, async () => {
await cmdBar.cmdBarOpenBtn.click() await cmdBar.cmdBarOpenBtn.click()
@ -542,7 +543,12 @@ c = 3 + a`
) )
const newValue = `2 * b + a` const newValue = `2 * b + a`
await test.step(`Edit the parameter via command bar`, async () => { await test.step(`Edit the parameter via command bar`, async () => {
// TODO: make the command palette command registration more static, and the enabled state more dynamic
// so that we can just open the command palette and know all commands will be there.
await expect(scene.startEditSketchBtn).toBeEnabled()
await cmdBar.cmdBarOpenBtn.click() await cmdBar.cmdBarOpenBtn.click()
await cmdBar.chooseCommand('edit parameter') await cmdBar.chooseCommand('edit parameter')
await cmdBar.expectState({ await cmdBar.expectState({

View File

@ -21,7 +21,7 @@ sketch001 = startSketchOn(XZ)
|> angledLine([-45, length001], %) |> angledLine([-45, length001], %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
revolve001 = revolve(sketch001, axis = "X") revolve001 = revolve(sketch001, axis = X)
triangle() triangle()
|> extrude(length = 30) |> extrude(length = 30)
plane001 = offsetPlane(XY, offset = 10) plane001 = offsetPlane(XY, offset = 10)
@ -126,7 +126,7 @@ test.describe('Feature Tree pane', () => {
await testViewSource({ await testViewSource({
operationName: 'Revolve', operationName: 'Revolve',
operationIndex: 0, operationIndex: 0,
expectedActiveLine: 'revolve001 = revolve(sketch001, axis = "X")', expectedActiveLine: 'revolve001 = revolve(sketch001, axis = X)',
}) })
await testViewSource({ await testViewSource({
operationName: 'Triangle', operationName: 'Triangle',

View File

@ -257,6 +257,46 @@ export const isErrorWhitelisted = (exception: Error) => {
project: 'Google Chrome', project: 'Google Chrome',
foundInSpec: 'e2e/playwright/testing-settings.spec.ts', foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
}, },
// TODO: fix this error in the code
{
name: 'TypeError',
message: "Cannot read properties of undefined (reading 'length')",
stack: '',
project: 'Google Chrome',
foundInSpec: '', // many tests are impacted by this error
},
// TODO: fix this error in the code
{
name: 'ReferenceError',
message: '_testUtils is not defined',
stack: '',
project: 'Google Chrome',
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
},
// TODO: fix this error in the code
{
name: 'TypeError',
message: 'Failed to fetch',
stack: '',
project: 'Google Chrome',
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
},
// TODO: fix this error in the code
{
name: 'ReferenceError',
message: 'originalCode is not defined',
stack: '',
project: 'Google Chrome',
foundInSpec: 'e2e/playwright/onboarding-tests.spec.ts',
},
// TODO: fix this error in the code
{
name: 'ReferenceError',
message: 'createNewVariableCheckbox is not defined',
stack: '',
project: 'Google Chrome',
foundInSpec: 'e2e/playwright/testing-constraints.spec.ts',
},
] ]
const cleanString = (str: string) => str.replace(/[`"]/g, '') const cleanString = (str: string) => str.replace(/[`"]/g, '')

View File

@ -0,0 +1,292 @@
import { test, expect } from './zoo-test'
import { PROJECT_SETTINGS_FILE_NAME } from 'lib/constants'
import * as fsp from 'fs/promises'
import { join } from 'path'
import {
createProject,
tomlToPerProjectSettings,
perProjectsettingsToToml,
} from './test-utils'
import { NamedView } from '@rust/kcl-lib/bindings/NamedView'
// Helper function to determine if the file path on disk exists
// Specifically this is used to check if project.toml exists on disk
const fileExists = async (path: string) => {
return !!(await fsp
.stat(path)
.then((_) => true)
.catch((_) => false))
}
// Here are a few uuids.
// When created named views rust will auto generate uuids and they will
// never match the snapshots. Overwrite them in memory to these
// values to have them match the snapshots.
const uuid1: string = '0656fb1a-9640-473e-b334-591dc70c0138'
const uuid2: string = 'c810cf04-c6cc-4a4a-8b11-17bf445dcab7'
const uuid3: string = 'cfecbfee-48a6-4561-b96d-ffbe5678bb7d'
// Look up the named view by name and then rewrite it with the same uuid each time
const nameToUuid: Map<string, string> = new Map()
nameToUuid.set('uuid1', uuid1)
nameToUuid.set('uuid2', uuid2)
nameToUuid.set('uuid3', uuid3)
/**
* Given the project.toml string, overwrite the named views to be the constant uuid
* values to match the snapshots. The uuids are randomly generated
*/
function tomlStringOverWriteNamedViewUuids(toml: string): string {
const settings = tomlToPerProjectSettings(toml)
const namedViews = settings.settings?.app?.named_views
if (namedViews) {
const entries = Object.entries(namedViews)
const remappedNamedViews: { [key: string]: NamedView } = {}
entries.forEach(([_, value]) => {
if (value) {
// {name:'uuid1'} -> uuid1 lookup
const staticUuid = nameToUuid.get(value.name)
if (staticUuid) {
remappedNamedViews[staticUuid] = value
}
}
})
if (settings && settings.settings && settings.settings.app) {
settings.settings.app.named_views = remappedNamedViews
}
}
return perProjectsettingsToToml(settings)
}
test.describe('Named view tests', () => {
test('Verify project.toml is not created', async ({ page }, testInfo) => {
// Create project and load it
const projectName = 'named-views'
await createProject({ name: projectName, page })
// Generate file paths for project.toml
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
// project.toml should not exist on initial project creation
let exists = await fileExists(tempProjectSettingsFilePath)
expect(exists).toBe(false)
})
test('Verify named view gets created', async ({
cmdBar,
scene,
page,
}, testInfo) => {
const projectName = 'named-views'
const myNamedView = 'uuid1'
// Create and load project
await createProject({ name: projectName, page })
await scene.waitForExecutionDone()
// Create named view
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('create named view')
await cmdBar.argumentInput.fill(myNamedView)
await cmdBar.progressCmdBar(false)
// Generate paths for the project.toml
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
// Expect project.toml to be generated on disk since a named view was created
await expect(async () => {
let exists = await fileExists(tempProjectSettingsFilePath)
expect(exists).toBe(true)
}).toPass()
// Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
})
test('Verify named view gets deleted', async ({
cmdBar,
scene,
page,
}, testInfo) => {
const projectName = 'named-views'
const myNamedView1 = 'uuid1'
const myNamedView2 = 'uuid2'
// Create project and go into the project
await createProject({ name: projectName, page })
await scene.waitForExecutionDone()
// Create a new named view
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('create named view')
await cmdBar.argumentInput.fill(myNamedView1)
await cmdBar.progressCmdBar(false)
// Generate file paths for project.toml
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
// Except the project.toml to be written to disk since a named view was created
await expect(async () => {
let exists = await fileExists(tempProjectSettingsFilePath)
expect(exists).toBe(true)
}).toPass()
// Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
// Delete a named view
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('delete named view')
cmdBar.selectOption({ name: myNamedView2 })
await cmdBar.progressCmdBar(false)
// Read project.toml into memory again since we deleted a named view
tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// // Write the entire tomlString to a snapshot.
// // There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-deleted')
})
test('Verify named view gets loaded', async ({
cmdBar,
scene,
page,
}, testInfo) => {
const projectName = 'named-views'
const myNamedView = 'uuid1'
// Create project and go into the project
await createProject({ name: projectName, page })
await scene.waitForExecutionDone()
// Create a new named view
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('create named view')
await cmdBar.argumentInput.fill(myNamedView)
await cmdBar.progressCmdBar(false)
// Generate file paths for project.toml
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
// Except the project.toml to be written to disk since a named view was created
await expect(async () => {
let exists = await fileExists(tempProjectSettingsFilePath)
expect(exists).toBe(true)
}).toPass()
// Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-named-view-gets-created')
// Create a load a named view
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('load named view')
await cmdBar.argumentInput.fill(myNamedView)
await cmdBar.progressCmdBar(false)
// Check the toast appeared
await expect(
page.getByText(`Named view ${myNamedView} loaded.`)
).toBeVisible()
})
test('Verify two named views get created', async ({
cmdBar,
scene,
page,
}, testInfo) => {
const projectName = 'named-views'
const myNamedView1 = 'uuid1'
const myNamedView2 = 'uuid2'
// Create and load project
await createProject({ name: projectName, page })
await scene.waitForExecutionDone()
// Create named view
const projectDirName = testInfo.outputPath('electron-test-projects-dir')
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('create named view')
await cmdBar.argumentInput.fill(myNamedView1)
await cmdBar.progressCmdBar(false)
await page.waitForTimeout(1000)
const orbitMouseStart = { x: 800, y: 130 }
const orbitMouseEnd = { x: 0, y: 130 }
await page.mouse.move(orbitMouseStart.x, orbitMouseStart.y)
await page.mouse.down({ button: 'middle' })
await page.mouse.move(orbitMouseEnd.x, orbitMouseEnd.y, {
steps: 3,
})
await page.mouse.up({ button: 'middle' })
await page.waitForTimeout(1000)
await cmdBar.openCmdBar()
await cmdBar.chooseCommand('create named view')
await cmdBar.argumentInput.fill(myNamedView2)
await cmdBar.progressCmdBar(false)
// Wait a moment for the project.toml to get written to disk with the new view point
await page.waitForTimeout(1000)
// Generate paths for the project.toml
const tempProjectSettingsFilePath = join(
projectDirName,
projectName,
PROJECT_SETTINGS_FILE_NAME
)
// Expect project.toml to be generated on disk since a named view was created
await expect(async () => {
let exists = await fileExists(tempProjectSettingsFilePath)
expect(exists).toBe(true)
}).toPass()
// Read project.toml into memory
let tomlString = await fsp.readFile(tempProjectSettingsFilePath, 'utf-8')
// Rewrite the uuids in the named views to match snapshot otherwise they will be randomly generated from rust and break
tomlString = tomlStringOverWriteNamedViewUuids(tomlString)
// Write the entire tomlString to a snapshot.
// There are many key/value pairs to check this is a safer match.
expect(tomlString).toMatchSnapshot('verify-two-named-view-gets-created')
})
})

View File

@ -0,0 +1,16 @@
[settings]
modeling = { }
text_editor = { }
command_bar = { }
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
name = "uuid1"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 0, 0, 0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1

View File

@ -0,0 +1,16 @@
[settings]
modeling = { }
text_editor = { }
command_bar = { }
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
name = "uuid1"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 0, 0, 0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1

View File

@ -0,0 +1,28 @@
[settings]
modeling = { }
text_editor = { }
command_bar = { }
[settings.app.named_views.0656fb1a-9640-473e-b334-591dc70c0138]
name = "uuid1"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 0, 0, 0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1
[settings.app.named_views.c810cf04-c6cc-4a4a-8b11-17bf445dcab7]
name = "uuid2"
eye_offset = 1_378.0059
fov_y = 45
is_ortho = false
ortho_scale_enabled = true
ortho_scale_factor = 1.6
pivot_position = [ 1_826.5239, 0.0, 0.0 ]
pivot_rotation = [ 0.5380994, 0.0, 0.0, 0.8428814 ]
world_coord_system = "right_handed_up_z"
version = 1

View File

@ -0,0 +1,312 @@
import { test, expect } from './zoo-test'
/**
* Not all menu actions are tested. Some are default electron menu actions.
* Test file menu actions that trigger something in the frontend
*/
test.describe('Native file menu', { tag: ['@electron'] }, () => {
test.describe('Home page', () => {
test.describe('File role', () => {
test('File.Create project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const newProject =
app.applicationMenu.getMenuItemById('File.New project')
if (!newProject) fail()
newProject.click()
})
// Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists
const actualArgument = await cmdBar.cmdBarElement
.getByTestId('cmd-bar-arg-value')
.inputValue()
const expectedArgument = 'project-$nnn'
expect(actualArgument).toBe(expectedArgument)
})
test('File.Open project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const openProject =
app.applicationMenu.getMenuItemById('File.Open project')
if (!openProject) fail()
openProject.click()
})
// Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = 'Open project'
expect(actual).toBe(expected)
})
test('File.Preferences.User settings', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const userSettings = app.applicationMenu.getMenuItemById(
'File.Preferences.User settings'
)
if (!userSettings) fail()
userSettings.click()
})
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
// You are viewing the user tab
const actualText = settings.getByText(
'The overall appearance of the app'
)
await expect(actualText).toBeVisible()
})
test('File.Preferences.Keybindings', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const keybindings = app.applicationMenu.getMenuItemById(
'File.Preferences.Keybindings'
)
if (!keybindings) fail()
keybindings.click()
})
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
// You are viewing the keybindings tab
const enterSketchMode = settings.locator('#enter-sketch-mode')
await expect(enterSketchMode).toBeVisible()
})
test('File.Preferences.User default units', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.User default units'
)
if (!menu) fail()
menu.click()
})
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
const defaultUnit = settings.locator('#defaultUnit')
await expect(defaultUnit).toBeVisible()
})
test('File.Preferences.Theme', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.Theme'
)
if (!menu) fail()
menu.click()
})
// Check that the command bar is opened
await expect(cmdBar.cmdBarElement).toBeVisible()
// Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = 'Settings · app · theme'
expect(actual).toBe(expected)
})
test('File.Preferences.Theme color', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'File.Preferences.Theme color'
)
if (!menu) fail()
menu.click()
})
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
const defaultUnit = settings.locator('#themeColor')
await expect(defaultUnit).toBeVisible()
})
test('File.Preferences.Sign out', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById('File.Sign out')
if (!menu) fail()
// FIXME: Add back when you can actually sign out
// menu.click()
})
// FIXME: When signing out during E2E the page is not bound correctly.
// It cannot find the button
// const signIn = page.getByTestId('sign-in-button')
// await expect(signIn).toBeVisible()
})
})
test.describe('Edit role', () => {
test('Edit.Rename project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Edit.Rename project'
)
if (!menu) fail()
menu.click()
})
// Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = 'Rename project'
expect(actual).toBe(expected)
})
test('Edit.Delete project', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Edit.Delete project'
)
if (!menu) fail()
menu.click()
})
// Check the placeholder project name exists
const actual = await cmdBar.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = 'Delete project'
expect(actual).toBe(expected)
})
test('Edit.Change project directory', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Edit.Change project directory'
)
if (!menu) fail()
menu.click()
})
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
const projectDirectory = settings.locator('#projectDirectory')
await expect(projectDirectory).toBeVisible()
})
})
test.describe('View role', () => {
test('View.Command Palette...', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'View.Command Palette...'
)
if (!menu) fail()
menu.click()
})
// Check the placeholder project name exists
const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search')
await expect(actual).toBeVisible()
})
})
test.describe('Help role', () => {
test('Help.Show all commands', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Help.Show all commands'
)
if (!menu) fail()
menu.click()
})
// Check the placeholder project name exists
const actual = cmdBar.cmdBarElement.getByTestId('cmd-bar-search')
await expect(actual).toBeVisible()
})
test('Help.KCL code samples', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Help.KCL code samples'
)
if (!menu) fail()
})
})
test('Help.Refresh and report a bug', async ({
tronApp,
cmdBar,
page,
}) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Help.Refresh and report a bug'
)
if (!menu) fail()
menu.click()
})
// Core dump and refresh magic number timeout
await page.waitForTimeout(7000)
const actual = page.getByText(
'No Projects found, ready to make your first one?'
)
await expect(actual).toBeVisible()
})
test('Help.Reset onboarding', async ({ tronApp, cmdBar, page }) => {
if (!tronApp) fail()
// Run electron snippet to find the Menu!
await tronApp.electron.evaluate(async ({ app }) => {
if (!app || !app.applicationMenu) fail()
const menu = app.applicationMenu.getMenuItemById(
'Help.Reset onboarding'
)
if (!menu) fail()
menu.click()
})
const actual = page.getByText(
`This is a hardware design tool that lets you edit visually, with code, or both. It's powered by the KittyCAD Design API, the first API created for anyone to build hardware design tools.`
)
await expect(actual).toBeVisible()
})
})
})
})

View File

@ -1082,8 +1082,8 @@ openSketch = startSketchOn(XY)
}) => { }) => {
// One dumb hardcoded screen pixel value // One dumb hardcoded screen pixel value
const testPoint = { x: 620, y: 257 } const testPoint = { x: 620, y: 257 }
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, ccw = false, radius = 5, axis = 'X', length = 5,)` const expectedOutput = `helix001 = helix( axis = 'X', radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
const expectedLine = `revolutions=1,` const expectedLine = `axis='X',`
await homePage.goToModelingScene() await homePage.goToModelingScene()
@ -1091,17 +1091,17 @@ openSketch = startSketchOn(XY)
await toolbar.helixButton.click() await toolbar.helixButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'axisOrEdge', currentArgKey: 'mode',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
Mode: '',
AngleStart: '', AngleStart: '',
AxisOrEdge: '', Revolutions: '',
CounterClockWise: '',
Length: '', Length: '',
Radius: '', Radius: '',
Revolutions: '', CounterClockWise: '',
}, },
highlightedHeaderArg: 'axisOrEdge', highlightedHeaderArg: 'mode',
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -1110,7 +1110,19 @@ openSketch = startSketchOn(XY)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.expectState({
stage: 'review',
headerArguments: {
Mode: 'Axis',
Axis: 'X',
AngleStart: '360',
Revolutions: '1',
Length: '5',
Radius: '5',
CounterClockWise: '',
},
commandName: 'Helix',
})
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
}) })
@ -1134,30 +1146,31 @@ openSketch = startSketchOn(XY)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Helix', commandName: 'Helix',
stage: 'arguments', stage: 'arguments',
currentArgKey: 'length', currentArgKey: 'CounterClockWise',
currentArgValue: initialInput, currentArgValue: '',
headerArguments: { headerArguments: {
AngleStart: '360',
Axis: 'X', Axis: 'X',
CounterClockWise: '', AngleStart: '360',
Length: initialInput,
Radius: '5',
Revolutions: '1', Revolutions: '1',
Radius: '5',
Length: initialInput,
CounterClockWise: '',
}, },
highlightedHeaderArg: 'length', highlightedHeaderArg: 'CounterClockWise',
}) })
await page.keyboard.press('Shift+Backspace')
await expect(cmdBar.currentArgumentInput).toBeVisible() await expect(cmdBar.currentArgumentInput).toBeVisible()
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput) await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
AngleStart: '360',
Axis: 'X', Axis: 'X',
CounterClockWise: '', AngleStart: '360',
Length: newInput,
Radius: '5',
Revolutions: '1', Revolutions: '1',
Radius: '5',
Length: newInput,
CounterClockWise: '',
}, },
commandName: 'Helix', commandName: 'Helix',
}) })
@ -1181,14 +1194,14 @@ openSketch = startSketchOn(XY)
{ {
selectionType: 'segment', selectionType: 'segment',
testPoint: { x: 513, y: 221 }, testPoint: { x: 513, y: 221 },
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 100,)`, expectedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 50,)`, expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
}, },
{ {
selectionType: 'sweepEdge', selectionType: 'sweepEdge',
testPoint: { x: 564, y: 364 }, testPoint: { x: 564, y: 364 },
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`, expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`, expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
}, },
] ]
helixCases.map( helixCases.map(
@ -1225,17 +1238,17 @@ openSketch = startSketchOn(XY)
await toolbar.helixButton.click() await toolbar.helixButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'axisOrEdge', currentArgKey: 'mode',
currentArgValue: '', currentArgValue: '',
headerArguments: { headerArguments: {
AngleStart: '', AngleStart: '',
AxisOrEdge: '', Mode: '',
CounterClockWise: '', CounterClockWise: '',
Length: '', Length: '',
Radius: '', Radius: '',
Revolutions: '', Revolutions: '',
}, },
highlightedHeaderArg: 'axisOrEdge', highlightedHeaderArg: 'mode',
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.selectOption({ name: 'Edge' }).click() await cmdBar.selectOption({ name: 'Edge' }).click()
@ -1246,7 +1259,6 @@ openSketch = startSketchOn(XY)
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('0') await page.keyboard.insertText('0')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.selectOption({ name: 'True' }).click()
await page.keyboard.insertText('1') await page.keyboard.insertText('1')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await page.keyboard.insertText('100') await page.keyboard.insertText('100')
@ -1254,13 +1266,13 @@ openSketch = startSketchOn(XY)
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
AngleStart: '0', Mode: 'Edge',
AxisOrEdge: 'Edge',
Edge: `1 ${selectionType}`, Edge: `1 ${selectionType}`,
CounterClockWise: '', AngleStart: '0',
Length: '100',
Radius: '1',
Revolutions: '20', Revolutions: '20',
Radius: '1',
Length: '100',
CounterClockWise: '',
}, },
commandName: 'Helix', commandName: 'Helix',
}) })
@ -1285,17 +1297,18 @@ openSketch = startSketchOn(XY)
await cmdBar.expectState({ await cmdBar.expectState({
commandName: 'Helix', commandName: 'Helix',
stage: 'arguments', stage: 'arguments',
currentArgKey: 'length', currentArgKey: 'CounterClockWise',
currentArgValue: initialInput, currentArgValue: '',
headerArguments: { headerArguments: {
AngleStart: '0', AngleStart: '0',
CounterClockWise: '',
Length: initialInput,
Radius: '1',
Revolutions: '20', Revolutions: '20',
Radius: '1',
Length: initialInput,
CounterClockWise: '',
}, },
highlightedHeaderArg: 'length', highlightedHeaderArg: 'CounterClockWise',
}) })
await page.keyboard.press('Shift+Backspace')
await expect(cmdBar.currentArgumentInput).toBeVisible() await expect(cmdBar.currentArgumentInput).toBeVisible()
await cmdBar.currentArgumentInput await cmdBar.currentArgumentInput
.locator('.cm-content') .locator('.cm-content')
@ -1305,10 +1318,10 @@ openSketch = startSketchOn(XY)
stage: 'review', stage: 'review',
headerArguments: { headerArguments: {
AngleStart: '0', AngleStart: '0',
CounterClockWise: '',
Length: newInput,
Radius: '1',
Revolutions: '20', Revolutions: '20',
Radius: '1',
Length: newInput,
CounterClockWise: '',
}, },
commandName: 'Helix', commandName: 'Helix',
}) })
@ -1336,6 +1349,141 @@ openSketch = startSketchOn(XY)
} }
) )
test('Helix point-and-click on cylinder', async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(XY)
profile001 = circle(
sketch001,
center = [0, 0],
radius = 100,
tag = $seg01,
)
extrude001 = extrude(profile001, length = 100)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
// One dumb hardcoded screen pixel value
const testPoint = { x: 620, y: 257 }
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const expectedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = false,)`
const expectedLine = `cylinder = extrude001,`
const expectedEditedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = true,)`
await test.step(`Go through the command bar flow`, async () => {
await toolbar.helixButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'mode',
currentArgValue: '',
headerArguments: {
Mode: '',
AngleStart: '',
Revolutions: '',
Length: '',
Radius: '',
CounterClockWise: '',
},
highlightedHeaderArg: 'mode',
commandName: 'Helix',
})
await cmdBar.selectOption({ name: 'Cylinder' }).click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'cylinder',
currentArgValue: '',
headerArguments: {
Mode: 'Cylinder',
Cylinder: '',
AngleStart: '',
Revolutions: '',
CounterClockWise: '',
},
highlightedHeaderArg: 'cylinder',
commandName: 'Helix',
})
await clickOnWall()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Mode: 'Cylinder',
Cylinder: '1 face',
AngleStart: '360',
Revolutions: '1',
CounterClockWise: '',
},
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(expectedOutput)
await editor.expectState({
diagnostics: [],
activeLines: [expectedLine],
highlightedCode: '',
})
})
await test.step(`Edit helix through the feature tree`, async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
await operationButton.dblclick()
await cmdBar.expectState({
commandName: 'Helix',
stage: 'arguments',
currentArgKey: 'CounterClockWise',
currentArgValue: '',
headerArguments: {
AngleStart: '360',
Revolutions: '1',
CounterClockWise: '',
},
highlightedHeaderArg: 'CounterClockWise',
})
await cmdBar.selectOption({ name: 'True' }).click()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
AngleStart: '360',
Revolutions: '1',
CounterClockWise: 'true',
},
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.toContain(expectedEditedOutput)
await editor.closePane()
})
await test.step('Delete helix via feature tree selection', async () => {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Delete')
await toolbar.closePane('feature-tree')
await toolbar.openPane('code')
await editor.expectEditor.not.toContain(expectedEditedOutput)
})
})
const loftPointAndClickCases = [ const loftPointAndClickCases = [
{ shouldPreselect: true }, { shouldPreselect: true },
{ shouldPreselect: false }, { shouldPreselect: false },
@ -1773,7 +1921,7 @@ extrude001 = extrude(sketch001, length = -12)
const filletColor: [number, number, number] = [127, 127, 127] const filletColor: [number, number, number] = [127, 127, 127]
const backgroundColor: [number, number, number] = [30, 30, 30] const backgroundColor: [number, number, number] = [30, 30, 30]
const lowTolerance = 20 const lowTolerance = 20
const highTolerance = 40 const highTolerance = 70 // TODO: understand why I needed that for edgeColorYellow on macos (local)
// Setup // Setup
await test.step(`Initial test setup`, async () => { await test.step(`Initial test setup`, async () => {
@ -1860,6 +2008,54 @@ extrude001 = extrude(sketch001, length = -12)
await scene.expectPixelColor(filletColor, firstEdgeLocation, lowTolerance) await scene.expectPixelColor(filletColor, firstEdgeLocation, lowTolerance)
}) })
// Test 1.1: Edit fillet (segment type)
async function editFillet(
featureTreeIndex: number,
oldValue: string,
newValue: string
) {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Fillet',
featureTreeIndex
)
await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({
commandName: 'Fillet',
currentArgKey: 'radius',
currentArgValue: oldValue,
headerArguments: {
Radius: oldValue,
},
highlightedHeaderArg: 'radius',
stage: 'arguments',
})
await page.keyboard.insertText(newValue)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Radius: newValue,
},
commandName: 'Fillet',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
}
await test.step('Edit fillet via feature tree selection works', async () => {
const firstFilletFeatureTreeIndex = 0
const editedRadius = '1'
await editFillet(firstFilletFeatureTreeIndex, '5', editedRadius)
await editor.expectEditor.toContain(
firstFilletDeclaration.replace('radius = 5', 'radius = ' + editedRadius)
)
// Edit back to original radius
await editFillet(firstFilletFeatureTreeIndex, editedRadius, '5')
await editor.expectEditor.toContain(firstFilletDeclaration)
})
// Test 2: Command bar flow without preselected edges // Test 2: Command bar flow without preselected edges
await test.step(`Open fillet UI without selecting edges`, async () => { await test.step(`Open fillet UI without selecting edges`, async () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -1944,6 +2140,23 @@ extrude001 = extrude(sketch001, length = -12)
) )
}) })
// Test 2.1: Edit fillet (edgeSweep type)
await test.step('Edit fillet via feature tree selection works', async () => {
const secondFilletFeatureTreeIndex = 1
const editedRadius = '2'
await editFillet(secondFilletFeatureTreeIndex, '5', editedRadius)
await editor.expectEditor.toContain(
secondFilletDeclaration.replace(
'radius = 5',
'radius = ' + editedRadius
)
)
// Edit back to original radius
await editFillet(secondFilletFeatureTreeIndex, editedRadius, '5')
await editor.expectEditor.toContain(secondFilletDeclaration)
})
// Test 3: Delete fillets // Test 3: Delete fillets
await test.step('Delete fillet via feature tree selection', async () => { await test.step('Delete fillet via feature tree selection', async () => {
await test.step('Open Feature Tree Pane', async () => { await test.step('Open Feature Tree Pane', async () => {
@ -1966,6 +2179,43 @@ extrude001 = extrude(sketch001, length = -12)
}) })
}) })
test(`Fillet point-and-click edit rejected when not in pipe`, async ({
context,
page,
homePage,
scene,
toolbar,
}) => {
const initialCode = `sketch001 = startSketchOn(XY)
profile001 = circle(
sketch001,
center = [0, 0],
radius = 100,
tag = $seg01,
)
extrude001 = extrude(profile001, length = 100)
fillet001 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.waitForExecutionDone()
await test.step('Double-click in feature tree and expect error toast', async () => {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation('Fillet', 0)
await operationButton.dblclick({ button: 'left' })
await expect(
page.getByText(
'Only chamfer and fillet in pipe expressions are supported for edits'
)
).toBeVisible()
await page.waitForTimeout(1000)
})
})
test(`Fillet point-and-click delete`, async ({ test(`Fillet point-and-click delete`, async ({
context, context,
page, page,
@ -2262,7 +2512,7 @@ extrude001 = extrude(sketch001, length = -12)
const chamferColor: [number, number, number] = [168, 168, 168] const chamferColor: [number, number, number] = [168, 168, 168]
const backgroundColor: [number, number, number] = [30, 30, 30] const backgroundColor: [number, number, number] = [30, 30, 30]
const lowTolerance = 20 const lowTolerance = 20
const highTolerance = 40 const highTolerance = 70 // TODO: understand why I needed that for edgeColorYellow on macos (local)
// Setup // Setup
await test.step(`Initial test setup`, async () => { await test.step(`Initial test setup`, async () => {
@ -2344,6 +2594,57 @@ extrude001 = extrude(sketch001, length = -12)
) )
}) })
// Test 1.1: Edit sweep
async function editChamfer(
featureTreeIndex: number,
oldValue: string,
newValue: string
) {
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Chamfer',
featureTreeIndex
)
await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({
commandName: 'Chamfer',
currentArgKey: 'length',
currentArgValue: oldValue,
headerArguments: {
Length: oldValue,
},
highlightedHeaderArg: 'length',
stage: 'arguments',
})
await page.keyboard.insertText(newValue)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Length: newValue,
},
commandName: 'Chamfer',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
}
await test.step('Edit chamfer via feature tree selection works', async () => {
const firstChamferFeatureTreeIndex = 0
const editedLength = '1'
await editChamfer(firstChamferFeatureTreeIndex, '5', editedLength)
await editor.expectEditor.toContain(
firstChamferDeclaration.replace(
'length = 5',
'length = ' + editedLength
)
)
// Edit back to original radius
await editChamfer(firstChamferFeatureTreeIndex, editedLength, '5')
await editor.expectEditor.toContain(firstChamferDeclaration)
})
// Test 2: Command bar flow without preselected edges // Test 2: Command bar flow without preselected edges
await test.step(`Open chamfer UI without selecting edges`, async () => { await test.step(`Open chamfer UI without selecting edges`, async () => {
await page.waitForTimeout(100) await page.waitForTimeout(100)
@ -2428,6 +2729,23 @@ extrude001 = extrude(sketch001, length = -12)
) )
}) })
// Test 2.1: Edit chamfer (edgeSweep type)
await test.step('Edit chamfer via feature tree selection works', async () => {
const secondChamferFeatureTreeIndex = 1
const editedLength = '2'
await editChamfer(secondChamferFeatureTreeIndex, '5', editedLength)
await editor.expectEditor.toContain(
secondChamferDeclaration.replace(
'length = 5',
'length = ' + editedLength
)
)
// Edit back to original length
await editChamfer(secondChamferFeatureTreeIndex, editedLength, '5')
await editor.expectEditor.toContain(secondChamferDeclaration)
})
// Test 3: Delete chamfer via feature tree selection // Test 3: Delete chamfer via feature tree selection
await test.step('Open Feature Tree Pane', async () => { await test.step('Open Feature Tree Pane', async () => {
await toolbar.openPane('feature-tree') await toolbar.openPane('feature-tree')
@ -3151,7 +3469,7 @@ segAng(rectangleSegmentA002),
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = 'X')` const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)`
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
}) })
test('revolve surface around edge from an extruded solid2d', async ({ test('revolve surface around edge from an extruded solid2d', async ({

View File

@ -8,6 +8,7 @@ import {
createProject, createProject,
getPlaywrightDownloadDir, getPlaywrightDownloadDir,
orRunWhenFullSuiteEnabled, orRunWhenFullSuiteEnabled,
runningOnWindows,
} from './test-utils' } from './test-utils'
import fsp from 'fs/promises' import fsp from 'fs/promises'
import fs from 'fs' import fs from 'fs'
@ -351,6 +352,9 @@ test(
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene', 'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page }, testInfo) => {
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true }) await fsp.mkdir(bracketDir, { recursive: true })
@ -469,6 +473,9 @@ test.describe('Can export from electron app', () => {
if (!tronApp) { if (!tronApp) {
fail() fail()
} }
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const bracketDir = path.join(dir, 'bracket') const bracketDir = path.join(dir, 'bracket')
@ -1328,6 +1335,9 @@ test(
'Can load a file with CRLF line endings', 'Can load a file with CRLF line endings',
{ tag: '@electron' }, { tag: '@electron' },
async ({ context, page }, testInfo) => { async ({ context, page }, testInfo) => {
if (runningOnWindows()) {
test.fixme(orRunWhenFullSuiteEnabled())
}
await context.folderSetupFn(async (dir) => { await context.folderSetupFn(async (dir) => {
const routerTemplateDir = path.join(dir, 'router-template-slate') const routerTemplateDir = path.join(dir, 'router-template-slate')
await fsp.mkdir(routerTemplateDir, { recursive: true }) await fsp.mkdir(routerTemplateDir, { recursive: true })

View File

@ -778,6 +778,19 @@ plane002 = offsetPlane(XZ, offset = -2 * x)`
await editor.expectEditor.not.toContain(`plane002`) await editor.expectEditor.not.toContain(`plane002`)
}) })
}) })
test.fail(
'Console errors cause tests to fail',
async ({ page, homePage }) => {
const u = await getUtils(page)
await homePage.goToModelingScene()
await u.openAndClearDebugPanel()
await page.getByTestId('custom-cmd-input').fill('foobar')
await page.getByTestId('custom-cmd-send-button').scrollIntoViewIfNeeded()
await page.getByTestId('custom-cmd-send-button').click()
}
)
}) })
async function clickExportButton(page: Page) { async function clickExportButton(page: Page) {

View File

@ -674,7 +674,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|> line(end = [12.73, -0.09]) |> line(end = [12.73, -0.09])
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -5.38], %)
|> close() |> close()
|> revolve(axis = "X")` |> revolve(axis = X)`
) )
}) })
@ -761,7 +761,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|> tangentialArcTo([24.95, -5.38], %) |> tangentialArcTo([24.95, -5.38], %)
|> line(end = [1.97, 2.06]) |> line(end = [1.97, 2.06])
|> close() |> close()
|> revolve(axis = "X")`) |> revolve(axis = X)`)
}) })
test('Can add multiple sketches', async ({ page, homePage }) => { test('Can add multiple sketches', async ({ page, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
@ -1209,7 +1209,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|> xLine(endAbsolute = 0 + .001) |> xLine(endAbsolute = 0 + .001)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()
|> revolve(axis = "Y") |> revolve(axis = Y)
return lugSketch return lugSketch
} }

View File

@ -76,11 +76,11 @@ part001 = startSketchOn(-XZ)
|> xLine(endAbsolute = totalLen, tag = $seg03) |> xLine(endAbsolute = totalLen, tag = $seg03)
|> yLine(length = -armThick, tag = $seg01) |> yLine(length = -armThick, tag = $seg01)
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle = HALF_TURN, angle = turns::HALF_TURN,
offset = -armThick, offset = -armThick,
intersectTag = seg04 intersectTag = seg04
}, %) }, %)
|> angledLineToY([segAng(seg04, %) + 180, ZERO], %) |> angledLineToY([segAng(seg04, %) + 180, turns::ZERO], %)
|> angledLineToY({ |> angledLineToY({
angle = -bottomAng, angle = -bottomAng,
to = -totalHeightHalf - armThick, to = -totalHeightHalf - armThick,
@ -88,12 +88,12 @@ part001 = startSketchOn(-XZ)
|> xLine(length = endAbsolute = segEndX(seg03) + 0) |> xLine(length = endAbsolute = segEndX(seg03) + 0)
|> yLine(length = -segLen(seg01, %)) |> yLine(length = -segLen(seg01, %))
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle = HALF_TURN, angle = turns::HALF_TURN,
offset = -armThick, offset = -armThick,
intersectTag = seg02 intersectTag = seg02
}, %) }, %)
|> angledLineToY([segAng(seg02, %) + 180, -baseHeight], %) |> angledLineToY([segAng(seg02, %) + 180, -baseHeight], %)
|> xLine(endAbsolute = ZERO) |> xLine(endAbsolute = turns::ZERO)
|> close() |> close()
|> extrude(length = 4)` |> extrude(length = 4)`
) )

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

After

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -29,5 +29,5 @@
} }
} }
], ],
"kcl_version": "0.2.53" "kcl_version": "0.2.54"
} }

View File

@ -69,28 +69,31 @@ describe('utility to bypass unreliable tests', () => {
afterAll(() => { afterAll(() => {
process.env = { ...originalEnv } process.env = { ...originalEnv }
}) })
it('always runs them on dedicated branch', () => { it('always runs them on dedicated branch', () => {
process.env.GITHUB_EVENT_NAME = 'push' process.env.GITHUB_EVENT_NAME = 'push'
process.env.GITHUB_REF = 'refs/heads/all-e2e' process.env.GITHUB_REF = 'refs/heads/all-e2e'
process.env.GITHUB_HEAD_REF = '' process.env.GITHUB_HEAD_REF = ''
process.env.GITHUB_BASE_REF = '' process.env.GITHUB_BASE_REF = ''
const condition = orRunWhenFullSuiteEnabled() const shouldSkip = orRunWhenFullSuiteEnabled()
expect(condition).toBe(false) expect(shouldSkip).toBe(false)
}) })
it('skips them on the main branch', () => { it('skips them on the main branch', () => {
process.env.GITHUB_EVENT_NAME = 'push' process.env.GITHUB_EVENT_NAME = 'push'
process.env.GITHUB_REF = 'refs/heads/main' process.env.GITHUB_REF = 'refs/heads/main'
process.env.GITHUB_HEAD_REF = '' process.env.GITHUB_HEAD_REF = ''
process.env.GITHUB_BASE_REF = '' process.env.GITHUB_BASE_REF = ''
const condition = orRunWhenFullSuiteEnabled() const shouldSkip = orRunWhenFullSuiteEnabled()
expect(condition).toBe(true) expect(shouldSkip).toBe(true)
}) })
it('skips them on pull requests', () => { it('skips them on pull requests', () => {
process.env.GITHUB_EVENT_NAME = 'pull_request' process.env.GITHUB_EVENT_NAME = 'pull_request'
process.env.GITHUB_REF = 'refs/pull/5883/merge' process.env.GITHUB_REF = 'refs/pull/5883/merge'
process.env.GITHUB_HEAD_REF = 'my-branch' process.env.GITHUB_HEAD_REF = 'my-branch'
process.env.GITHUB_BASE_REF = 'main' process.env.GITHUB_BASE_REF = 'main'
const condition = orRunWhenFullSuiteEnabled() const shouldSkip = orRunWhenFullSuiteEnabled()
expect(condition).toBe(true) expect(shouldSkip).toBe(true)
}) })
}) })

View File

@ -26,6 +26,7 @@ import { isArray } from 'lib/utils'
import { reportRejection } from 'lib/trap' import { reportRejection } from 'lib/trap'
import { DeepPartial } from 'lib/types' import { DeepPartial } from 'lib/types'
import { Configuration } from 'lang/wasm' import { Configuration } from 'lang/wasm'
import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
const toNormalizedCode = (text: string) => { const toNormalizedCode = (text: string) => {
return text.replace(/\s+/g, '') return text.replace(/\s+/g, '')
@ -761,7 +762,7 @@ export interface Paths {
} }
export const doExport = async ( export const doExport = async (
output: Models['OutputFormat_type'], output: Models['OutputFormat3d_type'],
rootDir: string, rootDir: string,
page: Page, page: Page,
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown' exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
@ -934,47 +935,39 @@ export async function setup(
} }
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) { function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
// enabled for chrome for now page.on('pageerror', (exception: any) => {
if (page.context().browser()?.browserType().name() === 'chromium') { if (isErrorWhitelisted(exception)) {
// No idea wtf exception is return
page.on('pageerror', (exception: any) => { }
if (isErrorWhitelisted(exception)) { // Only disable this environment variable if you want to collect console errors
return if (process.env.FAIL_ON_CONSOLE_ERRORS !== 'false') {
} // Use expect to prevent page from closing and not cleaning up
expect(`An error was detected in the console: \r\n message:${exception.message} \r\n name:${exception.name} \r\n stack:${exception.stack}
// only set this env var to false if you want to collect console errors *Either fix the console error or add it to the whitelist defined in ./lib/console-error-whitelist.ts (if the error can be safely ignored)
// This can be configured in the GH workflow. This should be set to true by default (we want tests to fail when `).toEqual('Console error detected')
// unwhitelisted console errors are detected). } else {
if (process.env.FAIL_ON_CONSOLE_ERRORS === 'true') { // Add errors to `test-results/exceptions.txt` as a test artifact
// Fail when running on CI and FAIL_ON_CONSOLE_ERRORS is set fsp
// use expect to prevent page from closing and not cleaning up .appendFile(
expect(`An error was detected in the console: \r\n message:${exception.message} \r\n name:${exception.name} \r\n stack:${exception.stack} './test-results/exceptions.txt',
[
*Either fix the console error or add it to the whitelist defined in ./lib/console-error-whitelist.ts (if the error can be safely ignored) '~~~',
`).toEqual('Console error detected') `triggered_by_test:${
} else { testInfo?.file + ' ' + (testInfo?.title || ' ')
// the (test-results/exceptions.txt) file will be uploaded as part of an upload artifact in GH }`,
fsp `name:${exception.name}`,
.appendFile( `message:${exception.message}`,
'./test-results/exceptions.txt', `stack:${exception.stack}`,
[ `project:${testInfo?.project.name}`,
'~~~', '~~~',
`triggered_by_test:${ ].join('\n')
testInfo?.file + ' ' + (testInfo?.title || ' ') )
}`, .catch((err) => {
`name:${exception.name}`, console.error(err)
`message:${exception.message}`, })
`stack:${exception.stack}`, }
`project:${testInfo?.project.name}`, })
'~~~',
].join('\n')
)
.catch((err) => {
console.error(err)
})
}
})
}
} }
export async function isOutOfViewInScrollContainer( export async function isOutOfViewInScrollContainer(
element: Locator, element: Locator,
@ -1125,3 +1118,15 @@ export function settingsToToml(settings: DeepPartial<Configuration>) {
export function tomlToSettings(toml: string): DeepPartial<Configuration> { export function tomlToSettings(toml: string): DeepPartial<Configuration> {
return TOML.parse(toml) return TOML.parse(toml)
} }
export function tomlToPerProjectSettings(
toml: string
): DeepPartial<ProjectConfiguration> {
return TOML.parse(toml)
}
export function perProjectsettingsToToml(
settings: DeepPartial<ProjectConfiguration>
) {
return TOML.stringify(settings as any)
}

View File

@ -486,13 +486,13 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
testName: 'Add variable, selecting axis', testName: 'Add variable, selecting axis',
addVariable: true, addVariable: true,
axisSelect: true, axisSelect: true,
value: 'QUARTER_TURN - angle001', value: 'turns::QUARTER_TURN - angle001',
}, },
{ {
testName: 'No variable, selecting axis', testName: 'No variable, selecting axis',
addVariable: false, addVariable: false,
axisSelect: true, axisSelect: true,
value: 'QUARTER_TURN - 7', value: 'turns::QUARTER_TURN - 7',
}, },
] as const ] as const
for (const { testName, addVariable, value, axisSelect } of cases) { for (const { testName, addVariable, value, axisSelect } of cases) {
@ -935,12 +935,12 @@ part002 = startSketchOn(XZ)
test.describe('Axis & segment - no modal constraints', () => { test.describe('Axis & segment - no modal constraints', () => {
const cases = [ const cases = [
{ {
codeAfter: `|> line(endAbsolute = [154.9, ZERO])`, codeAfter: `|> line(endAbsolute = [154.9, turns::ZERO])`,
axisClick: { x: 950, y: 250 }, axisClick: { x: 950, y: 250 },
constraintName: 'Snap To X', constraintName: 'Snap To X',
}, },
{ {
codeAfter: `|> line(endAbsolute = [ZERO, 61.34])`, codeAfter: `|> line(endAbsolute = [turns::ZERO, 61.34])`,
axisClick: { x: 600, y: 150 }, axisClick: { x: 600, y: 150 },
constraintName: 'Snap To Y', constraintName: 'Snap To Y',
}, },

View File

@ -319,7 +319,7 @@ part009 = startSketchOn(XY)
|> line(end = [0, pipeLength]) |> line(end = [0, pipeLength])
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %) |> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|> close() |> close()
rev = revolve(part009, axis = 'y') rev = revolve(part009, axis = Y)
sketch006 = startSketchOn(XY) sketch006 = startSketchOn(XY)
profile001 = circle( profile001 = circle(
sketch006, sketch006,
@ -376,7 +376,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
await page.waitForTimeout(200) await page.waitForTimeout(200)
await expect(u.codeLocator).not.toContainText( await expect(u.codeLocator).not.toContainText(
`rev = revolve(part009, axis: 'y')` `rev = revolve(part009, axis: Y)`
) )
// FIXME (commented section below), this test would select a wall that had a sketch on it, and delete the underlying extrude // FIXME (commented section below), this test would select a wall that had a sketch on it, and delete the underlying extrude

View File

@ -67,11 +67,11 @@ part001 = startSketchOn(-XZ)
|> xLine(endAbsolute = totalLen, tag = $seg03) |> xLine(endAbsolute = totalLen, tag = $seg03)
|> yLine(length = -armThick, tag = $seg01) |> yLine(length = -armThick, tag = $seg01)
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle = HALF_TURN, angle = turns::HALF_TURN,
offset = -armThick, offset = -armThick,
intersectTag = seg04 intersectTag = seg04
}, %) }, %)
|> angledLineToY([segAng(seg04) + 180, ZERO], %) |> angledLineToY([segAng(seg04) + 180, turns::ZERO], %)
|> angledLineToY({ |> angledLineToY({
angle = -bottomAng, angle = -bottomAng,
to = -totalHeightHalf - armThick, to = -totalHeightHalf - armThick,
@ -79,12 +79,12 @@ part001 = startSketchOn(-XZ)
|> xLine(endAbsolute = segEndX(seg03) + 0) |> xLine(endAbsolute = segEndX(seg03) + 0)
|> yLine(length = -segLen(seg01)) |> yLine(length = -segLen(seg01))
|> angledLineThatIntersects({ |> angledLineThatIntersects({
angle = HALF_TURN, angle = turns::HALF_TURN,
offset = -armThick, offset = -armThick,
intersectTag = seg02 intersectTag = seg02
}, %) }, %)
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %) |> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|> xLine(endAbsolute = ZERO) |> xLine(endAbsolute = turns::ZERO)
|> close() |> close()
|> extrude(length = 4)` |> extrude(length = 4)`
) )

20
interface.d.ts vendored
View File

@ -3,6 +3,18 @@ import fsSync from 'node:fs'
import path from 'path' import path from 'path'
import { dialog, shell } from 'electron' import { dialog, shell } from 'electron'
import { MachinesListing } from 'components/MachineManagerProvider' import { MachinesListing } from 'components/MachineManagerProvider'
import type { Channel } from 'src/menu/channels'
import { Menu, WebContents } from 'electron'
import { ZooLabel, ZooMenuEvents } from 'menu/roles'
import type { MenuActionIPC } from 'menu/rules'
import type { WebContentSendPayload } from 'menu/channels'
// Extend the interface with additional custom properties
declare module 'electron' {
interface Menu {
label?: ZooLabel
}
}
type EnvFn = (value?: string) => string type EnvFn = (value?: string) => string
@ -94,6 +106,14 @@ export interface IElectronAPI {
appCheckForUpdates: () => Promise<unknown> appCheckForUpdates: () => Promise<unknown>
getArgvParsed: () => any getArgvParsed: () => any
getAppTestProperty: (propertyName: string) => any getAppTestProperty: (propertyName: string) => any
// Helper functions to create application Menus
createHomePageMenu: () => Promise<any>
createModelingPageMenu: () => Promise<any>
createFallbackMenu: () => Promise<any>
enableMenu(menuId: string): Promise<any>
disableMenu(menuId: string): Promise<any>
menuOn: (callback: (payload: WebContentSendPayload) => void) => any
} }
declare global { declare global {

View File

@ -26,7 +26,7 @@
"@fortawesome/react-fontawesome": "^0.2.0", "@fortawesome/react-fontawesome": "^0.2.0",
"@headlessui/react": "^1.7.19", "@headlessui/react": "^1.7.19",
"@headlessui/tailwindcss": "^0.2.0", "@headlessui/tailwindcss": "^0.2.0",
"@kittycad/lib": "2.0.17", "@kittycad/lib": "2.0.21",
"@lezer/highlight": "^1.2.1", "@lezer/highlight": "^1.2.1",
"@lezer/lr": "^1.4.1", "@lezer/lr": "^1.4.1",
"@react-hook/resize-observer": "^2.0.1", "@react-hook/resize-observer": "^2.0.1",

View File

@ -7,6 +7,7 @@ import { platform } from 'os'
export default defineConfig({ export default defineConfig({
timeout: 120_000, // override the default 30s timeout timeout: 120_000, // override the default 30s timeout
testDir: './e2e/playwright', testDir: './e2e/playwright',
testIgnore: '*.test.ts', // ignore unit tests
/* Run tests in files in parallel */ /* Run tests in files in parallel */
fullyParallel: true, fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */ /* Fail the build on CI if you accidentally left test.only in the source code. */

View File

@ -7,7 +7,7 @@
// Define function // Define function
fn rail8020(originStart, railHeight, railLength) { fn rail8020(originStart, railHeight, railLength) {
// Sketch side 1 of profile // Sketch side 1 of profile
sketch001 = startSketchOn('-XZ') sketch001 = startSketchOn(-XZ)
|> startProfileAt([ |> startProfileAt([
originStart[0], originStart[0],
0.1 * railHeight + originStart[1] 0.1 * railHeight + originStart[1]
@ -194,7 +194,7 @@ fn rail8020(originStart, railHeight, railLength) {
.5 * railHeight + originStart[0], .5 * railHeight + originStart[0],
.5 * railHeight + originStart[1] .5 * railHeight + originStart[1]
], ],
radius = .205 * railHeight / 2 radius = .205 * railHeight / 2,
), %) ), %)
|> extrude(length = railLength) |> extrude(length = railLength)
|> fillet( |> fillet(
@ -216,7 +216,7 @@ fn rail8020(originStart, railHeight, railLength) {
getNextAdjacentEdge(edge28), getNextAdjacentEdge(edge28),
getNextAdjacentEdge(edge29), getNextAdjacentEdge(edge29),
getNextAdjacentEdge(edge30) getNextAdjacentEdge(edge30)
] ],
) )
|> fillet( |> fillet(
radius = 0.03, radius = 0.03,
@ -237,7 +237,7 @@ fn rail8020(originStart, railHeight, railLength) {
getNextAdjacentEdge(edge26), getNextAdjacentEdge(edge26),
getNextAdjacentEdge(edge31), getNextAdjacentEdge(edge31),
getNextAdjacentEdge(edge32) getNextAdjacentEdge(edge32)
] ],
) )
return sketch001 return sketch001
} }

View File

@ -41,6 +41,8 @@ When you submit a PR to add or modify KCL samples, images and STEP files will be
[![cycloidal-gear](screenshots/cycloidal-gear.png)](cycloidal-gear/main.kcl) [![cycloidal-gear](screenshots/cycloidal-gear.png)](cycloidal-gear/main.kcl)
#### [dodecahedron](dodecahedron/main.kcl) ([screenshot](screenshots/dodecahedron.png)) #### [dodecahedron](dodecahedron/main.kcl) ([screenshot](screenshots/dodecahedron.png))
[![dodecahedron](screenshots/dodecahedron.png)](dodecahedron/main.kcl) [![dodecahedron](screenshots/dodecahedron.png)](dodecahedron/main.kcl)
#### [dual-basin-utility-sink](dual-basin-utility-sink/main.kcl) ([screenshot](screenshots/dual-basin-utility-sink.png))
[![dual-basin-utility-sink](screenshots/dual-basin-utility-sink.png)](dual-basin-utility-sink/main.kcl)
#### [enclosure](enclosure/main.kcl) ([screenshot](screenshots/enclosure.png)) #### [enclosure](enclosure/main.kcl) ([screenshot](screenshots/enclosure.png))
[![enclosure](screenshots/enclosure.png)](enclosure/main.kcl) [![enclosure](screenshots/enclosure.png)](enclosure/main.kcl)
#### [exhaust-manifold](exhaust-manifold/main.kcl) ([screenshot](screenshots/exhaust-manifold.png)) #### [exhaust-manifold](exhaust-manifold/main.kcl) ([screenshot](screenshots/exhaust-manifold.png))
@ -75,6 +77,8 @@ When you submit a PR to add or modify KCL samples, images and STEP files will be
[![kitt](screenshots/kitt.png)](kitt/main.kcl) [![kitt](screenshots/kitt.png)](kitt/main.kcl)
#### [lego](lego/main.kcl) ([screenshot](screenshots/lego.png)) #### [lego](lego/main.kcl) ([screenshot](screenshots/lego.png))
[![lego](screenshots/lego.png)](lego/main.kcl) [![lego](screenshots/lego.png)](lego/main.kcl)
#### [makeup-mirror](makeup-mirror/main.kcl) ([screenshot](screenshots/makeup-mirror.png))
[![makeup-mirror](screenshots/makeup-mirror.png)](makeup-mirror/main.kcl)
#### [mounting-plate](mounting-plate/main.kcl) ([screenshot](screenshots/mounting-plate.png)) #### [mounting-plate](mounting-plate/main.kcl) ([screenshot](screenshots/mounting-plate.png))
[![mounting-plate](screenshots/mounting-plate.png)](mounting-plate/main.kcl) [![mounting-plate](screenshots/mounting-plate.png)](mounting-plate/main.kcl)
#### [multi-axis-robot](multi-axis-robot/main.kcl) ([screenshot](screenshots/multi-axis-robot.png)) #### [multi-axis-robot](multi-axis-robot/main.kcl) ([screenshot](screenshots/multi-axis-robot.png))

View File

@ -15,57 +15,37 @@ padding = 1.5
bearingDia = 3 bearingDia = 3
// (Needs to be updated). Sketch the block and extrude up to where the counterbore diameter starts. // (Needs to be updated). Sketch the block and extrude up to where the counterbore diameter starts.
extrude001 = startSketchOn('XY') extrude001 = startSketchOn(XY)
|> startProfileAt([-width / 2, -length / 2], %) |> startProfileAt([-width / 2, -length / 2], %)
|> line(endAbsolute = [width / 2, -length / 2]) |> line(endAbsolute = [width / 2, -length / 2])
|> line(endAbsolute = [width / 2, length / 2]) |> line(endAbsolute = [width / 2, length / 2])
|> line(endAbsolute = [-width / 2, length / 2]) |> line(endAbsolute = [-width / 2, length / 2])
|> close() |> close()
|> extrude(length = height) |> extrude(length = height)
extrude002 = startSketchOn(extrude001, 'end') extrude002 = startSketchOn(extrude001, 'end')
|> circle( |> circle(
center = [ center = [
-(width / 2 - (padding / 2)), -(width / 2 - (padding / 2)),
-(length / 2 - (padding / 2)) -(length / 2 - (padding / 2))
], ],
radius = cbDia / 2, radius = cbDia / 2,
) )
|> patternLinear2d( |> patternLinear2d(instances = 2, distance = length - padding, axis = [0, 1])
instances = 2, |> patternLinear2d(instances = 2, distance = width - padding, axis = [1, 0])
distance = length - padding,
axis = [0, 1],
)
|> patternLinear2d(
instances = 2,
distance = width - padding,
axis = [1, 0],
)
|> extrude(%, length = -cbDepth) |> extrude(%, length = -cbDepth)
extrude003 = startSketchOn(extrude001, 'start') extrude003 = startSketchOn(extrude001, 'start')
|> circle( |> circle(
center = [ center = [
-(width / 2 - (padding / 2)), -(width / 2 - (padding / 2)),
-(length / 2 - (padding / 2)) -(length / 2 - (padding / 2))
], ],
radius = holeDia / 2, radius = holeDia / 2,
) )
|> patternLinear2d( |> patternLinear2d(instances = 2, distance = length - padding, axis = [0, 1])
instances = 2, |> patternLinear2d(instances = 2, distance = width - padding, axis = [1, 0])
distance = length - padding,
axis = [0, 1],
)
|> patternLinear2d(
instances = 2,
distance = width - padding,
axis = [1, 0],
)
|> extrude(length = -height + cbDepth) |> extrude(length = -height + cbDepth)
extrude004 = startSketchOn(extrude001, 'end') extrude004 = startSketchOn(extrude001, 'end')
|> circle( |> circle(center = [0, 0], radius = bearingDia / 2)
center = [0, 0], |> extrude(length = -height)
radius = bearingDia/2,
)
|> extrude(length = -height)

View File

@ -17,21 +17,15 @@ chainThickness = sphereDia / 8
linkDiameter = sphereDia / 4 linkDiameter = sphereDia / 4
// Sketch the inside bearing piece // Sketch the inside bearing piece
insideWallSketch = startSketchOn(offsetPlane("XY", offset = -overallThickness / 2)) insideWallSketch = startSketchOn(offsetPlane(XY, offset = -overallThickness / 2))
|> circle( |> circle(center = [0, 0], radius = shaftDia / 2 + wallThickness)
center = [0, 0], |> hole(circle(center = [0, 0], radius = shaftDia / 2), %)
radius = shaftDia / 2 + wallThickness
)
|> hole(circle(
center = [0, 0],
radius = shaftDia / 2
), %)
// Extrude the inside bearing piece // Extrude the inside bearing piece
insideWall = extrude(insideWallSketch, length = overallThickness) insideWall = extrude(insideWallSketch, length = overallThickness)
// Create the sketch of one of the balls // Create the sketch of one of the balls
ballsSketch = startSketchOn("XY") ballsSketch = startSketchOn(XY)
|> startProfileAt([shaftDia / 2 + wallThickness, 0.001], %) |> startProfileAt([shaftDia / 2 + wallThickness, 0.001], %)
|> arc({ |> arc({
angleEnd = 0, angleEnd = 0,
@ -41,17 +35,17 @@ ballsSketch = startSketchOn("XY")
|> close() |> close()
// Revolve the ball to make a sphere and pattern around the inside wall // Revolve the ball to make a sphere and pattern around the inside wall
balls = revolve(ballsSketch, axis = "X") balls = revolve(ballsSketch, axis = X)
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
instances = nBalls, instances = nBalls,
rotateDuplicates = true rotateDuplicates = true,
) )
// Create the sketch for the chain around the balls // Create the sketch for the chain around the balls
chainSketch = startSketchOn("XY") chainSketch = startSketchOn(XY)
|> startProfileAt([ |> startProfileAt([
shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2), shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2),
0.125 * sin(toRadians(60)) 0.125 * sin(toRadians(60))
@ -66,45 +60,39 @@ chainSketch = startSketchOn("XY")
|> close() |> close()
// Revolve the chain sketch // Revolve the chain sketch
chainHead = revolve(chainSketch, axis = "X") chainHead = revolve(chainSketch, axis = X)
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
instances = nBalls, instances = nBalls,
rotateDuplicates = true rotateDuplicates = true,
) )
// Create the sketch for the links in between the chains // Create the sketch for the links in between the chains
linkSketch = startSketchOn("XZ") linkSketch = startSketchOn(XZ)
|> circle( |> circle(
center = [ center = [
shaftDia / 2 + wallThickness + sphereDia / 2, shaftDia / 2 + wallThickness + sphereDia / 2,
0 0
], ],
radius = linkDiameter / 2 radius = linkDiameter / 2,
) )
// Revolve the link sketch // Revolve the link sketch
linkRevolve = revolve(linkSketch, axis = 'Y', angle = 360 / nBalls) linkRevolve = revolve(linkSketch, axis = Y, angle = 360 / nBalls)
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360,
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
instances = nBalls, instances = nBalls,
rotateDuplicates = true rotateDuplicates = true,
) )
// Create the sketch for the outside walls // Create the sketch for the outside walls
outsideWallSketch = startSketchOn(offsetPlane("XY", offset = -overallThickness / 2)) outsideWallSketch = startSketchOn(offsetPlane(XY, offset = -overallThickness / 2))
|> circle( |> circle(center = [0, 0], radius = outsideDiameter / 2)
center = [0, 0], |> hole(circle(center = [0, 0], radius = shaftDia / 2 + wallThickness + sphereDia), %)
radius = outsideDiameter / 2
)
|> hole(circle(
center = [0, 0],
radius = shaftDia / 2 + wallThickness + sphereDia
), %)
outsideWall = extrude(outsideWallSketch, length = overallThickness) outsideWall = extrude(outsideWallSketch, length = overallThickness)

View File

@ -130,7 +130,7 @@ fn armRestProfile(plane, offset) {
export fn armRest(plane, offset) { export fn armRest(plane, offset) {
path = armRestPath( offsetPlane(plane, offset = offset)) path = armRestPath( offsetPlane(plane, offset = offset))
profile = armRestProfile( offsetPlane("-XZ", offset = 20), offset) profile = armRestProfile( offsetPlane(-XZ, offset = 20), offset)
sweep(profile, path = path) sweep(profile, path = path)
return 0 return 0
} }

View File

@ -16,19 +16,19 @@ import backSlats from "bench-parts.kcl"
import armRest from "bench-parts.kcl" import armRest from "bench-parts.kcl"
// Create the dividers, these hold the seat and back slats // Create the dividers, these hold the seat and back slats
divider("YZ") divider(YZ)
divider(offsetPlane("-YZ", offset = benchLength / 2)) divider(offsetPlane(-YZ, offset = benchLength / 2))
divider(offsetPlane("YZ", offset = benchLength / 2)) divider(offsetPlane(YZ, offset = benchLength / 2))
// Create the connectors to join the dividers // Create the connectors to join the dividers
connector(offsetPlane("YZ", offset = -benchLength / 2), benchLength) connector(offsetPlane(YZ, offset = -benchLength / 2), benchLength)
// Create the seat slats // Create the seat slats
seatSlats(offsetPlane("YZ", offset = -benchLength / 2 - dividerThickness / 2), benchLength + dividerThickness) seatSlats(offsetPlane(YZ, offset = -benchLength / 2 - (dividerThickness / 2)), benchLength + dividerThickness)
// Create the back slats // Create the back slats
backSlats(offsetPlane("YZ", offset = -benchLength / 2 - dividerThickness / 2), benchLength + dividerThickness) backSlats(offsetPlane(YZ, offset = -benchLength / 2 - (dividerThickness / 2)), benchLength + dividerThickness)
// Create the arm rests // Create the arm rests
armRest("-YZ", benchLength / 2) armRest(-YZ, benchLength / 2)
armRest("-YZ", -benchLength / 2) armRest(-YZ, -benchLength / 2)

View File

@ -1,7 +1,6 @@
// Shelf Bracket // 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. // 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.
// Define constants // Define constants
sigmaAllow = 35000 // psi (6061-T6 aluminum) sigmaAllow = 35000 // psi (6061-T6 aluminum)
width = 6 // inch width = 6 // inch
@ -12,14 +11,16 @@ wallMountL = 2 // inches
shelfDepth = 12 // Shelf is 12 inches in depth from the wall 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) moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in)
// Calculate required thickness of bracket // Calculate required thickness of bracket
thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches) thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
filletRadius = .25 filletRadius = .25
extFilletRadius = filletRadius + thickness extFilletRadius = filletRadius + thickness
mountingHoleDiameter = 0.5 mountingHoleDiameter = 0.5
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn(XZ)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> xLine(length = shelfMountL - thickness, tag = $seg01) |> xLine(length = shelfMountL - thickness, tag = $seg01)
|> yLine(length = thickness, tag = $seg02) |> yLine(length = thickness, tag = $seg02)
@ -29,48 +30,18 @@ sketch001 = startSketchOn('XZ')
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg06) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg06)
|> close() |> close()
|> extrude(%, length = width) |> extrude(%, length = width)
|> fillet( |> fillet(radius = extFilletRadius, tags = [getNextAdjacentEdge(seg03)])
radius = extFilletRadius, |> fillet(radius = filletRadius, tags = [getNextAdjacentEdge(seg06)])
tags = [getNextAdjacentEdge(seg03)], |> fillet(radius = filletRadius, tags = [seg02, getOppositeEdge(seg02)])
) |> fillet(radius = filletRadius, tags = [seg05, getOppositeEdge(seg05)])
|> fillet(
radius = filletRadius,
tags = [getNextAdjacentEdge(seg06)],
)
|> fillet(
radius = filletRadius,
tags = [seg02, getOppositeEdge(seg02)],
)
|> fillet(
radius = filletRadius,
tags = [seg05, getOppositeEdge(seg05)],
)
sketch002 = startSketchOn(sketch001, seg03) sketch002 = startSketchOn(sketch001, seg03)
|> circle( |> circle(center = [-1.25, 1], radius = mountingHoleDiameter / 2)
center = [-1.25, 1], |> patternLinear2d(instances = 2, distance = 2.5, axis = [-1, 0])
radius = mountingHoleDiameter / 2, |> patternLinear2d(instances = 2, distance = 4, axis = [0, 1])
) |> extrude(%, length = -thickness - .01)
|> patternLinear2d(
instances = 2,
distance = 2.5,
axis = [-1, 0],
)
|> patternLinear2d(
instances = 2,
distance = 4,
axis = [0, 1],
)
|> extrude(%, length = -thickness-.01)
sketch003 = startSketchOn(sketch001, seg04) sketch003 = startSketchOn(sketch001, seg04)
|> circle( |> circle(center = [1, -1], radius = mountingHoleDiameter / 2)
center = [1, -1], |> patternLinear2d(instances = 2, distance = 4, axis = [1, 0])
radius = mountingHoleDiameter / 2, |> extrude(%, length = -thickness - 0.1)
)
|> patternLinear2d(
instances = 2,
distance = 4,
axis = [1, 0],
)
|> extrude(%, length = -thickness-0.1)

View File

@ -1,16 +1,14 @@
// Brake Caliper // 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. // 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 // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Import Constants // Import Constants
import caliperTolerance, caliperPadLength, caliperThickness, caliperOuterEdgeRadius, caliperInnerEdgeRadius, rotorDiameter, rotorTotalThickness, yAxisOffset from "globals.kcl" import caliperTolerance, caliperPadLength, caliperThickness, caliperOuterEdgeRadius, caliperInnerEdgeRadius, rotorDiameter, rotorTotalThickness, yAxisOffset from "globals.kcl"
// Sketch the brake caliper profile // Sketch the brake caliper profile
brakeCaliperSketch = startSketchOn('XY') brakeCaliperSketch = startSketchOn(XY)
|> startProfileAt([ |> startProfileAt([
rotorDiameter / 2 + caliperTolerance, rotorDiameter / 2 + caliperTolerance,
0 0
@ -82,5 +80,5 @@ brakeCaliperSketch = startSketchOn('XY')
|> close() |> close()
// Revolve the brake caliper sketch // Revolve the brake caliper sketch
revolve(brakeCaliperSketch, axis = "Y", angle = -70) revolve(brakeCaliperSketch, axis = Y, angle = -70)
|> appearance(color = "#c82d2d", metalness = 90, roughness = 90) |> appearance(color = "#c82d2d", metalness = 90, roughness = 90)

View File

@ -1,39 +1,28 @@
// Wheel rotor // 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. // 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 // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Import Constants // Import Constants
import rotorDiameter, rotorInnerDiameter, rotorSinglePlateThickness, rotorInnerDiameterThickness, lugHolePatternDia, lugSpacing, rotorTotalThickness, spacerPatternDiameter, spacerDiameter, spacerLength, spacerCount, wheelDiameter, lugCount, yAxisOffset, drillAndSlotCount from "globals.kcl" import rotorDiameter, rotorInnerDiameter, rotorSinglePlateThickness, rotorInnerDiameterThickness, lugHolePatternDia, lugSpacing, rotorTotalThickness, spacerPatternDiameter, spacerDiameter, spacerLength, spacerCount, wheelDiameter, lugCount, yAxisOffset, drillAndSlotCount from "globals.kcl"
rotorSketch = startSketchOn('XZ') rotorSketch = startSketchOn(XZ)
|> circle( |> circle(center = [0, 0], radius = rotorDiameter / 2)
center = [0, 0],
radius = rotorDiameter / 2
)
rotor = extrude(rotorSketch, length = rotorSinglePlateThickness) rotor = extrude(rotorSketch, length = rotorSinglePlateThickness)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90) |> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
rotorBumpSketch = startSketchOn(rotor, 'end') rotorBumpSketch = startSketchOn(rotor, 'end')
|> circle( |> circle(center = [0, 0], radius = rotorInnerDiameter / 2)
center = [0, 0],
radius = rotorInnerDiameter / 2
)
rotorBump = extrude(rotorBumpSketch, length = rotorInnerDiameterThickness) rotorBump = extrude(rotorBumpSketch, length = rotorInnerDiameterThickness)
lugHoles = startSketchOn(rotorBump, 'end') lugHoles = startSketchOn(rotorBump, 'end')
|> circle( |> circle(center = [-lugSpacing / 2, 0], radius = 0.315)
center = [-lugSpacing / 2, 0],
radius = 0.315
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = lugCount, instances = lugCount,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(%, length = -(rotorInnerDiameterThickness + rotorSinglePlateThickness)) |> extrude(%, length = -(rotorInnerDiameterThickness + rotorSinglePlateThickness))
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90) |> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
@ -44,35 +33,26 @@ centerSpacer = startSketchOn(rotor, 'start')
|> extrude(%, length = spacerLength) |> extrude(%, length = spacerLength)
secondaryRotorSketch = startSketchOn(centerSpacer, 'end') secondaryRotorSketch = startSketchOn(centerSpacer, 'end')
|> circle( |> circle(center = [0, 0], radius = rotorDiameter / 2)
center = [0, 0],
radius = rotorDiameter / 2
)
secondRotor = extrude(secondaryRotorSketch, length = rotorSinglePlateThickness) secondRotor = extrude(secondaryRotorSketch, length = rotorSinglePlateThickness)
lugHoles2 = startSketchOn(secondRotor, 'end') lugHoles2 = startSketchOn(secondRotor, 'end')
|> circle( |> circle(center = [-lugSpacing / 2, 0], radius = 0.315)
center = [-lugSpacing / 2, 0],
radius = 0.315
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = lugCount, instances = lugCount,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(length = -rotorSinglePlateThickness) |> extrude(length = -rotorSinglePlateThickness)
spacerSketch = startSketchOn(rotor, 'start') spacerSketch = startSketchOn(rotor, 'start')
|> circle( |> circle(center = [spacerPatternDiameter / 2, 0], radius = spacerDiameter)
center = [spacerPatternDiameter / 2, 0],
radius = spacerDiameter
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = spacerCount, instances = spacerCount,
rotateDuplicates = true rotateDuplicates = true,
) )
spacers = extrude(spacerSketch, length = spacerLength) spacers = extrude(spacerSketch, length = spacerLength)
@ -87,7 +67,7 @@ rotorSlottedSketch = startSketchOn(rotor, 'START')
center = [0, 0], center = [0, 0],
instances = drillAndSlotCount, instances = drillAndSlotCount,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
rotorSlotted = extrude(rotorSlottedSketch, length = -rotorSinglePlateThickness / 2) rotorSlotted = extrude(rotorSlottedSketch, length = -rotorSinglePlateThickness / 2)
@ -102,7 +82,7 @@ secondRotorSlottedSketch = startSketchOn(secondRotor, 'END')
center = [0, 0], center = [0, 0],
instances = drillAndSlotCount, instances = drillAndSlotCount,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
extrude(secondRotorSlottedSketch, length = -rotorSinglePlateThickness / 2) extrude(secondRotorSlottedSketch, length = -rotorSinglePlateThickness / 2)

View File

@ -1,21 +1,22 @@
// Tire // 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. // 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 // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Import Constants // Import Constants
import tireInnerDiameter, tireOuterDiameter, tireDepth, bendRadius, tireTreadWidth, tireTreadDepth, tireTreadOffset from "globals.kcl" import tireInnerDiameter, tireOuterDiameter, tireDepth, bendRadius, tireTreadWidth, tireTreadDepth, tireTreadOffset from "globals.kcl"
// Create the sketch of the tire // Create the sketch of the tire
tireSketch = startSketchOn("XY") tireSketch = startSketchOn(XY)
|> startProfileAt([tireInnerDiameter / 2, tireDepth / 2], %) |> startProfileAt([tireInnerDiameter / 2, tireDepth / 2], %)
|> line(endAbsolute = [ |> line(
tireOuterDiameter / 2 - bendRadius, endAbsolute = [
tireDepth / 2 tireOuterDiameter / 2 - bendRadius,
], tag = $edge1) tireDepth / 2
],
tag = $edge1,
)
|> tangentialArc({ offset = -90, radius = bendRadius }, %) |> tangentialArc({ offset = -90, radius = bendRadius }, %)
|> line(endAbsolute = [ |> line(endAbsolute = [
tireOuterDiameter / 2, tireOuterDiameter / 2,
@ -40,5 +41,5 @@ tireSketch = startSketchOn("XY")
|> close() |> close()
// Revolve the sketch to create the tire // Revolve the sketch to create the tire
revolve(tireSketch, axis = "Y") revolve(tireSketch, axis = Y)
|> appearance(color = "#0f0f0f", roughness = 80) |> appearance(color = "#0f0f0f", roughness = 80)

View File

@ -1,69 +1,49 @@
// Car Wheel // Car Wheel
// A sports car wheel with a circular lug pattern and spokes. // A sports car wheel with a circular lug pattern and spokes.
// Set units // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Import Constants // Import Constants
import lugCount, lugSpacing, offset, backSpacing, wheelWidth, wheelDiameter, spokeCount, spokeGap, spokeAngle, spokeThickness from "globals.kcl" import lugCount, lugSpacing, offset, backSpacing, wheelWidth, wheelDiameter, spokeCount, spokeGap, spokeAngle, spokeThickness from "globals.kcl"
// Create the wheel center // Create the wheel center
lugBase = startSketchOn('XZ') lugBase = startSketchOn(XZ)
|> circle( |> circle(center = [0, 0], radius = (lugSpacing + 1.5) / 2)
center = [0, 0], |> hole(circle(center = [0, 0], radius = (lugSpacing - 1.5) / 2), %)
radius = (lugSpacing + 1.5) / 2
)
|> hole(circle(
center = [0, 0],
radius = (lugSpacing - 1.5) / 2
), %)
|> extrude(length = wheelWidth / 20) |> extrude(length = wheelWidth / 20)
// Extend the wheel center and bore holes to accomidate the lug heads // Extend the wheel center and bore holes to accomidate the lug heads
lugExtrusion = startSketchOn(lugBase, 'END') lugExtrusion = startSketchOn(lugBase, 'END')
|> circle( |> circle(center = [0, 0], radius = (lugSpacing + 1.5) / 2)
center = [0, 0], |> hole(circle(center = [0, 0], radius = (lugSpacing - 1.5) / 2), %)
radius = (lugSpacing + 1.5) / 2
)
|> hole(circle(
center = [0, 0],
radius = (lugSpacing - 1.5) / 2
), %)
|> extrude(length = wheelWidth / 10) |> extrude(length = wheelWidth / 10)
// Create the circular pattern for the lugs // Create the circular pattern for the lugs
lugClearance = startSketchOn(lugExtrusion, 'END') lugClearance = startSketchOn(lugExtrusion, 'END')
|> circle( |> circle(center = [lugSpacing / 2, 0], radius = 1.2 / 2)
center = [lugSpacing / 2, 0],
radius = 1.2 / 2
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = lugCount, instances = lugCount,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(length = -wheelWidth / 10) |> extrude(length = -wheelWidth / 10)
// Create the circular pattern for the lug holes // Create the circular pattern for the lug holes
lugHoles = startSketchOn(lugBase, 'END') lugHoles = startSketchOn(lugBase, 'END')
|> circle( |> circle(center = [lugSpacing / 2, 0], radius = 16 * mm() / 2)
center = [lugSpacing / 2, 0],
radius = 16 * mm() / 2
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = lugCount, instances = lugCount,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(length = -wheelWidth / 20) |> extrude(length = -wheelWidth / 20)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0) |> appearance(color = "#ffffff", metalness = 0, roughness = 0)
// Add detail to the wheel center by revolving curved edge profiles // Add detail to the wheel center by revolving curved edge profiles
wheelCenterInner = startSketchOn('XY') wheelCenterInner = startSketchOn(XY)
|> startProfileAt([(lugSpacing - 1.5) / 2, 0], %) |> startProfileAt([(lugSpacing - 1.5) / 2, 0], %)
|> yLine(length = -wheelWidth / 10 - (wheelWidth / 20)) |> yLine(length = -wheelWidth / 10 - (wheelWidth / 20))
|> bezierCurve({ |> bezierCurve({
@ -74,10 +54,10 @@ wheelCenterInner = startSketchOn('XY')
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0) |> appearance(color = "#ffffff", metalness = 0, roughness = 0)
wheelCenterOuter = startSketchOn('XY') wheelCenterOuter = startSketchOn(XY)
|> startProfileAt([(lugSpacing + 1.5) / 2, 0], %) |> startProfileAt([(lugSpacing + 1.5) / 2, 0], %)
|> yLine(length = -wheelWidth / 10 - (wheelWidth / 20)) |> yLine(length = -wheelWidth / 10 - (wheelWidth / 20))
|> bezierCurve({ |> bezierCurve({
@ -88,7 +68,7 @@ wheelCenterOuter = startSketchOn('XY')
|> yLine(endAbsolute = -wheelWidth / 20) |> yLine(endAbsolute = -wheelWidth / 20)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0) |> appearance(color = "#ffffff", metalness = 0, roughness = 0)
// Write a function that defines the spoke geometry, patterns and extrudes it // Write a function that defines the spoke geometry, patterns and extrudes it
@ -145,7 +125,7 @@ fn spoke(spokeGap, spokeAngle, spokeThickness) {
center = [0, -2000, 0], center = [0, -2000, 0],
instances = spokeCount, instances = spokeCount,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
|> appearance(color = "#ffffff", metalness = 0, roughness = 0) |> appearance(color = "#ffffff", metalness = 0, roughness = 0)
return spokePattern return spokePattern
@ -155,7 +135,7 @@ spoke(spokeGap, spokeAngle, spokeThickness)
spoke(-spokeGap, -spokeAngle, -spokeThickness) spoke(-spokeGap, -spokeAngle, -spokeThickness)
// Define and revolve wheel exterior // Define and revolve wheel exterior
startSketchOn('XY') startSketchOn(XY)
|> startProfileAt([ |> startProfileAt([
wheelDiameter / 2, wheelDiameter / 2,
-wheelWidth + backSpacing + offset -wheelWidth + backSpacing + offset
@ -193,5 +173,5 @@ startSketchOn('XY')
|> xLine(length = wheelWidth * 0.03) |> xLine(length = wheelWidth * 0.03)
|> yLine(length = wheelWidth * 0.05) |> yLine(length = wheelWidth * 0.05)
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
|> appearance(color = "#ffffff", metalness = 0, roughness = 0) |> appearance(color = "#ffffff", metalness = 0, roughness = 0)

View File

@ -20,7 +20,7 @@ export lugDiameter = 24 * mm()
export lugHeadLength = lugDiameter * .5 export lugHeadLength = lugDiameter * .5
export lugThreadDiameter = lugDiameter / 2 * .85 export lugThreadDiameter = lugDiameter / 2 * .85
export lugLength = 30 * mm() export lugLength = 30 * mm()
export lugThreadDepth = lugLength - 12.7 * mm() export lugThreadDepth = lugLength - (12.7 * mm())
// Car Rotor // Car Rotor
export rotorDiameter = 12 export rotorDiameter = 12
@ -50,4 +50,4 @@ export caliperTolerance = 0.050
export caliperPadLength = 1.6 export caliperPadLength = 1.6
export caliperThickness = 0.39 export caliperThickness = 0.39
export caliperOuterEdgeRadius = 0.39 export caliperOuterEdgeRadius = 0.39
export caliperInnerEdgeRadius = 0.12 export caliperInnerEdgeRadius = 0.12

View File

@ -1,11 +1,9 @@
// Lug Nut // 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. // 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 // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
// Import Constants // Import Constants
import lugDiameter, lugHeadLength, lugThreadDiameter, lugLength, lugThreadDepth, lugSpacing from "globals.kcl" import lugDiameter, lugHeadLength, lugThreadDiameter, lugLength, lugThreadDepth, lugSpacing from "globals.kcl"
@ -34,7 +32,7 @@ fn lug(plane, length, diameter) {
|> xLine(endAbsolute = lugThreadDiameter) |> xLine(endAbsolute = lugThreadDiameter)
|> yLine(endAbsolute = 0) |> yLine(endAbsolute = 0)
|> close() |> close()
|> revolve(axis = "Y") |> revolve(axis = Y)
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90) |> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
return lugSketch return lugSketch
} }

View File

@ -4,24 +4,24 @@
// Set units // Set units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
import 'car-wheel.kcl' as carWheel import "car-wheel.kcl" as carWheel
import 'car-rotor.kcl' as carRotor import "car-rotor.kcl" as carRotor
import "brake-caliper.kcl" as brakeCaliper import "brake-caliper.kcl" as brakeCaliper
import 'lug-nut.kcl' as lugNut import "lug-nut.kcl" as lugNut
import 'car-tire.kcl' as carTire import "car-tire.kcl" as carTire
import lugCount from 'globals.kcl' import lugCount from "globals.kcl"
carRotor carRotor
|> translate(translate = [0, 0.5, 0]) |> translate(translate = [0, 0.5, 0])
carWheel carWheel
lugNut lugNut
|> patternCircular3d( |> patternCircular3d(
arcDegrees = 360, arcDegrees = 360,
axis = [0, 1, 0], axis = [0, 1, 0],
center = [0, 0, 0], center = [0, 0, 0],
instances = lugCount, instances = lugCount,
rotateDuplicates = false rotateDuplicates = false,
) )
brakeCaliper brakeCaliper
|> translate(translate = [0, 0.5, 0]) |> translate(translate = [0, 0.5, 0])
carTire carTire

View File

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

View File

@ -4,12 +4,11 @@
// Set Units // Set Units
@settings(defaultLengthUnit = in) @settings(defaultLengthUnit = in)
fn cycloidalGear(gearPitch, gearHeight, holeDiameter, helixAngle) { 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 // 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) { fn gearSketch(gHeight) {
helixAngleP = helixAngle * gHeight / gearHeight helixAngleP = helixAngle * gHeight / gearHeight
gearProfile = startSketchOn(offsetPlane("XY", offset = gHeight)) gearProfile = startSketchOn(offsetPlane(XY, offset = gHeight))
|> startProfileAt([ |> startProfileAt([
gearPitch * 1.55 * cos(toRadians(helixAngleP)) + gearPitch * sin(toRadians(-helixAngleP)), gearPitch * 1.55 * cos(toRadians(helixAngleP)) + gearPitch * sin(toRadians(-helixAngleP)),
gearPitch * 1.55 * sin(toRadians(helixAngleP)) + gearPitch * cos(toRadians(-helixAngleP)) gearPitch * 1.55 * sin(toRadians(helixAngleP)) + gearPitch * cos(toRadians(-helixAngleP))
@ -31,10 +30,7 @@ fn cycloidalGear(gearPitch, gearHeight, holeDiameter, helixAngle) {
|> tangentialArc({ radius = gearPitch, offset = -180 }, %) |> tangentialArc({ radius = gearPitch, offset = -180 }, %)
|> tangentialArcTo([profileStartX(%), profileStartY(%)], %) |> tangentialArcTo([profileStartX(%), profileStartY(%)], %)
|> close(%) |> close(%)
|> hole(circle( |> hole(circle(center = [0, 0], radius = holeDiameter / 2), %)
center = [0, 0],
radius = holeDiameter / 2
), %)
return gearProfile return gearProfile
} }

View File

@ -39,7 +39,7 @@ plane = {
} }
// Create a regular pentagon inscribed in a circle of radius pentR // Create a regular pentagon inscribed in a circle of radius pentR
bottomFace = startSketchOn('XY') bottomFace = startSketchOn(XY)
|> polygon({ |> polygon({
radius = pentR, radius = pentR,
numSides = 5, numSides = 5,
@ -66,7 +66,7 @@ bottomBowl = patternCircular3d(
axis = [0, 0, 1], axis = [0, 0, 1],
center = [0, 0, 0], center = [0, 0, 0],
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
// pattern the bottom to create the top face // pattern the bottom to create the top face
@ -76,7 +76,7 @@ patternCircular3d(
axis = [0, 1, 0], axis = [0, 1, 0],
center = [0, 0, inscR], center = [0, 0, inscR],
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
// pattern the bottom angled faces to create the top // pattern the bottom angled faces to create the top
@ -86,5 +86,5 @@ patternCircular3d(
axis = [0, 1, 0], axis = [0, 1, 0],
center = [0, 0, inscR], center = [0, 0, inscR],
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )

View File

@ -0,0 +1,200 @@
// Dual-Basin Utility Sink
// A stainless steel sink unit with dual rectangular basins and six under-counter storage compartments.
@settings(defaultLengthUnit = mm)
// globals
tableHeight = 850
tableWidth = 3400
tableDepth = 400
profileThickness = 13
metalThickness = 2
blockCount = 3
blockWidth = (tableWidth-profileThickness) / 3
blockHeight = tableHeight - metalThickness - 0.5
blockDepth = tableDepth - profileThickness
blockSubdivisionCount = 2
blockSubdivisionWidth = blockWidth / blockSubdivisionCount
// Geometry
floorPlane = startSketchOn(XY)
// legs
legHeight = blockHeight - profileThickness
legCount = blockCount + 1
legBody = startProfileAt([0, 0], floorPlane)
|> yLine(length=profileThickness)
|> xLine(length=profileThickness)
|> yLine(length=-profileThickness)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = legCount, distance = blockWidth)
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|> extrude(length = legHeight)
// lower belt
lowerBeltHeightAboveTheFloor = 150
lowerBeltLengthX = blockWidth - profileThickness
lowerBeltPlane = startSketchOn(offsetPlane(XY, offset = lowerBeltHeightAboveTheFloor))
lowerBeltBodyX = startProfileAt([profileThickness, 0], lowerBeltPlane)
|> yLine(length=profileThickness)
|> xLine(length=lowerBeltLengthX)
|> yLine(length=-profileThickness)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = blockCount, distance = blockWidth)
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|> extrude(length = profileThickness)
lowerBeltLengthY = blockDepth - profileThickness
lowerBeltBodyY = startProfileAt([0, profileThickness], lowerBeltPlane)
|> yLine(length=lowerBeltLengthY)
|> xLine(length=profileThickness)
|> yLine(length=-lowerBeltLengthY)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = 2, distance = tableWidth-profileThickness)
|> extrude(length = profileThickness)
// pillars
pillarHeightAboveTheFloor = lowerBeltHeightAboveTheFloor + profileThickness
pillarPlane = startSketchOn(offsetPlane(XY, offset = pillarHeightAboveTheFloor))
pillarTotalHeight = blockHeight - profileThickness - pillarHeightAboveTheFloor
pillarBody = startProfileAt([blockSubdivisionWidth, 0], pillarPlane)
|> yLine(length=profileThickness)
|> xLine(length=profileThickness)
|> yLine(length=-profileThickness)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = blockCount, distance = blockWidth)
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|> extrude(length = pillarTotalHeight)
// upper belt
upperBeltPlane = startSketchOn(offsetPlane(XY, offset = blockHeight))
upperBeltBodyX = startProfileAt([0, 0], upperBeltPlane)
|> yLine(length=profileThickness)
|> xLine(length=tableWidth)
|> yLine(length=-profileThickness)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|> extrude(length = -profileThickness)
upperBeltLengthY = blockDepth - profileThickness
upperBeltBodyY = startProfileAt([0, profileThickness], upperBeltPlane)
|> yLine(length=upperBeltLengthY)
|> xLine(length=profileThickness)
|> yLine(length=-upperBeltLengthY)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = 2, distance = tableWidth-profileThickness)
|> extrude(length = -profileThickness)
// sink
tableTopPlane = startSketchOn(offsetPlane(XY, offset = tableHeight))
tableTopBody = startProfileAt([0, 0], tableTopPlane)
|> yLine(length=tableDepth)
|> xLine(length=tableWidth)
|> yLine(length=-tableDepth)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> extrude(length = -metalThickness)
sinkCount = 2
sinkWidth = 1000
sinkLength = 250
sinkDepth = 200
sinkOffsetFront = 40
sinkOffsetLeft = 350
sinkSpacing = tableWidth - sinkWidth - sinkOffsetLeft*2
sinkPlaneOutside = startSketchOn(tableTopBody, 'START')
sinkBodyOutside = startProfileAt([-sinkOffsetLeft, sinkOffsetFront], sinkPlaneOutside)
|> yLine(length=sinkLength)
|> xLine(length=-sinkWidth)
|> yLine(length=-sinkLength)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [-1, 0], instances = sinkCount, distance = sinkSpacing)
|> extrude(length = sinkDepth)
sinkPlaneInside = startSketchOn(tableTopBody, 'END')
sinkBodyInside = startProfileAt([sinkOffsetLeft+metalThickness, sinkOffsetFront+metalThickness], sinkPlaneInside)
|> yLine(length=sinkLength-metalThickness*2)
|> xLine(length=sinkWidth-metalThickness*2)
|> yLine(length=-sinkLength+metalThickness*2)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = sinkCount, distance = sinkSpacing)
|> extrude(length = -sinkDepth)
// door panels
doorGap = 2
doorWidth = blockSubdivisionWidth - profileThickness - doorGap*2
doorStart = profileThickness+doorGap
doorHeightAboveTheFloor = pillarHeightAboveTheFloor + doorGap
doorHeight = blockHeight - doorHeightAboveTheFloor - profileThickness - doorGap
doorCount = blockCount * blockSubdivisionCount
doorPlane = startSketchOn(offsetPlane(XY, offset = doorHeightAboveTheFloor))
doorBody = startProfileAt([doorStart, 0], doorPlane)
|> yLine(length=profileThickness)
|> xLine(length=doorWidth)
|> yLine(length=-profileThickness)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = doorCount, distance = blockSubdivisionWidth)
|> extrude(length = doorHeight)
// side panels
panelWidth = blockDepth - profileThickness - doorGap*2
panelCount = doorCount + 1
panelSpacing = tableWidth - profileThickness
panelBody = startProfileAt([0, doorStart], doorPlane)
|> yLine(length=panelWidth)
|> xLine(length=profileThickness)
|> yLine(length=-panelWidth)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
|> patternLinear2d(axis = [1, 0], instances = 2, distance = panelSpacing)
|> extrude(length = doorHeight)
// handle
handleDepth = 40
handleWidth = 120
handleFillet = 20
handleHeightAboveTheFloor = 780
handleOffset = doorStart + doorWidth / 2 - (handleWidth / 2)
handleLengthSegmentA = handleDepth - handleFillet
handleLengthSegmentB = handleWidth - (handleFillet * 2)
handlePlane = startSketchOn(offsetPlane(XY, offset = handleHeightAboveTheFloor))
handleProfilePath = startProfileAt([0 + handleOffset, 0], handlePlane)
|> yLine(length=-handleLengthSegmentA)
|> tangentialArcTo([
handleFillet + handleOffset,
-handleDepth
], %)
|> xLine(length=handleLengthSegmentB)
|> tangentialArcTo([
handleOffset + handleWidth,
-handleLengthSegmentA
], %)
|> yLine(length=handleLengthSegmentA)
handleSectionPlane = startSketchOn(XZ)
handleProfileSection = circle(
handleSectionPlane,
center = [handleOffset, handleHeightAboveTheFloor],
radius = 2)
handleBody = sweep(handleProfileSection, path = handleProfilePath)
|> patternLinear3d(axis = [1, 0, 0], instances = doorCount, distance = blockSubdivisionWidth)

View File

@ -11,7 +11,7 @@ wallThickness = 3
holeDia = 4 holeDia = 4
// Model a box with base enclosure dimensions // Model a box with base enclosure dimensions
sketch001 = startSketchOn('XY') sketch001 = startSketchOn(XY)
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> angledLine([0, width], %, $rectangleSegmentA001) |> angledLine([0, width], %, $rectangleSegmentA001)
|> angledLine([ |> angledLine([
@ -32,14 +32,11 @@ extrude001 = extrude(sketch001, length = height)
getNextAdjacentEdge(rectangleSegmentB001), getNextAdjacentEdge(rectangleSegmentB001),
getNextAdjacentEdge(rectangleSegmentC001), getNextAdjacentEdge(rectangleSegmentC001),
getNextAdjacentEdge(rectangleSegmentD001) getNextAdjacentEdge(rectangleSegmentD001)
] ],
) )
// Apply a shell to the enclosure base to create the internal storage // Apply a shell to the enclosure base to create the internal storage
|> shell( |> shell(faces = ["end"], thickness = wallThickness)
faces = ["end"],
thickness = wallThickness
)
// Define a function to create the internal structure to secure a fastener at each corner // Define a function to create the internal structure to secure a fastener at each corner
fn function001(originStart) { fn function001(originStart) {
@ -55,14 +52,8 @@ fn function001(originStart) {
// Create a pillar with a fasterner hole at the center // Create a pillar with a fasterner hole at the center
sketch002 = startSketchOn(plane001) sketch002 = startSketchOn(plane001)
|> circle( |> circle(center = [originStart[0], originStart[1]], radius = holeDia + wallThickness)
center = [originStart[0], originStart[1]], |> hole(circle(center = [originStart[0], originStart[1]], radius = holeDia), %)
radius = holeDia + wallThickness
)
|> hole(circle(
center = [originStart[0], originStart[1]],
radius = holeDia
), %)
extrude002 = extrude(sketch002, length = height - wallThickness) extrude002 = extrude(sketch002, length = height - wallThickness)
return extrude002 return extrude002
@ -87,7 +78,7 @@ function001([
]) ])
// Define lid position and outer surface // Define lid position and outer surface
sketch003 = startSketchOn('XY') sketch003 = startSketchOn(XY)
|> startProfileAt([width * 1.2, 0], %) |> startProfileAt([width * 1.2, 0], %)
|> angledLine([0, width], %, $rectangleSegmentA002) |> angledLine([0, width], %, $rectangleSegmentA002)
|> angledLine([ |> angledLine([
@ -105,28 +96,28 @@ sketch003 = startSketchOn('XY')
width * 1.2 + wallThickness * 3 + holeDia, width * 1.2 + wallThickness * 3 + holeDia,
wallThickness * 3 + holeDia wallThickness * 3 + holeDia
], ],
radius = holeDia radius = holeDia,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 1.2 + wallThickness * 3 + holeDia, width * 1.2 + wallThickness * 3 + holeDia,
length - (wallThickness * 3 + holeDia) length - (wallThickness * 3 + holeDia)
], ],
radius = holeDia radius = holeDia,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 2.2 - (wallThickness * 3 + holeDia), width * 2.2 - (wallThickness * 3 + holeDia),
wallThickness * 3 + holeDia wallThickness * 3 + holeDia
], ],
radius = holeDia radius = holeDia,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 2.2 - (wallThickness * 3 + holeDia), width * 2.2 - (wallThickness * 3 + holeDia),
length - (wallThickness * 3 + holeDia) length - (wallThickness * 3 + holeDia)
], ],
radius = holeDia radius = holeDia,
), %) ), %)
extrude003 = extrude(sketch003, length = wallThickness) extrude003 = extrude(sketch003, length = wallThickness)
|> fillet( |> fillet(
@ -136,7 +127,7 @@ extrude003 = extrude(sketch003, length = wallThickness)
getNextAdjacentEdge(rectangleSegmentB002), getNextAdjacentEdge(rectangleSegmentB002),
getNextAdjacentEdge(rectangleSegmentC002), getNextAdjacentEdge(rectangleSegmentC002),
getNextAdjacentEdge(rectangleSegmentD002) getNextAdjacentEdge(rectangleSegmentD002)
] ],
) )
// Define lid inner and sealing surfaces // Define lid inner and sealing surfaces
@ -161,28 +152,28 @@ sketch004 = startSketchOn(extrude003, 'END')
width * 1.2 + wallThickness * 3 + holeDia, width * 1.2 + wallThickness * 3 + holeDia,
wallThickness * 3 + holeDia wallThickness * 3 + holeDia
], ],
radius = holeDia + wallThickness radius = holeDia + wallThickness,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 1.2 + wallThickness * 3 + holeDia, width * 1.2 + wallThickness * 3 + holeDia,
length - (wallThickness * 3 + holeDia) length - (wallThickness * 3 + holeDia)
], ],
radius = holeDia + wallThickness radius = holeDia + wallThickness,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 2.2 - (wallThickness * 3 + holeDia), width * 2.2 - (wallThickness * 3 + holeDia),
wallThickness * 3 + holeDia wallThickness * 3 + holeDia
], ],
radius = holeDia + wallThickness radius = holeDia + wallThickness,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
width * 2.2 - (wallThickness * 3 + holeDia), width * 2.2 - (wallThickness * 3 + holeDia),
length - (wallThickness * 3 + holeDia) length - (wallThickness * 3 + holeDia)
], ],
radius = holeDia + wallThickness radius = holeDia + wallThickness,
), %) ), %)
extrude004 = extrude(sketch004, length = wallThickness) extrude004 = extrude(sketch004, length = wallThickness)
|> fillet( |> fillet(
@ -192,5 +183,5 @@ extrude004 = extrude(sketch004, length = wallThickness)
getNextAdjacentEdge(rectangleSegmentB003), getNextAdjacentEdge(rectangleSegmentB003),
getNextAdjacentEdge(rectangleSegmentC003), getNextAdjacentEdge(rectangleSegmentC003),
getNextAdjacentEdge(rectangleSegmentD003) getNextAdjacentEdge(rectangleSegmentD003)
] ],
) )

View File

@ -45,15 +45,9 @@ fn primaryTube(n, angle001, length001, length002, length003) {
}, %) }, %)
// Create the cross section of each tube and sweep them // Create the cross section of each tube and sweep them
sweepProfile = startSketchOn('XY') sweepProfile = startSketchOn(XY)
|> circle( |> circle(center = [pos001, 0], radius = primaryTubeDiameter / 2)
center = [pos001, 0], |> hole(circle(center = [pos001, 0], radius = primaryTubeDiameter / 2 - wallThickness), %)
radius = primaryTubeDiameter / 2
)
|> hole(circle(
center = [pos001, 0],
radius = primaryTubeDiameter / 2 - wallThickness
), %)
|> sweep(path = sweepPath) |> sweep(path = sweepPath)
return { } return { }
@ -66,7 +60,7 @@ primaryTube(2, 24.3, 5, 5, 3)
primaryTube(3, 25.2, 5, 5, 3) primaryTube(3, 25.2, 5, 5, 3)
// Create the mounting flange for the header // Create the mounting flange for the header
flangeSketch = startSketchOn('XY') flangeSketch = startSketchOn(XY)
|> startProfileAt([3 + 1.3, -1.25], %) |> startProfileAt([3 + 1.3, -1.25], %)
|> xLine(length = -2.6, tag = $seg01) |> xLine(length = -2.6, tag = $seg01)
|> tangentialArc({ radius = .3, offset = -40 }, %) |> tangentialArc({ radius = .3, offset = -40 }, %)
@ -87,22 +81,10 @@ flangeSketch = startSketchOn('XY')
|> close() |> close()
// Create openings in the flange to accommodate each tube // Create openings in the flange to accommodate each tube
|> hole(circle( |> hole(circle(center = [0, 0], radius = primaryTubeDiameter / 2 - wallThickness), %)
center = [0, 0], |> hole(circle(center = [2, 0], radius = primaryTubeDiameter / 2 - wallThickness), %)
radius = primaryTubeDiameter / 2 - wallThickness |> hole(circle(center = [4, 0], radius = primaryTubeDiameter / 2 - wallThickness), %)
), %) |> hole(circle(center = [6, 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 // Add mounting holes to the flange
|> hole(circle( |> hole(circle(
@ -110,28 +92,28 @@ flangeSketch = startSketchOn('XY')
-primaryTubeDiameter * .6, -primaryTubeDiameter * .6,
-primaryTubeDiameter * .6 -primaryTubeDiameter * .6
], ],
radius = 0.25 / 2 radius = 0.25 / 2,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
primaryTubeDiameter * .6, primaryTubeDiameter * .6,
primaryTubeDiameter * .6 primaryTubeDiameter * .6
], ],
radius = 0.25 / 2 radius = 0.25 / 2,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
3 * 2 - (primaryTubeDiameter * .6), 3 * 2 - (primaryTubeDiameter * .6),
primaryTubeDiameter * .6 primaryTubeDiameter * .6
], ],
radius = 0.25 / 2 radius = 0.25 / 2,
), %) ), %)
|> hole(circle( |> hole(circle(
center = [ center = [
3 * 2 + primaryTubeDiameter * .6, 3 * 2 + primaryTubeDiameter * .6,
-primaryTubeDiameter * .6 -primaryTubeDiameter * .6
], ],
radius = 0.25 / 2 radius = 0.25 / 2,
), %) ), %)
// Extrude the flange and fillet the edges // Extrude the flange and fillet the edges
@ -141,12 +123,12 @@ flangeSketch = startSketchOn('XY')
tags = [ tags = [
getNextAdjacentEdge(seg04), getNextAdjacentEdge(seg04),
getNextAdjacentEdge(seg07) getNextAdjacentEdge(seg07)
] ],
) )
|> fillet( |> fillet(
radius = .25, radius = .25,
tags = [ tags = [
getNextAdjacentEdge(seg03), getNextAdjacentEdge(seg03),
getNextAdjacentEdge(seg08) getNextAdjacentEdge(seg08)
] ],
) )

View File

@ -21,44 +21,32 @@ nHoles = 4
assertGreaterThan(nHoles, 1, "nHoles must be greater than 1") assertGreaterThan(nHoles, 1, "nHoles must be greater than 1")
// Create the circular pattern for the mounting holes // Create the circular pattern for the mounting holes
circles = startSketchOn('XY') circles = startSketchOn(XY)
|> circle( |> circle(center = [mountingHolePlacementDiameter / 2, 0], radius = mountingHoleDia / 2)
center = [mountingHolePlacementDiameter / 2, 0],
radius = mountingHoleDia / 2
)
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
center = [0, 0], center = [0, 0],
instances = nHoles, instances = nHoles,
rotateDuplicates = true rotateDuplicates = true,
) )
// Create the base of the flange and add the mounting holes // Create the base of the flange and add the mounting holes
flangeBase = startSketchOn('XY') flangeBase = startSketchOn(XY)
|> circle( |> circle(center = [0, 0], radius = baseDia / 2)
center = [0, 0],
radius = baseDia / 2
)
|> hole(circles, %) |> hole(circles, %)
|> extrude(length = baseThickness) |> extrude(length = baseThickness)
// Create the extrusion on the top of the flange base // Create the extrusion on the top of the flange base
topExtrusion = startSketchOn(flangeBase, 'end') topExtrusion = startSketchOn(flangeBase, 'end')
|> circle( |> circle(center = [0, 0], radius = topTotalDiameter / 2)
center = [0, 0],
radius = topTotalDiameter / 2
)
|> extrude(length = topTotalThickness) |> extrude(length = topTotalThickness)
// Create the extrusion on the bottom of the flange base // Create the extrusion on the bottom of the flange base
bottomExtrusion = startSketchOn(flangeBase, 'start') bottomExtrusion = startSketchOn(flangeBase, 'start')
|> circle( |> circle(center = [0, 0], radius = bottomTotalDiameter / 2)
center = [0, 0],
radius = bottomTotalDiameter / 2
)
|> extrude(length = bottomThickness) |> extrude(length = bottomThickness)
// Cut a hole through the entire body // Cut a hole through the entire body
pipeHole = startSketchOn(topExtrusion, 'end') pipeHole = startSketchOn(topExtrusion, 'end')
|> circle(center = [0, 0], radius = pipeDia/2) |> circle(center = [0, 0], radius = pipeDia / 2)
|> extrude(%, length = -(topTotalThickness + baseThickness + bottomThickness)) |> extrude(%, length = -(topTotalThickness + baseThickness + bottomThickness))

View File

@ -62,7 +62,7 @@ bracketBody = bs
getPreviousAdjacentEdge(bs.tags.edge2), getPreviousAdjacentEdge(bs.tags.edge2),
getPreviousAdjacentEdge(bs.tags.edge3), getPreviousAdjacentEdge(bs.tags.edge3),
getPreviousAdjacentEdge(bs.tags.edge6) getPreviousAdjacentEdge(bs.tags.edge6)
] ],
) )
// define the tab plane // define the tab plane
@ -87,7 +87,7 @@ tabsR = startSketchOn(tabPlane)
width / 2 + thk + tabWidth / 2, width / 2 + thk + tabWidth / 2,
length / 2 + thk - (tabLength / (3 / 2)) length / 2 + thk - (tabLength / (3 / 2))
], ],
radius = holeDiam / 2 radius = holeDiam / 2,
), %) ), %)
|> extrude(length = -tabThk) |> extrude(length = -tabThk)
|> fillet( |> fillet(
@ -95,13 +95,9 @@ tabsR = startSketchOn(tabPlane)
tags = [ tags = [
getNextAdjacentEdge(edge11), getNextAdjacentEdge(edge11),
getNextAdjacentEdge(edge12) getNextAdjacentEdge(edge12)
] ],
)
|> patternLinear3d(
axis = [0, -1, 0],
instances = 2,
distance = length + 2 * thk - (tabLength * 4 / 3)
) )
|> patternLinear3d(axis = [0, -1, 0], instances = 2, distance = length + 2 * thk - (tabLength * 4 / 3))
// build the tabs of the mounting bracket (left side) // build the tabs of the mounting bracket (left side)
tabsL = startSketchOn(tabPlane) tabsL = startSketchOn(tabPlane)
@ -115,7 +111,7 @@ tabsL = startSketchOn(tabPlane)
-width / 2 - thk - (tabWidth / 2), -width / 2 - thk - (tabWidth / 2),
length / 2 + thk - (tabLength / (3 / 2)) length / 2 + thk - (tabLength / (3 / 2))
], ],
radius = holeDiam / 2 radius = holeDiam / 2,
), %) ), %)
|> extrude(length = -tabThk) |> extrude(length = -tabThk)
|> fillet( |> fillet(
@ -123,13 +119,9 @@ tabsL = startSketchOn(tabPlane)
tags = [ tags = [
getNextAdjacentEdge(edge21), getNextAdjacentEdge(edge21),
getNextAdjacentEdge(edge22) getNextAdjacentEdge(edge22)
] ],
)
|> patternLinear3d(
axis = [0, -1, 0],
instances = 2,
distance = length + 2 * thk - (tabLength * 4 / 3)
) )
|> patternLinear3d(axis = [0, -1, 0], instances = 2, distance = length + 2 * thk - (tabLength * 4 / 3))
// define a plane for retention bumps // define a plane for retention bumps
retPlane = { retPlane = {

View File

@ -44,7 +44,7 @@ fn slot(sketch1, start, end, width) {
} }
// create a sketch on the "XY" plane // create a sketch on the "XY" plane
sketch000 = startSketchOn('XY') sketch000 = startSketchOn(XY)
// create a profile of the flipper // create a profile of the flipper
flipperProfile = startProfileAt([-flipperLength, -32.0], sketch000) flipperProfile = startProfileAt([-flipperLength, -32.0], sketch000)
@ -83,11 +83,11 @@ fillet(
tags = [ tags = [
getNextAdjacentEdge(backEdge), getNextAdjacentEdge(backEdge),
getPreviousAdjacentEdge(backEdge) getPreviousAdjacentEdge(backEdge)
] ],
) )
// create a sketch on the "XZ" plane offset by half the thickness // create a sketch on the "XZ" plane offset by half the thickness
sketch001 = startSketchOn(offsetPlane("XZ", offset = -handleWidth / 2)) sketch001 = startSketchOn(offsetPlane(XZ, offset = -handleWidth / 2))
// create a profile of the spatula handle // create a profile of the spatula handle
handleProfile = startProfileAt([0.0, flipperThickness], sketch001) handleProfile = startProfileAt([0.0, flipperThickness], sketch001)
@ -109,7 +109,7 @@ fillet(
tags = [ tags = [
getNextAdjacentEdge(handleBottomEdge), getNextAdjacentEdge(handleBottomEdge),
getNextAdjacentEdge(handleTopEdge) getNextAdjacentEdge(handleTopEdge)
] ],
) )
// define a plane which is at the end of the handle // define a plane which is at the end of the handle
@ -163,4 +163,4 @@ sketch003 = startSketchOn(grip, gripEdgeTop)
gripHoleProfile = slot(sketch003, [0, 200], [0, 210], gripSlotWidth) gripHoleProfile = slot(sketch003, [0, 200], [0, 210], gripSlotWidth)
// cut a hole in the grip // cut a hole in the grip
extrude(gripHoleProfile, length = -gripWidth-20) extrude(gripHoleProfile, length = -gripWidth - 20)

View File

@ -10,7 +10,7 @@ carafeHeight = 7.32
handleThickness = 0.65 handleThickness = 0.65
// Upper ring of the metal structure // Upper ring of the metal structure
sketch001 = startSketchOn('XZ') sketch001 = startSketchOn(XZ)
|> startProfileAt([carafeDiameter / 2, 5.7], %) |> startProfileAt([carafeDiameter / 2, 5.7], %)
|> angledLine([0, 0.1], %, $rectangleSegmentA001) |> angledLine([0, 0.1], %, $rectangleSegmentA001)
|> angledLine([ |> angledLine([
@ -23,7 +23,7 @@ sketch001 = startSketchOn('XZ')
], %, $rectangleSegmentC001) ], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(angle = 360, axis = 'Y') |> revolve(angle = 360, axis = Y)
// Create an angled plane to sketch the supports // Create an angled plane to sketch the supports
plane001 = { plane001 = {
@ -91,15 +91,12 @@ sketch002 = startSketchOn(plane001)
center = [0, 0, 0], center = [0, 0, 0],
instances = 4, instances = 4,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
// Cross plate // Cross plate
sketch003 = startSketchOn(offsetPlane('XY', offset = 1)) sketch003 = startSketchOn(offsetPlane(XY, offset = 1))
|> circle( |> circle(center = [0, 0], radius = carafeDiameter / 2 - 0.15)
center = [0, 0],
radius = carafeDiameter / 2 - 0.15
)
extrude001 = extrude(sketch003, length = 0.050) extrude001 = extrude(sketch003, length = 0.050)
@ -117,13 +114,13 @@ sketch004 = startSketchOn(extrude001, 'END')
center = [0, 0], center = [0, 0],
instances = 3, instances = 3,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
extrude002 = extrude(sketch004, length = -0.050) extrude002 = extrude(sketch004, length = -0.050)
// Filter screen // Filter screen
sketch005 = startSketchOn('XZ') sketch005 = startSketchOn(XZ)
|> startProfileAt([0.15, 1.11], %) |> startProfileAt([0.15, 1.11], %)
|> xLine(endAbsolute = carafeDiameter / 2 - 0.2) |> xLine(endAbsolute = carafeDiameter / 2 - 0.2)
|> angledLineToX({ |> angledLineToX({
@ -135,10 +132,10 @@ sketch005 = startSketchOn('XZ')
|> xLine(endAbsolute = 0.15) |> xLine(endAbsolute = 0.15)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
// Plunger and stem // Plunger and stem
sketch006 = startSketchOn('XZ') sketch006 = startSketchOn(XZ)
|> startProfileAt([0.1, 1], %) |> startProfileAt([0.1, 1], %)
|> line(end = [0.1, 0]) |> line(end = [0.1, 0])
|> angledLineToX({ angle = 10, to = 0.05 }, %) |> angledLineToX({ angle = 10, to = 0.05 }, %)
@ -148,14 +145,11 @@ sketch006 = startSketchOn('XZ')
|> tangentialArc({ radius = 0.6, offset = -90 }, %) |> tangentialArc({ radius = 0.6, offset = -90 }, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
// Spiral plate // Spiral plate
sketch007 = startSketchOn(offsetPlane('XY', offset = 1.12)) sketch007 = startSketchOn(offsetPlane(XY, offset = 1.12))
|> circle( |> circle(center = [0, 0], radius = carafeDiameter / 2 - 0.24)
center = [0, 0],
radius = carafeDiameter / 2 - 0.24
)
|> hole(circle(center = [0, 0], radius = .15), %) |> hole(circle(center = [0, 0], radius = .15), %)
extrude003 = extrude(sketch007, length = 0.050) extrude003 = extrude(sketch007, length = 0.050)
@ -167,7 +161,7 @@ sketch008 = startSketchOn(extrude003, 'END')
center = [0, 0], center = [0, 0],
instances = 8, instances = 8,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
extrude004 = extrude(sketch008, length = -0.050) extrude004 = extrude(sketch008, length = -0.050)
@ -179,24 +173,21 @@ sketch009 = startSketchOn(extrude003, 'END')
center = [0, 0], center = [0, 0],
instances = 4, instances = 4,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
extrude005 = extrude(sketch009, length = -0.050) extrude005 = extrude(sketch009, length = -0.050)
// Extrude a glass carafe body // Extrude a glass carafe body
sketch010 = startSketchOn("XY") sketch010 = startSketchOn(XY)
|> circle( |> circle(center = [0, 0], radius = carafeDiameter / 2)
center = [0, 0],
radius = carafeDiameter / 2
)
// Perform a shell operation to hollow the carafe body with the top face removed // Perform a shell operation to hollow the carafe body with the top face removed
extrude006 = extrude(sketch010, length = carafeHeight) extrude006 = extrude(sketch010, length = carafeHeight)
|> shell(faces = ["end"], thickness = .07) |> shell(faces = ["end"], thickness = .07)
// Draw and revolve the lid // Draw and revolve the lid
sketch011 = startSketchOn('XZ') sketch011 = startSketchOn(XZ)
|> startProfileAt([0.2, carafeHeight - 0.7], %) |> startProfileAt([0.2, carafeHeight - 0.7], %)
|> xLine(length = carafeDiameter / 2 - 0.3) |> xLine(length = carafeDiameter / 2 - 0.3)
|> yLine(length = 0.7) |> yLine(length = 0.7)
@ -210,10 +201,10 @@ sketch011 = startSketchOn('XZ')
}, %) }, %)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
|> revolve(axis = 'y') |> revolve(axis = Y)
// Draw and extrude handle // Draw and extrude handle
sketch012 = startSketchOn(offsetPlane('XZ', offset = handleThickness / 2)) sketch012 = startSketchOn(offsetPlane(XZ, offset = handleThickness / 2))
|> startProfileAt([2.3, 6.4], %) |> startProfileAt([2.3, 6.4], %)
|> line(end = [0.56, 0]) |> line(end = [0.56, 0])
|> tangentialArcTo([4.1, 5.26], %) |> tangentialArcTo([4.1, 5.26], %)

View File

@ -12,7 +12,7 @@ height = 12
minHeight = 10.875 minHeight = 10.875
// Create the body of the rack // Create the body of the rack
rackBody = startSketchOn('XY') rackBody = startSketchOn(XY)
|> startProfileAt([-length / 2, 0], %) |> startProfileAt([-length / 2, 0], %)
|> line(end = [length, 0]) |> line(end = [length, 0])
|> line(end = [0, minHeight]) |> line(end = [0, minHeight])
@ -22,7 +22,7 @@ rackBody = startSketchOn('XY')
// Create a function for sketch of a single tooth // Create a function for sketch of a single tooth
fn tooth() { fn tooth() {
toothSketch = startSketchOn('XY') toothSketch = startSketchOn(XY)
|> startProfileAt([-length / 2 + 0.567672, minHeight], %) |> startProfileAt([-length / 2 + 0.567672, minHeight], %)
|> tangentialArcToRelative([0.157636, 0.110378], %) |> tangentialArcToRelative([0.157636, 0.110378], %)
|> line(end = [0.329118, 0.904244]) |> line(end = [0.329118, 0.904244])
@ -38,14 +38,10 @@ fn tooth() {
// Pattern the single tooth over the length of the rack body // Pattern the single tooth over the length of the rack body
teeth = tooth() teeth = tooth()
|> patternLinear3d( |> patternLinear3d(axis = [10, 0, 0], distance = 1.570796, instances = 63)
axis = [10, 0, 0],
distance = 1.570796,
instances = 63
)
// Sketch and extrude the first end cap. This is a partial tooth // Sketch and extrude the first end cap. This is a partial tooth
endCapTooth = startSketchOn('XY') endCapTooth = startSketchOn(XY)
|> startProfileAt([-length / 2, 11.849525], %) |> startProfileAt([-length / 2, 11.849525], %)
|> line(end = [0.314524, -0.864147]) |> line(end = [0.314524, -0.864147])
|> tangentialArcToRelative([0.157636, -0.110378], %) |> tangentialArcToRelative([0.157636, -0.110378], %)
@ -54,7 +50,7 @@ endCapTooth = startSketchOn('XY')
|> extrude(length = width) |> extrude(length = width)
// Sketch and extrude the second end cap. This is a partial tooth // Sketch and extrude the second end cap. This is a partial tooth
endCapTooth2 = startSketchOn('XY') endCapTooth2 = startSketchOn(XY)
|> startProfileAt([length / 2, 11.849525], %) |> startProfileAt([length / 2, 11.849525], %)
|> line(end = [-0.314524, -0.864147]) |> line(end = [-0.314524, -0.864147])
|> tangentialArcToRelative([-0.157636, -0.110378], %) |> tangentialArcToRelative([-0.157636, -0.110378], %)

View File

@ -17,35 +17,32 @@ gearHeight = 3
// Interpolate points along the involute curve // Interpolate points along the involute curve
cmo = 101 cmo = 101
rs = map([0..cmo], fn (i) { rs = map([0..cmo], fn(i) {
return baseDiameter / 2 + i / cmo * (tipDiameter - baseDiameter) / 2 return baseDiameter / 2 + i / cmo * (tipDiameter - baseDiameter) / 2
}) })
// Calculate operating pressure angle // Calculate operating pressure angle
angles = map(rs, fn (r) { angles = map(rs, fn(r) {
return toDegrees( acos(baseDiameter / 2 / r)) return toDegrees( acos(baseDiameter / 2 / r))
}) })
// Calculate the involute function // Calculate the involute function
invas = map(angles, fn (a) { invas = map(angles, fn(a) {
return tan(toRadians(a)) - toRadians(a) return tan(toRadians(a)) - toRadians(a)
}) })
// Map the involute curve // Map the involute curve
xs = map([0..cmo], fn (i) { xs = map([0..cmo], fn(i) {
return rs[i] * cos(invas[i]) return rs[i] * cos(invas[i])
}) })
ys = map([0..cmo], fn (i) { ys = map([0..cmo], fn(i) {
return rs[i] * sin(invas[i]) return rs[i] * sin(invas[i])
}) })
// Extrude the gear body // Extrude the gear body
body = startSketchOn('XY') body = startSketchOn(XY)
|> circle( |> circle(center = [0, 0], radius = baseDiameter / 2)
center = [0, 0],
radius = baseDiameter / 2
)
|> extrude(length = gearHeight) |> extrude(length = gearHeight)
toothAngle = 360 / nTeeth / 1.5 toothAngle = 360 / nTeeth / 1.5
@ -63,7 +60,7 @@ fn rightInvolute(i, sg) {
} }
// Draw gear teeth // Draw gear teeth
start = startSketchOn('XY') start = startSketchOn(XY)
|> startProfileAt([xs[101], ys[101]], %) |> startProfileAt([xs[101], ys[101]], %)
teeth = reduce([0..100], start, leftInvolute) teeth = reduce([0..100], start, leftInvolute)
|> arc({ |> arc({
@ -79,7 +76,7 @@ teeth = reduce([0..100], start, leftInvolute)
center = [0, 0, 0], center = [0, 0, 0],
instances = nTeeth, instances = nTeeth,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
// Define the constants of the keyway and the bore hole // Define the constants of the keyway and the bore hole

View File

@ -34,7 +34,7 @@ fn face(plane) {
} }
// extrude a single side of the bin // extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius)), length = binLength - (cornerRadius * 2), ) singleSide = extrude(face(offsetPlane(YZ, offset = cornerRadius)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern // create the other sides of the bin by using a circular pattern
sides = patternCircular3d( sides = patternCircular3d(
@ -43,19 +43,17 @@ sides = patternCircular3d(
axis = [0, 0, 1], axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0], center = [binLength / 2, binLength / 2, 0],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// define an axis axis000 // define an axis axis000
axis000 = { axis000 = {
custom = { direction = [0.0, 1.0],
axis = [0.0, 1.0], origin = [cornerRadius, cornerRadius]
origin = [cornerRadius, cornerRadius]
}
} }
// create a single corner of the bin // create a single corner of the bin
singleCorner = revolve(face(offsetPlane("YZ", offset = cornerRadius)), angle = -90, axis = axis000) singleCorner = revolve(face(offsetPlane(YZ, offset = cornerRadius)), angle = -90, axis = axis000)
// create the corners of the bin // create the corners of the bin
corners = patternCircular3d( corners = patternCircular3d(
@ -64,7 +62,7 @@ corners = patternCircular3d(
axis = [0, 0, 1], axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0], center = [binLength / 2, binLength / 2, 0],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// create the baseplate by patterning sides // create the baseplate by patterning sides
@ -72,26 +70,18 @@ basePlateSides = patternLinear3d(
sides, sides,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)
// create the corners of the baseplate by patterning the corners // create the corners of the baseplate by patterning the corners
basePlateCorners = patternLinear3d( basePlateCorners = patternLinear3d(
corners, corners,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)
// create the center cutout for the magnet profile // create the center cutout for the magnet profile
fn magnetCenterCutout(plane) { fn magnetCenterCutout(plane) {
@ -149,20 +139,17 @@ fn magnetBase(plane) {
} }
// create sketch profile sketch000Profile002 // create sketch profile sketch000Profile002
magnetsSketch = startSketchOn('XY') magnetsSketch = startSketchOn(XY)
|> circle( |> circle(center = [cornerRadius * 2, cornerRadius * 2], radius = magOuterDiam / 2)
center = [cornerRadius * 2, cornerRadius * 2],
radius = magOuterDiam / 2
)
|> patternCircular2d( |> patternCircular2d(
center = [binLength / 2, binLength / 2], center = [binLength / 2, binLength / 2],
instances = 4, instances = 4,
arcDegrees = 360, arcDegrees = 360,
rotateDuplicates = true rotateDuplicates = true,
) )
// create a profile with holes for the magnets // create a profile with holes for the magnets
magnetProfile = magnetBase("XY") magnetProfile = magnetBase(XY)
|> hole(magnetsSketch, %) |> hole(magnetsSketch, %)
// create an extrusion of the magnet cutout with holes // create an extrusion of the magnet cutout with holes
@ -177,11 +164,11 @@ magnetHolesExtrudeFillets = fillet(
getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line001), getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line001),
getNextAdjacentEdge(magnetHolesExtrude.sketch.tags.line003), getNextAdjacentEdge(magnetHolesExtrude.sketch.tags.line003),
getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line003) getPreviousAdjacentEdge(magnetHolesExtrude.sketch.tags.line003)
] ],
) )
// create a profile without the holes for the magnets // create a profile without the holes for the magnets
magnetProfileNoMagnets = magnetBase(offsetPlane("XY", offset = -magDepth)) magnetProfileNoMagnets = magnetBase(offsetPlane(XY, offset = -magDepth))
// create an extrusion of the magnet cutout without holes // create an extrusion of the magnet cutout without holes
magnetCutoutExtrude = extrude(magnetProfileNoMagnets, length = -magDepth) magnetCutoutExtrude = extrude(magnetProfileNoMagnets, length = -magDepth)
@ -195,7 +182,7 @@ magnetCutoutExtrudeFillets = fillet(
getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line001), getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line001),
getNextAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003), getNextAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003),
getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003) getPreviousAdjacentEdge(magnetCutoutExtrude.sketch.tags.line003)
] ],
) )
// pattern the magnet cutouts with holes // pattern the magnet cutouts with holes
@ -203,23 +190,15 @@ patternLinear3d(
magnetHolesExtrudeFillets, magnetHolesExtrudeFillets,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)
// pattern the magnet cutouts without holes // pattern the magnet cutouts without holes
patternLinear3d( patternLinear3d(
magnetCutoutExtrudeFillets, magnetCutoutExtrudeFillets,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)

View File

@ -31,7 +31,7 @@ fn face(plane) {
} }
// extrude a single side of the bin // extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius)), length = binLength - (cornerRadius * 2)) singleSide = extrude(face(offsetPlane(YZ, offset = cornerRadius)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern // create the other sides of the bin by using a circular pattern
sides = patternCircular3d( sides = patternCircular3d(
@ -40,19 +40,17 @@ sides = patternCircular3d(
axis = [0, 0, 1], axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0], center = [binLength / 2, binLength / 2, 0],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// define an axis axis000 // define an axis axis000
axis000 = { axis000 = {
custom = { direction = [0.0, 1.0],
axis = [0.0, 1.0], origin = [cornerRadius, cornerRadius]
origin = [cornerRadius, cornerRadius]
}
} }
// create a single corner of the bin // create a single corner of the bin
singleCorner = revolve(face(offsetPlane("YZ", offset = cornerRadius)), angle = -90, axis = axis000) singleCorner = revolve(face(offsetPlane(YZ, offset = cornerRadius)), angle = -90, axis = axis000)
// create the corners of the bin // create the corners of the bin
corners = patternCircular3d( corners = patternCircular3d(
@ -61,7 +59,7 @@ corners = patternCircular3d(
axis = [0, 0, 1], axis = [0, 0, 1],
center = [binLength / 2, binLength / 2, 0], center = [binLength / 2, binLength / 2, 0],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// create the baseplate by patterning sides // create the baseplate by patterning sides
@ -69,23 +67,15 @@ basePlateSides = patternLinear3d(
sides, sides,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)
// create the corners of the baseplate by patterning the corners // create the corners of the baseplate by patterning the corners
basePlateCorners = patternLinear3d( basePlateCorners = patternLinear3d(
corners, corners,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength distance = binLength,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength)

View File

@ -47,7 +47,7 @@ fn face(plane) {
} }
// extrude a single side of the bin // extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2)) singleSide = extrude(face(offsetPlane(YZ, offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern // create the other sides of the bin by using a circular pattern
sides = patternCircular3d( sides = patternCircular3d(
@ -60,22 +60,20 @@ sides = patternCircular3d(
0 0
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// define an axis axis000 // define an axis axis000
axis000 = { axis000 = {
custom = { direction = [0.0, 1.0],
axis = [0.0, 1.0], origin = [
origin = [ cornerRadius + binTol,
cornerRadius + binTol, cornerRadius + binTol
cornerRadius + binTol ]
]
}
} }
// create a single corner of the bin // create a single corner of the bin
singleCorner = revolve(face(offsetPlane("YZ", offset = cornerRadius + binTol)), angle = -90, axis = axis000) singleCorner = revolve(face(offsetPlane(YZ, offset = cornerRadius + binTol)), angle = -90, axis = axis000)
// create the corners of the bin // create the corners of the bin
corners = patternCircular3d( corners = patternCircular3d(
@ -88,10 +86,10 @@ corners = patternCircular3d(
0 0
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
singleBinFill = startSketchOn("XY") singleBinFill = startSketchOn(XY)
|> startProfileAt([ |> startProfileAt([
binBaseLength + binTol, binBaseLength + binTol,
binBaseLength + binTol binBaseLength + binTol
@ -108,7 +106,7 @@ singleBinFill = startSketchOn("XY")
getPreviousAdjacentEdge(line000), getPreviousAdjacentEdge(line000),
getNextAdjacentEdge(line002), getNextAdjacentEdge(line002),
getPreviousAdjacentEdge(line002) getPreviousAdjacentEdge(line002)
] ],
) )
magCutout000 = startSketchOn(singleBinFill, "start") magCutout000 = startSketchOn(singleBinFill, "start")
@ -117,7 +115,7 @@ magCutout000 = startSketchOn(singleBinFill, "start")
-magOffset - binBaseLength - binTol, -magOffset - binBaseLength - binTol,
magOffset + binBaseLength + binTol magOffset + binBaseLength + binTol
], ],
radius = magOuterDiam / 2 radius = magOuterDiam / 2,
) )
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
@ -126,7 +124,7 @@ magCutout000 = startSketchOn(singleBinFill, "start")
(binLength + 2 * binTol) / 2 (binLength + 2 * binTol) / 2
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(length = -magDepth) |> extrude(length = -magDepth)
@ -135,42 +133,30 @@ binSides = patternLinear3d(
sides, sides,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
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 // create the corners of the baseplate by patterning the corners
binCorners = patternLinear3d( binCorners = patternLinear3d(
corners, corners,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
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 // create the fill of the bin by patterning the corners
binFill = patternLinear3d( binFill = patternLinear3d(
singleBinFill, singleBinFill,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength + binTol * 2)
// //
binTop = startSketchOn(offsetPlane("XY", offset = height)) binTop = startSketchOn(offsetPlane(XY, offset = height))
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> xLine(length = (binLength + 2 * binTol) * countBinWidth, tag = $line010) |> xLine(length = (binLength + 2 * binTol) * countBinWidth, tag = $line010)
|> yLine(length = (binLength + 2 * binTol) * countBinLength, tag = $line011) |> yLine(length = (binLength + 2 * binTol) * countBinLength, tag = $line011)
@ -184,7 +170,7 @@ binTop = startSketchOn(offsetPlane("XY", offset = height))
getPreviousAdjacentEdge(line010), getPreviousAdjacentEdge(line010),
getNextAdjacentEdge(line012), getNextAdjacentEdge(line012),
getPreviousAdjacentEdge(line012) getPreviousAdjacentEdge(line012)
] ],
) )
|> shell(faces = ["end"], thickness = binThk) |> shell(faces = ["end"], thickness = binThk)
@ -265,7 +251,7 @@ lipLengths = patternCircular3d(
0 0
], ],
instances = 2, instances = 2,
rotateDuplicates = true rotateDuplicates = true,
) )
// create the other sides of the lips by using a circular pattern // create the other sides of the lips by using a circular pattern
@ -279,15 +265,13 @@ lipWidths = patternCircular3d(
0 0
], ],
instances = 2, instances = 2,
rotateDuplicates = true rotateDuplicates = true,
) )
// define an axis axis000 // define an axis axis000
axis001 = { axis001 = {
custom = { direction = [0.0, 1.0],
axis = [0.0, 1.0], origin = [cornerRadius, cornerRadius]
origin = [cornerRadius, cornerRadius]
}
} }
// create a single corner of the bin // create a single corner of the bin
@ -307,7 +291,7 @@ lipCorners000 = patternCircular3d(
0 0
], ],
instances = 2, instances = 2,
rotateDuplicates = true rotateDuplicates = true,
) )
// create the corners of the bin // create the corners of the bin
@ -321,5 +305,5 @@ lipCorners001 = patternCircular3d(
0 0
], ],
instances = 2, instances = 2,
rotateDuplicates = true rotateDuplicates = true,
) )

View File

@ -40,7 +40,7 @@ fn face(plane) {
} }
// extrude a single side of the bin // extrude a single side of the bin
singleSide = extrude(face(offsetPlane("YZ", offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2), ) singleSide = extrude(face(offsetPlane(YZ, offset = cornerRadius + binTol)), length = binLength - (cornerRadius * 2))
// create the other sides of the bin by using a circular pattern // create the other sides of the bin by using a circular pattern
sides = patternCircular3d( sides = patternCircular3d(
@ -53,22 +53,20 @@ sides = patternCircular3d(
0 0
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
// define an axis axis000 // define an axis axis000
axis000 = { axis000 = {
custom = { direction = [0.0, 1.0],
axis = [0.0, 1.0], origin = [
origin = [ cornerRadius + binTol,
cornerRadius + binTol, cornerRadius + binTol
cornerRadius + binTol ]
]
}
} }
// create a single corner of the bin // create a single corner of the bin
singleCorner = revolve(face(offsetPlane("YZ", offset = cornerRadius + binTol)), angle = -90, axis = axis000) singleCorner = revolve(face(offsetPlane(YZ, offset = cornerRadius + binTol)), angle = -90, axis = axis000)
// create the corners of the bin // create the corners of the bin
corners = patternCircular3d( corners = patternCircular3d(
@ -81,10 +79,10 @@ corners = patternCircular3d(
0 0
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
singleBinFill = startSketchOn("XY") singleBinFill = startSketchOn(XY)
|> startProfileAt([ |> startProfileAt([
binBaseLength + binTol, binBaseLength + binTol,
binBaseLength + binTol binBaseLength + binTol
@ -101,7 +99,7 @@ singleBinFill = startSketchOn("XY")
getPreviousAdjacentEdge(line000), getPreviousAdjacentEdge(line000),
getNextAdjacentEdge(line002), getNextAdjacentEdge(line002),
getPreviousAdjacentEdge(line002) getPreviousAdjacentEdge(line002)
] ],
) )
magCutout000 = startSketchOn(singleBinFill, "start") magCutout000 = startSketchOn(singleBinFill, "start")
@ -110,7 +108,7 @@ magCutout000 = startSketchOn(singleBinFill, "start")
-magOffset - binBaseLength - binTol, -magOffset - binBaseLength - binTol,
magOffset + binBaseLength + binTol magOffset + binBaseLength + binTol
], ],
radius = magOuterDiam / 2 radius = magOuterDiam / 2,
) )
|> patternCircular2d( |> patternCircular2d(
arcDegrees = 360, arcDegrees = 360,
@ -119,7 +117,7 @@ magCutout000 = startSketchOn(singleBinFill, "start")
(binLength + 2 * binTol) / 2 (binLength + 2 * binTol) / 2
], ],
instances = 4, instances = 4,
rotateDuplicates = true rotateDuplicates = true,
) )
|> extrude(length = -magDepth) |> extrude(length = -magDepth)
@ -128,42 +126,30 @@ binSides = patternLinear3d(
sides, sides,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
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 // create the corners of the baseplate by patterning the corners
binCorners = patternLinear3d( binCorners = patternLinear3d(
corners, corners,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
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 // create the fill of the bin by patterning the corners
binFill = patternLinear3d( binFill = patternLinear3d(
singleBinFill, singleBinFill,
axis = [1.0, 0.0, 0.0], axis = [1.0, 0.0, 0.0],
instances = countBinWidth, instances = countBinWidth,
distance = binLength + binTol * 2 distance = binLength + binTol * 2,
)
|> patternLinear3d(
axis = [0.0, 1.0, 0.0],
instances = countBinLength,
distance = binLength + binTol * 2
) )
|> patternLinear3d(axis = [0.0, 1.0, 0.0], instances = countBinLength, distance = binLength + binTol * 2)
// create the top of the bin // create the top of the bin
binTop = startSketchOn(offsetPlane("XY", offset = height)) binTop = startSketchOn(offsetPlane(XY, offset = height))
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> xLine(length = (binLength + 2 * binTol) * countBinWidth, tag = $line010) |> xLine(length = (binLength + 2 * binTol) * countBinWidth, tag = $line010)
|> yLine(length = (binLength + 2 * binTol) * countBinLength, tag = $line011) |> yLine(length = (binLength + 2 * binTol) * countBinLength, tag = $line011)
@ -177,6 +163,6 @@ binTop = startSketchOn(offsetPlane("XY", offset = height))
getPreviousAdjacentEdge(line010), getPreviousAdjacentEdge(line010),
getNextAdjacentEdge(line012), getNextAdjacentEdge(line012),
getPreviousAdjacentEdge(line012) getPreviousAdjacentEdge(line012)
] ],
) )
|> shell(faces = ["end"], thickness = binThk) |> shell(faces = ["end"], thickness = binThk)

View File

@ -11,7 +11,7 @@ diameter = 0.3125
// Define a function for the hex nut // Define a function for the hex nut
fn hexNut(start, thk, innerDia) { fn hexNut(start, thk, innerDia) {
hexNutSketch = startSketchOn('-XZ') hexNutSketch = startSketchOn(-XZ)
|> startProfileAt([start[0] + innerDia, start[1]], %) |> startProfileAt([start[0] + innerDia, start[1]], %)
|> angledLine({ angle = 240, length = innerDia }, %) |> angledLine({ angle = 240, length = innerDia }, %)
|> angledLine({ angle = 180, length = innerDia }, %) |> angledLine({ angle = 180, length = innerDia }, %)
@ -19,10 +19,7 @@ fn hexNut(start, thk, innerDia) {
|> angledLine({ angle = 60, length = innerDia }, %) |> angledLine({ angle = 60, length = innerDia }, %)
|> angledLine({ angle = 0, length = innerDia * .90 }, %) |> angledLine({ angle = 0, length = innerDia * .90 }, %)
|> close() |> close()
|> hole(circle( |> hole(circle(center = [start[0], start[1]], radius = innerDia / 2), %)
center = [start[0], start[1]],
radius = innerDia / 2
), %)
|> extrude(length = thk) |> extrude(length = thk)
return hexNutSketch return hexNutSketch
} }

View File

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

Some files were not shown because too many files have changed in this diff Show More