Compare commits
3 Commits
ascii
...
nrc-std-ax
Author | SHA1 | Date | |
---|---|---|---|
87dfda28a9 | |||
42f44e11f5 | |||
16ad7ff77a |
12
.eslintrc
12
.eslintrc
@ -20,20 +20,9 @@
|
||||
"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-floating-promises": "error",
|
||||
"no-implied-eval": "off", // This is wrong; use the @typescript-eslint one instead.
|
||||
"@typescript-eslint/no-implied-eval": "error",
|
||||
"@typescript-eslint/no-misused-promises": "error",
|
||||
"@typescript-eslint/no-namespace": "error",
|
||||
"@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": "^_",
|
||||
@ -41,7 +30,6 @@
|
||||
"vars": "all",
|
||||
"args": "none"
|
||||
}],
|
||||
"@typescript-eslint/prefer-as-const": "warn",
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/no-autofocus": "off",
|
||||
"jsx-a11y/no-noninteractive-element-interactions": "off",
|
||||
|
144
.github/workflows/static-analysis.yml
vendored
144
.github/workflows/static-analysis.yml
vendored
@ -28,87 +28,43 @@ jobs:
|
||||
- run: yarn fmt-check
|
||||
|
||||
yarn-build-wasm:
|
||||
# Build the wasm blob once on the fastest runner.
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- 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
|
||||
- run: yarn install
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
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
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- name: Build Wasm
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
|
||||
- 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
|
||||
- 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 build:wasm
|
||||
- run: yarn tsc
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -117,23 +73,7 @@ jobs:
|
||||
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 --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
- run: yarn lint
|
||||
|
||||
python-codespell:
|
||||
@ -151,7 +91,6 @@ jobs:
|
||||
|
||||
yarn-unit-test-kcl-samples:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -164,22 +103,7 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- 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 build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
@ -196,7 +120,6 @@ jobs:
|
||||
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -209,22 +132,7 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
|
||||
- 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 build:wasm
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
4
Makefile
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:dev
|
||||
yarn build:wasm
|
||||
|
||||
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
|
||||
|
@ -54,7 +54,7 @@ example = extrude(exampleSketch, length = 5)
|
||||
// Add color to a revolved solid.
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [15, 0], radius = 5)
|
||||
|> revolve(angle = 360, axis = 'y')
|
||||
|> revolve(angle = 360, axis = Y)
|
||||
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)
|
||||
```
|
||||
|
||||
|
@ -9,13 +9,12 @@ 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)
|
||||
- [`X`](/docs/kcl/consts/std-X)
|
||||
- [`XY`](/docs/kcl/consts/std-XY)
|
||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||
- [`Y`](/docs/kcl/consts/std-Y)
|
||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||
- [`ZERO`](/docs/kcl/consts/std-ZERO)
|
||||
- [`Z`](/docs/kcl/consts/std-Z)
|
||||
|
||||
### `std::math`
|
||||
|
||||
@ -23,3 +22,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,5 +1,5 @@
|
||||
---
|
||||
title: "std::ZERO"
|
||||
title: "std::X"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
@ -9,7 +9,7 @@ layout: manual
|
||||
|
||||
|
||||
```js
|
||||
std::ZERO: number = 0
|
||||
std::X
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-Y.md
Normal file
15
docs/kcl/consts/std-Y.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::Y"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::Y
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-Z.md
Normal file
15
docs/kcl/consts/std-Z.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::Z"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::Z
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-HALF_TURN.md
Normal file
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
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
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
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
|
||||
```
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
@ -22,20 +22,22 @@ layout: manual
|
||||
* [`string`](kcl/types/string)
|
||||
* [`tag`](kcl/types/tag)
|
||||
* **std**
|
||||
* [`Axis2d`](kcl/types/Axis2d)
|
||||
* [`Axis3d`](kcl/types/Axis3d)
|
||||
* [`Edge`](kcl/types/Edge)
|
||||
* [`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)
|
||||
* [`X`](kcl/consts/std-X)
|
||||
* [`XY`](kcl/consts/std-XY)
|
||||
* [`XZ`](kcl/consts/std-XZ)
|
||||
* [`Y`](kcl/consts/std-Y)
|
||||
* [`YZ`](kcl/consts/std-YZ)
|
||||
* [`ZERO`](kcl/consts/std-ZERO)
|
||||
* [`Z`](kcl/consts/std-Z)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
@ -72,7 +74,7 @@ layout: manual
|
||||
* [`getNextAdjacentEdge`](kcl/getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||
* [`helix`](kcl/helix)
|
||||
* [`helix`](kcl/std-helix)
|
||||
* [`hole`](kcl/hole)
|
||||
* [`hollow`](kcl/hollow)
|
||||
* [`inch`](kcl/inch)
|
||||
@ -91,7 +93,6 @@ layout: manual
|
||||
* [`map`](kcl/map)
|
||||
* [`max`](kcl/max)
|
||||
* [`min`](kcl/min)
|
||||
* [`mirror2d`](kcl/mirror2d)
|
||||
* [`mm`](kcl/mm)
|
||||
* [`offsetPlane`](kcl/offsetPlane)
|
||||
* [`patternCircular2d`](kcl/patternCircular2d)
|
||||
@ -110,7 +111,7 @@ layout: manual
|
||||
* [`push`](kcl/push)
|
||||
* [`reduce`](kcl/reduce)
|
||||
* [`rem`](kcl/rem)
|
||||
* [`revolve`](kcl/revolve)
|
||||
* [`revolve`](kcl/std-revolve)
|
||||
* [`rotate`](kcl/rotate)
|
||||
* [`round`](kcl/round)
|
||||
* [`scale`](kcl/scale)
|
||||
@ -146,3 +147,9 @@ layout: manual
|
||||
* [`tan`](kcl/std-math-tan)
|
||||
* **std::sketch**
|
||||
* [`circle`](kcl/std-sketch-circle)
|
||||
* [`mirror2d`](kcl/std-sketch-mirror2d)
|
||||
* **std::turns**
|
||||
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
|
||||
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
|
||||
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -6,10 +6,6 @@ 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).
|
||||
@ -170,7 +166,7 @@ fn square() {
|
||||
profile001 = square()
|
||||
|
||||
profile002 = square()
|
||||
|> translate(x = 0, y = 0, z = 20)
|
||||
|> translate(translate = [0, 0, 20])
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
|
||||
loft([profile001, profile002])
|
||||
|
File diff suppressed because one or more lines are too long
@ -146,7 +146,7 @@ exampleSketch = startSketchOn(XY)
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|
||||
example = revolve(exampleSketch, axis = 'y', angle = 180)
|
||||
example = revolve(exampleSketch, axis = Y, angle = 180)
|
||||
|
||||
exampleSketch002 = startSketchOn(example, 'end')
|
||||
|> startProfileAt([4.5, -5], %)
|
||||
@ -177,7 +177,7 @@ exampleSketch = startSketchOn(XY)
|
||||
|
||||
example = revolve(
|
||||
exampleSketch,
|
||||
axis = 'y',
|
||||
axis = Y,
|
||||
angle = 180,
|
||||
tagEnd = $end01,
|
||||
)
|
||||
|
116
docs/kcl/std-helix.md
Normal file
116
docs/kcl/std-helix.md
Normal file
File diff suppressed because one or more lines are too long
246
docs/kcl/std-revolve.md
Normal file
246
docs/kcl/std-revolve.md
Normal file
File diff suppressed because one or more lines are too long
98
docs/kcl/std-sketch-mirror2d.md
Normal file
98
docs/kcl/std-sketch-mirror2d.md
Normal file
File diff suppressed because one or more lines are too long
23858
docs/kcl/std.json
23858
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -74,7 +74,7 @@ helixPath = helix(
|
||||
revolutions = 4,
|
||||
length = 10,
|
||||
radius = 5,
|
||||
axis = 'Z',
|
||||
axis = Z,
|
||||
)
|
||||
|
||||
// Create a spring by sweeping around the helix path.
|
||||
|
File diff suppressed because one or more lines are too long
12
docs/kcl/types/Axis2d.md
Normal file
12
docs/kcl/types/Axis2d.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "std::Axis2d"
|
||||
excerpt: "An infinte line in 2d space."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An infinte line in 2d space.
|
||||
|
||||
|
||||
|
||||
|
||||
|
12
docs/kcl/types/Axis3d.md
Normal file
12
docs/kcl/types/Axis3d.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "std::Axis3d"
|
||||
excerpt: "An infinte line in 3d space."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An infinte line in 3d space.
|
||||
|
||||
|
||||
|
||||
|
||||
|
12
docs/kcl/types/Edge.md
Normal file
12
docs/kcl/types/Edge.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: "std::Edge"
|
||||
excerpt: "The edge of a solid."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
The edge of a solid.
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,117 +0,0 @@
|
||||
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)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
@ -21,7 +21,7 @@ sketch001 = startSketchOn(XZ)
|
||||
|> angledLine([-45, length001], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
revolve001 = revolve(sketch001, axis = "X")
|
||||
revolve001 = revolve(sketch001, axis = X)
|
||||
triangle()
|
||||
|> extrude(length = 30)
|
||||
plane001 = offsetPlane(XY, offset = 10)
|
||||
@ -126,7 +126,7 @@ test.describe('Feature Tree pane', () => {
|
||||
await testViewSource({
|
||||
operationName: 'Revolve',
|
||||
operationIndex: 0,
|
||||
expectedActiveLine: 'revolve001 = revolve(sketch001, axis = "X")',
|
||||
expectedActiveLine: 'revolve001 = revolve(sketch001, axis = X)',
|
||||
})
|
||||
await testViewSource({
|
||||
operationName: 'Triangle',
|
||||
|
@ -181,14 +181,6 @@ 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
|
||||
|
@ -258,6 +258,14 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: "Cannot read properties of undefined (reading 'length')",
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: '', // many tests are impacted by this error
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: '_testUtils is not defined',
|
||||
@ -265,6 +273,7 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: 'Failed to fetch',
|
||||
@ -273,6 +282,14 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: 'originalCode is not defined',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/onboarding-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: 'createNewVariableCheckbox is not defined',
|
||||
@ -280,14 +297,6 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-constraints.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, '')
|
||||
|
@ -230,9 +230,9 @@ test.describe('Onboarding tests', () => {
|
||||
|
||||
// Override beforeEach test setup
|
||||
await context.addInitScript(
|
||||
async ({ settingsKey, settings, code }) => {
|
||||
async ({ settingsKey, settings }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', code)
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
@ -240,7 +240,6 @@ test.describe('Onboarding tests', () => {
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
code: originalCode,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1097,6 +1097,7 @@ openSketch = startSketchOn(XY)
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
@ -1193,14 +1194,14 @@ openSketch = startSketchOn(XY)
|
||||
{
|
||||
selectionType: 'segment',
|
||||
testPoint: { x: 513, y: 221 },
|
||||
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,)`,
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
{
|
||||
selectionType: 'sweepEdge',
|
||||
testPoint: { x: 564, y: 364 },
|
||||
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,)`,
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
]
|
||||
helixCases.map(
|
||||
@ -1243,6 +1244,7 @@ openSketch = startSketchOn(XY)
|
||||
AngleStart: '',
|
||||
Mode: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
},
|
||||
@ -1259,6 +1261,8 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('1')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('100')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
@ -1267,6 +1271,7 @@ openSketch = startSketchOn(XY)
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: '1',
|
||||
Length: '100',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
@ -1287,8 +1292,8 @@ openSketch = startSketchOn(XY)
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick()
|
||||
const initialInput = '1'
|
||||
const newInput = '5'
|
||||
const initialInput = '100'
|
||||
const newInput = '50'
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
@ -1297,14 +1302,13 @@ openSketch = startSketchOn(XY)
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: initialInput,
|
||||
Radius: '1',
|
||||
Length: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page
|
||||
.getByRole('button', { name: 'radius', exact: false })
|
||||
.click()
|
||||
await page.keyboard.press('Shift+Backspace')
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput
|
||||
.locator('.cm-content')
|
||||
@ -1315,7 +1319,8 @@ openSketch = startSketchOn(XY)
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: newInput,
|
||||
Radius: '1',
|
||||
Length: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
@ -1386,6 +1391,7 @@ extrude001 = extrude(profile001, length = 100)
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
@ -3463,7 +3469,7 @@ segAng(rectangleSegmentA002),
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = 'X')`
|
||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)`
|
||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||
})
|
||||
test('revolve surface around edge from an extruded solid2d', async ({
|
||||
|
@ -674,7 +674,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> line(end = [12.73, -0.09])
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> close()
|
||||
|> revolve(axis = "X")`
|
||||
|> revolve(axis = X)`
|
||||
)
|
||||
})
|
||||
|
||||
@ -761,7 +761,7 @@ sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||
|> tangentialArcTo([24.95, -5.38], %)
|
||||
|> line(end = [1.97, 2.06])
|
||||
|> close()
|
||||
|> revolve(axis = "X")`)
|
||||
|> revolve(axis = X)`)
|
||||
})
|
||||
test('Can add multiple sketches', async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
@ -1209,7 +1209,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||
|> xLine(endAbsolute = 0 + .001)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|> revolve(axis = "Y")
|
||||
|> revolve(axis = Y)
|
||||
|
||||
return lugSketch
|
||||
}
|
||||
|
@ -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)`
|
||||
)
|
||||
|
@ -486,13 +486,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) {
|
||||
@ -935,12 +935,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',
|
||||
},
|
||||
|
@ -319,7 +319,7 @@ part009 = startSketchOn(XY)
|
||||
|> line(end = [0, pipeLength])
|
||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||
|> close()
|
||||
rev = revolve(part009, axis = 'y')
|
||||
rev = revolve(part009, axis = Y)
|
||||
sketch006 = startSketchOn(XY)
|
||||
profile001 = circle(
|
||||
sketch006,
|
||||
@ -376,7 +376,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
await page.waitForTimeout(200)
|
||||
|
||||
await expect(u.codeLocator).not.toContainText(
|
||||
`rev = revolve(part009, axis: 'y')`
|
||||
`rev = revolve(part009, axis: Y)`
|
||||
)
|
||||
|
||||
// FIXME (commented section below), this test would select a wall that had a sketch on it, and delete the underlying extrude
|
||||
|
@ -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)`
|
||||
)
|
||||
|
@ -94,11 +94,9 @@
|
||||
"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",
|
||||
|
@ -90,7 +90,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
}
|
||||
|
||||
add(bytes: Uint8Array): void {
|
||||
const message = Codec.decode<vsrpc.Message>(bytes)
|
||||
const message = Codec.decode(bytes) as vsrpc.Message
|
||||
if (this.trace) {
|
||||
Tracer.server(message)
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ ballsSketch = startSketchOn(XY)
|
||||
|> close()
|
||||
|
||||
// Revolve the ball to make a sphere and pattern around the inside wall
|
||||
balls = revolve(ballsSketch, axis = "X")
|
||||
balls = revolve(ballsSketch, axis = X)
|
||||
|> patternCircular3d(
|
||||
arcDegrees = 360,
|
||||
axis = [0, 0, 1],
|
||||
@ -60,7 +60,7 @@ chainSketch = startSketchOn(XY)
|
||||
|> close()
|
||||
|
||||
// Revolve the chain sketch
|
||||
chainHead = revolve(chainSketch, axis = "X")
|
||||
chainHead = revolve(chainSketch, axis = X)
|
||||
|> patternCircular3d(
|
||||
arcDegrees = 360,
|
||||
axis = [0, 0, 1],
|
||||
@ -80,7 +80,7 @@ linkSketch = startSketchOn(XZ)
|
||||
)
|
||||
|
||||
// Revolve the link sketch
|
||||
linkRevolve = revolve(linkSketch, axis = 'Y', angle = 360 / nBalls)
|
||||
linkRevolve = revolve(linkSketch, axis = Y, angle = 360 / nBalls)
|
||||
|> patternCircular3d(
|
||||
arcDegrees = 360,
|
||||
axis = [0, 0, 1],
|
||||
|
@ -80,5 +80,5 @@ brakeCaliperSketch = startSketchOn(XY)
|
||||
|> close()
|
||||
|
||||
// Revolve the brake caliper sketch
|
||||
revolve(brakeCaliperSketch, axis = "Y", angle = -70)
|
||||
revolve(brakeCaliperSketch, axis = Y, angle = -70)
|
||||
|> appearance(color = "#c82d2d", metalness = 90, roughness = 90)
|
||||
|
@ -41,5 +41,5 @@ tireSketch = startSketchOn(XY)
|
||||
|> close()
|
||||
|
||||
// Revolve the sketch to create the tire
|
||||
revolve(tireSketch, axis = "Y")
|
||||
revolve(tireSketch, axis = Y)
|
||||
|> appearance(color = "#0f0f0f", roughness = 80)
|
||||
|
@ -54,7 +54,7 @@ wheelCenterInner = startSketchOn(XY)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
|
||||
wheelCenterOuter = startSketchOn(XY)
|
||||
@ -68,7 +68,7 @@ wheelCenterOuter = startSketchOn(XY)
|
||||
|> yLine(endAbsolute = -wheelWidth / 20)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
|
||||
// Write a function that defines the spoke geometry, patterns and extrudes it
|
||||
@ -173,5 +173,5 @@ startSketchOn(XY)
|
||||
|> xLine(length = wheelWidth * 0.03)
|
||||
|> yLine(length = wheelWidth * 0.05)
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|> appearance(color = "#ffffff", metalness = 0, roughness = 0)
|
||||
|
@ -32,7 +32,7 @@ fn lug(plane, length, diameter) {
|
||||
|> xLine(endAbsolute = lugThreadDiameter)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|> revolve(axis = "Y")
|
||||
|> revolve(axis = Y)
|
||||
|> appearance(color = "#dbcd70", roughness = 90, metalness = 90)
|
||||
return lugSketch
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import "car-tire.kcl" as carTire
|
||||
import lugCount from "globals.kcl"
|
||||
|
||||
carRotor
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
carWheel
|
||||
lugNut
|
||||
|> patternCircular3d(
|
||||
@ -23,5 +23,5 @@ lugNut
|
||||
rotateDuplicates = false,
|
||||
)
|
||||
brakeCaliper
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
carTire
|
||||
|
@ -23,7 +23,7 @@ sketch001 = startSketchOn(XZ)
|
||||
], %, $rectangleSegmentC001)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(angle = 360, axis = 'Y')
|
||||
|> revolve(angle = 360, axis = Y)
|
||||
|
||||
// Create an angled plane to sketch the supports
|
||||
plane001 = {
|
||||
@ -132,7 +132,7 @@ sketch005 = startSketchOn(XZ)
|
||||
|> xLine(endAbsolute = 0.15)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Plunger and stem
|
||||
sketch006 = startSketchOn(XZ)
|
||||
@ -145,7 +145,7 @@ sketch006 = startSketchOn(XZ)
|
||||
|> tangentialArc({ radius = 0.6, offset = -90 }, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Spiral plate
|
||||
sketch007 = startSketchOn(offsetPlane(XY, offset = 1.12))
|
||||
@ -201,7 +201,7 @@ sketch011 = startSketchOn(XZ)
|
||||
}, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Draw and extrude handle
|
||||
sketch012 = startSketchOn(offsetPlane(XZ, offset = handleThickness / 2))
|
||||
|
@ -48,10 +48,8 @@ sides = patternCircular3d(
|
||||
|
||||
// define an axis axis000
|
||||
axis000 = {
|
||||
custom = {
|
||||
axis = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
direction = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
|
||||
// create a single corner of the bin
|
||||
|
@ -45,10 +45,8 @@ sides = patternCircular3d(
|
||||
|
||||
// define an axis axis000
|
||||
axis000 = {
|
||||
custom = {
|
||||
axis = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
direction = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
|
||||
// create a single corner of the bin
|
||||
|
@ -65,13 +65,11 @@ sides = patternCircular3d(
|
||||
|
||||
// define an axis axis000
|
||||
axis000 = {
|
||||
custom = {
|
||||
axis = [0.0, 1.0],
|
||||
origin = [
|
||||
cornerRadius + binTol,
|
||||
cornerRadius + binTol
|
||||
]
|
||||
}
|
||||
direction = [0.0, 1.0],
|
||||
origin = [
|
||||
cornerRadius + binTol,
|
||||
cornerRadius + binTol
|
||||
]
|
||||
}
|
||||
|
||||
// create a single corner of the bin
|
||||
@ -272,10 +270,8 @@ lipWidths = patternCircular3d(
|
||||
|
||||
// define an axis axis000
|
||||
axis001 = {
|
||||
custom = {
|
||||
axis = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
direction = [0.0, 1.0],
|
||||
origin = [cornerRadius, cornerRadius]
|
||||
}
|
||||
|
||||
// create a single corner of the bin
|
||||
|
@ -58,13 +58,11 @@ sides = patternCircular3d(
|
||||
|
||||
// define an axis axis000
|
||||
axis000 = {
|
||||
custom = {
|
||||
axis = [0.0, 1.0],
|
||||
origin = [
|
||||
cornerRadius + binTol,
|
||||
cornerRadius + binTol
|
||||
]
|
||||
}
|
||||
direction = [0.0, 1.0],
|
||||
origin = [
|
||||
cornerRadius + binTol,
|
||||
cornerRadius + binTol
|
||||
]
|
||||
}
|
||||
|
||||
// create a single corner of the bin
|
||||
|
@ -20,6 +20,6 @@ sketch001 = startSketchOn(-XZ)
|
||||
|> xLine(endAbsolute = webThickness / 2 + rootRadius)
|
||||
|> tangentialArc({ radius = rootRadius, offset = 90 }, %)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> mirror2d({ axis = 'X' }, %)
|
||||
|> mirror2d({ axis = 'Y' }, %)
|
||||
|> mirror2d(axis = X)
|
||||
|> mirror2d(axis = Y)
|
||||
|> extrude(length = beamLength)
|
||||
|
@ -19,27 +19,27 @@ import pipe from "1120t74-pipe.kcl"
|
||||
flange()
|
||||
flange()
|
||||
|> rotate(axis = [0, 1, 0], angle = 180)
|
||||
|> translate(
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = flangeBackHeight * 2 + gasketThickness,
|
||||
)
|
||||
|> translate(translate = [
|
||||
0,
|
||||
0,
|
||||
flangeBackHeight * 2 + gasketThickness
|
||||
])
|
||||
|
||||
// place gasket between the flanges
|
||||
gasket()
|
||||
|> translate(
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = -flangeBackHeight - gasketThickness
|
||||
)
|
||||
|> translate(translate = [
|
||||
0,
|
||||
0,
|
||||
-flangeBackHeight - gasketThickness
|
||||
])
|
||||
|
||||
// place eight washers (four front, four back)
|
||||
washer()
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = flangeBaseThickness
|
||||
)
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
flangeBaseThickness
|
||||
])
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
instances = 4,
|
||||
@ -57,11 +57,11 @@ washer()
|
||||
|
||||
// place four bolts
|
||||
bolt()
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = flangeBaseThickness + washerThickness,
|
||||
)
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
flangeBaseThickness + washerThickness
|
||||
])
|
||||
|> rotate(roll = 90, pitch = 0, yaw = 0)
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
@ -74,11 +74,11 @@ bolt()
|
||||
|
||||
// place four hex nuts
|
||||
hexNut()
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + washerThickness + hexNutThickness),
|
||||
)
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
-(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + washerThickness + hexNutThickness)
|
||||
])
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
instances = 4,
|
||||
@ -97,11 +97,13 @@ pipe()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
%,
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = flangeBaseThickness + flangeFrontHeight - 0.5,
|
||||
global = true,
|
||||
%,
|
||||
translate = [
|
||||
0,
|
||||
0,
|
||||
flangeBaseThickness + flangeFrontHeight - 0.5
|
||||
],
|
||||
global = true,
|
||||
)
|
||||
|
||||
pipe()
|
||||
@ -112,9 +114,11 @@ pipe()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
%,
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + flangeFrontHeight - 0.5),
|
||||
global = true,
|
||||
%,
|
||||
translate = [
|
||||
0,
|
||||
0,
|
||||
-(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + flangeFrontHeight - 0.5)
|
||||
],
|
||||
global = true,
|
||||
)
|
||||
|
@ -24,4 +24,4 @@ pipeProfile = outerProfile
|
||||
|> hole(innerProfile, %)
|
||||
|
||||
// revolve the pipe profile at the desired angle
|
||||
pipe = revolve(pipeProfile, axis = 'Y', angle = bendAngle)
|
||||
pipe = revolve(pipeProfile, axis = Y, angle = bendAngle)
|
||||
|
@ -33,4 +33,4 @@ pipeSketch = startSketchOn(XY)
|
||||
|> close()
|
||||
|
||||
// Revolve the sketch to create the pipe
|
||||
pipe = revolve(pipeSketch, axis = 'y')
|
||||
pipe = revolve(pipeSketch, axis = Y)
|
||||
|
@ -34,10 +34,8 @@ part001 = revolve(
|
||||
sketch001,
|
||||
angle = 90,
|
||||
axis = {
|
||||
custom = {
|
||||
axis = [1.0, 0.0],
|
||||
origin = [0.0, height + .0001]
|
||||
}
|
||||
direction = [1.0, 0.0],
|
||||
origin = [0.0, height + .0001]
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -14,7 +14,7 @@ radius = 10
|
||||
depth = 30
|
||||
distanceToInsideEdge = slateWidthHalf + templateThickness + templateGap
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([ZERO, depth + templateGap], %)
|
||||
|> startProfileAt([turns::ZERO, depth + templateGap], %)
|
||||
|> xLine(length = slateWidthHalf - radius, tag = $seg01)
|
||||
|> arc({
|
||||
angleEnd = 0,
|
||||
@ -28,7 +28,7 @@ sketch001 = startSketchOn(XZ)
|
||||
|> yLine(length = templateThickness * 2, tag = $seg08)
|
||||
|> xLine(endAbsolute = segEndX(seg02) + 0, tag = $seg05)
|
||||
|> yLine(endAbsolute = segEndY(seg01) + templateThickness, tag = $seg10)
|
||||
|> xLine(endAbsolute = ZERO, tag = $seg04)
|
||||
|> xLine(endAbsolute = turns::ZERO, tag = $seg04)
|
||||
|> xLine(length = -segLen(seg04))
|
||||
|> yLine(length = -segLen(seg10))
|
||||
|> xLine(length = -segLen(seg05))
|
||||
|
@ -28,7 +28,7 @@ sketch001 = startSketchOn(XZ)
|
||||
|> yLine(endAbsolute = -templateGap * 2 - (templateDiameter / 2), tag = $seg05)
|
||||
|> xLine(endAbsolute = slateWidthHalf + templateThickness, tag = $seg04)
|
||||
|> yLine(length = -length002, tag = $seg03)
|
||||
|> xLine(endAbsolute = ZERO, tag = $seg02)
|
||||
|> xLine(endAbsolute = turns::ZERO, tag = $seg02)
|
||||
// |> line(end = [7.78, 11.16])
|
||||
|> xLine(length = -segLen(seg02))
|
||||
|> yLine(length = segLen(seg03))
|
||||
|
@ -21,7 +21,7 @@ export fn knob() {
|
||||
}, %)
|
||||
|> xLine(endAbsolute = 0.0001)
|
||||
|> close()
|
||||
|> revolve(axis = "Y")
|
||||
|> revolve(axis = Y)
|
||||
|> appearance(color = '#D0FF01', metalness = 90, roughness = 50)
|
||||
|
||||
return knob
|
||||
|
@ -20,37 +20,37 @@ body()
|
||||
|
||||
// import the antenna
|
||||
antenna()
|
||||
|> translate(x = -width / 2 + .45, y = -0.10, z = height / 2)
|
||||
|> translate(translate = [-width / 2 + .45, -0.10, height / 2])
|
||||
|
||||
// import the case
|
||||
case()
|
||||
|> translate(x = 0, y = -1, z = 0)
|
||||
|> translate(translate = [0, -1, 0])
|
||||
|
||||
// import the talk button
|
||||
talkButton()
|
||||
|> translate(x = width / 2, y = -thickness / 2, z = .5)
|
||||
|> translate(translate = [width / 2, -thickness / 2, .5])
|
||||
|
||||
// import the frequency knob
|
||||
knob()
|
||||
|> translate(
|
||||
x = width / 2 - 0.70,
|
||||
y = -thickness / 2,
|
||||
z = height / 2
|
||||
)
|
||||
|> translate(translate = [
|
||||
width / 2 - 0.70,
|
||||
-thickness / 2,
|
||||
height / 2
|
||||
])
|
||||
|
||||
// import the buttons
|
||||
button()
|
||||
|> translate(
|
||||
x = -(screenWidth / 2 + tolerance),
|
||||
y = -1,
|
||||
z = screenYPosition
|
||||
)
|
||||
|> translate(translate = [
|
||||
-(screenWidth / 2 + tolerance),
|
||||
-1,
|
||||
screenYPosition
|
||||
])
|
||||
button()
|
||||
|> translate(
|
||||
x = -(screenWidth / 2 + tolerance),
|
||||
y = -1,
|
||||
z = screenYPosition - buttonHeight - (tolerance * 2)
|
||||
)
|
||||
|> translate(translate = [
|
||||
-(screenWidth / 2 + tolerance),
|
||||
-1,
|
||||
screenYPosition - buttonHeight - (tolerance * 2)
|
||||
])
|
||||
button()
|
||||
|> rotate(
|
||||
%,
|
||||
@ -59,21 +59,25 @@ button()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
x = screenWidth / 2 + tolerance,
|
||||
y = -1,
|
||||
z = screenYPosition - buttonHeight,
|
||||
global = true,
|
||||
)
|
||||
button()
|
||||
|> rotate(
|
||||
%,
|
||||
roll = 0,
|
||||
pitch = 180,
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
x = screenWidth / 2 + tolerance,
|
||||
y = -1,
|
||||
z = screenYPosition - (buttonHeight * 2) - (tolerance * 2),
|
||||
translate = [
|
||||
screenWidth / 2 + tolerance,
|
||||
-1,
|
||||
screenYPosition - buttonHeight
|
||||
],
|
||||
global = true,
|
||||
)
|
||||
button()
|
||||
|> rotate(
|
||||
%,
|
||||
roll = 0,
|
||||
pitch = 180,
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
translate = [
|
||||
screenWidth / 2 + tolerance,
|
||||
-1,
|
||||
screenYPosition - (buttonHeight * 2) - (tolerance * 2)
|
||||
],
|
||||
global = true,
|
||||
)
|
||||
|
@ -43,10 +43,6 @@ overwrite-sim-test test_name:
|
||||
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} -p kcl-lib --no-quiet -- tests::{{test_name}}::kcl_test_execute
|
||||
EXPECTORATE=overwrite {{cita}} -p kcl-lib --no-quiet -- simulation_tests::{{test_name}}::test_after_engine
|
||||
|
||||
# Regenerate all the simulation test output.
|
||||
redo-sim-tests:
|
||||
EXPECTORATE=overwrite TWENTY_TWENTY=overwrite {{cita}} -p kcl-lib --no-quiet -- simulation_tests
|
||||
|
||||
test:
|
||||
export RUST_BRACKTRACE="full" && cargo nextest run --workspace --no-fail-fast
|
||||
|
||||
|
@ -1,77 +0,0 @@
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = circle(sketch001, center = [154.36, 113.92], radius = 41.09)
|
||||
extrude001 = extrude(profile001, length = 200)
|
||||
sketch002 = startSketchOn(XY)
|
||||
profile002 = startProfileAt([72.24, -52.05], sketch002)
|
||||
|> angledLine([0, 181.26], %, $rectangleSegmentA001)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001) - 90,
|
||||
21.54
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA001),
|
||||
-segLen(rectangleSegmentA001)
|
||||
], %, $mySeg)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude002 = extrude(profile002, length = 150)
|
||||
|> chamfer(
|
||||
%,
|
||||
length = 15,
|
||||
tags = [mySeg],
|
||||
tag = $seg02,
|
||||
)
|
||||
|
||||
sketch003 = startSketchOn(extrude002, mySeg)
|
||||
profile003 = startProfileAt([207.36, 126.19], sketch003)
|
||||
|> angledLine([0, 33.57], %, $rectangleSegmentA002)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA002) - 90,
|
||||
99.11
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA002),
|
||||
-segLen(rectangleSegmentA002)
|
||||
], %, $seg01)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude003 = extrude(profile003, length = -20)
|
||||
sketch004 = startSketchOn(extrude003, seg01)
|
||||
profile004 = startProfileAt([-235.38, 66.16], sketch004)
|
||||
|> angledLine([0, 24.21], %, $rectangleSegmentA003)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003) - 90,
|
||||
3.72
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA003),
|
||||
-segLen(rectangleSegmentA003)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude004 = extrude(profile004, length = 30)
|
||||
|
||||
sketch005 = startSketchOn(extrude002, seg02)
|
||||
profile005 = startProfileAt([-129.93, -59.19], sketch005)
|
||||
|> xLine(length = 48.79)
|
||||
|> line(end = [1.33, 11.03])
|
||||
|> xLine(length = -60.56, tag = $seg03)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude005 = extrude(profile005, length = -10)
|
||||
sketch006 = startSketchOn(extrude005, seg03)
|
||||
profile006 = startProfileAt([-95.86, 38.73], sketch006)
|
||||
|> angledLine([0, 3.48], %, $rectangleSegmentA004)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA004) - 90,
|
||||
3.36
|
||||
], %)
|
||||
|> angledLine([
|
||||
segAng(rectangleSegmentA004),
|
||||
-segLen(rectangleSegmentA004)
|
||||
], %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude006 = extrude(profile006, length = 13)
|
@ -876,7 +876,7 @@ async fn kcl_test_simple_revolve() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'y')
|
||||
|> revolve(axis = Y)
|
||||
|
||||
"#;
|
||||
|
||||
@ -896,7 +896,7 @@ async fn kcl_test_simple_revolve_uppercase() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'Y')
|
||||
|> revolve(axis = Y)
|
||||
|
||||
"#;
|
||||
|
||||
@ -916,7 +916,7 @@ async fn kcl_test_simple_revolve_negative() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = '-Y', angle = 180)
|
||||
|> revolve(axis = -Y, angle = 180)
|
||||
|
||||
"#;
|
||||
|
||||
@ -936,7 +936,7 @@ async fn kcl_test_revolve_bad_angle_low() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'y', angle = -455)
|
||||
|> revolve(axis = Y, angle = -455)
|
||||
|
||||
"#;
|
||||
|
||||
@ -962,7 +962,7 @@ async fn kcl_test_revolve_bad_angle_high() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'y', angle = 455)
|
||||
|> revolve(axis = Y, angle = 455)
|
||||
|
||||
"#;
|
||||
|
||||
@ -988,7 +988,7 @@ async fn kcl_test_simple_revolve_custom_angle() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'y', angle = 180)
|
||||
|> revolve(axis = Y, angle = 180)
|
||||
|
||||
"#;
|
||||
|
||||
@ -1008,7 +1008,7 @@ async fn kcl_test_simple_revolve_custom_axis() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = {custom: {axis: [0, -1], origin: [0,0]}}, angle = 180)
|
||||
|> revolve(axis = { direction = [0, -1], origin: [0,0] }, angle = 180)
|
||||
|
||||
"#;
|
||||
|
||||
@ -1106,7 +1106,7 @@ sketch001 = startSketchOn(box, "END")
|
||||
|> circle(center = [10,10], radius= 4 )
|
||||
|> revolve(
|
||||
angle = -90,
|
||||
axis = 'y'
|
||||
axis = Y
|
||||
)
|
||||
"#;
|
||||
|
||||
@ -1131,7 +1131,7 @@ sketch001 = startSketchOn(box, "end")
|
||||
|> line(end = [0, 10])
|
||||
|> close()
|
||||
|> revolve(
|
||||
axis = 'y',
|
||||
axis = Y,
|
||||
angle = -90,
|
||||
)
|
||||
"#;
|
||||
@ -1146,7 +1146,7 @@ async fn kcl_test_basic_revolve_circle() {
|
||||
|> circle(center = [15, 0], radius= 5)
|
||||
|> revolve(
|
||||
angle = 360,
|
||||
axis = 'y'
|
||||
axis = Y
|
||||
)
|
||||
"#;
|
||||
|
||||
@ -1166,7 +1166,7 @@ async fn kcl_test_simple_revolve_sketch_on_edge() {
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = 'y', angle = 180)
|
||||
|> revolve(axis = Y, angle = 180)
|
||||
|
||||
part002 = startSketchOn(part001, 'end')
|
||||
|> startProfileAt([4.5, -5], %)
|
||||
|
@ -23,8 +23,9 @@ use crate::{
|
||||
const TYPES_DIR: &str = "../../docs/kcl/types";
|
||||
const LANG_TOPICS: [&str; 5] = ["Types", "Modules", "Settings", "Known Issues", "Constants"];
|
||||
// These types are declared in std.
|
||||
const DECLARED_TYPES: [&str; 11] = [
|
||||
"number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Point2d", "Point3d",
|
||||
const DECLARED_TYPES: [&str; 14] = [
|
||||
"number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Edge", "Point2d", "Point3d",
|
||||
"Axis2d", "Axis3d",
|
||||
];
|
||||
|
||||
fn init_handlebars() -> Result<handlebars::Handlebars<'static>> {
|
||||
@ -339,9 +340,9 @@ fn generate_index(combined: &IndexMap<String, Box<dyn StdLibFn>>, kcl_lib: &[Doc
|
||||
}
|
||||
|
||||
functions.entry(d.mod_name()).or_default().push(match d {
|
||||
DocData::Fn(f) => (f.name.clone(), d.file_name()),
|
||||
DocData::Const(c) => (c.name.clone(), d.file_name()),
|
||||
DocData::Ty(t) => (t.name.clone(), d.file_name()),
|
||||
DocData::Fn(f) => (f.preferred_name.clone(), d.file_name()),
|
||||
DocData::Const(c) => (c.preferred_name.clone(), d.file_name()),
|
||||
DocData::Ty(t) => (t.preferred_name.clone(), d.file_name()),
|
||||
});
|
||||
|
||||
if let DocData::Const(c) = d {
|
||||
|
@ -9,7 +9,7 @@ use tower_lsp::lsp_types::{
|
||||
use crate::{
|
||||
execution::annotations,
|
||||
parsing::{
|
||||
ast::types::{Annotation, Node, PrimitiveType, Type, VariableKind},
|
||||
ast::types::{Annotation, ImportSelector, Node, PrimitiveType, Type, VariableKind},
|
||||
token::NumericSuffix,
|
||||
},
|
||||
ModuleId,
|
||||
@ -17,7 +17,7 @@ use crate::{
|
||||
|
||||
pub fn walk_prelude() -> Vec<DocData> {
|
||||
let mut visitor = CollectionVisitor::default();
|
||||
visitor.visit_module("prelude").unwrap();
|
||||
visitor.visit_module("prelude", "").unwrap();
|
||||
visitor.result
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ struct CollectionVisitor {
|
||||
}
|
||||
|
||||
impl CollectionVisitor {
|
||||
fn visit_module(&mut self, name: &str) -> Result<(), String> {
|
||||
fn visit_module(&mut self, name: &str, preferred_prefix: &str) -> Result<(), String> {
|
||||
let old_name = std::mem::replace(&mut self.name, name.to_owned());
|
||||
let source = crate::modules::read_std(name).unwrap();
|
||||
let parsed = crate::parsing::parse_str(source, ModuleId::from_usize(self.id))
|
||||
@ -40,14 +40,16 @@ impl CollectionVisitor {
|
||||
for n in &parsed.body {
|
||||
match n {
|
||||
crate::parsing::ast::types::BodyItem::ImportStatement(import) if !import.visibility.is_default() => {
|
||||
// Only supports glob imports for now.
|
||||
assert!(matches!(
|
||||
import.selector,
|
||||
crate::parsing::ast::types::ImportSelector::Glob(..)
|
||||
));
|
||||
match &import.path {
|
||||
crate::parsing::ast::types::ImportPath::Std { path } => {
|
||||
self.visit_module(&path[1])?;
|
||||
match import.selector {
|
||||
ImportSelector::Glob(..) => self.visit_module(&path[1], "")?,
|
||||
ImportSelector::None { .. } => {
|
||||
self.visit_module(&path[1], &format!("{}::", import.module_name().unwrap()))?
|
||||
}
|
||||
// Only supports glob or whole-module imports for now.
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
p => return Err(format!("Unexpected import: `{p}`")),
|
||||
}
|
||||
@ -59,8 +61,8 @@ impl CollectionVisitor {
|
||||
format!("std::{}::", self.name)
|
||||
};
|
||||
let mut dd = match var.kind {
|
||||
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name)),
|
||||
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name)),
|
||||
VariableKind::Fn => DocData::Fn(FnData::from_ast(var, qual_name, preferred_prefix)),
|
||||
VariableKind::Const => DocData::Const(ConstData::from_ast(var, qual_name, preferred_prefix)),
|
||||
};
|
||||
|
||||
dd.with_meta(&var.outer_attrs);
|
||||
@ -77,7 +79,7 @@ impl CollectionVisitor {
|
||||
} else {
|
||||
format!("std::{}::", self.name)
|
||||
};
|
||||
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name));
|
||||
let mut dd = DocData::Ty(TyData::from_ast(ty, qual_name, preferred_prefix));
|
||||
|
||||
dd.with_meta(&ty.outer_attrs);
|
||||
for a in &ty.outer_attrs {
|
||||
@ -200,6 +202,8 @@ impl DocData {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ConstData {
|
||||
pub name: String,
|
||||
/// How the const is indexed, etc.
|
||||
pub preferred_name: String,
|
||||
/// The fully qualified name.
|
||||
pub qual_name: String,
|
||||
pub value: Option<String>,
|
||||
@ -216,7 +220,11 @@ pub struct ConstData {
|
||||
}
|
||||
|
||||
impl ConstData {
|
||||
fn from_ast(var: &crate::parsing::ast::types::VariableDeclaration, mut qual_name: String) -> Self {
|
||||
fn from_ast(
|
||||
var: &crate::parsing::ast::types::VariableDeclaration,
|
||||
mut qual_name: String,
|
||||
preferred_prefix: &str,
|
||||
) -> Self {
|
||||
assert_eq!(var.kind, crate::parsing::ast::types::VariableKind::Const);
|
||||
|
||||
let (value, ty) = match &var.declaration.init {
|
||||
@ -240,6 +248,7 @@ impl ConstData {
|
||||
let name = var.declaration.id.name.clone();
|
||||
qual_name.push_str(&name);
|
||||
ConstData {
|
||||
preferred_name: format!("{preferred_prefix}{name}"),
|
||||
name,
|
||||
qual_name,
|
||||
value,
|
||||
@ -272,7 +281,7 @@ impl ConstData {
|
||||
detail.push_str(ty);
|
||||
}
|
||||
CompletionItem {
|
||||
label: self.name.clone(),
|
||||
label: self.preferred_name.clone(),
|
||||
label_details: Some(CompletionItemLabelDetails {
|
||||
detail: self.value.clone(),
|
||||
description: None,
|
||||
@ -306,6 +315,8 @@ impl ConstData {
|
||||
pub struct FnData {
|
||||
/// The name of the function.
|
||||
pub name: String,
|
||||
/// How the function is indexed, etc.
|
||||
pub preferred_name: String,
|
||||
/// The fully qualified name.
|
||||
pub qual_name: String,
|
||||
/// The args of the function.
|
||||
@ -326,7 +337,11 @@ pub struct FnData {
|
||||
}
|
||||
|
||||
impl FnData {
|
||||
fn from_ast(var: &crate::parsing::ast::types::VariableDeclaration, mut qual_name: String) -> Self {
|
||||
fn from_ast(
|
||||
var: &crate::parsing::ast::types::VariableDeclaration,
|
||||
mut qual_name: String,
|
||||
preferred_prefix: &str,
|
||||
) -> Self {
|
||||
assert_eq!(var.kind, crate::parsing::ast::types::VariableKind::Fn);
|
||||
let crate::parsing::ast::types::Expr::FunctionExpression(expr) = &var.declaration.init else {
|
||||
unreachable!();
|
||||
@ -345,6 +360,7 @@ impl FnData {
|
||||
}
|
||||
|
||||
FnData {
|
||||
preferred_name: format!("{preferred_prefix}{name}"),
|
||||
name,
|
||||
qual_name,
|
||||
args: expr.params.iter().map(ArgData::from_ast).collect(),
|
||||
@ -443,7 +459,7 @@ impl FnData {
|
||||
}
|
||||
// We end with ${} so you can jump to the end of the snippet.
|
||||
// After the last argument.
|
||||
format!("{}({})${{}}", self.name, args.join(", "))
|
||||
format!("{}({})${{}}", self.preferred_name, args.join(", "))
|
||||
}
|
||||
|
||||
fn to_signature_help(&self) -> SignatureHelp {
|
||||
@ -452,7 +468,7 @@ impl FnData {
|
||||
|
||||
SignatureHelp {
|
||||
signatures: vec![SignatureInformation {
|
||||
label: self.name.clone(),
|
||||
label: self.preferred_name.clone(),
|
||||
documentation: self.short_docs().map(|s| {
|
||||
Documentation::MarkupContent(MarkupContent {
|
||||
kind: MarkupKind::Markdown,
|
||||
@ -492,6 +508,7 @@ pub struct ArgData {
|
||||
pub ty: Option<String>,
|
||||
/// If the argument is required.
|
||||
pub kind: ArgKind,
|
||||
pub override_in_snippet: Option<bool>,
|
||||
/// Additional information that could be used instead of the type's description.
|
||||
/// This is helpful if the type is really basic, like "number" -- that won't tell the user much about
|
||||
/// how this argument is meant to be used.
|
||||
@ -512,6 +529,7 @@ impl ArgData {
|
||||
name: arg.identifier.name.clone(),
|
||||
ty: arg.type_.as_ref().map(|t| t.to_string()),
|
||||
docs: None,
|
||||
override_in_snippet: None,
|
||||
kind: if arg.labeled {
|
||||
ArgKind::Labelled(arg.optional())
|
||||
} else {
|
||||
@ -519,26 +537,54 @@ impl ArgData {
|
||||
},
|
||||
};
|
||||
|
||||
for attr in &arg.identifier.outer_attrs {
|
||||
if let Annotation {
|
||||
name: None,
|
||||
properties: Some(props),
|
||||
..
|
||||
} = &attr.inner
|
||||
{
|
||||
for p in props {
|
||||
match &*p.key.name {
|
||||
"include_in_snippet" => {
|
||||
if let Some(b) = p.value.literal_bool() {
|
||||
result.override_in_snippet = Some(b);
|
||||
} else {
|
||||
panic!(
|
||||
"Invalid value for `include_in_snippet`, expected bool literal, found {:?}",
|
||||
p.value
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.with_comments(&arg.identifier.pre_comments);
|
||||
result
|
||||
}
|
||||
|
||||
pub fn get_autocomplete_snippet(&self, index: usize) -> Option<(usize, String)> {
|
||||
match self.override_in_snippet {
|
||||
Some(false) => return None,
|
||||
None if !self.kind.required() => return None,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let label = if self.kind == ArgKind::Special {
|
||||
String::new()
|
||||
} else {
|
||||
format!("{} = ", self.name)
|
||||
};
|
||||
match self.ty.as_deref() {
|
||||
Some(s) if ["Sketch", "Solid", "Plane | Face", "Sketch | Plane | Face"].contains(&s) => {
|
||||
Some((index, format!("{label}${{{}:{}}}", index, "%")))
|
||||
}
|
||||
Some("number") if self.kind.required() => Some((index, format!(r#"{label}${{{}:3.14}}"#, index))),
|
||||
Some("Point2d") if self.kind.required() => Some((
|
||||
Some(s) if s.starts_with("number") => Some((index, format!(r#"{label}${{{}:3.14}}"#, index))),
|
||||
Some("Point2d") => Some((
|
||||
index + 1,
|
||||
format!(r#"{label}[${{{}:3.14}}, ${{{}:3.14}}]"#, index, index + 1),
|
||||
)),
|
||||
Some("Point3d") if self.kind.required() => Some((
|
||||
Some("Point3d") => Some((
|
||||
index + 2,
|
||||
format!(
|
||||
r#"{label}[${{{}:3.14}}, ${{{}:3.14}}, ${{{}:3.14}}]"#,
|
||||
@ -547,8 +593,10 @@ impl ArgData {
|
||||
index + 2
|
||||
),
|
||||
)),
|
||||
Some("string") if self.kind.required() => Some((index, format!(r#"{label}${{{}:"string"}}"#, index))),
|
||||
Some("bool") if self.kind.required() => Some((index, format!(r#"{label}${{{}:false}}"#, index))),
|
||||
Some("Axis2d | Edge") | Some("Axis3d | Edge") => Some((index, format!(r#"{label}${{{}:X}}"#, index))),
|
||||
|
||||
Some("string") => Some((index, format!(r#"{label}${{{}:"string"}}"#, index))),
|
||||
Some("bool") => Some((index, format!(r#"{label}${{{}:false}}"#, index))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -580,6 +628,8 @@ impl ArgKind {
|
||||
pub struct TyData {
|
||||
/// The name of the function.
|
||||
pub name: String,
|
||||
/// How the type is indexed, etc.
|
||||
pub preferred_name: String,
|
||||
/// The fully qualified name.
|
||||
pub qual_name: String,
|
||||
pub properties: Properties,
|
||||
@ -597,7 +647,11 @@ pub struct TyData {
|
||||
}
|
||||
|
||||
impl TyData {
|
||||
fn from_ast(ty: &crate::parsing::ast::types::TypeDeclaration, mut qual_name: String) -> Self {
|
||||
fn from_ast(
|
||||
ty: &crate::parsing::ast::types::TypeDeclaration,
|
||||
mut qual_name: String,
|
||||
preferred_prefix: &str,
|
||||
) -> Self {
|
||||
let name = ty.name.name.clone();
|
||||
qual_name.push_str(&name);
|
||||
let mut referenced_types = HashSet::new();
|
||||
@ -606,6 +660,7 @@ impl TyData {
|
||||
}
|
||||
|
||||
TyData {
|
||||
preferred_name: format!("{preferred_prefix}{name}"),
|
||||
name,
|
||||
qual_name,
|
||||
properties: Properties {
|
||||
@ -641,7 +696,7 @@ impl TyData {
|
||||
|
||||
fn to_completion_item(&self) -> CompletionItem {
|
||||
CompletionItem {
|
||||
label: self.name.clone(),
|
||||
label: self.preferred_name.clone(),
|
||||
label_details: self.alias.as_ref().map(|t| CompletionItemLabelDetails {
|
||||
detail: Some(format!("type {} = {t}", self.name)),
|
||||
description: None,
|
||||
@ -658,7 +713,7 @@ impl TyData {
|
||||
preselect: None,
|
||||
sort_text: None,
|
||||
filter_text: None,
|
||||
insert_text: Some(self.name.clone()),
|
||||
insert_text: Some(self.preferred_name.clone()),
|
||||
insert_text_format: Some(InsertTextFormat::SNIPPET),
|
||||
insert_text_mode: None,
|
||||
text_edit: None,
|
||||
|
@ -1000,9 +1000,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn get_autocomplete_snippet_revolve() {
|
||||
let revolve_fn: Box<dyn StdLibFn> = Box::new(crate::std::revolve::Revolve);
|
||||
let snippet = revolve_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(snippet, r#"revolve(${0:%}, axis = ${1:"X"})${}"#);
|
||||
let data = kcl_doc::walk_prelude();
|
||||
let DocData::Fn(revolve_fn) = data.into_iter().find(|d| d.name() == "revolve").unwrap() else {
|
||||
panic!();
|
||||
};
|
||||
let snippet = revolve_fn.to_autocomplete_snippet();
|
||||
assert_eq!(snippet, r#"revolve(axis = ${0:X})${}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1015,7 +1018,7 @@ mod tests {
|
||||
let snippet = circle_fn.to_autocomplete_snippet();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"circle(${0:%}, center = [${1:3.14}, ${2:3.14}], radius = ${3:3.14})${}"#
|
||||
r#"circle(center = [${0:3.14}, ${1:3.14}], radius = ${2:3.14})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
@ -1089,11 +1092,14 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(clippy::literal_string_with_formatting_args)]
|
||||
fn get_autocomplete_snippet_helix() {
|
||||
let helix_fn: Box<dyn StdLibFn> = Box::new(crate::std::helix::Helix);
|
||||
let snippet = helix_fn.to_autocomplete_snippet().unwrap();
|
||||
let data = kcl_doc::walk_prelude();
|
||||
let DocData::Fn(helix_fn) = data.into_iter().find(|d| d.name() == "helix").unwrap() else {
|
||||
panic!();
|
||||
};
|
||||
let snippet = helix_fn.to_autocomplete_snippet();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"helix(revolutions = ${0:3.14}, angleStart = ${1:3.14}, radius = ${2:3.14}, axis = ${3:"X"}, length = ${4:3.14})${}"#
|
||||
r#"helix(revolutions = ${0:3.14}, angleStart = ${1:3.14}, radius = ${2:3.14}, axis = ${3:X}, length = ${4:3.14})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
@ -1141,7 +1147,7 @@ mod tests {
|
||||
let snippet = scale_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"scale(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
|
||||
r#"scale(${0:%}, scale = [${1:3.14}, ${2:3.14}, ${3:3.14}])${}"#
|
||||
);
|
||||
}
|
||||
|
||||
@ -1152,7 +1158,7 @@ mod tests {
|
||||
let snippet = translate_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"translate(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
|
||||
r#"translate(${0:%}, translate = [${1:3.14}, ${2:3.14}, ${3:3.14}])${}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -121,13 +121,6 @@ impl EngineConnection {
|
||||
}
|
||||
})?;
|
||||
|
||||
if value.is_null() || value.is_undefined() {
|
||||
return Err(KclError::Engine(KclErrorDetails {
|
||||
message: "Received null or undefined response from engine".into(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
// Convert JsValue to a Uint8Array
|
||||
let data = js_sys::Uint8Array::from(value);
|
||||
|
||||
|
@ -27,6 +27,19 @@ pub enum Operation {
|
||||
is_error: bool,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
KclStdLibCall {
|
||||
name: String,
|
||||
/// The unlabeled argument to the function.
|
||||
unlabeled_arg: Option<OpArg>,
|
||||
/// The labeled keyword arguments to the function.
|
||||
labeled_args: IndexMap<String, OpArg>,
|
||||
/// The source range of the operation in the source code.
|
||||
source_range: SourceRange,
|
||||
/// True if the operation resulted in an error.
|
||||
#[serde(default, skip_serializing_if = "is_false")]
|
||||
is_error: bool,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
UserDefinedFunctionCall {
|
||||
/// The name of the user-defined function being called. Anonymous
|
||||
/// functions have no name.
|
||||
@ -49,6 +62,7 @@ impl Operation {
|
||||
pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
|
||||
match self {
|
||||
Self::StdLibCall { ref mut is_error, .. } => *is_error = is_err,
|
||||
Self::KclStdLibCall { ref mut is_error, .. } => *is_error = is_err,
|
||||
Self::UserDefinedFunctionCall { .. } | Self::UserDefinedFunctionReturn => {}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
||||
use async_recursion::async_recursion;
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use super::kcl_value::TypeDef;
|
||||
use super::{kcl_value::TypeDef, types::PrimitiveType};
|
||||
use crate::{
|
||||
engine::ExecutionKind,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
@ -1031,6 +1031,15 @@ impl Node<UnaryExpression> {
|
||||
}
|
||||
|
||||
let value = &self.argument.get_result(exec_state, ctx).await?;
|
||||
let err = || {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"You can only negate numbers, planes, or lines, but this is a {}",
|
||||
value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.into()],
|
||||
})
|
||||
};
|
||||
match value {
|
||||
KclValue::Number { value, ty, .. } => {
|
||||
let meta = vec![Metadata {
|
||||
@ -1052,13 +1061,63 @@ impl Node<UnaryExpression> {
|
||||
plane.id = exec_state.next_uuid();
|
||||
Ok(KclValue::Plane { value: plane })
|
||||
}
|
||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"You can only negate numbers or planes, but this is a {}",
|
||||
value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.into()],
|
||||
})),
|
||||
KclValue::Object { value: values, meta } => {
|
||||
// Special-case for negating line-like objects.
|
||||
let Some(direction) = values.get("direction") else {
|
||||
return Err(err());
|
||||
};
|
||||
|
||||
let direction = match direction {
|
||||
KclValue::MixedArray { value: values, meta } => {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
|
||||
value: *value * -1.0,
|
||||
ty: ty.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
_ => Err(err()),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
KclValue::MixedArray {
|
||||
value: values,
|
||||
meta: meta.clone(),
|
||||
}
|
||||
}
|
||||
KclValue::HomArray {
|
||||
value: values,
|
||||
ty: ty @ RuntimeType::Primitive(PrimitiveType::Number(_)),
|
||||
} => {
|
||||
let values = values
|
||||
.iter()
|
||||
.map(|v| match v {
|
||||
KclValue::Number { value, ty, meta } => Ok(KclValue::Number {
|
||||
value: *value * -1.0,
|
||||
ty: ty.clone(),
|
||||
meta: meta.clone(),
|
||||
}),
|
||||
_ => Err(err()),
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
KclValue::HomArray {
|
||||
value: values,
|
||||
ty: ty.clone(),
|
||||
}
|
||||
}
|
||||
_ => return Err(err()),
|
||||
};
|
||||
|
||||
let mut value = values.clone();
|
||||
value.insert("direction".to_owned(), direction);
|
||||
Ok(KclValue::Object {
|
||||
value,
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
_ => Err(err()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2145,6 +2204,27 @@ impl FunctionSource {
|
||||
}
|
||||
}
|
||||
|
||||
let op = if props.include_in_feature_tree {
|
||||
let op_labeled_args = args
|
||||
.kw_args
|
||||
.labeled
|
||||
.iter()
|
||||
.map(|(k, arg)| (k.clone(), OpArg::new(OpKclValue::from(&arg.value), arg.source_range)))
|
||||
.collect();
|
||||
Some(Operation::KclStdLibCall {
|
||||
// TODO
|
||||
name: String::new(),
|
||||
unlabeled_arg: args
|
||||
.unlabeled_kw_arg_unconverted()
|
||||
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)),
|
||||
labeled_args: op_labeled_args,
|
||||
source_range: callsite,
|
||||
is_error: false,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Attempt to call the function.
|
||||
exec_state.mut_stack().push_new_env_for_rust_call();
|
||||
let mut result = {
|
||||
@ -2152,7 +2232,15 @@ impl FunctionSource {
|
||||
let result = func(exec_state, args).await;
|
||||
exec_state.mut_stack().pop_env();
|
||||
|
||||
// TODO support recording op into the feature tree
|
||||
if let Some(mut op) = op {
|
||||
op.set_std_lib_call_is_error(result.is_err());
|
||||
// Track call operation. We do this after the call
|
||||
// since things like patternTransform may call user code
|
||||
// before running, and we will likely want to use the
|
||||
// return value. The call takes ownership of the args,
|
||||
// so we need to build the op before the call.
|
||||
exec_state.global.operations.push(op);
|
||||
}
|
||||
result
|
||||
}?;
|
||||
|
||||
|
@ -201,35 +201,6 @@
|
||||
//! check safety, it is not ever used for any decision. In any case, modifying the env storage is
|
||||
//! must be safe if the env is in either state, so even if the transition happens at the same time
|
||||
//! as the storage modification, it is ok.
|
||||
//!
|
||||
//! Here is an ascii art diagram of the memory system:
|
||||
//!
|
||||
//! +----------------------------+
|
||||
//! | ProgramMemory |
|
||||
//! |----------------------------|
|
||||
//! | Envs: |
|
||||
//! | |
|
||||
//! | [ Env 1 ] |
|
||||
//! | - Vars: { "a" = 10 } |
|
||||
//! | - Epoch: 1 |
|
||||
//! | - Owner ID: 0 | <- read-only
|
||||
//! | |
|
||||
//! | [ Env 2 ] |
|
||||
//! | - Vars: { "b" = a } |
|
||||
//! | - Epoch: 2 |
|
||||
//! | - Owner ID: 42 | <- owned by Stack 42
|
||||
//! | |
|
||||
//! +-------------^--------------+
|
||||
//! |
|
||||
//! | shared, immutable OR
|
||||
//! | uniquely owned mutable reference
|
||||
//! +---------+---------+
|
||||
//! | Stack 42 |
|
||||
//! |-------------------|
|
||||
//! | stack_id: 42 |
|
||||
//! | env_stack: [2] |
|
||||
//! | epoch: 2 |
|
||||
//! +-------------------+
|
||||
|
||||
use std::{
|
||||
cell::UnsafeCell,
|
||||
|
@ -2044,8 +2044,6 @@ let w = f() + f()
|
||||
|
||||
// Ensure the settings are as expected.
|
||||
assert_eq!(settings_state, ctx.settings);
|
||||
|
||||
ctx.close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2059,9 +2057,6 @@ let w = f() + f()
|
||||
let program2 = crate::Program::parse_no_errs("z = x + 1").unwrap();
|
||||
let result = ctx2.run_mock(program2, true).await.unwrap();
|
||||
assert_eq!(result.variables.get("z").unwrap().as_f64().unwrap(), 3.0);
|
||||
|
||||
ctx.close().await;
|
||||
ctx2.close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
|
@ -56,6 +56,20 @@ impl RuntimeType {
|
||||
RuntimeType::Primitive(PrimitiveType::ImportedGeometry)
|
||||
}
|
||||
|
||||
/// `[number; 2]`
|
||||
pub fn point2d() -> Self {
|
||||
RuntimeType::Array(Box::new(RuntimeType::number_any()), ArrayLen::Known(2))
|
||||
}
|
||||
|
||||
/// `[number; 3]`
|
||||
pub fn point3d() -> Self {
|
||||
RuntimeType::Array(Box::new(RuntimeType::number_any()), ArrayLen::Known(3))
|
||||
}
|
||||
|
||||
pub fn number_any() -> Self {
|
||||
RuntimeType::Primitive(PrimitiveType::Number(NumericType::Any))
|
||||
}
|
||||
|
||||
pub fn from_parsed(
|
||||
value: Type,
|
||||
exec_state: &mut ExecState,
|
||||
@ -93,24 +107,30 @@ impl RuntimeType {
|
||||
AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
|
||||
NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
|
||||
)),
|
||||
AstPrimitiveType::Named(name) => {
|
||||
let ty_val = exec_state
|
||||
.stack()
|
||||
.get(&format!("{}{}", memory::TYPE_PREFIX, name.name), source_range)
|
||||
.map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", name.name)))?;
|
||||
|
||||
match ty_val {
|
||||
KclValue::Type { value, .. } => match value {
|
||||
TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()),
|
||||
TypeDef::Alias(ty) => ty.clone(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
|
||||
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn from_alias(
|
||||
alias: &str,
|
||||
exec_state: &mut ExecState,
|
||||
source_range: SourceRange,
|
||||
) -> Result<Self, CompilationError> {
|
||||
let ty_val = exec_state
|
||||
.stack()
|
||||
.get(&format!("{}{}", memory::TYPE_PREFIX, alias), source_range)
|
||||
.map_err(|_| CompilationError::err(source_range, format!("Unknown type: {}", alias)))?;
|
||||
|
||||
Ok(match ty_val {
|
||||
KclValue::Type { value, .. } => match value {
|
||||
TypeDef::RustRepr(ty, _) => RuntimeType::Primitive(ty.clone()),
|
||||
TypeDef::Alias(ty) => ty.clone(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn human_friendly_type(&self) -> String {
|
||||
match self {
|
||||
RuntimeType::Primitive(ty) => ty.to_string(),
|
||||
@ -143,6 +163,35 @@ impl RuntimeType {
|
||||
(Object(t1), Object(t2)) => t2
|
||||
.iter()
|
||||
.all(|(f, t)| t1.iter().any(|(ff, tt)| f == ff && tt.subtype(t))),
|
||||
// Equality between Axis types and there object representation.
|
||||
(Object(t1), Primitive(PrimitiveType::Axis2d)) => {
|
||||
t1.iter()
|
||||
.any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
|
||||
&& t1
|
||||
.iter()
|
||||
.any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
|
||||
}
|
||||
(Object(t1), Primitive(PrimitiveType::Axis3d)) => {
|
||||
t1.iter()
|
||||
.any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
|
||||
&& t1
|
||||
.iter()
|
||||
.any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
|
||||
}
|
||||
(Primitive(PrimitiveType::Axis2d), Object(t2)) => {
|
||||
t2.iter()
|
||||
.any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point2d()))
|
||||
&& t2
|
||||
.iter()
|
||||
.any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point2d()))
|
||||
}
|
||||
(Primitive(PrimitiveType::Axis3d), Object(t2)) => {
|
||||
t2.iter()
|
||||
.any(|(n, t)| n == "origin" && t.subtype(&RuntimeType::point3d()))
|
||||
&& t2
|
||||
.iter()
|
||||
.any(|(n, t)| n == "direction" && t.subtype(&RuntimeType::point3d()))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -213,11 +262,11 @@ impl ArrayLen {
|
||||
}
|
||||
|
||||
/// True if the length constraint is satisfied by the supplied length.
|
||||
fn satisfied(self, len: usize) -> bool {
|
||||
fn satisfied(self, len: usize, allow_shrink: bool) -> Option<usize> {
|
||||
match self {
|
||||
ArrayLen::None => true,
|
||||
ArrayLen::NonEmpty => len > 0,
|
||||
ArrayLen::Known(s) => len == s,
|
||||
ArrayLen::None => Some(len),
|
||||
ArrayLen::NonEmpty => (len > 0).then_some(len),
|
||||
ArrayLen::Known(s) => (if allow_shrink { len >= s } else { len == s }).then_some(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -233,6 +282,9 @@ pub enum PrimitiveType {
|
||||
Plane,
|
||||
Helix,
|
||||
Face,
|
||||
Edge,
|
||||
Axis2d,
|
||||
Axis3d,
|
||||
ImportedGeometry,
|
||||
}
|
||||
|
||||
@ -248,6 +300,9 @@ impl PrimitiveType {
|
||||
PrimitiveType::Plane => "Planes".to_owned(),
|
||||
PrimitiveType::Helix => "Helices".to_owned(),
|
||||
PrimitiveType::Face => "Faces".to_owned(),
|
||||
PrimitiveType::Edge => "Edges".to_owned(),
|
||||
PrimitiveType::Axis2d => "2d axes".to_owned(),
|
||||
PrimitiveType::Axis3d => "3d axes".to_owned(),
|
||||
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
|
||||
PrimitiveType::Tag => "tags".to_owned(),
|
||||
}
|
||||
@ -273,6 +328,9 @@ impl fmt::Display for PrimitiveType {
|
||||
PrimitiveType::Solid => write!(f, "Solid"),
|
||||
PrimitiveType::Plane => write!(f, "Plane"),
|
||||
PrimitiveType::Face => write!(f, "Face"),
|
||||
PrimitiveType::Edge => write!(f, "Edge"),
|
||||
PrimitiveType::Axis2d => write!(f, "Axis2d"),
|
||||
PrimitiveType::Axis3d => write!(f, "Axis3d"),
|
||||
PrimitiveType::Helix => write!(f, "Helix"),
|
||||
PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
|
||||
}
|
||||
@ -298,6 +356,10 @@ impl NumericType {
|
||||
NumericType::Known(UnitType::Count)
|
||||
}
|
||||
|
||||
pub fn mm() -> Self {
|
||||
NumericType::Known(UnitType::Length(UnitLen::Mm))
|
||||
}
|
||||
|
||||
/// Combine two types when we expect them to be equal.
|
||||
pub fn combine_eq(self, other: &NumericType) -> NumericType {
|
||||
if &self == other {
|
||||
@ -541,7 +603,7 @@ impl KclValue {
|
||||
pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
match ty {
|
||||
RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
|
||||
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state),
|
||||
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state, false),
|
||||
RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
|
||||
RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
|
||||
RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
|
||||
@ -609,6 +671,55 @@ impl KclValue {
|
||||
KclValue::Helix { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
},
|
||||
PrimitiveType::Edge => match value {
|
||||
KclValue::Uuid { .. } => Some(value.clone()),
|
||||
KclValue::TagIdentifier { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
},
|
||||
PrimitiveType::Axis2d => match value {
|
||||
KclValue::Object { value: values, meta } => {
|
||||
if values.get("origin")?.has_type(&RuntimeType::point2d())
|
||||
&& values.get("direction")?.has_type(&RuntimeType::point2d())
|
||||
{
|
||||
return Some(value.clone());
|
||||
}
|
||||
|
||||
let origin = values.get("origin").and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(2), exec_state, true)
|
||||
})?;
|
||||
|
||||
Some(KclValue::Object {
|
||||
value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
PrimitiveType::Axis3d => match value {
|
||||
KclValue::Object { value: values, meta } => {
|
||||
if values.get("origin")?.has_type(&RuntimeType::point3d())
|
||||
&& values.get("direction")?.has_type(&RuntimeType::point3d())
|
||||
{
|
||||
return Some(value.clone());
|
||||
}
|
||||
|
||||
let origin = values.get("origin").and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
let direction = values.get("direction").and_then(|p| {
|
||||
p.coerce_to_array_type(&RuntimeType::number_any(), ArrayLen::Known(3), exec_state, true)
|
||||
})?;
|
||||
|
||||
Some(KclValue::Object {
|
||||
value: [("origin".to_owned(), origin), ("direction".to_owned(), direction)].into(),
|
||||
meta: meta.clone(),
|
||||
})
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
PrimitiveType::ImportedGeometry => match value {
|
||||
KclValue::ImportedGeometry { .. } => Some(value.clone()),
|
||||
_ => None,
|
||||
@ -621,60 +732,35 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
fn coerce_to_array_type(&self, ty: &RuntimeType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||
fn coerce_to_array_type(
|
||||
&self,
|
||||
ty: &RuntimeType,
|
||||
len: ArrayLen,
|
||||
exec_state: &mut ExecState,
|
||||
allow_shrink: bool,
|
||||
) -> Option<KclValue> {
|
||||
match self {
|
||||
KclValue::HomArray { value, ty: aty } if aty == ty => {
|
||||
let value = match len {
|
||||
ArrayLen::None => value.clone(),
|
||||
ArrayLen::NonEmpty => {
|
||||
if value.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
value.clone()
|
||||
}
|
||||
ArrayLen::Known(n) => {
|
||||
if n != value.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
value[..n].to_vec()
|
||||
}
|
||||
};
|
||||
|
||||
Some(KclValue::HomArray { value, ty: ty.clone() })
|
||||
KclValue::HomArray { value, ty: aty } if aty.subtype(ty) => {
|
||||
len.satisfied(value.len(), allow_shrink).map(|len| KclValue::HomArray {
|
||||
value: value[..len].to_vec(),
|
||||
ty: aty.clone(),
|
||||
})
|
||||
}
|
||||
value if len.satisfied(1) && value.has_type(ty) => Some(KclValue::HomArray {
|
||||
value if len.satisfied(1, false).is_some() && value.has_type(ty) => Some(KclValue::HomArray {
|
||||
value: vec![value.clone()],
|
||||
ty: ty.clone(),
|
||||
}),
|
||||
KclValue::MixedArray { value, .. } => {
|
||||
let value = match len {
|
||||
ArrayLen::None => value.clone(),
|
||||
ArrayLen::NonEmpty => {
|
||||
if value.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let len = len.satisfied(value.len(), allow_shrink)?;
|
||||
|
||||
value.clone()
|
||||
}
|
||||
ArrayLen::Known(n) => {
|
||||
if n != value.len() {
|
||||
return None;
|
||||
}
|
||||
|
||||
value[..n].to_vec()
|
||||
}
|
||||
};
|
||||
|
||||
let value = value
|
||||
let value = value[..len]
|
||||
.iter()
|
||||
.map(|v| v.coerce(ty, exec_state))
|
||||
.collect::<Option<Vec<_>>>()?;
|
||||
|
||||
Some(KclValue::HomArray { value, ty: ty.clone() })
|
||||
}
|
||||
KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
|
||||
KclValue::KclNone { .. } if len.satisfied(0, false).is_some() => Some(KclValue::HomArray {
|
||||
value: Vec::new(),
|
||||
ty: ty.clone(),
|
||||
}),
|
||||
@ -1251,4 +1337,119 @@ mod test {
|
||||
assert!(count.coerce(&tyb, &mut exec_state).is_none());
|
||||
assert!(count.coerce(&tyb2, &mut exec_state).is_none());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn coerce_axes() {
|
||||
let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock().await);
|
||||
|
||||
// Subtyping
|
||||
assert!(RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
|
||||
assert!(RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
|
||||
assert!(!RuntimeType::Primitive(PrimitiveType::Axis3d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis2d)));
|
||||
assert!(!RuntimeType::Primitive(PrimitiveType::Axis2d).subtype(&RuntimeType::Primitive(PrimitiveType::Axis3d)));
|
||||
|
||||
// Coercion
|
||||
let a2d = KclValue::Object {
|
||||
value: [
|
||||
(
|
||||
"origin".to_owned(),
|
||||
KclValue::HomArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
|
||||
},
|
||||
),
|
||||
(
|
||||
"direction".to_owned(),
|
||||
KclValue::HomArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into(),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
let a3d = KclValue::Object {
|
||||
value: [
|
||||
(
|
||||
"origin".to_owned(),
|
||||
KclValue::HomArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
|
||||
},
|
||||
),
|
||||
(
|
||||
"direction".to_owned(),
|
||||
KclValue::HomArray {
|
||||
value: vec![
|
||||
KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 0.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
KclValue::Number {
|
||||
value: 1.0,
|
||||
ty: NumericType::mm(),
|
||||
meta: Vec::new(),
|
||||
},
|
||||
],
|
||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::mm())),
|
||||
},
|
||||
),
|
||||
]
|
||||
.into(),
|
||||
meta: Vec::new(),
|
||||
};
|
||||
|
||||
let ty2d = RuntimeType::Primitive(PrimitiveType::Axis2d);
|
||||
let ty3d = RuntimeType::Primitive(PrimitiveType::Axis3d);
|
||||
|
||||
assert_coerce_results(&a2d, &ty2d, &a2d, &mut exec_state);
|
||||
assert_coerce_results(&a3d, &ty3d, &a3d, &mut exec_state);
|
||||
assert_coerce_results(&a3d, &ty2d, &a2d, &mut exec_state);
|
||||
assert!(a2d.coerce(&ty3d, &mut exec_state).is_none());
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +120,7 @@ const Part001 = startSketchOn('XY')
|
||||
|> line([0, pipeLength], %)
|
||||
|> angledLineToX({ angle: 60, to: pipeLargeDia }, %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
"
|
||||
);
|
||||
|
||||
@ -156,7 +156,7 @@ const part001 = startSketchOn('XY')
|
||||
|> line([0, pipeLength], %)
|
||||
|> angledLineToX({ angle: 60, to: pipeLargeDia }, %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
"
|
||||
);
|
||||
|
||||
|
@ -1632,7 +1632,7 @@ insideRevolve = startSketchOn(XZ)
|
||||
|> line(end = [0, -thickness])
|
||||
|> line(end = [-overHangLength, 0])
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
|
||||
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
|
||||
sphere = startSketchOn(XZ)
|
||||
@ -1647,7 +1647,7 @@ sphere = startSketchOn(XZ)
|
||||
radius: sphereDia / 2 - 0.05
|
||||
}, %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'x' }, %)
|
||||
|> revolve({ axis = X }, %)
|
||||
|> patternCircular3d(
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
@ -1671,7 +1671,7 @@ outsideRevolve = startSketchOn(XZ)
|
||||
|> line(end = [0, thickness])
|
||||
|> line(end = [overHangLength - thickness, 0])
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)"#
|
||||
|> revolve({ axis = Y }, %)"#
|
||||
.to_string(),
|
||||
},
|
||||
})
|
||||
@ -1705,7 +1705,7 @@ outsideRevolve = startSketchOn(XZ)
|
||||
start: tower_lsp::lsp_types::Position { line: 0, character: 0 },
|
||||
end: tower_lsp::lsp_types::Position {
|
||||
line: 60,
|
||||
character: 30
|
||||
character: 29
|
||||
}
|
||||
}
|
||||
);
|
||||
@ -1732,7 +1732,7 @@ insideRevolve = startSketchOn(XZ)
|
||||
|> line(end = [0, -thickness])
|
||||
|> line(end = [-overHangLength, 0])
|
||||
|> close()
|
||||
|> revolve({ axis = 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
|
||||
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
|
||||
sphere = startSketchOn(XZ)
|
||||
@ -1747,7 +1747,7 @@ sphere = startSketchOn(XZ)
|
||||
radius = sphereDia / 2 - 0.05
|
||||
}, %)
|
||||
|> close()
|
||||
|> revolve({ axis = 'x' }, %)
|
||||
|> revolve({ axis = X }, %)
|
||||
|> patternCircular3d(
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
@ -1771,7 +1771,7 @@ outsideRevolve = startSketchOn(XZ)
|
||||
|> line(end = [0, thickness])
|
||||
|> line(end = [overHangLength - thickness, 0])
|
||||
|> close()
|
||||
|> revolve({ axis = 'y' }, %)"#
|
||||
|> revolve({ axis = Y }, %)"#
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -88,6 +88,7 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
|
||||
"prelude" => Some(include_str!("../std/prelude.kcl")),
|
||||
"math" => Some(include_str!("../std/math.kcl")),
|
||||
"sketch" => Some(include_str!("../std/sketch.kcl")),
|
||||
"turns" => Some(include_str!("../std/turns.kcl")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -133,13 +133,6 @@ impl<T> Node<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn reset_source(&mut self) {
|
||||
self.start = 0;
|
||||
self.end = 0;
|
||||
self.module_id = ModuleId::default();
|
||||
self.comment_start = 0;
|
||||
}
|
||||
|
||||
pub fn as_source_range(&self) -> SourceRange {
|
||||
SourceRange::new(self.start, self.end, self.module_id)
|
||||
}
|
||||
@ -352,10 +345,7 @@ impl Node<Program> {
|
||||
let mut found = false;
|
||||
for node in &mut new_program.inner_attrs {
|
||||
if node.name() == Some(annotations::SETTINGS) {
|
||||
node.inner = Annotation::new_from_meta_settings(&settings);
|
||||
// Previous source range no longer makes sense, but we want to
|
||||
// preserve other things like comments.
|
||||
node.reset_source();
|
||||
*node = Node::no_src(Annotation::new_from_meta_settings(&settings));
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -1619,19 +1609,21 @@ impl ImportStatement {
|
||||
return Some(alias.name.clone());
|
||||
}
|
||||
|
||||
let mut parts = match &self.path {
|
||||
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => s.split('.'),
|
||||
_ => return None,
|
||||
};
|
||||
let path = parts.next()?;
|
||||
let _ext = parts.next()?;
|
||||
let rest = parts.next();
|
||||
match &self.path {
|
||||
ImportPath::Kcl { filename: s } | ImportPath::Foreign { path: s } => {
|
||||
let mut parts = s.split('.');
|
||||
let path = parts.next()?;
|
||||
let _ext = parts.next()?;
|
||||
let rest = parts.next();
|
||||
|
||||
if rest.is_some() {
|
||||
return None;
|
||||
if rest.is_some() {
|
||||
return None;
|
||||
}
|
||||
|
||||
path.rsplit(&['/', '\\']).next().map(str::to_owned)
|
||||
}
|
||||
ImportPath::Std { path } => path.last().cloned(),
|
||||
}
|
||||
|
||||
path.rsplit(&['/', '\\']).next().map(str::to_owned)
|
||||
}
|
||||
}
|
||||
|
||||
@ -4150,50 +4142,6 @@ startSketchOn(XY)
|
||||
r#"@settings(defaultLengthUnit = mm)
|
||||
|
||||
startSketchOn(XY)
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_change_meta_settings_preserves_comments() {
|
||||
let code = r#"// Title
|
||||
|
||||
// Set Units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
// Between
|
||||
|
||||
// Above Code
|
||||
5
|
||||
"#;
|
||||
let program = crate::parsing::top_level_parse(code).unwrap();
|
||||
|
||||
let new_program = program
|
||||
.change_meta_settings(crate::execution::MetaSettings {
|
||||
default_length_units: crate::execution::types::UnitLen::Cm,
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let result = new_program.meta_settings().unwrap();
|
||||
assert!(result.is_some());
|
||||
let meta_settings = result.unwrap();
|
||||
|
||||
assert_eq!(meta_settings.default_length_units, crate::execution::types::UnitLen::Cm);
|
||||
|
||||
let formatted = new_program.recast(&Default::default(), 0);
|
||||
|
||||
assert_eq!(
|
||||
formatted,
|
||||
r#"// Title
|
||||
|
||||
// Set Units
|
||||
@settings(defaultLengthUnit = cm)
|
||||
|
||||
// Between
|
||||
|
||||
// Above Code
|
||||
5
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
@ -152,7 +152,12 @@ const STR_DEPRECATIONS: [(&str, &str); 6] = [
|
||||
("-YZ", "-YZ"),
|
||||
];
|
||||
const FN_DEPRECATIONS: [(&str, &str); 3] = [("pi", "PI"), ("e", "E"), ("tau", "TAU")];
|
||||
const CONST_DEPRECATIONS: [(&str, &str); 0] = [];
|
||||
const CONST_DEPRECATIONS: [(&str, &str); 4] = [
|
||||
("ZERO", "turns::ZERO"),
|
||||
("QUARTER_TURN", "turns::QUARTER_TURN"),
|
||||
("HALF_TURN", "turns::HALF_TURN"),
|
||||
("THREE_QUARTER_TURN", "turns::THREE_QUARTER_TURN"),
|
||||
];
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DeprecationKind {
|
||||
|
@ -286,6 +286,11 @@ fn non_code_node(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
alt((non_code_node_leading_whitespace, non_code_node_no_leading_whitespace)).parse_next(i)
|
||||
}
|
||||
|
||||
fn outer_annotation(i: &mut TokenSlice) -> PResult<Node<Annotation>> {
|
||||
peek((at_sign, open_paren)).parse_next(i)?;
|
||||
annotation(i)
|
||||
}
|
||||
|
||||
fn annotation(i: &mut TokenSlice) -> PResult<Node<Annotation>> {
|
||||
let at = at_sign.parse_next(i)?;
|
||||
let name = opt(binding_name).parse_next(i)?;
|
||||
@ -1823,14 +1828,6 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
} else if matches!(path, ImportPath::Std { .. }) && matches!(selector, ImportSelector::None { .. }) {
|
||||
return Err(ErrMode::Cut(
|
||||
CompilationError::fatal(
|
||||
SourceRange::new(start, end, module_id),
|
||||
"the standard library cannot be imported as a part",
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Node::boxed(
|
||||
@ -2910,13 +2907,17 @@ struct ParamDescription {
|
||||
arg_name: Token,
|
||||
type_: std::option::Option<Node<Type>>,
|
||||
default_value: Option<DefaultParamVal>,
|
||||
attr: Option<Node<Annotation>>,
|
||||
comments: Option<Node<Vec<String>>>,
|
||||
}
|
||||
|
||||
fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
|
||||
let (_, comments, found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = (
|
||||
let (_, comments, _, attr, _, found_at_sign, arg_name, question_mark, _, type_, _ws, default_literal) = (
|
||||
opt(whitespace),
|
||||
opt(comments),
|
||||
opt(whitespace),
|
||||
opt(outer_annotation),
|
||||
opt(whitespace),
|
||||
opt(at_sign),
|
||||
any.verify(|token: &Token| !matches!(token.token_type, TokenType::Brace) || token.value != ")"),
|
||||
opt(question_mark),
|
||||
@ -2941,6 +2942,7 @@ fn parameter(i: &mut TokenSlice) -> PResult<ParamDescription> {
|
||||
return Err(ErrMode::Backtrack(ContextError::from(e)));
|
||||
}
|
||||
},
|
||||
attr,
|
||||
comments,
|
||||
})
|
||||
}
|
||||
@ -2962,6 +2964,7 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
|
||||
arg_name,
|
||||
type_,
|
||||
default_value,
|
||||
attr,
|
||||
comments,
|
||||
}| {
|
||||
let mut identifier = Node::<Identifier>::try_from(arg_name)?;
|
||||
@ -2969,6 +2972,9 @@ fn parameters(i: &mut TokenSlice) -> PResult<Vec<Parameter>> {
|
||||
identifier.comment_start = comments.start;
|
||||
identifier.pre_comments = comments.inner;
|
||||
}
|
||||
if let Some(attr) = attr {
|
||||
identifier.outer_attrs.push(attr);
|
||||
}
|
||||
|
||||
Ok(Parameter {
|
||||
identifier,
|
||||
|
@ -2379,66 +2379,3 @@ mod rotate_after_fillet {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod union_cubes {
|
||||
const TEST_NAME: &str = "union_cubes";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod subtract_cylinder_from_cube {
|
||||
const TEST_NAME: &str = "subtract_cylinder_from_cube";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod intersect_cubes {
|
||||
const TEST_NAME: &str = "intersect_cubes";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn unparse() {
|
||||
super::unparse(TEST_NAME).await
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// This will work on any solid, including extruded solids, revolved solids, and shelled solids.
|
||||
/// ```no_run
|
||||
/// // Add color to an extruded solid.
|
||||
/// exampleSketch = startSketchOn("XZ")
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(endAbsolute = [10, 0])
|
||||
/// |> line(endAbsolute = [0, 10])
|
||||
@ -89,9 +89,9 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Add color to a revolved solid.
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// |> circle( center = [15, 0], radius = 5 )
|
||||
/// |> revolve( angle = 360, axis = 'y')
|
||||
/// |> revolve( angle = 360, axis = Y)
|
||||
/// |> appearance(
|
||||
/// color = '#ff0000',
|
||||
/// metalness = 90,
|
||||
@ -102,7 +102,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// ```no_run
|
||||
/// // Add color to different solids.
|
||||
/// fn cube(center) {
|
||||
/// return startSketchOn('XY')
|
||||
/// return startSketchOn(XY)
|
||||
/// |> startProfileAt([center[0] - 10, center[1] - 10], %)
|
||||
/// |> line(endAbsolute = [center[0] + 10, center[1] - 10])
|
||||
/// |> line(endAbsolute = [center[0] + 10, center[1] + 10])
|
||||
@ -122,7 +122,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// ```no_run
|
||||
/// // You can set the appearance before or after you shell it will yield the same result.
|
||||
/// // This example shows setting the appearance _after_ the shell.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -145,7 +145,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// ```no_run
|
||||
/// // You can set the appearance before or after you shell it will yield the same result.
|
||||
/// // This example shows setting the appearance _before_ the shell.
|
||||
/// firstSketch = startSketchOn('XY')
|
||||
/// firstSketch = startSketchOn(XY)
|
||||
/// |> startProfileAt([-12, 12], %)
|
||||
/// |> line(end = [24, 0])
|
||||
/// |> line(end = [0, -24])
|
||||
@ -168,7 +168,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// ```no_run
|
||||
/// // Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
|
||||
/// // This example shows _before_ the pattern.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 2])
|
||||
/// |> line(end = [3, 1])
|
||||
@ -191,7 +191,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// ```no_run
|
||||
/// // Setting the appearance of a 3D pattern can be done _before_ or _after_ the pattern.
|
||||
/// // This example shows _after_ the pattern.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 2])
|
||||
/// |> line(end = [3, 1])
|
||||
@ -213,7 +213,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Color the result of a 2D pattern that was extruded.
|
||||
/// exampleSketch = startSketchOn('XZ')
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfileAt([.5, 25], %)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [-1, 0])
|
||||
@ -238,7 +238,7 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// // Color the result of a sweep.
|
||||
///
|
||||
/// // Create a path for the sweep.
|
||||
/// sweepPath = startSketchOn('XZ')
|
||||
/// sweepPath = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0.05, 0.05], %)
|
||||
/// |> line(end = [0, 7])
|
||||
/// |> tangentialArc({
|
||||
@ -252,13 +252,13 @@ pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclVal
|
||||
/// }, %)
|
||||
/// |> line(end = [0, 7])
|
||||
///
|
||||
/// pipeHole = startSketchOn('XY')
|
||||
/// pipeHole = startSketchOn(XY)
|
||||
/// |> circle(
|
||||
/// center = [0, 0],
|
||||
/// radius = 1.5,
|
||||
/// )
|
||||
///
|
||||
/// sweepSketch = startSketchOn('XY')
|
||||
/// sweepSketch = startSketchOn(XY)
|
||||
/// |> circle(
|
||||
/// center = [0, 0],
|
||||
/// radius = 2,
|
||||
|
@ -154,6 +154,22 @@ impl Args {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn get_kw_arg_opt_typed<T>(
|
||||
&self,
|
||||
label: &str,
|
||||
ty: &RuntimeType,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<Option<T>, KclError>
|
||||
where
|
||||
T: for<'a> FromKclValue<'a>,
|
||||
{
|
||||
if self.kw_args.labeled.get(label).is_none() {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
self.get_kw_arg_typed(label, ty, exec_state).map(Some)
|
||||
}
|
||||
|
||||
/// Get a keyword argument. If not set, returns Err.
|
||||
pub(crate) fn get_kw_arg<'a, T>(&'a self, label: &str) -> Result<T, KclError>
|
||||
where
|
||||
@ -675,37 +691,6 @@ impl Args {
|
||||
FromArgs::from_args(self, 0)
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketches<'a, T>(
|
||||
&'a self,
|
||||
exec_state: &mut ExecState,
|
||||
) -> Result<(T, Vec<Sketch>), KclError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned + FromArgs<'a>,
|
||||
{
|
||||
let data: T = FromArgs::from_args(self, 0)?;
|
||||
let Some(arg1) = self.args.get(1) else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Expected one or more sketches for second argument".to_owned(),
|
||||
source_ranges: vec![self.source_range],
|
||||
}));
|
||||
};
|
||||
let sarg = arg1
|
||||
.value
|
||||
.coerce(&RuntimeType::sketches(), exec_state)
|
||||
.ok_or(KclError::Type(KclErrorDetails {
|
||||
message: format!(
|
||||
"Expected one or more sketches for second argument, found {}",
|
||||
arg1.value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.source_range],
|
||||
}))?;
|
||||
let sketches = match sarg {
|
||||
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
Ok((data, sketches))
|
||||
}
|
||||
|
||||
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(
|
||||
&'a self,
|
||||
exec_state: &mut ExecState,
|
||||
@ -1583,50 +1568,6 @@ impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin2d {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
// Case 1: predefined planes.
|
||||
if let Some(s) = arg.as_str() {
|
||||
return match s {
|
||||
"X" | "x" => Some(Self::X),
|
||||
"Y" | "y" => Some(Self::Y),
|
||||
"-X" | "-x" => Some(Self::NegX),
|
||||
"-Y" | "-y" => Some(Self::NegY),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// Case 2: custom planes.
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, custom, &KclObjectFields);
|
||||
let_field_of!(custom, origin);
|
||||
let_field_of!(custom, axis);
|
||||
Some(Self::Custom { axis, origin })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::axis_or_reference::AxisAndOrigin3d {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
// Case 1: predefined planes.
|
||||
if let Some(s) = arg.as_str() {
|
||||
return match s {
|
||||
"X" | "x" => Some(Self::X),
|
||||
"Y" | "y" => Some(Self::Y),
|
||||
"Z" | "z" => Some(Self::Z),
|
||||
"-X" | "-x" => Some(Self::NegX),
|
||||
"-Y" | "-y" => Some(Self::NegY),
|
||||
"-Z" | "-z" => Some(Self::NegZ),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
// Case 2: custom planes.
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, custom, &KclObjectFields);
|
||||
let_field_of!(custom, origin);
|
||||
let_field_of!(custom, axis);
|
||||
Some(Self::Custom { axis, origin })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let id = arg.as_uuid().map(Self::Uuid);
|
||||
@ -1637,25 +1578,27 @@ impl<'a> FromKclValue<'a> for super::fillet::EdgeReference {
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis2dOrEdgeReference {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let case1 = super::axis_or_reference::AxisAndOrigin2d::from_kcl_val;
|
||||
let case1 = |arg: &KclValue| {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, direction);
|
||||
let_field_of!(obj, origin);
|
||||
Some(Self::Axis { direction, origin })
|
||||
};
|
||||
let case2 = super::fillet::EdgeReference::from_kcl_val;
|
||||
case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
|
||||
case1(arg).or_else(|| case2(arg).map(Self::Edge))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::axis_or_reference::Axis3dOrEdgeReference {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let case1 = super::axis_or_reference::AxisAndOrigin3d::from_kcl_val;
|
||||
let case1 = |arg: &KclValue| {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, direction);
|
||||
let_field_of!(obj, origin);
|
||||
Some(Self::Axis { direction, origin })
|
||||
};
|
||||
let case2 = super::fillet::EdgeReference::from_kcl_val;
|
||||
case1(arg).map(Self::Axis).or_else(|| case2(arg).map(Self::Edge))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for super::mirror::Mirror2dData {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let obj = arg.as_object()?;
|
||||
let_field_of!(obj, axis);
|
||||
Some(Self { axis })
|
||||
case1(arg).or_else(|| case2(arg).map(Self::Edge))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,233 +1,21 @@
|
||||
//! Types for referencing an axis or edge.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcmc::length_unit::LengthUnit;
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{errors::KclError, std::fillet::EdgeReference};
|
||||
use crate::std::fillet::EdgeReference;
|
||||
|
||||
/// A 2D axis or tagged edge.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Axis2dOrEdgeReference {
|
||||
/// 2D axis and origin.
|
||||
Axis(AxisAndOrigin2d),
|
||||
Axis { direction: [f64; 2], origin: [f64; 2] },
|
||||
/// Tagged edge.
|
||||
Edge(EdgeReference),
|
||||
}
|
||||
|
||||
/// A 2D axis and origin.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AxisAndOrigin2d {
|
||||
/// X-axis.
|
||||
#[serde(rename = "X", alias = "x")]
|
||||
X,
|
||||
/// Y-axis.
|
||||
#[serde(rename = "Y", alias = "y")]
|
||||
Y,
|
||||
/// Flip the X-axis.
|
||||
#[serde(rename = "-X", alias = "-x")]
|
||||
NegX,
|
||||
/// Flip the Y-axis.
|
||||
#[serde(rename = "-Y", alias = "-y")]
|
||||
NegY,
|
||||
Custom {
|
||||
/// The axis.
|
||||
axis: [f64; 2],
|
||||
/// The origin.
|
||||
origin: [f64; 2],
|
||||
},
|
||||
}
|
||||
|
||||
impl AxisAndOrigin2d {
|
||||
/// Get the axis and origin.
|
||||
pub fn axis_and_origin(&self) -> Result<(kcmc::shared::Point3d<f64>, kcmc::shared::Point3d<LengthUnit>), KclError> {
|
||||
let (axis, origin) = match self {
|
||||
AxisAndOrigin2d::X => ([1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin2d::Y => ([0.0, 1.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin2d::NegX => ([-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin2d::NegY => ([0.0, -1.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin2d::Custom { axis, origin } => ([axis[0], axis[1], 0.0], [origin[0], origin[1], 0.0]),
|
||||
};
|
||||
|
||||
Ok((
|
||||
kcmc::shared::Point3d {
|
||||
x: axis[0],
|
||||
y: axis[1],
|
||||
z: axis[2],
|
||||
},
|
||||
kcmc::shared::Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(origin[2]),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
/// A 3D axis or tagged edge.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(untagged)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Axis3dOrEdgeReference {
|
||||
/// 3D axis and origin.
|
||||
Axis(AxisAndOrigin3d),
|
||||
Axis { direction: [f64; 3], origin: [f64; 3] },
|
||||
/// Tagged edge.
|
||||
Edge(EdgeReference),
|
||||
}
|
||||
|
||||
/// A 3D axis and origin.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum AxisAndOrigin3d {
|
||||
/// X-axis.
|
||||
#[serde(rename = "X", alias = "x")]
|
||||
X,
|
||||
/// Y-axis.
|
||||
#[serde(rename = "Y", alias = "y")]
|
||||
Y,
|
||||
/// Z-axis.
|
||||
#[serde(rename = "Z", alias = "z")]
|
||||
Z,
|
||||
/// Flip the X-axis.
|
||||
#[serde(rename = "-X", alias = "-x")]
|
||||
NegX,
|
||||
/// Flip the Y-axis.
|
||||
#[serde(rename = "-Y", alias = "-y")]
|
||||
NegY,
|
||||
/// Flip the Z-axis.
|
||||
#[serde(rename = "-Z", alias = "-z")]
|
||||
NegZ,
|
||||
Custom {
|
||||
/// The axis.
|
||||
axis: [f64; 3],
|
||||
/// The origin.
|
||||
origin: [f64; 3],
|
||||
},
|
||||
}
|
||||
|
||||
impl AxisAndOrigin3d {
|
||||
/// Get the axis and origin.
|
||||
pub fn axis_and_origin(&self) -> Result<(kcmc::shared::Point3d<f64>, kcmc::shared::Point3d<LengthUnit>), KclError> {
|
||||
let (axis, origin) = match self {
|
||||
AxisAndOrigin3d::X => ([1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::Y => ([0.0, 1.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::Z => ([0.0, 0.0, 1.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::NegX => ([-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::NegY => ([0.0, -1.0, 0.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::NegZ => ([0.0, 0.0, -1.0], [0.0, 0.0, 0.0]),
|
||||
AxisAndOrigin3d::Custom { axis, origin } => {
|
||||
([axis[0], axis[1], axis[2]], [origin[0], origin[1], origin[2]])
|
||||
}
|
||||
};
|
||||
|
||||
Ok((
|
||||
kcmc::shared::Point3d {
|
||||
x: axis[0],
|
||||
y: axis[1],
|
||||
z: axis[2],
|
||||
},
|
||||
kcmc::shared::Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(origin[2]),
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::std::axis_or_reference::{
|
||||
Axis2dOrEdgeReference, Axis3dOrEdgeReference, AxisAndOrigin2d, AxisAndOrigin3d,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_revolve_axis_2d() {
|
||||
let data = Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::X);
|
||||
let mut str_json = serde_json::to_string(&data).unwrap();
|
||||
assert_eq!(str_json, "\"X\"");
|
||||
|
||||
str_json = "\"Y\"".to_string();
|
||||
let data: Axis2dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::Y));
|
||||
|
||||
str_json = "\"-Y\"".to_string();
|
||||
let data: Axis2dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::NegY));
|
||||
|
||||
str_json = "\"-x\"".to_string();
|
||||
let data: Axis2dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::NegX));
|
||||
|
||||
let data = Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::Custom {
|
||||
axis: [0.0, -1.0],
|
||||
origin: [1.0, 0.0],
|
||||
});
|
||||
str_json = serde_json::to_string(&data).unwrap();
|
||||
assert_eq!(str_json, r#"{"custom":{"axis":[0.0,-1.0],"origin":[1.0,0.0]}}"#);
|
||||
|
||||
str_json = r#"{"custom": {"axis": [0,-1], "origin": [1,2.0]}}"#.to_string();
|
||||
let data: Axis2dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(
|
||||
data,
|
||||
Axis2dOrEdgeReference::Axis(AxisAndOrigin2d::Custom {
|
||||
axis: [0.0, -1.0],
|
||||
origin: [1.0, 2.0]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_revolve_axis_3d() {
|
||||
let data = Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::X);
|
||||
let mut str_json = serde_json::to_string(&data).unwrap();
|
||||
assert_eq!(str_json, "\"X\"");
|
||||
|
||||
str_json = "\"Y\"".to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::Y));
|
||||
|
||||
str_json = "\"Z\"".to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::Z));
|
||||
|
||||
str_json = "\"-Y\"".to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::NegY));
|
||||
|
||||
str_json = "\"-x\"".to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::NegX));
|
||||
|
||||
str_json = "\"-z\"".to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::NegZ));
|
||||
|
||||
let data = Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::Custom {
|
||||
axis: [0.0, -1.0, 0.0],
|
||||
origin: [1.0, 0.0, 0.0],
|
||||
});
|
||||
str_json = serde_json::to_string(&data).unwrap();
|
||||
assert_eq!(str_json, r#"{"custom":{"axis":[0.0,-1.0,0.0],"origin":[1.0,0.0,0.0]}}"#);
|
||||
|
||||
str_json = r#"{"custom": {"axis": [0,-1,0], "origin": [1,2.0,0]}}"#.to_string();
|
||||
let data: Axis3dOrEdgeReference = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(
|
||||
data,
|
||||
Axis3dOrEdgeReference::Axis(AxisAndOrigin3d::Custom {
|
||||
axis: [0.0, -1.0, 0.0],
|
||||
origin: [1.0, 2.0, 0.0]
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "union",
|
||||
feature_tree_operation = true,
|
||||
feature_tree_operation = false,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
deprecated = true,
|
||||
@ -107,7 +107,7 @@ pub async fn intersect(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "intersect",
|
||||
feature_tree_operation = true,
|
||||
feature_tree_operation = false,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
deprecated = true,
|
||||
@ -164,7 +164,7 @@ pub async fn subtract(exec_state: &mut ExecState, args: Args) -> Result<KclValue
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "subtract",
|
||||
feature_tree_operation = true,
|
||||
feature_tree_operation = false,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
deprecated = true,
|
||||
|
@ -1,13 +1,15 @@
|
||||
//! Standard library helices.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, Helix as HelixValue, KclValue, Solid},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
ExecState, Helix as HelixValue, KclValue, Solid,
|
||||
},
|
||||
std::{axis_or_reference::Axis3dOrEdgeReference, Args},
|
||||
};
|
||||
|
||||
@ -17,7 +19,14 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
let revolutions = args.get_kw_arg("revolutions")?;
|
||||
let ccw = args.get_kw_arg_opt("ccw")?;
|
||||
let radius = args.get_kw_arg_opt("radius")?;
|
||||
let axis = args.get_kw_arg_opt("axis")?;
|
||||
let axis: Option<Axis3dOrEdgeReference> = args.get_kw_arg_opt_typed(
|
||||
"axis",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Primitive(PrimitiveType::Edge),
|
||||
RuntimeType::Primitive(PrimitiveType::Axis3d),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let length = args.get_kw_arg_opt("length")?;
|
||||
let cylinder = args.get_kw_arg_opt("cylinder")?;
|
||||
|
||||
@ -84,100 +93,6 @@ pub async fn helix(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
Ok(KclValue::Helix { value })
|
||||
}
|
||||
|
||||
/// Create a helix.
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Create a helix around the Z axis.
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = 'Z',
|
||||
/// )
|
||||
///
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('YZ')
|
||||
/// |> circle( center = [0, 0], radius = 0.5)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Create a helix around an edge.
|
||||
/// helper001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 10], tag = $edge001)
|
||||
///
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = edge001,
|
||||
/// )
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('XY')
|
||||
/// |> circle( center = [0, 0], radius = 0.5 )
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Create a helix around a custom axis.
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = {
|
||||
/// custom = {
|
||||
/// axis = [0, 0, 1.0],
|
||||
/// origin = [0, 0.25, 0]
|
||||
/// }
|
||||
/// }
|
||||
/// )
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('XY')
|
||||
/// |> circle( center = [0, 0], radius = 1 )
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Create a helix on a cylinder.
|
||||
///
|
||||
/// part001 = startSketchOn('XY')
|
||||
/// |> circle( center= [5, 5], radius= 10 )
|
||||
/// |> extrude(length = 10)
|
||||
///
|
||||
/// helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 16,
|
||||
/// cylinder = part001,
|
||||
/// )
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "helix",
|
||||
keywords = true,
|
||||
unlabeled_first = false,
|
||||
args = {
|
||||
revolutions = { docs = "Number of revolutions."},
|
||||
angle_start = { docs = "Start angle (in degrees)."},
|
||||
ccw = { docs = "Is the helix rotation counter clockwise? The default is `false`.", include_in_snippet = false},
|
||||
radius = { docs = "Radius of the helix.", include_in_snippet = true},
|
||||
axis = { docs = "Axis to use for the helix.", include_in_snippet = true},
|
||||
length = { docs = "Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used.", include_in_snippet = true},
|
||||
cylinder = { docs = "Cylinder to create the helix on.", include_in_snippet = false},
|
||||
},
|
||||
feature_tree_operation = true,
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_helix(
|
||||
revolutions: f64,
|
||||
@ -221,9 +136,7 @@ async fn inner_helix(
|
||||
.await?;
|
||||
} else if let (Some(axis), Some(radius)) = (axis, radius) {
|
||||
match axis {
|
||||
Axis3dOrEdgeReference::Axis(axis) => {
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
|
||||
Axis3dOrEdgeReference::Axis { direction, origin } => {
|
||||
// Make sure they gave us a length.
|
||||
let Some(length) = length else {
|
||||
return Err(KclError::Semantic(crate::errors::KclErrorDetails {
|
||||
@ -240,8 +153,16 @@ async fn inner_helix(
|
||||
length: LengthUnit(length),
|
||||
revolutions,
|
||||
start_angle: Angle::from_degrees(angle_start),
|
||||
axis,
|
||||
center: origin,
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
z: direction[2],
|
||||
},
|
||||
center: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(origin[2]),
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
@ -1,109 +1,39 @@
|
||||
//! Standard library mirror.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use kittycad_modeling_cmds::{self as kcmc, length_unit::LengthUnit, shared::Point3d};
|
||||
|
||||
use crate::{
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue, Sketch},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
ExecState, KclValue, Sketch,
|
||||
},
|
||||
std::{axis_or_reference::Axis2dOrEdgeReference, Args},
|
||||
};
|
||||
|
||||
/// Data for a mirror.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Mirror2dData {
|
||||
/// Axis to use as mirror.
|
||||
pub axis: Axis2dOrEdgeReference,
|
||||
}
|
||||
|
||||
/// Mirror a sketch.
|
||||
///
|
||||
/// Only works on unclosed sketches for now.
|
||||
pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let (data, sketch_set): (Mirror2dData, Vec<Sketch>) = args.get_data_and_sketches(exec_state)?;
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let axis = args.get_kw_arg_typed(
|
||||
"axis",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Primitive(PrimitiveType::Edge),
|
||||
RuntimeType::Primitive(PrimitiveType::Axis2d),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
|
||||
let sketches = inner_mirror_2d(data, sketch_set, exec_state, args).await?;
|
||||
let sketches = inner_mirror_2d(sketches, axis, exec_state, args).await?;
|
||||
Ok(sketches.into())
|
||||
}
|
||||
|
||||
/// Mirror a sketch.
|
||||
///
|
||||
/// Only works on unclosed sketches for now.
|
||||
///
|
||||
/// Mirror occurs around a local sketch axis rather than a global axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Mirror an un-closed sketch across the Y axis.
|
||||
/// sketch001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 10], %)
|
||||
/// |> line(end = [15, 0])
|
||||
/// |> line(end = [-7, -3])
|
||||
/// |> line(end = [9, -1])
|
||||
/// |> line(end = [-8, -5])
|
||||
/// |> line(end = [9, -3])
|
||||
/// |> line(end = [-8, -3])
|
||||
/// |> line(end = [9, -1])
|
||||
/// |> line(end = [-19, -0])
|
||||
/// |> mirror2d({axis = 'Y'}, %)
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Mirror a un-closed sketch across the Y axis.
|
||||
/// sketch001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d({axis = 'Y'}, %)
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Mirror a un-closed sketch across an edge.
|
||||
/// helper001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 10], tag = $edge001)
|
||||
///
|
||||
/// sketch001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d({axis = edge001}, %)
|
||||
///
|
||||
/// // example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Mirror an un-closed sketch across a custom axis.
|
||||
/// sketch001 = startSketchOn('XZ')
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d({
|
||||
/// axis = {
|
||||
/// custom = {
|
||||
/// axis = [0.0, 1.0],
|
||||
/// origin = [0.0, 0.0]
|
||||
/// }
|
||||
/// }
|
||||
/// }, %)
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "mirror2d",
|
||||
}]
|
||||
async fn inner_mirror_2d(
|
||||
data: Mirror2dData,
|
||||
sketches: Vec<Sketch>,
|
||||
axis: Axis2dOrEdgeReference,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Vec<Sketch>, KclError> {
|
||||
@ -113,16 +43,22 @@ async fn inner_mirror_2d(
|
||||
return Ok(starting_sketches);
|
||||
}
|
||||
|
||||
match data.axis {
|
||||
Axis2dOrEdgeReference::Axis(axis) => {
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
|
||||
match axis {
|
||||
Axis2dOrEdgeReference::Axis { direction, origin } => {
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EntityMirror {
|
||||
ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
|
||||
axis,
|
||||
point: origin,
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
z: 0.0,
|
||||
},
|
||||
point: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
@ -26,7 +26,6 @@ pub mod shell;
|
||||
pub mod sketch;
|
||||
pub mod sweep;
|
||||
pub mod transform;
|
||||
pub mod types;
|
||||
pub mod units;
|
||||
pub mod utils;
|
||||
|
||||
@ -96,7 +95,6 @@ lazy_static! {
|
||||
Box::new(crate::std::sketch::TangentialArcToRelative),
|
||||
Box::new(crate::std::sketch::BezierCurve),
|
||||
Box::new(crate::std::sketch::Hole),
|
||||
Box::new(crate::std::mirror::Mirror2D),
|
||||
Box::new(crate::std::patterns::PatternLinear2D),
|
||||
Box::new(crate::std::patterns::PatternLinear3D),
|
||||
Box::new(crate::std::patterns::PatternCircular2D),
|
||||
@ -113,10 +111,8 @@ lazy_static! {
|
||||
Box::new(crate::std::edge::GetNextAdjacentEdge),
|
||||
Box::new(crate::std::edge::GetPreviousAdjacentEdge),
|
||||
Box::new(crate::std::edge::GetCommonEdge),
|
||||
Box::new(crate::std::helix::Helix),
|
||||
Box::new(crate::std::shell::Shell),
|
||||
Box::new(crate::std::shell::Hollow),
|
||||
Box::new(crate::std::revolve::Revolve),
|
||||
Box::new(crate::std::sweep::Sweep),
|
||||
Box::new(crate::std::loft::Loft),
|
||||
Box::new(crate::std::planes::OffsetPlane),
|
||||
@ -177,6 +173,7 @@ pub fn get_stdlib_fn(name: &str) -> Option<Box<dyn StdLibFn>> {
|
||||
pub struct StdFnProps {
|
||||
pub name: String,
|
||||
pub deprecated: bool,
|
||||
pub include_in_feature_tree: bool,
|
||||
}
|
||||
|
||||
impl StdFnProps {
|
||||
@ -184,8 +181,14 @@ impl StdFnProps {
|
||||
Self {
|
||||
name: name.to_owned(),
|
||||
deprecated: false,
|
||||
include_in_feature_tree: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn include_in_feature_tree(mut self) -> Self {
|
||||
self.include_in_feature_tree = true;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProps) {
|
||||
@ -206,6 +209,18 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
||||
|e, a| Box::pin(crate::std::shapes::circle(e, a)),
|
||||
StdFnProps::default("std::sketch::circle"),
|
||||
),
|
||||
("prelude", "helix") => (
|
||||
|e, a| Box::pin(crate::std::helix::helix(e, a)),
|
||||
StdFnProps::default("std::helix").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "mirror2d") => (
|
||||
|e, a| Box::pin(crate::std::mirror::mirror_2d(e, a)),
|
||||
StdFnProps::default("std::sketch::mirror2d"),
|
||||
),
|
||||
("prelude", "revolve") => (
|
||||
|e, a| Box::pin(crate::std::revolve::revolve(e, a)),
|
||||
StdFnProps::default("std::revolve").include_in_feature_tree(),
|
||||
),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
@ -217,6 +232,9 @@ pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
|
||||
("prelude", "Plane") => (PrimitiveType::Plane, StdFnProps::default("std::Plane")),
|
||||
("prelude", "Face") => (PrimitiveType::Face, StdFnProps::default("std::Face")),
|
||||
("prelude", "Helix") => (PrimitiveType::Helix, StdFnProps::default("std::Helix")),
|
||||
("prelude", "Edge") => (PrimitiveType::Edge, StdFnProps::default("std::Edge")),
|
||||
("prelude", "Axis2d") => (PrimitiveType::Axis2d, StdFnProps::default("std::Axis2d")),
|
||||
("prelude", "Axis3d") => (PrimitiveType::Axis3d, StdFnProps::default("std::Axis3d")),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,15 @@
|
||||
//! Standard library revolution surfaces.
|
||||
|
||||
use anyhow::Result;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, shared::Angle, ModelingCmd};
|
||||
use kittycad_modeling_cmds::{self as kcmc};
|
||||
use kittycad_modeling_cmds::{self as kcmc, shared::Point3d};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{types::RuntimeType, ExecState, KclValue, Sketch, Solid},
|
||||
execution::{
|
||||
types::{PrimitiveType, RuntimeType},
|
||||
ExecState, KclValue, Sketch, Solid,
|
||||
},
|
||||
parsing::ast::types::TagNode,
|
||||
std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||
};
|
||||
@ -15,7 +17,14 @@ use crate::{
|
||||
/// Revolve a sketch or set of sketches around an axis.
|
||||
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketches = args.get_unlabeled_kw_arg_typed("sketches", &RuntimeType::sketches(), exec_state)?;
|
||||
let axis: Axis2dOrEdgeReference = args.get_kw_arg("axis")?;
|
||||
let axis = args.get_kw_arg_typed(
|
||||
"axis",
|
||||
&RuntimeType::Union(vec![
|
||||
RuntimeType::Primitive(PrimitiveType::Edge),
|
||||
RuntimeType::Primitive(PrimitiveType::Axis2d),
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let angle = args.get_kw_arg_opt("angle")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tag_start = args.get_kw_arg_opt("tagStart")?;
|
||||
@ -25,215 +34,6 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
Ok(value.into())
|
||||
}
|
||||
|
||||
/// Rotate a sketch around some provided axis, creating a solid from its extent.
|
||||
///
|
||||
/// This, like extrude, is able to create a 3-dimensional solid from a
|
||||
/// 2-dimensional sketch. However, unlike extrude, this creates a solid
|
||||
/// by using the extent of the sketch as its revolved around an axis rather
|
||||
/// than using the extent of the sketch linearly translated through a third
|
||||
/// dimension.
|
||||
///
|
||||
/// Revolve occurs around a local sketch axis rather than a global axis.
|
||||
///
|
||||
/// You can provide more than one sketch to revolve, and they will all be
|
||||
/// revolved around the same axis.
|
||||
///
|
||||
/// ```no_run
|
||||
/// part001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = 'y') // default angle is 360
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // A donut shape.
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// |> circle( center = [15, 0], radius = 5 )
|
||||
/// |> revolve(
|
||||
/// angle = 360,
|
||||
/// axis = 'y'
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// part001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = 'y', angle = 180)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// part001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = 'y', angle = 180)
|
||||
///
|
||||
/// part002 = startSketchOn(part001, 'end')
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [0, -5])
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// box = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20])
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = -90,
|
||||
/// axis = 'y'
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// box = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20], tag = $revolveAxis)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = 90,
|
||||
/// axis = getOppositeEdge(revolveAxis)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// box = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20], tag = $revolveAxis)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = 90,
|
||||
/// axis = getOppositeEdge(revolveAxis),
|
||||
/// tolerance = 0.0001
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([10, 0], %)
|
||||
/// |> line(end = [5, -5])
|
||||
/// |> line(end = [5, 5])
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// part001 = revolve(
|
||||
/// sketch001,
|
||||
/// axis = {
|
||||
/// custom: {
|
||||
/// axis = [0.0, 1.0],
|
||||
/// origin: [0.0, 0.0]
|
||||
/// }
|
||||
/// }
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Revolve two sketches around the same axis.
|
||||
///
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// profile001 = startProfileAt([4, 8], sketch001)
|
||||
/// |> xLine(length = 3)
|
||||
/// |> yLine(length = -3)
|
||||
/// |> xLine(length = -3)
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// profile002 = startProfileAt([-5, 8], sketch001)
|
||||
/// |> xLine(length = 3)
|
||||
/// |> yLine(length = -3)
|
||||
/// |> xLine(length = -3)
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// revolve(
|
||||
/// [profile001, profile002],
|
||||
/// axis = "X",
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Revolve around a path that has not been extruded.
|
||||
///
|
||||
/// profile001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20], tag = $revolveAxis)
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20])
|
||||
/// |> close(%)
|
||||
///
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// |> circle(center = [-10, 10], radius = 4)
|
||||
/// |> revolve(angle = 90, axis = revolveAxis)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Revolve around a path that has not been extruded or closed.
|
||||
///
|
||||
/// profile001 = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20], tag = $revolveAxis)
|
||||
/// |> line(end = [20, 0])
|
||||
///
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// |> circle(center = [-10, 10], radius = 4)
|
||||
/// |> revolve(angle = 90, axis = revolveAxis)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "revolve",
|
||||
feature_tree_operation = true,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketches = { docs = "The sketch or set of sketches that should be revolved" },
|
||||
axis = { docs = "Axis of revolution." },
|
||||
angle = { docs = "Angle to revolve (in degrees). Default is 360." },
|
||||
tolerance = { docs = "Tolerance for the revolve operation." },
|
||||
tag_start = { docs = "A named tag for the face at the start of the revolve, i.e. the original sketch" },
|
||||
tag_end = { docs = "A named tag for the face at the end of the revolve" },
|
||||
}
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_revolve(
|
||||
sketches: Vec<Sketch>,
|
||||
@ -264,15 +64,22 @@ async fn inner_revolve(
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
match &axis {
|
||||
Axis2dOrEdgeReference::Axis(axis) => {
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
Axis2dOrEdgeReference::Axis { direction, origin } => {
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Revolve {
|
||||
angle,
|
||||
target: sketch.id.into(),
|
||||
axis,
|
||||
origin,
|
||||
axis: Point3d {
|
||||
x: direction[0],
|
||||
y: direction[1],
|
||||
z: 0.0,
|
||||
},
|
||||
origin: Point3d {
|
||||
x: LengthUnit(origin[0]),
|
||||
y: LengthUnit(origin[1]),
|
||||
z: LengthUnit(0.0),
|
||||
},
|
||||
tolerance: LengthUnit(tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units))),
|
||||
axis_is_2d: true,
|
||||
}),
|
||||
|
@ -1021,7 +1021,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = revolve(exampleSketch, axis = 'y', angle = 180)
|
||||
/// example = revolve(exampleSketch, axis = Y, angle = 180)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, 'end')
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
@ -1047,7 +1047,7 @@ pub async fn start_sketch_on(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
///
|
||||
/// example = revolve(exampleSketch, axis = 'y', angle = 180, tagEnd = $end01)
|
||||
/// example = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
|
||||
///
|
||||
/// exampleSketch002 = startSketchOn(example, end01)
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
|
@ -54,7 +54,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// // Create a pipe using a sweep.
|
||||
///
|
||||
/// // Create a path for the sweep.
|
||||
/// sweepPath = startSketchOn('XZ')
|
||||
/// sweepPath = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0.05, 0.05], %)
|
||||
/// |> line(end = [0, 7])
|
||||
/// |> tangentialArc({
|
||||
@ -69,13 +69,13 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> line(end = [0, 7])
|
||||
///
|
||||
/// // Create a hole for the pipe.
|
||||
/// pipeHole = startSketchOn('XY')
|
||||
/// pipeHole = startSketchOn(XY)
|
||||
/// |> circle(
|
||||
/// center = [0, 0],
|
||||
/// radius = 1.5,
|
||||
/// )
|
||||
///
|
||||
/// sweepSketch = startSketchOn('XY')
|
||||
/// sweepSketch = startSketchOn(XY)
|
||||
/// |> circle(
|
||||
/// center = [0, 0],
|
||||
/// radius = 2,
|
||||
@ -94,12 +94,12 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// revolutions = 4,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = 'Z',
|
||||
/// axis = Z,
|
||||
/// )
|
||||
///
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn('YZ')
|
||||
/// springSketch = startSketchOn(YZ)
|
||||
/// |> circle( center = [0, 0], radius = 1)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
@ -107,7 +107,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```
|
||||
/// // Sweep two sketches along the same path.
|
||||
///
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// rectangleSketch = startProfileAt([-200, 23.86], sketch001)
|
||||
/// |> angledLine([0, 73.47], %, $rectangleSegmentA001)
|
||||
/// |> angledLine([
|
||||
@ -123,7 +123,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
///
|
||||
/// sketch002 = startSketchOn('YZ')
|
||||
/// sketch002 = startSketchOn(YZ)
|
||||
/// sweepPath = startProfileAt([0, 0], sketch002)
|
||||
/// |> yLine(length = 231.81)
|
||||
/// |> tangentialArc({
|
||||
@ -137,7 +137,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```
|
||||
/// // Sectionally sweep one sketch along the path
|
||||
///
|
||||
/// sketch001 = startSketchOn('XY')
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
///
|
||||
/// sketch002 = startSketchOn('YZ')
|
||||
|
@ -28,22 +28,15 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let scale_x = args.get_kw_arg("x")?;
|
||||
let scale_y = args.get_kw_arg("y")?;
|
||||
let scale_z = args.get_kw_arg("z")?;
|
||||
let scale = args.get_kw_arg("scale")?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let objects = inner_scale(objects, scale_x, scale_y, scale_z, global, exec_state, args).await?;
|
||||
let objects = inner_scale(objects, scale, global, exec_state, args).await?;
|
||||
Ok(objects.into())
|
||||
}
|
||||
|
||||
/// Scale a solid or a sketch.
|
||||
///
|
||||
/// This is really useful for resizing parts. You can create a part and then scale it to the
|
||||
/// correct size.
|
||||
///
|
||||
/// For sketches, you can use this to scale a sketch and then loft it with another sketch.
|
||||
///
|
||||
/// By default the transform is applied in local sketch axis, therefore the origin will not move.
|
||||
///
|
||||
/// If you want to apply the transform in global space, set `global` to `true`. The origin of the
|
||||
@ -85,9 +78,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// |> scale(
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// scale = [1.0, 1.0, 2.5],
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -98,9 +89,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// cube
|
||||
/// |> scale(
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// scale = [1.0, 1.0, 2.5],
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -135,7 +124,7 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
///
|
||||
/// // Scale the sweep.
|
||||
/// scale(parts, x = 1.0, y = 1.0, z = 0.5)
|
||||
/// scale(parts, scale = [1.0, 1.0, 0.5])
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "scale",
|
||||
@ -144,17 +133,13 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
objects = {docs = "The solid, sketch, or set of solids or sketches to scale."},
|
||||
x = {docs = "The scale factor for the x axis."},
|
||||
y = {docs = "The scale factor for the y axis."},
|
||||
z = {docs = "The scale factor for the z axis."},
|
||||
scale = {docs = "The scale factor for the x, y, and z axes."},
|
||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||
}
|
||||
}]
|
||||
async fn inner_scale(
|
||||
objects: SolidOrSketchOrImportedGeometry,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
scale: [f64; 3],
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
@ -174,7 +159,11 @@ async fn inner_scale(
|
||||
object_id,
|
||||
transforms: vec![shared::ComponentTransform {
|
||||
scale: Some(shared::TransformBy::<Point3d<f64>> {
|
||||
property: Point3d { x, y, z },
|
||||
property: Point3d {
|
||||
x: scale[0],
|
||||
y: scale[1],
|
||||
z: scale[2],
|
||||
},
|
||||
set: false,
|
||||
is_local: !global.unwrap_or(false),
|
||||
}),
|
||||
@ -201,23 +190,15 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let translate_x = args.get_kw_arg("x")?;
|
||||
let translate_y = args.get_kw_arg("y")?;
|
||||
let translate_z = args.get_kw_arg("z")?;
|
||||
let translate = args.get_kw_arg("translate")?;
|
||||
let global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let objects = inner_translate(objects, translate_x, translate_y, translate_z, global, exec_state, args).await?;
|
||||
let objects = inner_translate(objects, translate, global, exec_state, args).await?;
|
||||
Ok(objects.into())
|
||||
}
|
||||
|
||||
/// Move a solid or a sketch.
|
||||
///
|
||||
/// This is really useful for assembling parts together. You can create a part
|
||||
/// and then move it to the correct location.
|
||||
///
|
||||
/// Translate is really useful for sketches if you want to move a sketch
|
||||
/// and then rotate it using the `rotate` function to create a loft.
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Move a pipe.
|
||||
///
|
||||
@ -251,9 +232,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// |> translate(
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// translate = [1.0, 1.0, 2.5],
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -264,9 +243,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
///
|
||||
/// cube
|
||||
/// |> translate(
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// translate = [1.0, 1.0, 2.5],
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -301,7 +278,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
///
|
||||
/// // Move the sweeps.
|
||||
/// translate(parts, x = 1.0, y = 1.0, z = 2.5)
|
||||
/// translate(parts, translate = [1.0, 1.0, 2.5])
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -324,9 +301,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
///
|
||||
/// square(10)
|
||||
/// |> translate(
|
||||
/// x = 5,
|
||||
/// y = 5,
|
||||
/// z = 0,
|
||||
/// translate = [5, 5, 0],
|
||||
/// )
|
||||
/// |> extrude(
|
||||
/// length = 10,
|
||||
@ -349,7 +324,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// profile001 = square()
|
||||
///
|
||||
/// profile002 = square()
|
||||
/// |> translate(x = 0, y = 0, z = 20)
|
||||
/// |> translate(translate = [0, 0, 20])
|
||||
/// |> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
///
|
||||
/// loft([profile001, profile002])
|
||||
@ -361,17 +336,13 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
objects = {docs = "The solid, sketch, or set of solids or sketches to move."},
|
||||
x = {docs = "The amount to move the solid or sketch along the x axis."},
|
||||
y = {docs = "The amount to move the solid or sketch along the y axis."},
|
||||
z = {docs = "The amount to move the solid or sketch along the z axis."},
|
||||
translate = {docs = "The amount to move the solid or sketch in all three axes."},
|
||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||
}
|
||||
}]
|
||||
async fn inner_translate(
|
||||
objects: SolidOrSketchOrImportedGeometry,
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
translate: [f64; 3],
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
@ -392,9 +363,9 @@ async fn inner_translate(
|
||||
transforms: vec![shared::ComponentTransform {
|
||||
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
|
||||
property: shared::Point3d {
|
||||
x: LengthUnit(x),
|
||||
y: LengthUnit(y),
|
||||
z: LengthUnit(z),
|
||||
x: LengthUnit(translate[0]),
|
||||
y: LengthUnit(translate[1]),
|
||||
z: LengthUnit(translate[2]),
|
||||
},
|
||||
set: false,
|
||||
is_local: !global.unwrap_or(false),
|
||||
@ -535,11 +506,6 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
|
||||
/// 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
|
||||
@ -701,7 +667,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// profile001 = square()
|
||||
///
|
||||
/// profile002 = square()
|
||||
/// |> translate(x = 0, y = 0, z = 20)
|
||||
/// |> translate(translate = [0, 0, 20])
|
||||
/// |> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
///
|
||||
/// loft([profile001, profile002])
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -1235,7 +1235,7 @@ insideRevolve = startSketchOn(XZ)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([-overHangLength, 0], %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
|
||||
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
|
||||
sphere = startSketchOn(XZ)
|
||||
@ -1250,7 +1250,7 @@ sphere = startSketchOn(XZ)
|
||||
radius = sphereDia / 2 - 0.05
|
||||
}, %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'x' }, %)
|
||||
|> revolve({ axis = X }, %)
|
||||
|> patternCircular3d(
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
@ -1274,7 +1274,7 @@ outsideRevolve = startSketchOn(XZ)
|
||||
|> line([0, thickness], %)
|
||||
|> line([overHangLength - thickness, 0], %)
|
||||
|> close()
|
||||
|> revolve({ axis: 'y' }, %)"#;
|
||||
|> revolve({ axis = Y }, %)"#;
|
||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
@ -1301,7 +1301,7 @@ insideRevolve = startSketchOn(XZ)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([-overHangLength, 0], %)
|
||||
|> close()
|
||||
|> revolve({ axis = 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
|
||||
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
|
||||
sphere = startSketchOn(XZ)
|
||||
@ -1316,7 +1316,7 @@ sphere = startSketchOn(XZ)
|
||||
radius = sphereDia / 2 - 0.05
|
||||
}, %)
|
||||
|> close()
|
||||
|> revolve({ axis = 'x' }, %)
|
||||
|> revolve({ axis = X }, %)
|
||||
|> patternCircular3d(
|
||||
axis = [0, 0, 1],
|
||||
center = [0, 0, 0],
|
||||
@ -1340,7 +1340,7 @@ outsideRevolve = startSketchOn(XZ)
|
||||
|> line([0, thickness], %)
|
||||
|> line([overHangLength - thickness, 0], %)
|
||||
|> close()
|
||||
|> revolve({ axis = 'y' }, %)
|
||||
|> revolve({ axis = Y }, %)
|
||||
"#
|
||||
);
|
||||
}
|
||||
@ -1613,9 +1613,9 @@ fn bracketSketch = (w, d, t) => {
|
||||
s = startSketchOn({
|
||||
plane: {
|
||||
origin: { x = 0, y = length / 2 + thk, z = 0 },
|
||||
x_axis: { x = 1, y = 0, z = 0 },
|
||||
y_axis: { x = 0, y = 0, z = 1 },
|
||||
z_axis: { x = 0, y = 1, z = 0 }
|
||||
x_axis = { x = 1, y = 0, z = 0 },
|
||||
y_axis = { x = 0, y = 0, z = 1 },
|
||||
z_axis = { x = 0, y = 1, z = 0 }
|
||||
}
|
||||
})
|
||||
|> startProfileAt([-w / 2 - t, d + t], %)
|
||||
@ -1645,9 +1645,9 @@ bracket_body = bracketSketch(width, depth, thk)
|
||||
tabs_r = startSketchOn({
|
||||
plane: {
|
||||
origin: { x = 0, y = 0, z = depth + thk },
|
||||
x_axis: { x = 1, y = 0, z = 0 },
|
||||
y_axis: { x = 0, y = 1, z = 0 },
|
||||
z_axis: { x = 0, y = 0, z = 1 }
|
||||
x_axis = { x = 1, y = 0, z = 0 },
|
||||
y_axis = { x = 0, y = 1, z = 0 },
|
||||
z_axis = { x = 0, y = 0, z = 1 }
|
||||
}
|
||||
})
|
||||
|> startProfileAt([width / 2 + thk, length / 2 + thk], %)
|
||||
@ -2528,9 +2528,9 @@ fn f() {
|
||||
sketch002 = startSketchOn({
|
||||
plane: {
|
||||
origin: { x = 1, y = 2, z = 3 },
|
||||
x_axis: { x = 4, y = 5, z = 6 },
|
||||
y_axis: { x = 7, y = 8, z = 9 },
|
||||
z_axis: { x = 10, y = 11, z = 12 }
|
||||
x_axis = { x = 4, y = 5, z = 6 },
|
||||
y_axis = { x = 7, y = 8, z = 9 },
|
||||
z_axis = { x = 10, y = 11, z = 12 }
|
||||
}
|
||||
})
|
||||
"#;
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
export import * from "std::math"
|
||||
export import * from "std::sketch"
|
||||
export import "std::turns"
|
||||
|
||||
/// A number
|
||||
///
|
||||
@ -233,6 +234,10 @@ export type Face
|
||||
@(impl = std_rust)
|
||||
export type Helix
|
||||
|
||||
/// The edge of a solid.
|
||||
@(impl = std_rust)
|
||||
export type Edge
|
||||
|
||||
/// A point in two dimensional space.
|
||||
///
|
||||
/// `Point2d` is an alias for a two-element array of [number](/docs/kcl/types/number)s. To write a value
|
||||
@ -245,11 +250,6 @@ export type Point2d = [number; 2]
|
||||
/// with type `Point3d`, use an array, e.g., `[0, 0, 0]` or `[5.0, 3.14, 6.8]`.
|
||||
export type Point3d = [number; 3]
|
||||
|
||||
export ZERO = 0
|
||||
export QUARTER_TURN = 90deg
|
||||
export HALF_TURN = 180deg
|
||||
export THREE_QUARTER_TURN = 270deg
|
||||
|
||||
export XY = {
|
||||
origin = { x = 0, y = 0, z = 0 },
|
||||
xAxis = { x = 1, y = 0, z = 0 },
|
||||
@ -270,3 +270,330 @@ export YZ = {
|
||||
yAxis = { x = 0, y = 0, z = 1 },
|
||||
zAxis = { x = 1, y = 0, z = 0 },
|
||||
}: Plane
|
||||
|
||||
/// An infinte line in 2d space.
|
||||
@(impl = std_rust)
|
||||
export type Axis2d
|
||||
|
||||
/// An infinte line in 3d space.
|
||||
@(impl = std_rust)
|
||||
export type Axis3d
|
||||
|
||||
export X = {
|
||||
origin = [0, 0, 0],
|
||||
direction = [1, 0, 0],
|
||||
}: Axis3d
|
||||
|
||||
export Y = {
|
||||
origin = [0, 0, 0],
|
||||
direction = [0, 1, 0],
|
||||
}: Axis3d
|
||||
|
||||
export Z = {
|
||||
origin = [0, 0, 0],
|
||||
direction = [0, 0, 1],
|
||||
}: Axis3d
|
||||
|
||||
/// Create a helix.
|
||||
///
|
||||
/// ```
|
||||
/// // Create a helix around the Z axis.
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = Z,
|
||||
/// )
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn(YZ)
|
||||
/// |> circle( center = [0, 0], radius = 0.5)
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Create a helix around an edge.
|
||||
/// helper001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 10], tag = $edge001)
|
||||
///
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = edge001,
|
||||
/// )
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn(XY)
|
||||
/// |> circle( center = [0, 0], radius = 0.5 )
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Create a helix around a custom axis.
|
||||
/// helixPath = helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 5,
|
||||
/// length = 10,
|
||||
/// radius = 5,
|
||||
/// axis = {
|
||||
/// direction = [0, 0, 1.0],
|
||||
/// origin = [0, 0.25, 0]
|
||||
/// }
|
||||
/// )
|
||||
///
|
||||
/// // Create a spring by sweeping around the helix path.
|
||||
/// springSketch = startSketchOn(XY)
|
||||
/// |> circle( center = [0, 0], radius = 1 )
|
||||
/// |> sweep(path = helixPath)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Create a helix on a cylinder.
|
||||
///
|
||||
/// part001 = startSketchOn(XY)
|
||||
/// |> circle( center= [5, 5], radius= 10 )
|
||||
/// |> extrude(length = 10)
|
||||
///
|
||||
/// helix(
|
||||
/// angleStart = 0,
|
||||
/// ccw = true,
|
||||
/// revolutions = 16,
|
||||
/// cylinder = part001,
|
||||
/// )
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn helix(
|
||||
/// Number of revolutions.
|
||||
revolutions: number(_),
|
||||
/// Start angle (in degrees).
|
||||
angleStart: number(deg),
|
||||
/// Is the helix rotation counter clockwise? The default is `false`.
|
||||
ccw?: bool,
|
||||
/// Radius of the helix.
|
||||
@(include_in_snippet = true)
|
||||
radius?: number(mm),
|
||||
/// Axis to use for the helix.
|
||||
@(include_in_snippet = true)
|
||||
axis?: Axis3d | Edge,
|
||||
/// Length of the helix. This is not necessary if the helix is created around an edge. If not given the length of the edge is used.
|
||||
@(include_in_snippet = true)
|
||||
length?: number(mm),
|
||||
/// Cylinder to create the helix on.
|
||||
cylinder?: Solid,
|
||||
): Helix {}
|
||||
|
||||
/// Rotate a sketch around some provided axis, creating a solid from its extent.
|
||||
///
|
||||
/// This, like extrude, is able to create a 3-dimensional solid from a
|
||||
/// 2-dimensional sketch. However, unlike extrude, this creates a solid
|
||||
/// by using the extent of the sketch as its revolved around an axis rather
|
||||
/// than using the extent of the sketch linearly translated through a third
|
||||
/// dimension.
|
||||
///
|
||||
/// Revolve occurs around a local sketch axis rather than a global axis.
|
||||
///
|
||||
/// You can provide more than one sketch to revolve, and they will all be
|
||||
/// revolved around the same axis.
|
||||
///
|
||||
/// ```
|
||||
/// part001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = Y) // default angle is 360
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // A donut shape.
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// |> circle( center = [15, 0], radius = 5 )
|
||||
/// |> revolve(
|
||||
/// angle = 360,
|
||||
/// axis = Y,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// part001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = Y, angle = 180)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// part001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([4, 12], %)
|
||||
/// |> line(end = [2, 0])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [4, -6])
|
||||
/// |> line(end = [0, -6])
|
||||
/// |> line(end = [-3.75, -4.5])
|
||||
/// |> line(end = [0, -5.5])
|
||||
/// |> line(end = [-2, 0])
|
||||
/// |> close()
|
||||
/// |> revolve(axis = Y, angle = 180)
|
||||
///
|
||||
/// part002 = startSketchOn(part001, 'end')
|
||||
/// |> startProfileAt([4.5, -5], %)
|
||||
/// |> line(end = [0, 5])
|
||||
/// |> line(end = [5, 0])
|
||||
/// |> line(end = [0, -5])
|
||||
/// |> close()
|
||||
/// |> extrude(length = 5)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// box = startSketchOn(XY)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20])
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = -90,
|
||||
/// axis = Y
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// box = startSketchOn(XY)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20], tag = $revolveAxis)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = 90,
|
||||
/// axis = getOppositeEdge(revolveAxis)
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// box = startSketchOn(XY)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20])
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20], tag = $revolveAxis)
|
||||
/// |> close()
|
||||
/// |> extrude(length = 20)
|
||||
///
|
||||
/// sketch001 = startSketchOn(box, "END")
|
||||
/// |> circle( center = [10,10], radius = 4 )
|
||||
/// |> revolve(
|
||||
/// angle = 90,
|
||||
/// axis = getOppositeEdge(revolveAxis),
|
||||
/// tolerance = 0.0001
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([10, 0], %)
|
||||
/// |> line(end = [5, -5])
|
||||
/// |> line(end = [5, 5])
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// part001 = revolve(
|
||||
/// sketch001,
|
||||
/// axis = {
|
||||
/// direction = [0.0, 1.0],
|
||||
/// origin: [0.0, 0.0]
|
||||
/// }
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Revolve two sketches around the same axis.
|
||||
///
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// profile001 = startProfileAt([4, 8], sketch001)
|
||||
/// |> xLine(length = 3)
|
||||
/// |> yLine(length = -3)
|
||||
/// |> xLine(length = -3)
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// profile002 = startProfileAt([-5, 8], sketch001)
|
||||
/// |> xLine(length = 3)
|
||||
/// |> yLine(length = -3)
|
||||
/// |> xLine(length = -3)
|
||||
/// |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
/// |> close()
|
||||
///
|
||||
/// revolve(
|
||||
/// [profile001, profile002],
|
||||
/// axis = X,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Revolve around a path that has not been extruded.
|
||||
///
|
||||
/// profile001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20], tag = $revolveAxis)
|
||||
/// |> line(end = [20, 0])
|
||||
/// |> line(end = [0, -20])
|
||||
/// |> close(%)
|
||||
///
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// |> circle(center = [-10, 10], radius = 4)
|
||||
/// |> revolve(angle = 90, axis = revolveAxis)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Revolve around a path that has not been extruded or closed.
|
||||
///
|
||||
/// profile001 = startSketchOn(XY)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 20], tag = $revolveAxis)
|
||||
/// |> line(end = [20, 0])
|
||||
///
|
||||
/// sketch001 = startSketchOn(XY)
|
||||
/// |> circle(center = [-10, 10], radius = 4)
|
||||
/// |> revolve(angle = 90, axis = revolveAxis)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn revolve(
|
||||
/// The sketch or set of sketches that should be revolved
|
||||
@sketches: [Sketch; 1+],
|
||||
/// Axis of revolution.
|
||||
axis: Axis2d | Edge,
|
||||
/// Angle to revolve (in degrees). Default is 360.
|
||||
angle?: number(deg),
|
||||
/// Tolerance for the revolve operation.
|
||||
tolerance?: number(mm),
|
||||
/// A named tag for the face at the start of the revolve, i.e. the original sketch.
|
||||
tagStart?: tag,
|
||||
/// A named tag for the face at the end of the revolve.
|
||||
tagEnd?: tag,
|
||||
): Solid {}
|
||||
|
@ -32,3 +32,74 @@ export fn circle(
|
||||
/// Create a new tag which refers to this circle.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Mirror a sketch.
|
||||
///
|
||||
/// Only works on unclosed sketches for now.
|
||||
///
|
||||
/// Mirror occurs around a local sketch axis rather than a global axis.
|
||||
///
|
||||
/// ```
|
||||
/// // Mirror an un-closed sketch across the Y axis.
|
||||
/// sketch001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 10], %)
|
||||
/// |> line(end = [15, 0])
|
||||
/// |> line(end = [-7, -3])
|
||||
/// |> line(end = [9, -1])
|
||||
/// |> line(end = [-8, -5])
|
||||
/// |> line(end = [9, -3])
|
||||
/// |> line(end = [-8, -3])
|
||||
/// |> line(end = [9, -1])
|
||||
/// |> line(end = [-19, -0])
|
||||
/// |> mirror2d(axis = Y)
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Mirror a un-closed sketch across the Y axis.
|
||||
/// sketch001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d(axis = Y)
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Mirror a un-closed sketch across an edge.
|
||||
/// helper001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line(end = [0, 10], tag = $edge001)
|
||||
///
|
||||
/// sketch001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d(axis = edge001)
|
||||
///
|
||||
/// // example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
///
|
||||
/// ```
|
||||
/// // Mirror an un-closed sketch across a custom axis.
|
||||
/// sketch001 = startSketchOn(XZ)
|
||||
/// |> startProfileAt([0, 8.5], %)
|
||||
/// |> line(end = [20, -8.5])
|
||||
/// |> line(end = [-20, -8.5])
|
||||
/// |> mirror2d(
|
||||
/// axis = {
|
||||
/// direction = [0.0, 1.0],
|
||||
/// origin = [0.0, 0.0]
|
||||
/// })
|
||||
///
|
||||
/// example = extrude(sketch001, length = 10)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn mirror2d(
|
||||
/// The sketch or sketches to be reflected.
|
||||
@sketches: [Sketch; 1+],
|
||||
/// The axis to refect around.
|
||||
axis: Axis2d | Edge,
|
||||
): Sketch {}
|
||||
|
6
rust/kcl-lib/std/turns.kcl
Normal file
6
rust/kcl-lib/std/turns.kcl
Normal file
@ -0,0 +1,6 @@
|
||||
@no_std
|
||||
|
||||
export ZERO = 0
|
||||
export QUARTER_TURN = 90deg
|
||||
export HALF_TURN = 180deg
|
||||
export THREE_QUARTER_TURN = 270deg
|
@ -1,25 +1,25 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[76, 113, 4]"]
|
||||
3["Segment<br>[119, 136, 4]"]
|
||||
4["Segment<br>[142, 160, 4]"]
|
||||
5["Segment<br>[166, 184, 4]"]
|
||||
6["Segment<br>[190, 246, 4]"]
|
||||
7["Segment<br>[252, 259, 4]"]
|
||||
2["Path<br>[76, 113, 5]"]
|
||||
3["Segment<br>[119, 136, 5]"]
|
||||
4["Segment<br>[142, 160, 5]"]
|
||||
5["Segment<br>[166, 184, 5]"]
|
||||
6["Segment<br>[190, 246, 5]"]
|
||||
7["Segment<br>[252, 259, 5]"]
|
||||
8[Solid2d]
|
||||
end
|
||||
subgraph path25 [Path]
|
||||
25["Path<br>[76, 111, 5]"]
|
||||
26["Segment<br>[117, 134, 5]"]
|
||||
27["Segment<br>[140, 158, 5]"]
|
||||
28["Segment<br>[164, 182, 5]"]
|
||||
29["Segment<br>[188, 244, 5]"]
|
||||
30["Segment<br>[250, 257, 5]"]
|
||||
25["Path<br>[76, 111, 6]"]
|
||||
26["Segment<br>[117, 134, 6]"]
|
||||
27["Segment<br>[140, 158, 6]"]
|
||||
28["Segment<br>[164, 182, 6]"]
|
||||
29["Segment<br>[188, 244, 6]"]
|
||||
30["Segment<br>[250, 257, 6]"]
|
||||
31[Solid2d]
|
||||
end
|
||||
1["Plane<br>[47, 66, 4]"]
|
||||
9["Sweep Extrusion<br>[265, 287, 4]"]
|
||||
1["Plane<br>[47, 66, 5]"]
|
||||
9["Sweep Extrusion<br>[265, 287, 5]"]
|
||||
10[Wall]
|
||||
11[Wall]
|
||||
12[Wall]
|
||||
@ -34,8 +34,8 @@ flowchart LR
|
||||
21["SweepEdge Adjacent"]
|
||||
22["SweepEdge Opposite"]
|
||||
23["SweepEdge Adjacent"]
|
||||
24["Plane<br>[47, 66, 5]"]
|
||||
32["Sweep Extrusion<br>[263, 285, 5]"]
|
||||
24["Plane<br>[47, 66, 6]"]
|
||||
32["Sweep Extrusion<br>[263, 285, 6]"]
|
||||
33[Wall]
|
||||
34[Wall]
|
||||
35[Wall]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user