Compare commits
53 Commits
kcl-55
...
jtran/prom
Author | SHA1 | Date | |
---|---|---|---|
5ab814d153 | |||
fe83cd94ca | |||
5178a72c52 | |||
f4e801351c | |||
7329753211 | |||
7d46e7e271 | |||
0583eb07d5 | |||
73694563cf | |||
bb4ed59191 | |||
566143757f | |||
822f2ffc73 | |||
eb3ceba497 | |||
2c6d0621c9 | |||
d8e84cb5e3 | |||
0b1e79871f | |||
954fddf578 | |||
f94f748339 | |||
e2b7b22ca9 | |||
efc8c82d8b | |||
eac5abba79 | |||
1956c14b8a | |||
017fac7041 | |||
0bdc50c78f | |||
57d78b6094 | |||
db5ce7ba85 | |||
51c16d0048 | |||
6532b23f1c | |||
49304b9ecd | |||
358b34de4c | |||
9973e5fde3 | |||
36875e05fd | |||
42123383bb | |||
ef4c606ed1 | |||
1ebb73b935 | |||
b57d31c0e7 | |||
678ebbc310 | |||
cc2efd316c | |||
d1f811f91d | |||
7ca3afff9f | |||
71b9e40bd9 | |||
4f35197a96 | |||
40b0cf5fd3 | |||
355e6acf0d | |||
4ff38e7f44 | |||
1dcd3b84b7 | |||
2957216bd3 | |||
11160f0b40 | |||
4b2c745db5 | |||
bb983021b1 | |||
d27b8871bc | |||
1753047d87 | |||
fa16fcedff | |||
0677474097 |
@ -1,5 +1,6 @@
|
||||
NODE_ENV=development
|
||||
DEV=true
|
||||
|
||||
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_SITE_BASE_URL=https://dev.zoo.dev
|
||||
@ -8,3 +9,5 @@ VITE_KC_SKIP_AUTH=false
|
||||
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!
|
||||
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
|
||||
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
|
10
.envrc
@ -1,3 +1,13 @@
|
||||
# Load optional shared environment variables
|
||||
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 .
|
||||
|
31
.eslintrc
@ -20,8 +20,26 @@
|
||||
"plugin:react-hooks/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"no-array-constructor": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-array-constructor": "error",
|
||||
"@typescript-eslint/no-array-delete": "error",
|
||||
"@typescript-eslint/no-duplicate-enum-values": "error",
|
||||
"@typescript-eslint/no-duplicate-type-constituents": "error",
|
||||
"@typescript-eslint/no-empty-object-type": "error",
|
||||
"@typescript-eslint/no-extra-non-null-assertion": "error",
|
||||
"@typescript-eslint/no-floating-promises": "error",
|
||||
"@typescript-eslint/no-for-in-array": "error",
|
||||
"no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-implied-eval": "error",
|
||||
"@typescript-eslint/no-misused-new": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@typescript-eslint/no-non-null-asserted-optional-chain": "error",
|
||||
"@typescript-eslint/no-redundant-type-constituents": "error",
|
||||
"@typescript-eslint/no-this-alias": "warn",
|
||||
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||
"@typescript-eslint/no-unnecessary-type-constraint": "error",
|
||||
"no-unused-vars": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-unused-vars": ["error", {
|
||||
"varsIgnorePattern": "^_",
|
||||
"argsIgnorePattern": "^_",
|
||||
@ -29,6 +47,13 @@
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}],
|
||||
"@typescript-eslint/no-unsafe-unary-minus": "error",
|
||||
"@typescript-eslint/no-wrapper-object-types": "error",
|
||||
"no-throw-literal": "off", // Use @typescript-eslint/only-throw-error instead.
|
||||
"@typescript-eslint/only-throw-error": "error",
|
||||
"@typescript-eslint/prefer-as-const": "warn",
|
||||
"@typescript-eslint/prefer-namespace-keyword": "error",
|
||||
"@typescript-eslint/restrict-plus-operands": "error",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
@ -37,7 +62,7 @@
|
||||
{
|
||||
"name": "isNaN",
|
||||
"message": "Use Number.isNaN() instead."
|
||||
},
|
||||
}
|
||||
],
|
||||
"no-restricted-syntax": [
|
||||
"error",
|
||||
@ -51,7 +76,7 @@
|
||||
"never"
|
||||
],
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"suggest-no-throw/suggest-no-throw": "warn",
|
||||
"suggest-no-throw/suggest-no-throw": "error"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
@ -71,7 +96,7 @@
|
||||
"plugin:testing-library/react"
|
||||
],
|
||||
"rules": {
|
||||
"suggest-no-throw/suggest-no-throw": "off",
|
||||
"suggest-no-throw/suggest-no-throw": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
8
.github/workflows/e2e-tests.yml
vendored
@ -229,10 +229,6 @@ jobs:
|
||||
timeout_minutes: 30
|
||||
max_attempts: 3
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
|
||||
|
||||
@ -377,11 +373,7 @@ jobs:
|
||||
timeout_minutes: 45
|
||||
max_attempts: 15
|
||||
env:
|
||||
CI: true
|
||||
FAIL_ON_CONSOLE_ERRORS: true
|
||||
NODE_ENV: development
|
||||
VITE_KC_DEV_TOKEN: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
VITE_KC_SKIP_AUTH: true
|
||||
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
|
144
.github/workflows/static-analysis.yml
vendored
@ -28,43 +28,57 @@ jobs:
|
||||
- run: yarn fmt-check
|
||||
|
||||
yarn-build-wasm:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
# Build the wasm blob once on the fastest runner.
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
- run: yarn tsc
|
||||
- name: Build Wasm
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-22.04
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-wasm
|
||||
path: |
|
||||
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-ts-rs-bindings
|
||||
path: |
|
||||
rust/kcl-lib/bindings/*
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -73,7 +87,53 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn tsc
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn lint
|
||||
|
||||
python-codespell:
|
||||
@ -91,6 +151,7 @@ jobs:
|
||||
|
||||
yarn-unit-test-kcl-samples:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -103,7 +164,22 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
@ -120,6 +196,7 @@ jobs:
|
||||
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -132,7 +209,22 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
16
.github/workflows/update-e2e-branch.yml
vendored
@ -28,14 +28,18 @@ jobs:
|
||||
|
||||
- name: Sync with main
|
||||
run: |
|
||||
# checkout our branch
|
||||
# Create the branch
|
||||
git checkout all-e2e || git checkout -b all-e2e
|
||||
# fetch origin
|
||||
|
||||
# Reset to main
|
||||
git fetch origin
|
||||
# reset to main
|
||||
git reset --hard origin/main
|
||||
# get a new SHA to prevent overwriting the commit status on main
|
||||
git commit --amend --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
|
||||
# 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 push --force origin all-e2e
|
||||
|
4
Makefile
@ -37,7 +37,7 @@ build-web: public/kcl_wasm_lib_bg.wasm build/index.html
|
||||
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
||||
|
||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES)$(RUST_SOURCES)
|
||||
yarn build:wasm
|
||||
yarn build:wasm:dev
|
||||
|
||||
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
yarn build:local
|
||||
@ -99,7 +99,7 @@ test-e2e-web: install build-web ## Run the web e2e tests
|
||||
|
||||
.PHONY: test-e2e-desktop
|
||||
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
|
||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep="$(E2E_GREP)"
|
||||
|
||||
###############################################################################
|
||||
# CLEAN
|
||||
|
@ -9,13 +9,9 @@ layout: manual
|
||||
|
||||
### `std`
|
||||
|
||||
- [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN)
|
||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN)
|
||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN)
|
||||
- [`XY`](/docs/kcl/consts/std-XY)
|
||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||
- [`ZERO`](/docs/kcl/consts/std-ZERO)
|
||||
|
||||
### `std::math`
|
||||
|
||||
@ -23,3 +19,10 @@ layout: manual
|
||||
- [`PI`](/docs/kcl/consts/std-math-PI)
|
||||
- [`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)
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-HALF_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-THREE_QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-ZERO.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
@ -23,19 +23,15 @@ layout: manual
|
||||
* [`tag`](kcl/types/tag)
|
||||
* **std**
|
||||
* [`Face`](kcl/types/Face)
|
||||
* [`HALF_TURN`](kcl/consts/std-HALF_TURN)
|
||||
* [`Helix`](kcl/types/Helix)
|
||||
* [`Plane`](kcl/types/Plane)
|
||||
* [`Point2d`](kcl/types/Point2d)
|
||||
* [`Point3d`](kcl/types/Point3d)
|
||||
* [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
|
||||
* [`Sketch`](kcl/types/Sketch)
|
||||
* [`Solid`](kcl/types/Solid)
|
||||
* [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN)
|
||||
* [`XY`](kcl/consts/std-XY)
|
||||
* [`XZ`](kcl/consts/std-XZ)
|
||||
* [`YZ`](kcl/consts/std-YZ)
|
||||
* [`ZERO`](kcl/consts/std-ZERO)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
@ -146,3 +142,8 @@ layout: manual
|
||||
* [`tan`](kcl/std-math-tan)
|
||||
* **std::sketch**
|
||||
* [`circle`](kcl/std-sketch-circle)
|
||||
* **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)
|
||||
|
@ -6,6 +6,10 @@ layout: manual
|
||||
|
||||
Rotate a solid or a sketch.
|
||||
|
||||
This is really useful for assembling parts together. You can create a part and then rotate it to the correct orientation.
|
||||
|
||||
For sketches, you can use this to rotate a sketch and then loft it with another sketch.
|
||||
|
||||
### Using Roll, Pitch, and Yaw
|
||||
|
||||
When rotating a part in 3D space, "roll," "pitch," and "yaw" refer to the three rotational axes used to describe its orientation: roll is rotation around the longitudinal axis (front-to-back), pitch is rotation around the lateral axis (wing-to-wing), and yaw is rotation around the vertical axis (up-down); essentially, it's like tilting the part on its side (roll), tipping the nose up or down (pitch), and turning it left or right (yaw).
|
||||
@ -166,7 +170,7 @@ fn square() {
|
||||
profile001 = square()
|
||||
|
||||
profile002 = square()
|
||||
|> translate(translate = [0, 0, 20])
|
||||
|> translate(x = 0, y = 0, z = 20)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
|
||||
loft([profile001, profile002])
|
||||
|
@ -17,7 +17,7 @@ circle(@sketch_or_surface: Sketch | Plane | Face, center: Point2d, radius: numbe
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `sketch_or_surface` | [`Sketch`](/docs/kcl/types/Sketch) `|` [`Plane`](/docs/kcl/types/Face) `|` [`Plane`](/docs/kcl/types/Face) | Sketch to extend, or plane or surface to sketch on. | Yes |
|
||||
| `sketch_or_surface` | [`Sketch`](/docs/kcl/types/Sketch) OR [`Plane`](/docs/kcl/types/Plane) OR [`Face`](/docs/kcl/types/Face) | Sketch to extend, or plane or surface to sketch on. | Yes |
|
||||
| `center` | [`Point2d`](/docs/kcl/types/Point2d) | The center of the circle. | Yes |
|
||||
| `radius` | [`number`](/docs/kcl/types/number) | The radius of the circle. | Yes |
|
||||
| [`tag`](/docs/kcl/types/tag) | [`tag`](/docs/kcl/types/tag) | Create a new tag which refers to this circle. | No |
|
||||
|
6814
docs/kcl/std.json
@ -28,7 +28,7 @@ An extrude plane.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude plane. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -48,7 +48,7 @@ An extruded arc.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The face id for the extrude plane. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -68,7 +68,7 @@ Geometry metadata.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the chamfer surface. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
@ -88,7 +88,7 @@ Geometry metadata.
|
||||
| `faceId` |[`string`](/docs/kcl/types/string)| The id for the fillet surface. | No |
|
||||
| [`tag`](/docs/kcl/types/tag) |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag. | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -17,6 +17,6 @@ Geometry metadata.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the geometry. | No |
|
||||
| `sourceRange` |[`SourceRange`](/docs/kcl/types/SourceRange)| The source range. | No |
|
||||
| `sourceRange` |`[integer, integer, integer]`| The source range. | No |
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ A helix.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `value` |[`string`](/docs/kcl/types/string)| The id of the helix. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `revolutions` |[`number`](/docs/kcl/types/number)| Number of revolutions. | No |
|
||||
| `angleStart` |[`number`](/docs/kcl/types/number)| Start angle (in degrees). | No |
|
||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||
|
@ -285,7 +285,7 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Module`| | No |
|
||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||
| `value` |`integer`| Identifier of a source file. Uses a u32 to keep the size small. | No |
|
||||
|
||||
|
||||
----
|
||||
|
@ -17,6 +17,6 @@ Data for polar coordinates.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `angle` |[`number`](/docs/kcl/types/number)| The angle of the line (in degrees). | No |
|
||||
| `length` |[`TyF64`](/docs/kcl/types/TyF64)| The length of the line. | No |
|
||||
| `length` |[`number`](/docs/kcl/types/number)| The length of the line. | No |
|
||||
|
||||
|
||||
|
@ -25,7 +25,7 @@ A sketch type.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `plane`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the plane. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| Type for a plane. | No |
|
||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
|
||||
@ -49,7 +49,7 @@ A face.
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `face`| | No |
|
||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the face. | No |
|
||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||
| `artifactId` |[`string`](/docs/kcl/types/string)| The artifact ID. | No |
|
||||
| `value` |[`string`](/docs/kcl/types/string)| The tag of the face. | No |
|
||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
|
||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "SourceRange"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `integer` (`uint`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -5,17 +5,11 @@ layout: manual
|
||||
---
|
||||
|
||||
|
||||
**Type:** `object`
|
||||
**Type:** [`number`](/docs/kcl/types/number) (`double`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `n` |[`number`](/docs/kcl/types/number)| | No |
|
||||
| `ty` |[`NumericType`](/docs/kcl/types/NumericType)| | No |
|
||||
|
||||
|
||||
|
@ -85,7 +85,7 @@ async function doBasicSketch(
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
if (openPanes.includes('code')) {
|
||||
await expect(u.codeLocator)
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
@ -119,10 +119,7 @@ async function doBasicSketch(
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
if (openPanes.includes('code')) {
|
||||
await expect(
|
||||
await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)
|
||||
).toBeLessThan(3)
|
||||
await expect(await u.getGreatestPixDiff(line1, [0, 0, 255])).toBeLessThan(3)
|
||||
expect(await u.getGreatestPixDiff(line1, TEST_COLORS.BLUE)).toBeLessThan(3)
|
||||
}
|
||||
|
||||
// hold down shift
|
||||
@ -145,7 +142,7 @@ async function doBasicSketch(
|
||||
// Open the code pane.
|
||||
await u.openKclCodePanel()
|
||||
await expect(u.codeLocator)
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1}, tag = $seg01)
|
||||
|
117
e2e/playwright/boolean.spec.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { test, expect } from './zoo-test'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
test.describe('Point and click for boolean workflows', () => {
|
||||
// Boolean operations to test
|
||||
const booleanOperations = [
|
||||
{
|
||||
name: 'union',
|
||||
code: 'union([extrude001, extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
code: 'intersect([extrude001, extrude006])',
|
||||
},
|
||||
] as const
|
||||
for (let i = 0; i < booleanOperations.length; i++) {
|
||||
const operation = booleanOperations[i]
|
||||
const operationName = operation.name
|
||||
const commandName = `Boolean ${
|
||||
operationName.charAt(0).toUpperCase() + operationName.slice(1)
|
||||
}`
|
||||
test(`Create boolean operation -- ${operationName}`, async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
page,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./rust/kcl-lib/e2e/executor/inputs/boolean-setup-with'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Test coordinates for selection - these might need adjustment based on actual scene layout
|
||||
const cylinderPoint = { x: 592, y: 174 }
|
||||
const secondObjectPoint = { x: 683, y: 273 }
|
||||
|
||||
// Create mouse helpers for selecting objects
|
||||
const [clickFirstObject] = scene.makeMouseHelpers(
|
||||
cylinderPoint.x,
|
||||
cylinderPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
const [clickSecondObject] = scene.makeMouseHelpers(
|
||||
secondObjectPoint.x,
|
||||
secondObjectPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
|
||||
await test.step(`Test ${operationName} operation`, async () => {
|
||||
// Click the boolean operation button in the toolbar
|
||||
await toolbar.selectBoolean(operationName)
|
||||
|
||||
// Verify command bar is showing the right command
|
||||
await expect(cmdBar.page.getByTestId('command-name')).toContainText(
|
||||
commandName
|
||||
)
|
||||
|
||||
// Select first object in the scene, expect there to be a pixel diff from the selection color change
|
||||
await clickFirstObject({ pixelDiff: 50 })
|
||||
|
||||
// For subtract, we need to proceed to the next step before selecting the second object
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
}
|
||||
|
||||
// Select second object
|
||||
await clickSecondObject({ pixelDiff: 50 })
|
||||
|
||||
// Confirm the operation in the command bar
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
if (operationName === 'union' || operationName === 'intersect') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Solids: '2 paths',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
} else if (operationName === 'subtract') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
}
|
||||
|
||||
await cmdBar.submit()
|
||||
|
||||
await editor.expectEditor.toContain(operation.code)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
@ -46,7 +46,7 @@ test.describe(
|
||||
},
|
||||
}
|
||||
|
||||
const code = `sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
||||
const code = `@settings(defaultLengthUnit = in)sketch001 = startSketchOn(${plane})profile001 = startProfileAt([0.91, -1.22], sketch001)`
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
|
@ -251,11 +251,11 @@ test(
|
||||
])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
join(routerTemplateDir, 'main.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
|
@ -20,11 +20,11 @@ test(
|
||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||
await Promise.all([
|
||||
fsp.copyFile(
|
||||
executorInputPath('router-template-slate.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'other.kcl')
|
||||
),
|
||||
fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
),
|
||||
])
|
||||
@ -107,7 +107,7 @@ test(
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(300_000)
|
||||
.toBeGreaterThan(30_000)
|
||||
})
|
||||
})
|
||||
|
||||
@ -187,7 +187,7 @@ test(
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(70_000)
|
||||
.toBeGreaterThan(50_000)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -32,26 +32,30 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('/')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
// |> close()`)
|
||||
// |> close()`.replaceAll('\n', '')
|
||||
)
|
||||
|
||||
// uncomment the code
|
||||
await page.keyboard.down('ControlOrMeta')
|
||||
await page.keyboard.press('/')
|
||||
await page.keyboard.up('ControlOrMeta')
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`)
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
|
||||
test('ensure we use the cache, and do not re-execute', async ({
|
||||
@ -178,13 +182,15 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.locator('#code-pane button:first-child').click()
|
||||
await page.locator('button:has-text("Format code")').click()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`)
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
|
||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||
@ -227,13 +233,15 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch_001 = startSketchOn(XY)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch_001 = startSketchOn(XY)
|
||||
|> startProfileAt([-10, -10], %)
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, 20])
|
||||
|> line(end = [-20, 0])
|
||||
|> close()`)
|
||||
|> close()`.replaceAll('\n', '')
|
||||
)
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -471,6 +479,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
test('if you write kcl with lint errors you get lints', async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
@ -490,10 +499,7 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.keyboard.press('ArrowLeft')
|
||||
await page.keyboard.press('ArrowRight')
|
||||
|
||||
// FIXME: lsp errors do not propagate to the frontend until engine is connected and code is executed
|
||||
// This timeout is to wait for engine connection. LSP and code execution errors should be handled differently
|
||||
// LSP can emit errors as fast as it waits and show them in the editor
|
||||
await page.waitForTimeout(10000)
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
// error in guter
|
||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||
@ -815,10 +821,12 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||
)
|
||||
|
||||
// expect there to be no KCL errors
|
||||
await expect(page.locator('.cm-lint-marker-error')).toHaveCount(0)
|
||||
@ -888,10 +896,12 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([3.14, 12], %)
|
||||
|> xLine(%, length = 5) // lin`)
|
||||
|> xLine(%, length = 5) // lin`.replaceAll('\n', '')
|
||||
)
|
||||
})
|
||||
})
|
||||
test('Can undo a click and point extrude with ctrl+z', async ({
|
||||
@ -1206,4 +1216,55 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test('Rectangle tool panning with middle click', async ({
|
||||
page,
|
||||
homePage,
|
||||
toolbar,
|
||||
scene,
|
||||
cmdBar,
|
||||
editor,
|
||||
}) => {
|
||||
await page.setBodyDimensions({ width: 1200, height: 900 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
// wait until scene is ready to be interacted with
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
|
||||
// select an axis plane
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
// Needed as we don't yet have a way to get a signal from the engine that the camera has animated to the sketch plane
|
||||
await page.waitForTimeout(1000)
|
||||
|
||||
const middleMousePan = async (
|
||||
startX: number,
|
||||
startY: number,
|
||||
endX: number,
|
||||
endY: number
|
||||
) => {
|
||||
const initialCode = await editor.getCurrentCode()
|
||||
|
||||
await page.mouse.click(startX, startY, { button: 'middle' })
|
||||
await page.mouse.move(endX, endY, {
|
||||
steps: 10,
|
||||
})
|
||||
|
||||
// We expect the code to be the same, middle mouse click should not modify the code, only do panning
|
||||
await editor.expectEditor.toBe(initialCode)
|
||||
}
|
||||
|
||||
await test.step(`Verify corner rectangle panning`, async () => {
|
||||
await page.getByTestId('corner-rectangle').click()
|
||||
await middleMousePan(800, 500, 900, 600)
|
||||
})
|
||||
|
||||
await test.step(`Verify center rectangle panning`, async () => {
|
||||
await toolbar.selectCenterRectangle()
|
||||
await middleMousePan(800, 200, 900, 300)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -81,6 +81,13 @@ export class EditorFixture {
|
||||
expectEditor = {
|
||||
toContain: this._expectEditorToContain(),
|
||||
not: { toContain: this._expectEditorToContain(true) },
|
||||
toBe: async (code: string) => {
|
||||
const currentCode = await this.getCurrentCode()
|
||||
return expect(currentCode).toBe(code)
|
||||
},
|
||||
}
|
||||
getCurrentCode = async () => {
|
||||
return await this.codeContent.innerText()
|
||||
}
|
||||
snapshot = async (options?: { timeout?: number; name?: string }) => {
|
||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||
|
@ -124,6 +124,7 @@ export class ElectronZoo {
|
||||
|
||||
// We need to expose this in order for some tests that require folder
|
||||
// creation and some code below.
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
const that = this
|
||||
|
||||
const options = {
|
||||
|
@ -310,7 +310,9 @@ export async function expectPixelColor(
|
||||
.toBeTruthy()
|
||||
.catch((cause) => {
|
||||
throw new Error(
|
||||
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
||||
`ExpectPixelColor: point ${JSON.stringify(
|
||||
coords
|
||||
)} was expecting ${colour} but got ${finalValue}`,
|
||||
{ cause }
|
||||
)
|
||||
})
|
||||
|
@ -181,6 +181,14 @@ export class ToolbarFixture {
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
||||
}
|
||||
selectBoolean = async (operation: 'union' | 'subtract' | 'intersect') => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down Union: open menu' })
|
||||
.click()
|
||||
const operationTestId = `dropdown-boolean-${operation}`
|
||||
await expect(this.page.getByTestId(operationTestId)).toBeVisible()
|
||||
await this.page.getByTestId(operationTestId).click()
|
||||
}
|
||||
|
||||
selectCircleThreePoint = async () => {
|
||||
await this.page
|
||||
|
@ -257,6 +257,29 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
},
|
||||
// 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',
|
||||
},
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: 'Failed to fetch',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
{
|
||||
name: 'Error',
|
||||
message: 'The "path" argument must be of type string. Received undefined',
|
||||
stack:
|
||||
'Error: The "path" argument must be of type string. Received undefined',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: '', // many tests are impacted by this error
|
||||
},
|
||||
]
|
||||
|
||||
const cleanString = (str: string) => str.replace(/[`"]/g, '')
|
||||
|
@ -11,7 +11,7 @@ test(
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -51,7 +51,7 @@ test(
|
||||
const bracketDir = join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
|
292
e2e/playwright/named-views.spec.ts
Normal 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')
|
||||
})
|
||||
})
|
@ -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
|
@ -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
|
@ -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
|
312
e2e/playwright/native-file-menu.spec.ts
Normal 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()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -230,9 +230,9 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings }) => {
|
||||
async ({ settingsKey, settings, code }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem('persistCode', code)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
@ -240,6 +240,7 @@ test.describe('Onboarding tests', () => {
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
code: originalCode,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -137,7 +137,7 @@ test.describe('Point-and-click tests', () => {
|
||||
|
||||
await scene.moveCameraTo(cameraPos, cameraTarget)
|
||||
|
||||
await test.step('check chamfer selection changes cursor positon', async () => {
|
||||
await test.step('check chamfer selection changes cursor position', async () => {
|
||||
await expect(async () => {
|
||||
// sometimes initial click doesn't register
|
||||
await clickChamfer()
|
||||
@ -173,7 +173,7 @@ test.describe('Point-and-click tests', () => {
|
||||
})
|
||||
await test.step('Check there is no errors after code created in previous steps executes', async () => {
|
||||
await editor.expectState({
|
||||
activeLines: ['sketch001 = startSketchOn(XZ)'],
|
||||
activeLines: ['@settings(defaultLengthUnit = in)'],
|
||||
highlightedCode: '',
|
||||
diagnostics: [],
|
||||
})
|
||||
@ -299,7 +299,8 @@ test.describe('Point-and-click tests', () => {
|
||||
|
||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -369,7 +370,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
})
|
||||
})
|
||||
|
||||
test('Works on chamfers that are non in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
test('Works on chamfers that are not in a pipeExpression can break up multi edges in a chamfer array', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
@ -418,7 +419,8 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
|>close()`,
|
||||
})
|
||||
await editor.expectEditor.toContain(
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([75.8, 317.2], %)
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -1082,8 +1084,8 @@ openSketch = startSketchOn(XY)
|
||||
}) => {
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, ccw = false, radius = 5, axis = 'X', length = 5,)`
|
||||
const expectedLine = `revolutions=1,`
|
||||
const expectedOutput = `helix001 = helix( axis = 'X', radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `axis='X',`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
@ -1091,17 +1093,16 @@ openSketch = startSketchOn(XY)
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'axisOrEdge',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
AxisOrEdge: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'axisOrEdge',
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -1110,7 +1111,19 @@ openSketch = startSketchOn(XY)
|
||||
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()
|
||||
})
|
||||
|
||||
@ -1134,30 +1147,31 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: initialInput,
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: initialInput,
|
||||
Radius: '5',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'length',
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page.keyboard.press('Shift+Backspace')
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: newInput,
|
||||
Radius: '5',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1181,14 +1195,14 @@ openSketch = startSketchOn(XY)
|
||||
{
|
||||
selectionType: 'segment',
|
||||
testPoint: { x: 513, y: 221 },
|
||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 100,)`,
|
||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 50,)`,
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
{
|
||||
selectionType: 'sweepEdge',
|
||||
testPoint: { x: 564, y: 364 },
|
||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`,
|
||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`,
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
]
|
||||
helixCases.map(
|
||||
@ -1225,17 +1239,16 @@ openSketch = startSketchOn(XY)
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'axisOrEdge',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '',
|
||||
AxisOrEdge: '',
|
||||
Mode: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
},
|
||||
highlightedHeaderArg: 'axisOrEdge',
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'Edge' }).click()
|
||||
@ -1246,21 +1259,17 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('0')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: 'True' }).click()
|
||||
await page.keyboard.insertText('1')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('100')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
AxisOrEdge: 'Edge',
|
||||
Mode: 'Edge',
|
||||
Edge: `1 ${selectionType}`,
|
||||
CounterClockWise: '',
|
||||
Length: '100',
|
||||
Radius: '1',
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1280,22 +1289,24 @@ openSketch = startSketchOn(XY)
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick()
|
||||
const initialInput = '100'
|
||||
const newInput = '50'
|
||||
const initialInput = '1'
|
||||
const newInput = '5'
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: initialInput,
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
CounterClockWise: '',
|
||||
Length: initialInput,
|
||||
Radius: '1',
|
||||
Revolutions: '20',
|
||||
Radius: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'length',
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page
|
||||
.getByRole('button', { name: 'radius', exact: false })
|
||||
.click()
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput
|
||||
.locator('.cm-content')
|
||||
@ -1305,10 +1316,9 @@ openSketch = startSketchOn(XY)
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
CounterClockWise: '',
|
||||
Length: newInput,
|
||||
Radius: '1',
|
||||
Revolutions: '20',
|
||||
Radius: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1336,6 +1346,140 @@ 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: '',
|
||||
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 = [
|
||||
{ shouldPreselect: true },
|
||||
{ shouldPreselect: false },
|
||||
@ -1497,9 +1641,10 @@ loft001 = loft([sketch001, sketch002])
|
||||
{
|
||||
targetType: 'circle',
|
||||
testPoint: { x: 700, y: 250 },
|
||||
initialCode: `sketch001 = startSketchOn('YZ')
|
||||
initialCode: `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
profile001 = circle(sketch001, center = [0, 0], radius = 500)
|
||||
sketch002 = startSketchOn('XZ')
|
||||
sketch002 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArcTo([-2000, 500], %)`,
|
||||
@ -1507,7 +1652,8 @@ sketch002 = startSketchOn('XZ')
|
||||
{
|
||||
targetType: 'rectangle',
|
||||
testPoint: { x: 710, y: 255 },
|
||||
initialCode: `sketch001 = startSketchOn('YZ')
|
||||
initialCode: `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
profile001 = startProfileAt([-400, -400], sketch001)
|
||||
|> angledLine([0, 800], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -1520,7 +1666,7 @@ profile001 = startProfileAt([-400, -400], sketch001)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
sketch002 = startSketchOn('XZ')
|
||||
sketch002 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(length = -500)
|
||||
|> tangentialArcTo([-2000, 500], %)`,
|
||||
@ -1664,7 +1810,8 @@ sketch002 = startSketchOn('XZ')
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(YZ)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(YZ)
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = 500
|
||||
@ -2333,7 +2480,8 @@ extrude001 = extrude(profile001, length = 5)
|
||||
cmdBar,
|
||||
}) => {
|
||||
// Code samples
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-12, -6], %)
|
||||
|> line(end = [0, 12])
|
||||
|> line(end = [24, 0])
|
||||
@ -2625,7 +2773,8 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
toolbar,
|
||||
}) => {
|
||||
// Code samples
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-12, -6], %)
|
||||
|> line(end = [0, 12])
|
||||
|> line(end = [24, 0], tag = $seg02)
|
||||
@ -2779,7 +2928,8 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 30)
|
||||
extrude001 = extrude(sketch001, length = 30)
|
||||
`
|
||||
@ -2914,7 +3064,8 @@ extrude001 = extrude(sketch001, length = 30)
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfileAt([-20, 20], %)
|
||||
|> xLine(length = 40)
|
||||
|> yLine(length = -60)
|
||||
@ -3032,7 +3183,8 @@ extrude001 = extrude(sketch001, length = 40)
|
||||
})
|
||||
|
||||
const shellSketchOnFacesCases = [
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 100)
|
||||
|> extrude(length = 100)
|
||||
|
||||
@ -3040,7 +3192,8 @@ sketch002 = startSketchOn(sketch001, 'END')
|
||||
|> circle(center = [0, 0], radius = 50)
|
||||
|> extrude(length = 50)
|
||||
`,
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = 100)
|
||||
extrude001 = extrude(sketch001, length = 100)
|
||||
|
||||
@ -3323,6 +3476,39 @@ segAng(rectangleSegmentA002),
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = 'X')`
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '90'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
|
||||
)
|
||||
})
|
||||
test('revolve surface around edge from an extruded solid2d', async ({
|
||||
context,
|
||||
@ -3333,26 +3519,22 @@ segAng(rectangleSegmentA002),
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-102.57, 101.72], %)
|
||||
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
202.6
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-102.57, 101.72], %)
|
||||
|> angledLine([0, 202.6], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
202.6
|
||||
], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 50)
|
||||
sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|
||||
|> circle(
|
||||
center = [-11.34, 10.0],
|
||||
radius = 8.69
|
||||
)
|
||||
|> circle(center = [-11.34, 10.0], radius = 8.69)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
@ -3370,9 +3552,49 @@ radius = 8.69
|
||||
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)`
|
||||
await page.getByText(lineCodeToSelection).click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = getOppositeEdge(rectangleSegmentA001)) `
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = rectangleSegmentA001)`
|
||||
await editor.expectEditor.toContain(newCodeToFind)
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '180'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||
'angle001'
|
||||
)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain('angle001 = ' + newAngle)
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = angle001')
|
||||
)
|
||||
})
|
||||
test('revolve sketch circle around line segment from startProfileAt sketch', async ({
|
||||
context,
|
||||
@ -3383,26 +3605,22 @@ radius = 8.69
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `
|
||||
sketch002 = startSketchOn(XY)
|
||||
|> startProfileAt([-2.02, 1.79], %)
|
||||
|> xLine(length = 2.6)
|
||||
sketch001 = startSketchOn('-XY')
|
||||
|> startProfileAt([-0.48, 1.25], %)
|
||||
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
sketch003 = startSketchOn(extrude001, 'START')
|
||||
|> circle(
|
||||
center = [-0.69, 0.56],
|
||||
radius = 0.28
|
||||
)
|
||||
const initialCode = `sketch002 = startSketchOn(XY)
|
||||
|> startProfileAt([-2.02, 1.79], %)
|
||||
|> xLine(length = 2.6)
|
||||
sketch001 = startSketchOn(-XY)
|
||||
|> startProfileAt([-0.48, 1.25], %)
|
||||
|> angledLine([0, 2.38], %, $rectangleSegmentA001)
|
||||
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 5)
|
||||
sketch003 = startSketchOn(extrude001, 'START')
|
||||
|> circle(center = [-0.69, 0.56], radius = 0.28)
|
||||
`
|
||||
|
||||
await context.addInitScript((initialCode) => {
|
||||
@ -3421,9 +3639,44 @@ radius = 8.69
|
||||
const lineCodeToSelection = `|> xLine(length = 2.6)`
|
||||
await page.getByText(lineCodeToSelection).click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch003, angle = 360, axis = seg01)`
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
|
||||
// Edit flow
|
||||
const newAngle = '270'
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Revolve',
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick({ button: 'left' })
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Revolve',
|
||||
currentArgKey: 'angle',
|
||||
currentArgValue: '360',
|
||||
headerArguments: {
|
||||
Angle: '360',
|
||||
},
|
||||
highlightedHeaderArg: 'angle',
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Angle: newAngle,
|
||||
},
|
||||
commandName: 'Revolve',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await editor.expectEditor.toContain(
|
||||
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@ -3436,7 +3689,8 @@ radius = 8.69
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||
const initialCode = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(
|
||||
sketch001,
|
||||
center = [0, 0],
|
||||
|
@ -87,7 +87,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -124,7 +124,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
const errorDir = path.join(dir, 'broken-code')
|
||||
@ -162,7 +162,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [110, 110, 110]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(20)
|
||||
@ -213,7 +213,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
const emptyDir = path.join(dir, 'empty')
|
||||
@ -248,7 +248,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -290,7 +290,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
|
||||
@ -319,7 +319,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -359,7 +359,7 @@ test(
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
await fsp.copyFile(
|
||||
@ -393,7 +393,7 @@ test(
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -443,7 +443,6 @@ test(
|
||||
await page.getByText('broken-code').click()
|
||||
|
||||
// Gotcha: You can not use scene.waitForExecutionDone() since the KCL code is going to fail
|
||||
await expect(page.getByTestId('loading')).toBeAttached()
|
||||
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||
timeout: 20_000,
|
||||
})
|
||||
@ -473,12 +472,15 @@ test.describe('Can export from electron app', () => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
if (runningOnWindows()) {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
}
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -510,7 +512,7 @@ test.describe('Can export from electron app', () => {
|
||||
// gray at this pixel means the stream has loaded in the most
|
||||
// user way we can verify it (pixel color)
|
||||
await expect
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||
.poll(() => u.getGreatestPixDiff(pointOnModel, [125, 125, 125]), {
|
||||
timeout: 10_000,
|
||||
})
|
||||
.toBeLessThan(15)
|
||||
@ -551,7 +553,7 @@ test.describe('Can export from electron app', () => {
|
||||
},
|
||||
{ timeout: 15_000 }
|
||||
)
|
||||
.toBeGreaterThan(300_000)
|
||||
.toBeGreaterThan(50_000)
|
||||
|
||||
// clean up exported file
|
||||
await fsp.rm(filepath)
|
||||
@ -1504,7 +1506,12 @@ test(
|
||||
|
||||
await u.waitForPageLoad()
|
||||
|
||||
await page.locator('.cm-content').fill(`sketch001 = startSketchOn(XZ)
|
||||
// The file should be prepopulated with the user's unit settings.
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
'@settings(defaultLengthUnit = in)'
|
||||
)
|
||||
|
||||
await page.locator('.cm-content').fill(`sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([-87.4, 282.92], %)
|
||||
|> line(end = [324.07, 27.199], tag = $seg01)
|
||||
|> line(end = [118.328, -291.754])
|
||||
|
@ -4,9 +4,9 @@ import path from 'path'
|
||||
import * as fsp from 'fs/promises'
|
||||
import {
|
||||
getUtils,
|
||||
executorInputPath,
|
||||
TEST_COLORS,
|
||||
TestColor,
|
||||
executorInputPath,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from './test-utils'
|
||||
import { TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR } from './storageStates'
|
||||
@ -331,7 +331,7 @@ extrude001 = extrude(sketch001, length = 50)
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = mm)
|
||||
sketch002 = startSketchOn('XY')
|
||||
sketch002 = startSketchOn(XY)
|
||||
profile002 = startProfileAt([72.24, -52.05], sketch002)
|
||||
|> angledLine([0, 181.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -582,7 +582,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -619,6 +619,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
}) => {
|
||||
const u = await getUtils(page)
|
||||
|
||||
@ -632,7 +633,7 @@ extrude002 = extrude(profile002, length = 150)
|
||||
|
||||
await test.step(`Load an empty file`, async () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', '')
|
||||
localStorage.setItem('persistCode', '@settings(defaultLengthUnit = in)')
|
||||
})
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
@ -646,22 +647,31 @@ extrude002 = extrude(profile002, length = 150)
|
||||
timeout: 5000,
|
||||
message: 'Plane color is visible',
|
||||
})
|
||||
.toBeLessThanOrEqual(15)
|
||||
.toBeLessThanOrEqual(20)
|
||||
await expect(scene.startEditSketchBtn).toBeEnabled()
|
||||
|
||||
let maxZoomOuts = 10
|
||||
let middlePixelIsBackgroundColor =
|
||||
(await middlePixelIsColor(bgColor)) < 10
|
||||
|
||||
console.time('pressing control')
|
||||
await page.keyboard.down('Control')
|
||||
|
||||
while (!middlePixelIsBackgroundColor && maxZoomOuts > 0) {
|
||||
await page.keyboard.down('Control')
|
||||
await page.mouse.move(600, 460)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(600, 50, { steps: 20 })
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Control')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.move(650, 460)
|
||||
console.time('moved to start point')
|
||||
await page.mouse.down({ button: 'right' })
|
||||
console.time('moused down')
|
||||
await page.mouse.move(650, 50, { steps: 20 })
|
||||
console.time('moved to end point')
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
console.time('moused up')
|
||||
maxZoomOuts--
|
||||
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 10
|
||||
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 15
|
||||
}
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
expect(middlePixelIsBackgroundColor, {
|
||||
message: 'We should not see the default planes',
|
||||
@ -678,13 +688,12 @@ extrude002 = extrude(profile002, length = 150)
|
||||
homePage,
|
||||
scene,
|
||||
toolbar,
|
||||
viewport,
|
||||
}) => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const legoDir = path.join(dir, 'lego')
|
||||
await fsp.mkdir(legoDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('lego.kcl'),
|
||||
executorInputPath('e2e-can-sketch-on-chamfer.kcl'),
|
||||
path.join(legoDir, 'main.kcl')
|
||||
)
|
||||
})
|
||||
@ -697,11 +706,8 @@ extrude002 = extrude(profile002, length = 150)
|
||||
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
||||
})
|
||||
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
||||
await scene.expectPixelColor(
|
||||
[143, 143, 143],
|
||||
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
||||
15
|
||||
)
|
||||
// TODO: use the viewport size to pick the center point, but the `viewport` fixture's values were wrong.
|
||||
await scene.expectPixelColor([116, 116, 116], { x: 500, y: 250 }, 15)
|
||||
})
|
||||
})
|
||||
|
||||
@ -778,6 +784,19 @@ plane002 = offsetPlane(XZ, offset = -2 * x)`
|
||||
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) {
|
||||
|
@ -113,7 +113,8 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([2.61, -4.01], %)
|
||||
|> xLine(length = 8.73)
|
||||
|> tangentialArcTo([8.33, -1.31], %)`
|
||||
@ -159,7 +160,10 @@ test.describe('Sketch tests', { tag: ['@skipWin'] }, () => {
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||
.toBe(`sketch002 = startSketchOn(XZ)
|
||||
.toBe(`@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
sketch002 = startSketchOn(XZ)
|
||||
sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> yLine(length = 12.34)
|
||||
|
||||
@ -789,7 +793,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
200
|
||||
)
|
||||
|
||||
let codeStr = 'sketch001 = startSketchOn(XY)'
|
||||
let codeStr =
|
||||
'@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XY)'
|
||||
|
||||
await page.mouse.click(center.x, viewportSize.height * 0.55)
|
||||
await expect(u.codeLocator).toHaveText(codeStr)
|
||||
@ -868,7 +873,8 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|
||||
await u.openDebugPanel()
|
||||
|
||||
const code = `sketch001 = startSketchOn(-XZ)
|
||||
const code = `@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(-XZ)
|
||||
profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
scale * 34.8
|
||||
)}], sketch001)
|
||||
@ -898,7 +904,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
await page.mouse.move(700, 200, { steps: 10 })
|
||||
await page.mouse.click(700, 200, { delay: 200 })
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`sketch001 = startSketchOn(-XZ)`
|
||||
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(-XZ)`
|
||||
)
|
||||
|
||||
let prevContent = await page.locator('.cm-content').innerText()
|
||||
@ -1426,7 +1432,8 @@ test.describe(`Sketching with offset planes`, () => {
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
`@settings(defaultLengthUnit = in)
|
||||
offsetPlane001 = offsetPlane(XY, offset = 10)`
|
||||
)
|
||||
})
|
||||
|
||||
@ -1440,7 +1447,7 @@ test.describe(`Sketching with offset planes`, () => {
|
||||
await test.step(`Hovering should highlight code`, async () => {
|
||||
await planeHover()
|
||||
await editor.expectState({
|
||||
activeLines: [`offsetPlane001=offsetPlane(XY,offset=10)`],
|
||||
activeLines: [`@settings(defaultLengthUnit = in)`],
|
||||
diagnostics: [],
|
||||
highlightedCode: 'offsetPlane(XY, offset = 10)',
|
||||
})
|
||||
@ -1453,7 +1460,7 @@ test.describe(`Sketching with offset planes`, () => {
|
||||
await expect(toolbar.lineBtn).toBeEnabled()
|
||||
await editor.expectEditor.toContain('startSketchOn(offsetPlane001)')
|
||||
await editor.expectState({
|
||||
activeLines: [`offsetPlane001=offsetPlane(XY,offset=10)`],
|
||||
activeLines: [`@settings(defaultLengthUnit = in)`],
|
||||
diagnostics: [],
|
||||
highlightedCode: '',
|
||||
})
|
||||
@ -1604,7 +1611,8 @@ profile002 = startProfileAt([117.2, 56.08], sketch001)
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile002 = startProfileAt([40.68, 87.67], sketch001)
|
||||
|> xLine(length = 239.17)
|
||||
profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
@ -2172,7 +2180,8 @@ profile003 = startProfileAt([206.63, -56.73], sketch001)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||
|> line(end = [-0.41, 6.99])
|
||||
|> line(end = [8.61, 0.74])
|
||||
@ -2317,7 +2326,8 @@ profile004 = circleThreePoint(sketch001, p1 = [13.44, -6.8], p2 = [13.39, -2.07]
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||
|> line(end = [-0.41, 6.99])
|
||||
|> line(end = [8.61, 0.74])
|
||||
@ -2422,7 +2432,8 @@ profile003 = circle(sketch001, center = [6.92, -4.2], radius = 3.16)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([-63.43, 193.08], sketch001)
|
||||
|> line(end = [168.52, 149.87])
|
||||
|> line(end = [190.29, -39.18])
|
||||
@ -2486,7 +2497,11 @@ extrude001 = extrude(profile003, length = 5)
|
||||
page,
|
||||
}) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem('persistCode', `myVar = 5`)
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`@settings(defaultLengthUnit = in)
|
||||
myVar = 5`
|
||||
)
|
||||
})
|
||||
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
@ -2533,7 +2548,8 @@ extrude001 = extrude(profile003, length = 5)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([85.19, 338.59], sketch001)
|
||||
|> line(end = [213.3, -94.52])
|
||||
|> line(end = [-230.09, -55.34])
|
||||
@ -2575,7 +2591,8 @@ profile002 = startProfileAt([85.81, 52.55], sketch002)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`thePart = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
thePart = startSketchOn(XZ)
|
||||
|> startProfileAt([7.53, 10.51], %)
|
||||
|> line(end = [12.54, 1.83])
|
||||
|> line(end = [6.65, -6.91])
|
||||
@ -2636,7 +2653,8 @@ extrude001 = extrude(thePart, length = 75)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([6.71, -3.66], sketch001)
|
||||
|> line(end = [2.65, 9.02], tag = $seg02)
|
||||
|> line(end = [3.73, -9.36], tag = $seg01)
|
||||
@ -2809,7 +2827,8 @@ extrude003 = extrude(profile011, length = 2.5)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([34, 42.66], sketch001)
|
||||
|> line(end = [102.65, 151.99])
|
||||
|> line(end = [76, -138.66])
|
||||
|
@ -76,11 +76,11 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(endAbsolute = totalLen, tag = $seg03)
|
||||
|> yLine(length = -armThick, tag = $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04, %) + 180, ZERO], %)
|
||||
|> angledLineToY([segAng(seg04, %) + 180, turns::ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle = -bottomAng,
|
||||
to = -totalHeightHalf - armThick,
|
||||
@ -88,12 +88,12 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(length = endAbsolute = segEndX(seg03) + 0)
|
||||
|> yLine(length = -segLen(seg01, %))
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02, %) + 180, -baseHeight], %)
|
||||
|> xLine(endAbsolute = ZERO)
|
||||
|> xLine(endAbsolute = turns::ZERO)
|
||||
|> close()
|
||||
|> extrude(length = 4)`
|
||||
)
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 67 KiB |
@ -1,5 +1,5 @@
|
||||
{
|
||||
"original_source_code": "sketch001 = startSketchOn('XZ')\nprofile001 = startProfileAt([57.81, 250.51], sketch001)\n |> line(end = [121.13, 56.63], tag = $seg02)\n |> line(end = [83.37, -34.61], tag = $seg01)\n |> line(end = [19.66, -116.4])\n |> line(end = [-221.8, -41.69])\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude001 = extrude(profile001, length = 200)\nsketch002 = startSketchOn('XZ')\n |> startProfileAt([-73.64, -42.89], %)\n |> xLine(length = 173.71)\n |> line(end = [-22.12, -94.4])\n |> xLine(length = -156.98)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude002 = extrude(sketch002, length = 50)\nsketch003 = startSketchOn('XY')\n |> startProfileAt([52.92, 157.81], %)\n |> angledLine([0, 176.4], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 53.4\n ], %, $rectangleSegmentB001)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %, $rectangleSegmentC001)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude003 = extrude(sketch003, length = 20)\n",
|
||||
"original_source_code": "sketch001 = startSketchOn('XZ')\nprofile001 = startProfileAt([57.81, 250.51], sketch001)\n |> line(end = [121.13, 56.63], tag = $seg02)\n |> line(end = [83.37, -34.61], tag = $seg01)\n |> line(end = [19.66, -116.4])\n |> line(end = [-221.8, -41.69])\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude001 = extrude(profile001, length = 200)\nsketch002 = startSketchOn('XZ')\n |> startProfileAt([-73.64, -42.89], %)\n |> xLine(length = 173.71)\n |> line(end = [-22.12, -94.4])\n |> xLine(length = -156.98)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude002 = extrude(sketch002, length = 50)\nsketch003 = startSketchOn(XY)\n |> startProfileAt([52.92, 157.81], %)\n |> angledLine([0, 176.4], %, $rectangleSegmentA001)\n |> angledLine([\n segAng(rectangleSegmentA001) - 90,\n 53.4\n ], %, $rectangleSegmentB001)\n |> angledLine([\n segAng(rectangleSegmentA001),\n -segLen(rectangleSegmentA001)\n ], %, $rectangleSegmentC001)\n |> line(endAbsolute = [profileStartX(%), profileStartY(%)])\n |> close()\nextrude003 = extrude(sketch003, length = 20)\n",
|
||||
"prompt": "make this neon green please, use #39FF14",
|
||||
"source_ranges": [
|
||||
{
|
||||
@ -30,4 +30,4 @@
|
||||
}
|
||||
],
|
||||
"kcl_version": "0.2.48"
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import { isArray } from 'lib/utils'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { Configuration } from 'lang/wasm'
|
||||
import { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
||||
|
||||
const toNormalizedCode = (text: string) => {
|
||||
return text.replace(/\s+/g, '')
|
||||
@ -761,7 +762,7 @@ export interface Paths {
|
||||
}
|
||||
|
||||
export const doExport = async (
|
||||
output: Models['OutputFormat_type'],
|
||||
output: Models['OutputFormat3d_type'],
|
||||
rootDir: string,
|
||||
page: Page,
|
||||
exportFrom: 'dropdown' | 'sidebarButton' | 'commandBar' = 'dropdown'
|
||||
@ -934,47 +935,39 @@ export async function setup(
|
||||
}
|
||||
|
||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||
// enabled for chrome for now
|
||||
if (page.context().browser()?.browserType().name() === 'chromium') {
|
||||
// No idea wtf exception is
|
||||
page.on('pageerror', (exception: any) => {
|
||||
if (isErrorWhitelisted(exception)) {
|
||||
return
|
||||
}
|
||||
page.on('pageerror', (exception: any) => {
|
||||
if (isErrorWhitelisted(exception)) {
|
||||
return
|
||||
}
|
||||
// Only disable this environment variable if you want to collect console errors
|
||||
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
|
||||
// This can be configured in the GH workflow. This should be set to true by default (we want tests to fail when
|
||||
// unwhitelisted console errors are detected).
|
||||
if (process.env.FAIL_ON_CONSOLE_ERRORS === 'true') {
|
||||
// Fail when running on CI and FAIL_ON_CONSOLE_ERRORS is set
|
||||
// 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}
|
||||
|
||||
*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')
|
||||
} else {
|
||||
// the (test-results/exceptions.txt) file will be uploaded as part of an upload artifact in GH
|
||||
fsp
|
||||
.appendFile(
|
||||
'./test-results/exceptions.txt',
|
||||
[
|
||||
'~~~',
|
||||
`triggered_by_test:${
|
||||
testInfo?.file + ' ' + (testInfo?.title || ' ')
|
||||
}`,
|
||||
`name:${exception.name}`,
|
||||
`message:${exception.message}`,
|
||||
`stack:${exception.stack}`,
|
||||
`project:${testInfo?.project.name}`,
|
||||
'~~~',
|
||||
].join('\n')
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
*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')
|
||||
} else {
|
||||
// Add errors to `test-results/exceptions.txt` as a test artifact
|
||||
fsp
|
||||
.appendFile(
|
||||
'./test-results/exceptions.txt',
|
||||
[
|
||||
'~~~',
|
||||
`triggered_by_test:${
|
||||
testInfo?.file + ' ' + (testInfo?.title || ' ')
|
||||
}`,
|
||||
`name:${exception.name}`,
|
||||
`message:${exception.message}`,
|
||||
`stack:${exception.stack}`,
|
||||
`project:${testInfo?.project.name}`,
|
||||
'~~~',
|
||||
].join('\n')
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
export async function isOutOfViewInScrollContainer(
|
||||
element: Locator,
|
||||
@ -1125,3 +1118,15 @@ export function settingsToToml(settings: DeepPartial<Configuration>) {
|
||||
export function tomlToSettings(toml: string): DeepPartial<Configuration> {
|
||||
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)
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import {
|
||||
getUtils,
|
||||
TEST_COLORS,
|
||||
pollEditorLinesSelectedLength,
|
||||
executorInputPath,
|
||||
orRunWhenFullSuiteEnabled,
|
||||
} from './test-utils'
|
||||
import { XOR } from 'lib/utils'
|
||||
@ -81,7 +80,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 79
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 79
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4], tag = $seg01)
|
||||
@ -145,7 +145,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4], tag = $seg01)
|
||||
@ -159,31 +160,6 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
|> xLine(length = segLen(seg_what))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])`
|
||||
)
|
||||
|
||||
const isChecked = await createNewVariableCheckbox.isChecked()
|
||||
const addVariable = testName === 'Add variable'
|
||||
XOR(isChecked, addVariable) && // XOR because no need to click the checkbox if the state is already correct
|
||||
(await createNewVariableCheckbox.click())
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'Add constraining value' })
|
||||
.click()
|
||||
|
||||
// Wait for the codemod to take effect
|
||||
await expect(page.locator('.cm-content')).toContainText(`angle: -57,`)
|
||||
await expect(page.locator('.cm-content')).toContainText(
|
||||
`offset: ${offset},`
|
||||
)
|
||||
|
||||
await pollEditorLinesSelectedLength(page, 2)
|
||||
const activeLinesContent = await page.locator('.cm-activeLine').all()
|
||||
await expect(activeLinesContent[0]).toHaveText(
|
||||
`|> line(end = [74.36, 130.4], tag = $seg01)`
|
||||
)
|
||||
await expect(activeLinesContent[1]).toHaveText(`}, %)`)
|
||||
|
||||
// checking the count of the overlays is a good proxy check that the client sketch scene is in a good state
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(4)
|
||||
})
|
||||
const u = await getUtils(page)
|
||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||
@ -277,7 +253,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -387,7 +364,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -486,13 +464,13 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
testName: 'Add variable, selecting axis',
|
||||
addVariable: true,
|
||||
axisSelect: true,
|
||||
value: 'QUARTER_TURN - angle001',
|
||||
value: 'turns::QUARTER_TURN - angle001',
|
||||
},
|
||||
{
|
||||
testName: 'No variable, selecting axis',
|
||||
addVariable: false,
|
||||
axisSelect: true,
|
||||
value: 'QUARTER_TURN - 7',
|
||||
value: 'turns::QUARTER_TURN - 7',
|
||||
},
|
||||
] as const
|
||||
for (const { testName, addVariable, value, axisSelect } of cases) {
|
||||
@ -500,7 +478,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -602,7 +581,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -688,7 +668,8 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -768,7 +749,8 @@ part002 = startSketchOn(XZ)
|
||||
await page.addInitScript(async (customCode) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -869,7 +851,8 @@ part002 = startSketchOn(XZ)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -935,12 +918,12 @@ part002 = startSketchOn(XZ)
|
||||
test.describe('Axis & segment - no modal constraints', () => {
|
||||
const cases = [
|
||||
{
|
||||
codeAfter: `|> line(endAbsolute = [154.9, ZERO])`,
|
||||
codeAfter: `|> line(endAbsolute = [154.9, turns::ZERO])`,
|
||||
axisClick: { x: 950, y: 250 },
|
||||
constraintName: 'Snap To X',
|
||||
},
|
||||
{
|
||||
codeAfter: `|> line(endAbsolute = [ZERO, 61.34])`,
|
||||
codeAfter: `|> line(endAbsolute = [turns::ZERO, 61.34])`,
|
||||
axisClick: { x: 600, y: 150 },
|
||||
constraintName: 'Snap To Y',
|
||||
},
|
||||
@ -950,7 +933,8 @@ part002 = startSketchOn(XZ)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 5
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 5
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> line(end = [74.36, 130.4])
|
||||
@ -1117,9 +1101,19 @@ test.describe('Electron constraint tests', () => {
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'test-sample')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('angled_line.kcl'),
|
||||
path.join(bracketDir, 'main.kcl')
|
||||
await fsp.writeFile(
|
||||
path.join(bracketDir, 'main.kcl'),
|
||||
`@settings(defaultLengthUnit = in)
|
||||
const part001 = startSketchOn(XY)
|
||||
|> startProfileAt([4.83, 12.56], %)
|
||||
|> line(end = [15.1, 2.48])
|
||||
|> line(end = [3.15, -9.85], tag = $seg01)
|
||||
|> line(end = [-15.17, -4.1])
|
||||
|> angledLine([segAng(seg01), 12.35], %)
|
||||
|> line(end = [-13.02, 10.03])
|
||||
|> close()
|
||||
|> extrude(length = 4)`,
|
||||
'utf-8'
|
||||
)
|
||||
})
|
||||
|
||||
|
@ -255,7 +255,7 @@ test.describe(`Testing gizmo, fixture-based`, () => {
|
||||
await context.addInitScript(() => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`
|
||||
`@settings(defaultLengthUnit = in)
|
||||
const sketch002 = startSketchOn(XZ)
|
||||
|> startProfileAt([-108.83, -57.48], %)
|
||||
|> angledLine([0, 105.13], %, $rectangleSegmentA001)
|
||||
|
@ -46,7 +46,7 @@ test.describe('Testing in-app sample loading', () => {
|
||||
page.getByRole('option', {
|
||||
name,
|
||||
})
|
||||
const warningText = page.getByText('Overwrite current file and units?')
|
||||
const warningText = page.getByText('Overwrite current file with sample?')
|
||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||
|
||||
await test.step(`Precondition: check the initial code`, async () => {
|
||||
@ -110,11 +110,9 @@ test.describe('Testing in-app sample loading', () => {
|
||||
const commandMethodOption = page.getByRole('option', {
|
||||
name: 'Overwrite',
|
||||
})
|
||||
const newFileWarning = page.getByText(
|
||||
'Create a new file, overwrite project units?'
|
||||
)
|
||||
const newFileWarning = page.getByText('Create a new file from sample?')
|
||||
const overwriteWarning = page.getByText(
|
||||
'Overwrite current file and units?'
|
||||
'Overwrite current file with sample?'
|
||||
)
|
||||
const confirmButton = page.getByRole('button', { name: 'Submit command' })
|
||||
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
|
||||
|
@ -210,7 +210,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([5 + 0, 20 + 0], %)
|
||||
|> line(end = [0.5, -14 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 32 + 0 }, %)
|
||||
@ -380,7 +381,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yRel001 = -14
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yRel001 = -14
|
||||
xRel001 = 0.5
|
||||
angle001 = 3
|
||||
len001 = 32
|
||||
@ -459,7 +461,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [0.5, -14 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 32 + 0 }, %)
|
||||
@ -590,7 +593,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [0.5, -14 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 32 + 0 }, %)
|
||||
@ -751,7 +755,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line(end = [0.5, -14 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 32 + 0 }, %)
|
||||
@ -831,7 +836,8 @@ test.describe('Testing segment overlays', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfileAt([56.37, 120.33], sketch001)
|
||||
|> line(end = [162.86, 106.48])
|
||||
|> arcTo({
|
||||
@ -957,7 +963,8 @@ profile001 = startProfileAt([56.37, 120.33], sketch001)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> circle(center = [1 + 0, 0], radius = 8)
|
||||
`
|
||||
)
|
||||
@ -1077,7 +1084,8 @@ profile001 = startProfileAt([56.37, 120.33], sketch001)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|>startProfileAt([0, 0], %)
|
||||
|> line(end = [0.5, -14 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 32 + 0 }, %)
|
||||
@ -1351,7 +1359,8 @@ profile001 = startProfileAt([56.37, 120.33], sketch001)
|
||||
async ({ lineToBeDeleted, extraLine }) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([5, 6], %)
|
||||
|> ${lineToBeDeleted}
|
||||
|> line(end = [-10, -15])
|
||||
@ -1516,7 +1525,8 @@ profile001 = startProfileAt([56.37, 120.33], sketch001)
|
||||
async ({ lineToBeDeleted }) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([5, 6], %)
|
||||
|> ${lineToBeDeleted}
|
||||
|> line(end = [-10, -15])
|
||||
|
@ -68,20 +68,20 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1})`)
|
||||
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
@ -89,7 +89,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
.toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfileAt(${
|
||||
commonPoints.startAt
|
||||
}, sketch001)
|
||||
|> xLine(length = ${commonPoints.num1})
|
||||
@ -260,7 +260,8 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line(end = [112.54, 127.64], tag = $seg02)
|
||||
|> line(end = [170.36, -121.61], tag = $seg01)
|
||||
@ -528,7 +529,8 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
|
||||
await page.addInitScript(async (KCL_DEFAULT_LENGTH) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`part001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([20, 0], %)
|
||||
|> line(end = [7.13, 4 + 0])
|
||||
|> angledLine({ angle = 3 + 0, length = 3.14 + 0 }, %)
|
||||
@ -747,7 +749,8 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await u.removeCurrentCode()
|
||||
await u.codeLocator.fill(`sketch001 = startSketchOn(XZ)
|
||||
await u.codeLocator.fill(`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
@ -965,7 +968,8 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
|
||||
async ({ cases }) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`yo = 79
|
||||
`@settings(defaultLengthUnit = in)
|
||||
yo = 79
|
||||
part001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-7.54, -26.74], %)
|
||||
|> ${cases[0].expectedCode}
|
||||
@ -1020,7 +1024,8 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([-79.26, 95.04], %)
|
||||
|> line(end = [112.54, 127.64])
|
||||
|> line(end = [170.36, -121.61], tag = $seg01)
|
||||
@ -1253,7 +1258,7 @@ profile001 = startProfileAt([7.49, 9.96], sketch001)
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`sketch001 = startSketchOn(XZ)`
|
||||
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(600)
|
||||
|
@ -271,7 +271,7 @@ test.describe('Testing settings', () => {
|
||||
const bracketDir = join(dir, projectName)
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||
executorInputPath('cylinder-inches.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
}
|
||||
@ -699,19 +699,19 @@ test.describe('Testing settings', () => {
|
||||
name: 'Current units are: ',
|
||||
})
|
||||
await gizmo.click()
|
||||
const button = page.getByRole('button', {
|
||||
const button = page.locator('ul').getByRole('button', {
|
||||
name: copy,
|
||||
exact: true,
|
||||
})
|
||||
await button.click()
|
||||
const toastMessage = page.getByText(
|
||||
`Set default unit to "${unitOfMeasure}" for this project`
|
||||
`Updated per-file units to ${unitOfMeasure}`
|
||||
)
|
||||
await expect(toastMessage).toBeVisible()
|
||||
}
|
||||
|
||||
await changeUnitOfMeasureInGizmo('in', 'Inches')
|
||||
await changeUnitOfMeasureInGizmo('ft', 'Feet')
|
||||
await changeUnitOfMeasureInGizmo('in', 'Inches')
|
||||
await changeUnitOfMeasureInGizmo('yd', 'Yards')
|
||||
await changeUnitOfMeasureInGizmo('mm', 'Millimeters')
|
||||
await changeUnitOfMeasureInGizmo('cm', 'Centimeters')
|
||||
@ -951,9 +951,9 @@ test.describe('Testing settings', () => {
|
||||
)
|
||||
})
|
||||
|
||||
await test.step(`Initial units from settings`, async () => {
|
||||
await test.step(`Initial units from settings are ignored`, async () => {
|
||||
await homePage.openProject('project-000')
|
||||
await expect(unitsIndicator).toHaveText('Current units are: in')
|
||||
await expect(unitsIndicator).toHaveText('Current units are: mm')
|
||||
})
|
||||
|
||||
await test.step(`Manually write inline settings`, async () => {
|
||||
|
@ -67,11 +67,11 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(endAbsolute = totalLen, tag = $seg03)
|
||||
|> yLine(length = -armThick, tag = $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04) + 180, ZERO], %)
|
||||
|> angledLineToY([segAng(seg04) + 180, turns::ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle = -bottomAng,
|
||||
to = -totalHeightHalf - armThick,
|
||||
@ -79,12 +79,12 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(endAbsolute = segEndX(seg03) + 0)
|
||||
|> yLine(length = -segLen(seg01))
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02) + 180, -baseHeight], %)
|
||||
|> xLine(endAbsolute = ZERO)
|
||||
|> xLine(endAbsolute = turns::ZERO)
|
||||
|> close()
|
||||
|> extrude(length = 4)`
|
||||
)
|
||||
@ -483,7 +483,8 @@ test('Sketch on face', async ({ page, homePage, scene, cmdBar, toolbar }) => {
|
||||
await page.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`sketch001 = startSketchOn(XZ)
|
||||
`@settings(defaultLengthUnit = in)
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([3.29, 7.86], %)
|
||||
|> line(end = [2.48, 2.44])
|
||||
|> line(end = [2.66, 1.17])
|
||||
|
20
interface.d.ts
vendored
@ -3,6 +3,18 @@ import fsSync from 'node:fs'
|
||||
import path from 'path'
|
||||
import { dialog, shell } from 'electron'
|
||||
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
|
||||
|
||||
@ -94,6 +106,14 @@ export interface IElectronAPI {
|
||||
appCheckForUpdates: () => Promise<unknown>
|
||||
getArgvParsed: () => 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 {
|
||||
|
36
package.json
@ -13,20 +13,20 @@
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.18.6",
|
||||
"@codemirror/commands": "^6.8.0",
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
"@codemirror/language": "^6.11.0",
|
||||
"@codemirror/lint": "^6.8.4",
|
||||
"@codemirror/lint": "^6.8.5",
|
||||
"@codemirror/search": "^6.5.10",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.2",
|
||||
"@csstools/postcss-oklab-function": "^4.0.7",
|
||||
"@csstools/postcss-oklab-function": "^4.0.8",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.19",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "2.0.17",
|
||||
"@headlessui/tailwindcss": "^0.2.2",
|
||||
"@kittycad/lib": "2.0.23",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.1",
|
||||
"@react-hook/resize-observer": "^2.0.1",
|
||||
@ -37,11 +37,11 @@
|
||||
"@xstate/react": "^4.1.1",
|
||||
"bonjour-service": "^1.3.0",
|
||||
"bson": "^6.10.3",
|
||||
"chokidar": "^4.0.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"codemirror": "^6.0.1",
|
||||
"decamelize": "^6.0.0",
|
||||
"diff": "^7.0.0",
|
||||
"electron-updater": "^6.6.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
@ -94,9 +94,11 @@
|
||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
||||
"fetch:samples": "rm -rf public/kcl-samples* && curl -L -o public/kcl-samples.zip https://github.com/KittyCAD/kcl-samples/archive/refs/heads/achalmers/kw-args-xylineto.zip && unzip -o public/kcl-samples.zip -d public && mv public/kcl-samples-* public/kcl-samples",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd rust && wasm-pack build kcl-wasm-lib --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt:generated",
|
||||
"build:wasm": "./scripts/build-wasm.sh",
|
||||
"build:wasm:windows": "./scripts/build-wasm.ps1",
|
||||
"build:wasm-dev": "yarn build:wasm:dev",
|
||||
"build:wasm:dev": "./scripts/build-wasm-dev.sh",
|
||||
"build:wasm:dev:windows": "./scripts/build-wasm-dev.ps1",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
@ -162,15 +164,15 @@
|
||||
"@iarna/toml": "^2.2.5",
|
||||
"@lezer/generator": "^1.7.2",
|
||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||
"@playwright/test": "^1.51.0",
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^15.0.2",
|
||||
"@types/diff": "^7.0.1",
|
||||
"@types/diff": "^7.0.2",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/isomorphic-fetch": "^0.0.39",
|
||||
"@types/minimist": "^1.2.5",
|
||||
"@types/mocha": "^10.0.10",
|
||||
"@types/node": "^22.13.9",
|
||||
"@types/node": "^22.13.14",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/react": "^18.3.4",
|
||||
@ -180,13 +182,13 @@
|
||||
"@types/ua-parser-js": "^0.7.39",
|
||||
"@types/uuid": "^9.0.8",
|
||||
"@types/wicg-file-system-access": "^2023.10.5",
|
||||
"@types/ws": "^8.5.13",
|
||||
"@vitejs/plugin-react": "^4.3.0",
|
||||
"@types/ws": "^8.18.0",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"@vitest/web-worker": "^1.5.0",
|
||||
"@xstate/cli": "^0.5.17",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"electron": "^34.1.1",
|
||||
"electron-builder": "^26.0.6",
|
||||
"electron-builder": "^26.0.12",
|
||||
"eslint": "^8.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
@ -218,7 +220,7 @@
|
||||
"vite-tsconfig-paths": "^4.3.2",
|
||||
"vitest": "^1.6.1",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"ws": "^8.17.0",
|
||||
"ws": "^8.18.1",
|
||||
"yarn": "^1.22.22"
|
||||
},
|
||||
"packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e"
|
||||
|
@ -90,7 +90,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
}
|
||||
|
||||
add(bytes: Uint8Array): void {
|
||||
const message = Codec.decode(bytes) as vsrpc.Message
|
||||
const message = Codec.decode<vsrpc.Message>(bytes)
|
||||
if (this.trace) {
|
||||
Tracer.server(message)
|
||||
}
|
||||
|
@ -37,11 +37,11 @@ export class IntoServer
|
||||
implements AsyncGenerator<Uint8Array, never, void>
|
||||
{
|
||||
private worker: Worker | null = null
|
||||
private type_: String | null = null
|
||||
private type_: string | null = null
|
||||
|
||||
private trace: boolean = false
|
||||
|
||||
constructor(type_?: String, worker?: Worker, trace?: boolean) {
|
||||
constructor(type_?: string, worker?: Worker, trace?: boolean) {
|
||||
super()
|
||||
if (worker && type_) {
|
||||
this.worker = worker
|
||||
|
@ -7,7 +7,7 @@
|
||||
// Define function
|
||||
fn rail8020(originStart, railHeight, railLength) {
|
||||
// Sketch side 1 of profile
|
||||
sketch001 = startSketchOn('-XZ')
|
||||
sketch001 = startSketchOn(-XZ)
|
||||
|> startProfileAt([
|
||||
originStart[0],
|
||||
0.1 * railHeight + originStart[1]
|
||||
@ -194,7 +194,7 @@ fn rail8020(originStart, railHeight, railLength) {
|
||||
.5 * railHeight + originStart[0],
|
||||
.5 * railHeight + originStart[1]
|
||||
],
|
||||
radius = .205 * railHeight / 2
|
||||
radius = .205 * railHeight / 2,
|
||||
), %)
|
||||
|> extrude(length = railLength)
|
||||
|> fillet(
|
||||
@ -216,7 +216,7 @@ fn rail8020(originStart, railHeight, railLength) {
|
||||
getNextAdjacentEdge(edge28),
|
||||
getNextAdjacentEdge(edge29),
|
||||
getNextAdjacentEdge(edge30)
|
||||
]
|
||||
],
|
||||
)
|
||||
|> fillet(
|
||||
radius = 0.03,
|
||||
@ -237,7 +237,7 @@ fn rail8020(originStart, railHeight, railLength) {
|
||||
getNextAdjacentEdge(edge26),
|
||||
getNextAdjacentEdge(edge31),
|
||||
getNextAdjacentEdge(edge32)
|
||||
]
|
||||
],
|
||||
)
|
||||
return sketch001
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ When you submit a PR to add or modify KCL samples, images and STEP files will be
|
||||
[](cycloidal-gear/main.kcl)
|
||||
#### [dodecahedron](dodecahedron/main.kcl) ([screenshot](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/main.kcl)
|
||||
#### [enclosure](enclosure/main.kcl) ([screenshot](screenshots/enclosure.png))
|
||||
[](enclosure/main.kcl)
|
||||
#### [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/main.kcl)
|
||||
#### [lego](lego/main.kcl) ([screenshot](screenshots/lego.png))
|
||||
[](lego/main.kcl)
|
||||
#### [makeup-mirror](makeup-mirror/main.kcl) ([screenshot](screenshots/makeup-mirror.png))
|
||||
[](makeup-mirror/main.kcl)
|
||||
#### [mounting-plate](mounting-plate/main.kcl) ([screenshot](screenshots/mounting-plate.png))
|
||||
[](mounting-plate/main.kcl)
|
||||
#### [multi-axis-robot](multi-axis-robot/main.kcl) ([screenshot](screenshots/multi-axis-robot.png))
|
||||
|
@ -15,57 +15,37 @@ padding = 1.5
|
||||
bearingDia = 3
|
||||
|
||||
// (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], %)
|
||||
|> line(endAbsolute = [width / 2, -length / 2])
|
||||
|> line(endAbsolute = [width / 2, length / 2])
|
||||
|> line(endAbsolute = [-width / 2, length / 2])
|
||||
|> close()
|
||||
|> extrude(length = height)
|
||||
|
||||
extrude002 = startSketchOn(extrude001, 'end')
|
||||
|> circle(
|
||||
center = [
|
||||
center = [
|
||||
-(width / 2 - (padding / 2)),
|
||||
-(length / 2 - (padding / 2))
|
||||
],
|
||||
radius = cbDia / 2,
|
||||
],
|
||||
radius = cbDia / 2,
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = length - padding,
|
||||
axis = [0, 1],
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = width - padding,
|
||||
axis = [1, 0],
|
||||
)
|
||||
|> patternLinear2d(instances = 2, distance = length - padding, axis = [0, 1])
|
||||
|> patternLinear2d(instances = 2, distance = width - padding, axis = [1, 0])
|
||||
|> extrude(%, length = -cbDepth)
|
||||
|
||||
extrude003 = startSketchOn(extrude001, 'start')
|
||||
|> circle(
|
||||
center = [
|
||||
-(width / 2 - (padding / 2)),
|
||||
-(length / 2 - (padding / 2))
|
||||
],
|
||||
radius = holeDia / 2,
|
||||
center = [
|
||||
-(width / 2 - (padding / 2)),
|
||||
-(length / 2 - (padding / 2))
|
||||
],
|
||||
radius = holeDia / 2,
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = length - padding,
|
||||
axis = [0, 1],
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = width - padding,
|
||||
axis = [1, 0],
|
||||
)
|
||||
|> patternLinear2d(instances = 2, distance = length - padding, axis = [0, 1])
|
||||
|> patternLinear2d(instances = 2, distance = width - padding, axis = [1, 0])
|
||||
|> extrude(length = -height + cbDepth)
|
||||
|
||||
extrude004 = startSketchOn(extrude001, 'end')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = bearingDia/2,
|
||||
)
|
||||
|> extrude(length = -height)
|
||||
|> circle(center = [0, 0], radius = bearingDia / 2)
|
||||
|> extrude(length = -height)
|
||||
|
@ -17,21 +17,15 @@ chainThickness = sphereDia / 8
|
||||
linkDiameter = sphereDia / 4
|
||||
|
||||
// Sketch the inside bearing piece
|
||||
insideWallSketch = startSketchOn(offsetPlane("XY", offset = -overallThickness / 2))
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = shaftDia / 2 + wallThickness
|
||||
)
|
||||
|> hole(circle(
|
||||
center = [0, 0],
|
||||
radius = shaftDia / 2
|
||||
), %)
|
||||
insideWallSketch = startSketchOn(offsetPlane(XY, offset = -overallThickness / 2))
|
||||
|> circle(center = [0, 0], radius = shaftDia / 2 + wallThickness)
|
||||
|> hole(circle(center = [0, 0], radius = shaftDia / 2), %)
|
||||
|
||||
// Extrude the inside bearing piece
|
||||
insideWall = extrude(insideWallSketch, length = overallThickness)
|
||||
|
||||
// Create the sketch of one of the balls
|
||||
ballsSketch = startSketchOn("XY")
|
||||
ballsSketch = startSketchOn(XY)
|
||||
|> startProfileAt([shaftDia / 2 + wallThickness, 0.001], %)
|
||||
|> arc({
|
||||
angleEnd = 0,
|
||||
@ -47,11 +41,11 @@ balls = revolve(ballsSketch, axis = "X")
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
instances = nBalls,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|
||||
// Create the sketch for the chain around the balls
|
||||
chainSketch = startSketchOn("XY")
|
||||
chainSketch = startSketchOn(XY)
|
||||
|> startProfileAt([
|
||||
shaftDia / 2 + wallThickness + sphereDia / 2 - (chainWidth / 2),
|
||||
0.125 * sin(toRadians(60))
|
||||
@ -72,17 +66,17 @@ chainHead = revolve(chainSketch, axis = "X")
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
instances = nBalls,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|
||||
// Create the sketch for the links in between the chains
|
||||
linkSketch = startSketchOn("XZ")
|
||||
linkSketch = startSketchOn(XZ)
|
||||
|> circle(
|
||||
center = [
|
||||
shaftDia / 2 + wallThickness + sphereDia / 2,
|
||||
0
|
||||
],
|
||||
radius = linkDiameter / 2
|
||||
radius = linkDiameter / 2,
|
||||
)
|
||||
|
||||
// Revolve the link sketch
|
||||
@ -92,19 +86,13 @@ linkRevolve = revolve(linkSketch, axis = 'Y', angle = 360 / nBalls)
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
instances = nBalls,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|
||||
// Create the sketch for the outside walls
|
||||
outsideWallSketch = startSketchOn(offsetPlane("XY", offset = -overallThickness / 2))
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = outsideDiameter / 2
|
||||
)
|
||||
|> hole(circle(
|
||||
center = [0, 0],
|
||||
radius = shaftDia / 2 + wallThickness + sphereDia
|
||||
), %)
|
||||
outsideWallSketch = startSketchOn(offsetPlane(XY, offset = -overallThickness / 2))
|
||||
|> circle(center = [0, 0], radius = outsideDiameter / 2)
|
||||
|> hole(circle(center = [0, 0], radius = shaftDia / 2 + wallThickness + sphereDia), %)
|
||||
|
||||
outsideWall = extrude(outsideWallSketch, length = overallThickness)
|
||||
|
||||
|
@ -130,7 +130,7 @@ fn armRestProfile(plane, offset) {
|
||||
|
||||
export fn armRest(plane, 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)
|
||||
return 0
|
||||
}
|
||||
|
@ -16,19 +16,19 @@ import backSlats from "bench-parts.kcl"
|
||||
import armRest from "bench-parts.kcl"
|
||||
|
||||
// Create the dividers, these hold the seat and back slats
|
||||
divider("YZ")
|
||||
divider(offsetPlane("-YZ", offset = benchLength / 2))
|
||||
divider(offsetPlane("YZ", offset = benchLength / 2))
|
||||
divider(YZ)
|
||||
divider(offsetPlane(-YZ, offset = benchLength / 2))
|
||||
divider(offsetPlane(YZ, offset = benchLength / 2))
|
||||
|
||||
// Create the connectors to join the dividers
|
||||
connector(offsetPlane("YZ", offset = -benchLength / 2), benchLength)
|
||||
connector(offsetPlane(YZ, offset = -benchLength / 2), benchLength)
|
||||
|
||||
// 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
|
||||
backSlats(offsetPlane("YZ", offset = -benchLength / 2 - dividerThickness / 2), benchLength + dividerThickness)
|
||||
backSlats(offsetPlane(YZ, offset = -benchLength / 2 - (dividerThickness / 2)), benchLength + dividerThickness)
|
||||
|
||||
// Create the arm rests
|
||||
armRest("-YZ", benchLength / 2)
|
||||
armRest("-YZ", -benchLength / 2)
|
||||
armRest(-YZ, benchLength / 2)
|
||||
armRest(-YZ, -benchLength / 2)
|
||||
|
@ -1,7 +1,6 @@
|
||||
// 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.
|
||||
|
||||
|
||||
// Define constants
|
||||
sigmaAllow = 35000 // psi (6061-T6 aluminum)
|
||||
width = 6 // inch
|
||||
@ -12,14 +11,16 @@ wallMountL = 2 // inches
|
||||
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)
|
||||
|
||||
|
||||
// Calculate required thickness of bracket
|
||||
thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
|
||||
|
||||
|
||||
filletRadius = .25
|
||||
extFilletRadius = filletRadius + thickness
|
||||
mountingHoleDiameter = 0.5
|
||||
|
||||
sketch001 = startSketchOn('XZ')
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> xLine(length = shelfMountL - thickness, tag = $seg01)
|
||||
|> yLine(length = thickness, tag = $seg02)
|
||||
@ -29,48 +30,18 @@ sketch001 = startSketchOn('XZ')
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg06)
|
||||
|> close()
|
||||
|> extrude(%, length = width)
|
||||
|> fillet(
|
||||
radius = extFilletRadius,
|
||||
tags = [getNextAdjacentEdge(seg03)],
|
||||
)
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [getNextAdjacentEdge(seg06)],
|
||||
)
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [seg02, getOppositeEdge(seg02)],
|
||||
)
|
||||
|> fillet(
|
||||
radius = filletRadius,
|
||||
tags = [seg05, getOppositeEdge(seg05)],
|
||||
)
|
||||
|> fillet(radius = extFilletRadius, tags = [getNextAdjacentEdge(seg03)])
|
||||
|> fillet(radius = filletRadius, tags = [getNextAdjacentEdge(seg06)])
|
||||
|> fillet(radius = filletRadius, tags = [seg02, getOppositeEdge(seg02)])
|
||||
|> fillet(radius = filletRadius, tags = [seg05, getOppositeEdge(seg05)])
|
||||
|
||||
sketch002 = startSketchOn(sketch001, seg03)
|
||||
|> circle(
|
||||
center = [-1.25, 1],
|
||||
radius = mountingHoleDiameter / 2,
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = 2.5,
|
||||
axis = [-1, 0],
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = 4,
|
||||
axis = [0, 1],
|
||||
)
|
||||
|> extrude(%, length = -thickness-.01)
|
||||
|> circle(center = [-1.25, 1], radius = mountingHoleDiameter / 2)
|
||||
|> 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)
|
||||
|> circle(
|
||||
center = [1, -1],
|
||||
radius = mountingHoleDiameter / 2,
|
||||
)
|
||||
|> patternLinear2d(
|
||||
instances = 2,
|
||||
distance = 4,
|
||||
axis = [1, 0],
|
||||
)
|
||||
|> extrude(%, length = -thickness-0.1)
|
||||
|> circle(center = [1, -1], radius = mountingHoleDiameter / 2)
|
||||
|> patternLinear2d(instances = 2, distance = 4, axis = [1, 0])
|
||||
|> extrude(%, length = -thickness - 0.1)
|
||||
|
@ -1,16 +1,14 @@
|
||||
// Brake Caliper
|
||||
// Brake calipers are used to squeeze the brake pads against the rotor, causing larger and larger amounts of friction depending on how hard the brakes are pressed.
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import Constants
|
||||
import caliperTolerance, caliperPadLength, caliperThickness, caliperOuterEdgeRadius, caliperInnerEdgeRadius, rotorDiameter, rotorTotalThickness, yAxisOffset from "globals.kcl"
|
||||
|
||||
// Sketch the brake caliper profile
|
||||
brakeCaliperSketch = startSketchOn('XY')
|
||||
brakeCaliperSketch = startSketchOn(XY)
|
||||
|> startProfileAt([
|
||||
rotorDiameter / 2 + caliperTolerance,
|
||||
0
|
||||
|
@ -1,39 +1,28 @@
|
||||
// Wheel rotor
|
||||
// A component of a disc brake system. It provides a surface for brake pads to press against, generating the friction needed to slow or stop the vehicle.
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import Constants
|
||||
import rotorDiameter, rotorInnerDiameter, rotorSinglePlateThickness, rotorInnerDiameterThickness, lugHolePatternDia, lugSpacing, rotorTotalThickness, spacerPatternDiameter, spacerDiameter, spacerLength, spacerCount, wheelDiameter, lugCount, yAxisOffset, drillAndSlotCount from "globals.kcl"
|
||||
|
||||
rotorSketch = startSketchOn('XZ')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = rotorDiameter / 2
|
||||
)
|
||||
rotorSketch = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = rotorDiameter / 2)
|
||||
rotor = extrude(rotorSketch, length = rotorSinglePlateThickness)
|
||||
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
|
||||
|
||||
rotorBumpSketch = startSketchOn(rotor, 'end')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = rotorInnerDiameter / 2
|
||||
)
|
||||
|> circle(center = [0, 0], radius = rotorInnerDiameter / 2)
|
||||
rotorBump = extrude(rotorBumpSketch, length = rotorInnerDiameterThickness)
|
||||
|
||||
lugHoles = startSketchOn(rotorBump, 'end')
|
||||
|> circle(
|
||||
center = [-lugSpacing / 2, 0],
|
||||
radius = 0.315
|
||||
)
|
||||
|> circle(center = [-lugSpacing / 2, 0], radius = 0.315)
|
||||
|> patternCircular2d(
|
||||
arcDegrees = 360,
|
||||
center = [0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|> extrude(%, length = -(rotorInnerDiameterThickness + rotorSinglePlateThickness))
|
||||
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
|
||||
@ -44,35 +33,26 @@ centerSpacer = startSketchOn(rotor, 'start')
|
||||
|> extrude(%, length = spacerLength)
|
||||
|
||||
secondaryRotorSketch = startSketchOn(centerSpacer, 'end')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = rotorDiameter / 2
|
||||
)
|
||||
|> circle(center = [0, 0], radius = rotorDiameter / 2)
|
||||
secondRotor = extrude(secondaryRotorSketch, length = rotorSinglePlateThickness)
|
||||
|
||||
lugHoles2 = startSketchOn(secondRotor, 'end')
|
||||
|> circle(
|
||||
center = [-lugSpacing / 2, 0],
|
||||
radius = 0.315
|
||||
)
|
||||
|> circle(center = [-lugSpacing / 2, 0], radius = 0.315)
|
||||
|> patternCircular2d(
|
||||
arcDegrees = 360,
|
||||
center = [0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|> extrude(length = -rotorSinglePlateThickness)
|
||||
|
||||
spacerSketch = startSketchOn(rotor, 'start')
|
||||
|> circle(
|
||||
center = [spacerPatternDiameter / 2, 0],
|
||||
radius = spacerDiameter
|
||||
)
|
||||
|> circle(center = [spacerPatternDiameter / 2, 0], radius = spacerDiameter)
|
||||
|> patternCircular2d(
|
||||
arcDegrees = 360,
|
||||
center = [0, 0],
|
||||
instances = spacerCount,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
spacers = extrude(spacerSketch, length = spacerLength)
|
||||
|
||||
@ -87,7 +67,7 @@ rotorSlottedSketch = startSketchOn(rotor, 'START')
|
||||
center = [0, 0],
|
||||
instances = drillAndSlotCount,
|
||||
arcDegrees = 360,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
rotorSlotted = extrude(rotorSlottedSketch, length = -rotorSinglePlateThickness / 2)
|
||||
|
||||
@ -102,7 +82,7 @@ secondRotorSlottedSketch = startSketchOn(secondRotor, 'END')
|
||||
center = [0, 0],
|
||||
instances = drillAndSlotCount,
|
||||
arcDegrees = 360,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|
||||
extrude(secondRotorSlottedSketch, length = -rotorSinglePlateThickness / 2)
|
||||
|
@ -1,21 +1,22 @@
|
||||
// Tire
|
||||
// A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities.
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import Constants
|
||||
import tireInnerDiameter, tireOuterDiameter, tireDepth, bendRadius, tireTreadWidth, tireTreadDepth, tireTreadOffset from "globals.kcl"
|
||||
|
||||
// Create the sketch of the tire
|
||||
tireSketch = startSketchOn("XY")
|
||||
tireSketch = startSketchOn(XY)
|
||||
|> startProfileAt([tireInnerDiameter / 2, tireDepth / 2], %)
|
||||
|> line(endAbsolute = [
|
||||
tireOuterDiameter / 2 - bendRadius,
|
||||
tireDepth / 2
|
||||
], tag = $edge1)
|
||||
|> line(
|
||||
endAbsolute = [
|
||||
tireOuterDiameter / 2 - bendRadius,
|
||||
tireDepth / 2
|
||||
],
|
||||
tag = $edge1,
|
||||
)
|
||||
|> tangentialArc({ offset = -90, radius = bendRadius }, %)
|
||||
|> line(endAbsolute = [
|
||||
tireOuterDiameter / 2,
|
||||
|
@ -1,69 +1,49 @@
|
||||
// Car Wheel
|
||||
// A sports car wheel with a circular lug pattern and spokes.
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import Constants
|
||||
import lugCount, lugSpacing, offset, backSpacing, wheelWidth, wheelDiameter, spokeCount, spokeGap, spokeAngle, spokeThickness from "globals.kcl"
|
||||
|
||||
// Create the wheel center
|
||||
lugBase = startSketchOn('XZ')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = (lugSpacing + 1.5) / 2
|
||||
)
|
||||
|> hole(circle(
|
||||
center = [0, 0],
|
||||
radius = (lugSpacing - 1.5) / 2
|
||||
), %)
|
||||
lugBase = startSketchOn(XZ)
|
||||
|> circle(center = [0, 0], radius = (lugSpacing + 1.5) / 2)
|
||||
|> hole(circle(center = [0, 0], radius = (lugSpacing - 1.5) / 2), %)
|
||||
|> extrude(length = wheelWidth / 20)
|
||||
|
||||
// Extend the wheel center and bore holes to accomidate the lug heads
|
||||
lugExtrusion = startSketchOn(lugBase, 'END')
|
||||
|> circle(
|
||||
center = [0, 0],
|
||||
radius = (lugSpacing + 1.5) / 2
|
||||
)
|
||||
|> hole(circle(
|
||||
center = [0, 0],
|
||||
radius = (lugSpacing - 1.5) / 2
|
||||
), %)
|
||||
|> circle(center = [0, 0], radius = (lugSpacing + 1.5) / 2)
|
||||
|> hole(circle(center = [0, 0], radius = (lugSpacing - 1.5) / 2), %)
|
||||
|> extrude(length = wheelWidth / 10)
|
||||
|
||||
// Create the circular pattern for the lugs
|
||||
lugClearance = startSketchOn(lugExtrusion, 'END')
|
||||
|> circle(
|
||||
center = [lugSpacing / 2, 0],
|
||||
radius = 1.2 / 2
|
||||
)
|
||||
|> circle(center = [lugSpacing / 2, 0], radius = 1.2 / 2)
|
||||
|> patternCircular2d(
|
||||
arcDegrees = 360,
|
||||
center = [0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|> extrude(length = -wheelWidth / 10)
|
||||
|
||||
// Create the circular pattern for the lug holes
|
||||
lugHoles = startSketchOn(lugBase, 'END')
|
||||
|> circle(
|
||||
center = [lugSpacing / 2, 0],
|
||||
radius = 16 * mm() / 2
|
||||
)
|
||||
|> circle(center = [lugSpacing / 2, 0], radius = 16 * mm() / 2)
|
||||
|> patternCircular2d(
|
||||
arcDegrees = 360,
|
||||
center = [0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|> extrude(length = -wheelWidth / 20)
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
|
||||
// Add detail to the wheel center by revolving curved edge profiles
|
||||
wheelCenterInner = startSketchOn('XY')
|
||||
wheelCenterInner = startSketchOn(XY)
|
||||
|> startProfileAt([(lugSpacing - 1.5) / 2, 0], %)
|
||||
|> yLine(length = -wheelWidth / 10 - (wheelWidth / 20))
|
||||
|> bezierCurve({
|
||||
@ -77,7 +57,7 @@ wheelCenterInner = startSketchOn('XY')
|
||||
|> revolve(axis = 'y')
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
|
||||
wheelCenterOuter = startSketchOn('XY')
|
||||
wheelCenterOuter = startSketchOn(XY)
|
||||
|> startProfileAt([(lugSpacing + 1.5) / 2, 0], %)
|
||||
|> yLine(length = -wheelWidth / 10 - (wheelWidth / 20))
|
||||
|> bezierCurve({
|
||||
@ -145,7 +125,7 @@ fn spoke(spokeGap, spokeAngle, spokeThickness) {
|
||||
center = [0, -2000, 0],
|
||||
instances = spokeCount,
|
||||
arcDegrees = 360,
|
||||
rotateDuplicates = true
|
||||
rotateDuplicates = true,
|
||||
)
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
return spokePattern
|
||||
@ -155,7 +135,7 @@ spoke(spokeGap, spokeAngle, spokeThickness)
|
||||
spoke(-spokeGap, -spokeAngle, -spokeThickness)
|
||||
|
||||
// Define and revolve wheel exterior
|
||||
startSketchOn('XY')
|
||||
startSketchOn(XY)
|
||||
|> startProfileAt([
|
||||
wheelDiameter / 2,
|
||||
-wheelWidth + backSpacing + offset
|
||||
|
@ -20,7 +20,7 @@ export lugDiameter = 24 * mm()
|
||||
export lugHeadLength = lugDiameter * .5
|
||||
export lugThreadDiameter = lugDiameter / 2 * .85
|
||||
export lugLength = 30 * mm()
|
||||
export lugThreadDepth = lugLength - 12.7 * mm()
|
||||
export lugThreadDepth = lugLength - (12.7 * mm())
|
||||
|
||||
// Car Rotor
|
||||
export rotorDiameter = 12
|
||||
@ -50,4 +50,4 @@ export caliperTolerance = 0.050
|
||||
export caliperPadLength = 1.6
|
||||
export caliperThickness = 0.39
|
||||
export caliperOuterEdgeRadius = 0.39
|
||||
export caliperInnerEdgeRadius = 0.12
|
||||
export caliperInnerEdgeRadius = 0.12
|
||||
|
@ -1,11 +1,9 @@
|
||||
// Lug Nut
|
||||
// lug Nuts are essential components used to create secure connections, whether for electrical purposes, like terminating wires or grounding, or for mechanical purposes, such as providing mounting points or reinforcing structural joints.
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import Constants
|
||||
import lugDiameter, lugHeadLength, lugThreadDiameter, lugLength, lugThreadDepth, lugSpacing from "globals.kcl"
|
||||
|
||||
|
@ -4,24 +4,24 @@
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
import 'car-wheel.kcl' as carWheel
|
||||
import 'car-rotor.kcl' as carRotor
|
||||
import "car-wheel.kcl" as carWheel
|
||||
import "car-rotor.kcl" as carRotor
|
||||
import "brake-caliper.kcl" as brakeCaliper
|
||||
import 'lug-nut.kcl' as lugNut
|
||||
import 'car-tire.kcl' as carTire
|
||||
import lugCount from 'globals.kcl'
|
||||
import "lug-nut.kcl" as lugNut
|
||||
import "car-tire.kcl" as carTire
|
||||
import lugCount from "globals.kcl"
|
||||
|
||||
carRotor
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
carWheel
|
||||
lugNut
|
||||
|> patternCircular3d(
|
||||
arcDegrees = 360,
|
||||
axis = [0, 1, 0],
|
||||
center = [0, 0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = false
|
||||
)
|
||||
arcDegrees = 360,
|
||||
axis = [0, 1, 0],
|
||||
center = [0, 0, 0],
|
||||
instances = lugCount,
|
||||
rotateDuplicates = false,
|
||||
)
|
||||
brakeCaliper
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
carTire
|
||||
|