Compare commits
25 Commits
Author | SHA1 | Date | |
---|---|---|---|
0bdc50c78f | |||
57d78b6094 | |||
db5ce7ba85 | |||
51c16d0048 | |||
6532b23f1c | |||
49304b9ecd | |||
358b34de4c | |||
9973e5fde3 | |||
36875e05fd | |||
42123383bb | |||
ef4c606ed1 | |||
1ebb73b935 | |||
b57d31c0e7 | |||
678ebbc310 | |||
cc2efd316c | |||
d1f811f91d | |||
7ca3afff9f | |||
71b9e40bd9 | |||
4f35197a96 | |||
40b0cf5fd3 | |||
355e6acf0d | |||
4ff38e7f44 | |||
1dcd3b84b7 | |||
2957216bd3 | |||
11160f0b40 |
@ -1,5 +1,6 @@
|
||||
NODE_ENV=development
|
||||
DEV=true
|
||||
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||
@ -8,3 +9,5 @@ VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||
# ONLY add your token in .env.development.local if you want to skip auth, otherwise this token takes precedence!
|
||||
#VITE_KC_DEV_TOKEN="your token from dev.zoo.dev should go in .env.development.local"
|
||||
|
||||
FAIL_ON_CONSOLE_ERRORS=true
|
||||
|
10
.envrc
@ -1,3 +1,13 @@
|
||||
# Load optional shared environment variables
|
||||
source_up_if_exists
|
||||
|
||||
# Load default development environment variables
|
||||
dotenv .env.development
|
||||
|
||||
# Load optional environment variables overrides
|
||||
dotenv_if_exists .env.development.local
|
||||
|
||||
# Load optional testing environment variables
|
||||
dotenv_if_exists e2e/playwright/playwright-secrets.env
|
||||
|
||||
use flake .
|
||||
|
13
.eslintrc
@ -20,8 +20,20 @@
|
||||
"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": "^_",
|
||||
@ -29,6 +41,7 @@
|
||||
"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
@ -28,43 +28,57 @@ jobs:
|
||||
- run: yarn fmt-check
|
||||
|
||||
yarn-build-wasm:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
# Build the wasm blob once on the fastest runner.
|
||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install
|
||||
|
||||
- name: Use correct Rust toolchain
|
||||
shell: bash
|
||||
run: |
|
||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||
|
||||
- name: Install rust
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache: false # Configured below.
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-22.04
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: './rust'
|
||||
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
- run: yarn tsc
|
||||
- name: Build Wasm
|
||||
shell: bash
|
||||
run: yarn build:wasm
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-22.04
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-wasm
|
||||
path: |
|
||||
rust/kcl-wasm-lib/pkg/kcl_wasm_lib*
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: prepared-ts-rs-bindings
|
||||
path: |
|
||||
rust/kcl-lib/bindings/*
|
||||
|
||||
yarn-tsc:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
@ -73,7 +87,53 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
- run: yarn --cwd ./rust/kcl-language-server --modules-folder node_modules install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn tsc
|
||||
|
||||
yarn-lint:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
- run: yarn install
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn lint
|
||||
|
||||
python-codespell:
|
||||
@ -91,6 +151,7 @@ jobs:
|
||||
|
||||
yarn-unit-test-kcl-samples:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -103,7 +164,22 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
@ -120,6 +196,7 @@ jobs:
|
||||
|
||||
yarn-unit-test:
|
||||
runs-on: ubuntu-latest
|
||||
needs: yarn-build-wasm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -132,7 +209,22 @@ jobs:
|
||||
- uses: taiki-e/install-action@37bdc826eaedac215f638a96472df572feab0f9b
|
||||
with:
|
||||
tool: wasm-pack
|
||||
- run: yarn build:wasm
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
|
||||
- name: Copy prepared wasm
|
||||
run: |
|
||||
ls -R prepared-wasm
|
||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||
mkdir rust/kcl-wasm-lib/pkg
|
||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||
|
||||
- name: Copy prepared ts-rs bindings
|
||||
run: |
|
||||
ls -R prepared-ts-rs-bindings
|
||||
mkdir rust/kcl-lib/bindings
|
||||
cp -r prepared-ts-rs-bindings/* rust/kcl-lib/bindings/
|
||||
|
||||
- run: yarn simpleserver:bg
|
||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||
|
2
.github/workflows/update-e2e-branch.yml
vendored
@ -38,7 +38,7 @@ jobs:
|
||||
# Get a new SHA to prevent overwriting the commit status on main
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git commit --amend --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
|
||||
git commit --allow-empty --message="[all-e2e] $(git log --max-count=1 --pretty=%B)"
|
||||
|
||||
# Overwrite the branch
|
||||
git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git
|
||||
|
4
Makefile
@ -37,7 +37,7 @@ build-web: public/kcl_wasm_lib_bg.wasm build/index.html
|
||||
build-desktop: public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
||||
|
||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES)$(RUST_SOURCES)
|
||||
yarn build:wasm
|
||||
yarn build:wasm:dev
|
||||
|
||||
build/index.html: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||
yarn build:local
|
||||
@ -99,7 +99,7 @@ test-e2e-web: install build-web ## Run the web e2e tests
|
||||
|
||||
.PHONY: test-e2e-desktop
|
||||
test-e2e-desktop: install build-desktop ## Run the desktop e2e tests
|
||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep=$(E2E_GREP)
|
||||
yarn test:playwright:electron --workers=$(E2E_WORKERS) --max-failures=$(E2E_FAILURES) --grep="$(E2E_GREP)"
|
||||
|
||||
###############################################################################
|
||||
# CLEAN
|
||||
|
@ -9,13 +9,9 @@ layout: manual
|
||||
|
||||
### `std`
|
||||
|
||||
- [`HALF_TURN`](/docs/kcl/consts/std-HALF_TURN)
|
||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-QUARTER_TURN)
|
||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-THREE_QUARTER_TURN)
|
||||
- [`XY`](/docs/kcl/consts/std-XY)
|
||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||
- [`ZERO`](/docs/kcl/consts/std-ZERO)
|
||||
|
||||
### `std::math`
|
||||
|
||||
@ -23,3 +19,10 @@ layout: manual
|
||||
- [`PI`](/docs/kcl/consts/std-math-PI)
|
||||
- [`TAU`](/docs/kcl/consts/std-math-TAU)
|
||||
|
||||
### `std::turns`
|
||||
|
||||
- [`HALF_TURN`](/docs/kcl/consts/std-turns-HALF_TURN)
|
||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-turns-QUARTER_TURN)
|
||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||
- [`ZERO`](/docs/kcl/consts/std-turns-ZERO)
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
@ -1,15 +0,0 @@
|
||||
---
|
||||
title: "std::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-HALF_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::HALF_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::HALF_TURN: number(deg) = 180deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::QUARTER_TURN: number(deg) = 90deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-THREE_QUARTER_TURN.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::THREE_QUARTER_TURN"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::THREE_QUARTER_TURN: number(deg) = 270deg
|
||||
```
|
||||
|
||||
|
15
docs/kcl/consts/std-turns-ZERO.md
Normal file
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: "std::turns::ZERO"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
```js
|
||||
std::turns::ZERO: number = 0
|
||||
```
|
||||
|
||||
|
@ -23,19 +23,15 @@ layout: manual
|
||||
* [`tag`](kcl/types/tag)
|
||||
* **std**
|
||||
* [`Face`](kcl/types/Face)
|
||||
* [`HALF_TURN`](kcl/consts/std-HALF_TURN)
|
||||
* [`Helix`](kcl/types/Helix)
|
||||
* [`Plane`](kcl/types/Plane)
|
||||
* [`Point2d`](kcl/types/Point2d)
|
||||
* [`Point3d`](kcl/types/Point3d)
|
||||
* [`QUARTER_TURN`](kcl/consts/std-QUARTER_TURN)
|
||||
* [`Sketch`](kcl/types/Sketch)
|
||||
* [`Solid`](kcl/types/Solid)
|
||||
* [`THREE_QUARTER_TURN`](kcl/consts/std-THREE_QUARTER_TURN)
|
||||
* [`XY`](kcl/consts/std-XY)
|
||||
* [`XZ`](kcl/consts/std-XZ)
|
||||
* [`YZ`](kcl/consts/std-YZ)
|
||||
* [`ZERO`](kcl/consts/std-ZERO)
|
||||
* [`abs`](kcl/abs)
|
||||
* [`acos`](kcl/acos)
|
||||
* [`angleToMatchLengthX`](kcl/angleToMatchLengthX)
|
||||
@ -146,3 +142,8 @@ layout: manual
|
||||
* [`tan`](kcl/std-math-tan)
|
||||
* **std::sketch**
|
||||
* [`circle`](kcl/std-sketch-circle)
|
||||
* **std::turns**
|
||||
* [`turns::HALF_TURN`](kcl/consts/std-turns-HALF_TURN)
|
||||
* [`turns::QUARTER_TURN`](kcl/consts/std-turns-QUARTER_TURN)
|
||||
* [`turns::THREE_QUARTER_TURN`](kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||
* [`turns::ZERO`](kcl/consts/std-turns-ZERO)
|
||||
|
@ -6,6 +6,10 @@ layout: manual
|
||||
|
||||
Rotate a solid or a sketch.
|
||||
|
||||
This is really useful for assembling parts together. You can create a part and then rotate it to the correct orientation.
|
||||
|
||||
For sketches, you can use this to rotate a sketch and then loft it with another sketch.
|
||||
|
||||
### Using Roll, Pitch, and Yaw
|
||||
|
||||
When rotating a part in 3D space, "roll," "pitch," and "yaw" refer to the three rotational axes used to describe its orientation: roll is rotation around the longitudinal axis (front-to-back), pitch is rotation around the lateral axis (wing-to-wing), and yaw is rotation around the vertical axis (up-down); essentially, it's like tilting the part on its side (roll), tipping the nose up or down (pitch), and turning it left or right (yaw).
|
||||
@ -166,7 +170,7 @@ fn square() {
|
||||
profile001 = square()
|
||||
|
||||
profile002 = square()
|
||||
|> translate(translate = [0, 0, 20])
|
||||
|> translate(x = 0, y = 0, z = 20)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
|
||||
loft([profile001, profile002])
|
||||
|
6446
docs/kcl/std.json
117
e2e/playwright/boolean.spec.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import { test, expect } from './zoo-test'
|
||||
import fs from 'node:fs/promises'
|
||||
import path from 'node:path'
|
||||
|
||||
test.describe('Point and click for boolean workflows', () => {
|
||||
// Boolean operations to test
|
||||
const booleanOperations = [
|
||||
{
|
||||
name: 'union',
|
||||
code: 'union([extrude001, extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'subtract',
|
||||
code: 'subtract([extrude001], tools = [extrude006])',
|
||||
},
|
||||
{
|
||||
name: 'intersect',
|
||||
code: 'intersect([extrude001, extrude006])',
|
||||
},
|
||||
] as const
|
||||
for (let i = 0; i < booleanOperations.length; i++) {
|
||||
const operation = booleanOperations[i]
|
||||
const operationName = operation.name
|
||||
const commandName = `Boolean ${
|
||||
operationName.charAt(0).toUpperCase() + operationName.slice(1)
|
||||
}`
|
||||
test(`Create boolean operation -- ${operationName}`, async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
toolbar,
|
||||
scene,
|
||||
page,
|
||||
}) => {
|
||||
const file = await fs.readFile(
|
||||
path.resolve(
|
||||
__dirname,
|
||||
'../../',
|
||||
'./rust/kcl-lib/e2e/executor/inputs/boolean-setup-with'
|
||||
),
|
||||
'utf-8'
|
||||
)
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// Test coordinates for selection - these might need adjustment based on actual scene layout
|
||||
const cylinderPoint = { x: 592, y: 174 }
|
||||
const secondObjectPoint = { x: 683, y: 273 }
|
||||
|
||||
// Create mouse helpers for selecting objects
|
||||
const [clickFirstObject] = scene.makeMouseHelpers(
|
||||
cylinderPoint.x,
|
||||
cylinderPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
const [clickSecondObject] = scene.makeMouseHelpers(
|
||||
secondObjectPoint.x,
|
||||
secondObjectPoint.y,
|
||||
{ steps: 10 }
|
||||
)
|
||||
|
||||
await test.step(`Test ${operationName} operation`, async () => {
|
||||
// Click the boolean operation button in the toolbar
|
||||
await toolbar.selectBoolean(operationName)
|
||||
|
||||
// Verify command bar is showing the right command
|
||||
await expect(cmdBar.page.getByTestId('command-name')).toContainText(
|
||||
commandName
|
||||
)
|
||||
|
||||
// Select first object in the scene, expect there to be a pixel diff from the selection color change
|
||||
await clickFirstObject({ pixelDiff: 50 })
|
||||
|
||||
// For subtract, we need to proceed to the next step before selecting the second object
|
||||
if (operationName !== 'subtract') {
|
||||
// should down shift key to select multiple objects
|
||||
await page.keyboard.down('Shift')
|
||||
}
|
||||
|
||||
// Select second object
|
||||
await clickSecondObject({ pixelDiff: 50 })
|
||||
|
||||
// Confirm the operation in the command bar
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
if (operationName === 'union' || operationName === 'intersect') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Solids: '2 paths',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
} else if (operationName === 'subtract') {
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Tool: '1 path',
|
||||
Target: '1 path',
|
||||
},
|
||||
commandName,
|
||||
})
|
||||
}
|
||||
|
||||
await cmdBar.submit()
|
||||
|
||||
await editor.expectEditor.toContain(operation.code)
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
@ -181,6 +181,14 @@ export class ToolbarFixture {
|
||||
).toBeVisible()
|
||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
||||
}
|
||||
selectBoolean = async (operation: 'union' | 'subtract' | 'intersect') => {
|
||||
await this.page
|
||||
.getByRole('button', { name: 'caret down Union: open menu' })
|
||||
.click()
|
||||
const operationTestId = `dropdown-boolean-${operation}`
|
||||
await expect(this.page.getByTestId(operationTestId)).toBeVisible()
|
||||
await this.page.getByTestId(operationTestId).click()
|
||||
}
|
||||
|
||||
selectCircleThreePoint = async () => {
|
||||
await this.page
|
||||
|
@ -257,6 +257,37 @@ export const isErrorWhitelisted = (exception: Error) => {
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: '_testUtils is not defined',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
{
|
||||
name: 'TypeError',
|
||||
message: 'Failed to fetch',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/snapshot-tests.spec.ts',
|
||||
},
|
||||
// TODO: fix this error in the code
|
||||
{
|
||||
name: 'ReferenceError',
|
||||
message: 'createNewVariableCheckbox is not defined',
|
||||
stack: '',
|
||||
project: 'Google Chrome',
|
||||
foundInSpec: 'e2e/playwright/testing-constraints.spec.ts',
|
||||
},
|
||||
{
|
||||
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 }) => {
|
||||
async ({ settingsKey, settings, code }) => {
|
||||
// Give some initial code, so we can test that it's cleared
|
||||
localStorage.setItem('persistCode', originalCode)
|
||||
localStorage.setItem('persistCode', code)
|
||||
localStorage.setItem(settingsKey, settings)
|
||||
},
|
||||
{
|
||||
@ -240,6 +240,7 @@ test.describe('Onboarding tests', () => {
|
||||
settings: settingsToToml({
|
||||
settings: TEST_SETTINGS_ONBOARDING_EXPORT,
|
||||
}),
|
||||
code: originalCode,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -1082,8 +1082,8 @@ openSketch = startSketchOn(XY)
|
||||
}) => {
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, ccw = false, radius = 5, axis = 'X', length = 5,)`
|
||||
const expectedLine = `revolutions=1,`
|
||||
const expectedOutput = `helix001 = helix( axis = 'X', radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `axis='X',`
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
@ -1091,17 +1091,16 @@ openSketch = startSketchOn(XY)
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'axisOrEdge',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
AxisOrEdge: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'axisOrEdge',
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
@ -1110,7 +1109,19 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Mode: 'Axis',
|
||||
Axis: 'X',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
Length: '5',
|
||||
Radius: '5',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
@ -1134,30 +1145,31 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: initialInput,
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: initialInput,
|
||||
Radius: '5',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'length',
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page.keyboard.press('Shift+Backspace')
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Axis: 'X',
|
||||
CounterClockWise: '',
|
||||
Length: newInput,
|
||||
Radius: '5',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
Radius: '5',
|
||||
Length: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1181,14 +1193,14 @@ openSketch = startSketchOn(XY)
|
||||
{
|
||||
selectionType: 'segment',
|
||||
testPoint: { x: 513, y: 221 },
|
||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 100,)`,
|
||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 50,)`,
|
||||
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
{
|
||||
selectionType: 'sweepEdge',
|
||||
testPoint: { x: 564, y: 364 },
|
||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`,
|
||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`,
|
||||
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 5, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||
},
|
||||
]
|
||||
helixCases.map(
|
||||
@ -1225,17 +1237,16 @@ openSketch = startSketchOn(XY)
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'axisOrEdge',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '',
|
||||
AxisOrEdge: '',
|
||||
Mode: '',
|
||||
CounterClockWise: '',
|
||||
Length: '',
|
||||
Radius: '',
|
||||
Revolutions: '',
|
||||
},
|
||||
highlightedHeaderArg: 'axisOrEdge',
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'Edge' }).click()
|
||||
@ -1246,21 +1257,17 @@ openSketch = startSketchOn(XY)
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('0')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.selectOption({ name: 'True' }).click()
|
||||
await page.keyboard.insertText('1')
|
||||
await cmdBar.progressCmdBar()
|
||||
await page.keyboard.insertText('100')
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
AxisOrEdge: 'Edge',
|
||||
Mode: 'Edge',
|
||||
Edge: `1 ${selectionType}`,
|
||||
CounterClockWise: '',
|
||||
Length: '100',
|
||||
Radius: '1',
|
||||
AngleStart: '0',
|
||||
Revolutions: '20',
|
||||
Radius: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1280,22 +1287,24 @@ openSketch = startSketchOn(XY)
|
||||
0
|
||||
)
|
||||
await operationButton.dblclick()
|
||||
const initialInput = '100'
|
||||
const newInput = '50'
|
||||
const initialInput = '1'
|
||||
const newInput = '5'
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'length',
|
||||
currentArgValue: initialInput,
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
CounterClockWise: '',
|
||||
Length: initialInput,
|
||||
Radius: '1',
|
||||
Revolutions: '20',
|
||||
Radius: initialInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'length',
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await page
|
||||
.getByRole('button', { name: 'radius', exact: false })
|
||||
.click()
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
await cmdBar.currentArgumentInput
|
||||
.locator('.cm-content')
|
||||
@ -1305,10 +1314,9 @@ openSketch = startSketchOn(XY)
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '0',
|
||||
CounterClockWise: '',
|
||||
Length: newInput,
|
||||
Radius: '1',
|
||||
Revolutions: '20',
|
||||
Radius: newInput,
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
@ -1336,6 +1344,140 @@ openSketch = startSketchOn(XY)
|
||||
}
|
||||
)
|
||||
|
||||
test('Helix point-and-click on cylinder', async ({
|
||||
context,
|
||||
page,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
profile001 = circle(
|
||||
sketch001,
|
||||
center = [0, 0],
|
||||
radius = 100,
|
||||
tag = $seg01,
|
||||
)
|
||||
extrude001 = extrude(profile001, length = 100)
|
||||
`
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
// One dumb hardcoded screen pixel value
|
||||
const testPoint = { x: 620, y: 257 }
|
||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||
const expectedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||
const expectedLine = `cylinder = extrude001,`
|
||||
const expectedEditedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = true,)`
|
||||
|
||||
await test.step(`Go through the command bar flow`, async () => {
|
||||
await toolbar.helixButton.click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'mode',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
Radius: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'mode',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'Cylinder' }).click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'cylinder',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
Mode: 'Cylinder',
|
||||
Cylinder: '',
|
||||
AngleStart: '',
|
||||
Revolutions: '',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'cylinder',
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await clickOnWall()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
Mode: 'Cylinder',
|
||||
Cylinder: '1 face',
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
})
|
||||
|
||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||
await editor.expectEditor.toContain(expectedOutput)
|
||||
await editor.expectState({
|
||||
diagnostics: [],
|
||||
activeLines: [expectedLine],
|
||||
highlightedCode: '',
|
||||
})
|
||||
})
|
||||
|
||||
await test.step(`Edit helix through the feature tree`, async () => {
|
||||
await editor.closePane()
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.dblclick()
|
||||
await cmdBar.expectState({
|
||||
commandName: 'Helix',
|
||||
stage: 'arguments',
|
||||
currentArgKey: 'CounterClockWise',
|
||||
currentArgValue: '',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: '',
|
||||
},
|
||||
highlightedHeaderArg: 'CounterClockWise',
|
||||
})
|
||||
await cmdBar.selectOption({ name: 'True' }).click()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
headerArguments: {
|
||||
AngleStart: '360',
|
||||
Revolutions: '1',
|
||||
CounterClockWise: 'true',
|
||||
},
|
||||
commandName: 'Helix',
|
||||
})
|
||||
await cmdBar.progressCmdBar()
|
||||
await toolbar.closePane('feature-tree')
|
||||
await toolbar.openPane('code')
|
||||
await editor.expectEditor.toContain(expectedEditedOutput)
|
||||
await editor.closePane()
|
||||
})
|
||||
|
||||
await test.step('Delete helix via feature tree selection', async () => {
|
||||
await toolbar.openPane('feature-tree')
|
||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await toolbar.closePane('feature-tree')
|
||||
await toolbar.openPane('code')
|
||||
await editor.expectEditor.not.toContain(expectedEditedOutput)
|
||||
})
|
||||
})
|
||||
|
||||
const loftPointAndClickCases = [
|
||||
{ shouldPreselect: true },
|
||||
{ shouldPreselect: false },
|
||||
|
@ -473,6 +473,9 @@ test.describe('Can export from electron app', () => {
|
||||
if (!tronApp) {
|
||||
fail()
|
||||
}
|
||||
if (runningOnWindows()) {
|
||||
test.fixme(orRunWhenFullSuiteEnabled())
|
||||
}
|
||||
|
||||
await context.folderSetupFn(async (dir) => {
|
||||
const bracketDir = path.join(dir, 'bracket')
|
||||
|
@ -778,6 +778,19 @@ plane002 = offsetPlane(XZ, offset = -2 * x)`
|
||||
await editor.expectEditor.not.toContain(`plane002`)
|
||||
})
|
||||
})
|
||||
|
||||
test.fail(
|
||||
'Console errors cause tests to fail',
|
||||
async ({ page, homePage }) => {
|
||||
const u = await getUtils(page)
|
||||
await homePage.goToModelingScene()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
await page.getByTestId('custom-cmd-input').fill('foobar')
|
||||
await page.getByTestId('custom-cmd-send-button').scrollIntoViewIfNeeded()
|
||||
await page.getByTestId('custom-cmd-send-button').click()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
async function clickExportButton(page: Page) {
|
||||
|
@ -76,11 +76,11 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(endAbsolute = totalLen, tag = $seg03)
|
||||
|> yLine(length = -armThick, tag = $seg01)
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg04
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg04, %) + 180, ZERO], %)
|
||||
|> angledLineToY([segAng(seg04, %) + 180, turns::ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle = -bottomAng,
|
||||
to = -totalHeightHalf - armThick,
|
||||
@ -88,12 +88,12 @@ part001 = startSketchOn(-XZ)
|
||||
|> xLine(length = endAbsolute = segEndX(seg03) + 0)
|
||||
|> yLine(length = -segLen(seg01, %))
|
||||
|> angledLineThatIntersects({
|
||||
angle = HALF_TURN,
|
||||
angle = turns::HALF_TURN,
|
||||
offset = -armThick,
|
||||
intersectTag = seg02
|
||||
}, %)
|
||||
|> angledLineToY([segAng(seg02, %) + 180, -baseHeight], %)
|
||||
|> xLine(endAbsolute = ZERO)
|
||||
|> xLine(endAbsolute = turns::ZERO)
|
||||
|> close()
|
||||
|> extrude(length = 4)`
|
||||
)
|
||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
@ -935,47 +935,39 @@ export async function setup(
|
||||
}
|
||||
|
||||
function failOnConsoleErrors(page: Page, testInfo?: TestInfo) {
|
||||
// enabled for chrome for now
|
||||
if (page.context().browser()?.browserType().name() === 'chromium') {
|
||||
// No idea wtf exception is
|
||||
page.on('pageerror', (exception: any) => {
|
||||
if (isErrorWhitelisted(exception)) {
|
||||
return
|
||||
}
|
||||
page.on('pageerror', (exception: any) => {
|
||||
if (isErrorWhitelisted(exception)) {
|
||||
return
|
||||
}
|
||||
// Only disable this environment variable if you want to collect console errors
|
||||
if (process.env.FAIL_ON_CONSOLE_ERRORS !== 'false') {
|
||||
// Use expect to prevent page from closing and not cleaning up
|
||||
expect(`An error was detected in the console: \r\n message:${exception.message} \r\n name:${exception.name} \r\n stack:${exception.stack}
|
||||
|
||||
// only set this env var to false if you want to collect console errors
|
||||
// This can be configured in the GH workflow. This should be set to true by default (we want tests to fail when
|
||||
// unwhitelisted console errors are detected).
|
||||
if (process.env.FAIL_ON_CONSOLE_ERRORS === 'true') {
|
||||
// Fail when running on CI and FAIL_ON_CONSOLE_ERRORS is set
|
||||
// use expect to prevent page from closing and not cleaning up
|
||||
expect(`An error was detected in the console: \r\n message:${exception.message} \r\n name:${exception.name} \r\n stack:${exception.stack}
|
||||
|
||||
*Either fix the console error or add it to the whitelist defined in ./lib/console-error-whitelist.ts (if the error can be safely ignored)
|
||||
`).toEqual('Console error detected')
|
||||
} else {
|
||||
// the (test-results/exceptions.txt) file will be uploaded as part of an upload artifact in GH
|
||||
fsp
|
||||
.appendFile(
|
||||
'./test-results/exceptions.txt',
|
||||
[
|
||||
'~~~',
|
||||
`triggered_by_test:${
|
||||
testInfo?.file + ' ' + (testInfo?.title || ' ')
|
||||
}`,
|
||||
`name:${exception.name}`,
|
||||
`message:${exception.message}`,
|
||||
`stack:${exception.stack}`,
|
||||
`project:${testInfo?.project.name}`,
|
||||
'~~~',
|
||||
].join('\n')
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
*Either fix the console error or add it to the whitelist defined in ./lib/console-error-whitelist.ts (if the error can be safely ignored)
|
||||
`).toEqual('Console error detected')
|
||||
} else {
|
||||
// Add errors to `test-results/exceptions.txt` as a test artifact
|
||||
fsp
|
||||
.appendFile(
|
||||
'./test-results/exceptions.txt',
|
||||
[
|
||||
'~~~',
|
||||
`triggered_by_test:${
|
||||
testInfo?.file + ' ' + (testInfo?.title || ' ')
|
||||
}`,
|
||||
`name:${exception.name}`,
|
||||
`message:${exception.message}`,
|
||||
`stack:${exception.stack}`,
|
||||
`project:${testInfo?.project.name}`,
|
||||
'~~~',
|
||||
].join('\n')
|
||||
)
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
export async function isOutOfViewInScrollContainer(
|
||||
element: Locator,
|
||||
|
@ -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',
|
||||
},
|
||||
|
@ -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,9 +94,11 @@
|
||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
||||
"fetch:samples": "rm -rf public/kcl-samples* && curl -L -o public/kcl-samples.zip https://github.com/KittyCAD/kcl-samples/archive/refs/heads/achalmers/kw-args-xylineto.zip && unzip -o public/kcl-samples.zip -d public && mv public/kcl-samples-* public/kcl-samples",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd rust && wasm-pack build kcl-wasm-lib --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt:generated",
|
||||
"build:wasm": "./scripts/build-wasm.sh",
|
||||
"build:wasm:windows": "./scripts/build-wasm.ps1",
|
||||
"build:wasm-dev": "yarn build:wasm:dev",
|
||||
"build:wasm:dev": "./scripts/build-wasm-dev.sh",
|
||||
"build:wasm:dev:windows": "./scripts/build-wasm-dev.ps1",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||
|
@ -90,7 +90,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
||||
}
|
||||
|
||||
add(bytes: Uint8Array): void {
|
||||
const message = Codec.decode(bytes) as vsrpc.Message
|
||||
const message = Codec.decode<vsrpc.Message>(bytes)
|
||||
if (this.trace) {
|
||||
Tracer.server(message)
|
||||
}
|
||||
|
@ -41,6 +41,8 @@ When you submit a PR to add or modify KCL samples, images and STEP files will be
|
||||
[](cycloidal-gear/main.kcl)
|
||||
#### [dodecahedron](dodecahedron/main.kcl) ([screenshot](screenshots/dodecahedron.png))
|
||||
[](dodecahedron/main.kcl)
|
||||
#### [dual-basin-utility-sink](dual-basin-utility-sink/main.kcl) ([screenshot](screenshots/dual-basin-utility-sink.png))
|
||||
[](dual-basin-utility-sink/main.kcl)
|
||||
#### [enclosure](enclosure/main.kcl) ([screenshot](screenshots/enclosure.png))
|
||||
[](enclosure/main.kcl)
|
||||
#### [exhaust-manifold](exhaust-manifold/main.kcl) ([screenshot](screenshots/exhaust-manifold.png))
|
||||
@ -75,6 +77,8 @@ When you submit a PR to add or modify KCL samples, images and STEP files will be
|
||||
[](kitt/main.kcl)
|
||||
#### [lego](lego/main.kcl) ([screenshot](screenshots/lego.png))
|
||||
[](lego/main.kcl)
|
||||
#### [makeup-mirror](makeup-mirror/main.kcl) ([screenshot](screenshots/makeup-mirror.png))
|
||||
[](makeup-mirror/main.kcl)
|
||||
#### [mounting-plate](mounting-plate/main.kcl) ([screenshot](screenshots/mounting-plate.png))
|
||||
[](mounting-plate/main.kcl)
|
||||
#### [multi-axis-robot](multi-axis-robot/main.kcl) ([screenshot](screenshots/multi-axis-robot.png))
|
||||
|
@ -12,7 +12,7 @@ import "car-tire.kcl" as carTire
|
||||
import lugCount from "globals.kcl"
|
||||
|
||||
carRotor
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
carWheel
|
||||
lugNut
|
||||
|> patternCircular3d(
|
||||
@ -23,5 +23,5 @@ lugNut
|
||||
rotateDuplicates = false,
|
||||
)
|
||||
brakeCaliper
|
||||
|> translate(translate = [0, 0.5, 0])
|
||||
|> translate(x = 0, y = 0.5, z = 0)
|
||||
carTire
|
||||
|
200
public/kcl-samples/dual-basin-utility-sink/main.kcl
Normal file
@ -0,0 +1,200 @@
|
||||
// Dual-Basin Utility Sink
|
||||
// A stainless steel sink unit with dual rectangular basins and six under-counter storage compartments.
|
||||
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
// globals
|
||||
tableHeight = 850
|
||||
tableWidth = 3400
|
||||
tableDepth = 400
|
||||
|
||||
profileThickness = 13
|
||||
metalThickness = 2
|
||||
|
||||
blockCount = 3
|
||||
blockWidth = (tableWidth-profileThickness) / 3
|
||||
blockHeight = tableHeight - metalThickness - 0.5
|
||||
blockDepth = tableDepth - profileThickness
|
||||
|
||||
blockSubdivisionCount = 2
|
||||
blockSubdivisionWidth = blockWidth / blockSubdivisionCount
|
||||
|
||||
// Geometry
|
||||
floorPlane = startSketchOn(XY)
|
||||
|
||||
// legs
|
||||
legHeight = blockHeight - profileThickness
|
||||
legCount = blockCount + 1
|
||||
|
||||
legBody = startProfileAt([0, 0], floorPlane)
|
||||
|> yLine(length=profileThickness)
|
||||
|> xLine(length=profileThickness)
|
||||
|> yLine(length=-profileThickness)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = legCount, distance = blockWidth)
|
||||
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|
||||
|> extrude(length = legHeight)
|
||||
|
||||
// lower belt
|
||||
lowerBeltHeightAboveTheFloor = 150
|
||||
lowerBeltLengthX = blockWidth - profileThickness
|
||||
|
||||
lowerBeltPlane = startSketchOn(offsetPlane(XY, offset = lowerBeltHeightAboveTheFloor))
|
||||
lowerBeltBodyX = startProfileAt([profileThickness, 0], lowerBeltPlane)
|
||||
|> yLine(length=profileThickness)
|
||||
|> xLine(length=lowerBeltLengthX)
|
||||
|> yLine(length=-profileThickness)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = blockCount, distance = blockWidth)
|
||||
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|
||||
|> extrude(length = profileThickness)
|
||||
|
||||
lowerBeltLengthY = blockDepth - profileThickness
|
||||
lowerBeltBodyY = startProfileAt([0, profileThickness], lowerBeltPlane)
|
||||
|> yLine(length=lowerBeltLengthY)
|
||||
|> xLine(length=profileThickness)
|
||||
|> yLine(length=-lowerBeltLengthY)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = 2, distance = tableWidth-profileThickness)
|
||||
|> extrude(length = profileThickness)
|
||||
|
||||
// pillars
|
||||
pillarHeightAboveTheFloor = lowerBeltHeightAboveTheFloor + profileThickness
|
||||
pillarPlane = startSketchOn(offsetPlane(XY, offset = pillarHeightAboveTheFloor))
|
||||
pillarTotalHeight = blockHeight - profileThickness - pillarHeightAboveTheFloor
|
||||
|
||||
pillarBody = startProfileAt([blockSubdivisionWidth, 0], pillarPlane)
|
||||
|> yLine(length=profileThickness)
|
||||
|> xLine(length=profileThickness)
|
||||
|> yLine(length=-profileThickness)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = blockCount, distance = blockWidth)
|
||||
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|
||||
|> extrude(length = pillarTotalHeight)
|
||||
|
||||
// upper belt
|
||||
upperBeltPlane = startSketchOn(offsetPlane(XY, offset = blockHeight))
|
||||
|
||||
upperBeltBodyX = startProfileAt([0, 0], upperBeltPlane)
|
||||
|> yLine(length=profileThickness)
|
||||
|> xLine(length=tableWidth)
|
||||
|> yLine(length=-profileThickness)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [0, 1], instances = 2, distance = blockDepth)
|
||||
|> extrude(length = -profileThickness)
|
||||
|
||||
upperBeltLengthY = blockDepth - profileThickness
|
||||
upperBeltBodyY = startProfileAt([0, profileThickness], upperBeltPlane)
|
||||
|> yLine(length=upperBeltLengthY)
|
||||
|> xLine(length=profileThickness)
|
||||
|> yLine(length=-upperBeltLengthY)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = 2, distance = tableWidth-profileThickness)
|
||||
|> extrude(length = -profileThickness)
|
||||
|
||||
// sink
|
||||
tableTopPlane = startSketchOn(offsetPlane(XY, offset = tableHeight))
|
||||
tableTopBody = startProfileAt([0, 0], tableTopPlane)
|
||||
|> yLine(length=tableDepth)
|
||||
|> xLine(length=tableWidth)
|
||||
|> yLine(length=-tableDepth)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> extrude(length = -metalThickness)
|
||||
|
||||
sinkCount = 2
|
||||
sinkWidth = 1000
|
||||
sinkLength = 250
|
||||
sinkDepth = 200
|
||||
sinkOffsetFront = 40
|
||||
sinkOffsetLeft = 350
|
||||
sinkSpacing = tableWidth - sinkWidth - sinkOffsetLeft*2
|
||||
|
||||
sinkPlaneOutside = startSketchOn(tableTopBody, 'START')
|
||||
sinkBodyOutside = startProfileAt([-sinkOffsetLeft, sinkOffsetFront], sinkPlaneOutside)
|
||||
|> yLine(length=sinkLength)
|
||||
|> xLine(length=-sinkWidth)
|
||||
|> yLine(length=-sinkLength)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [-1, 0], instances = sinkCount, distance = sinkSpacing)
|
||||
|> extrude(length = sinkDepth)
|
||||
|
||||
sinkPlaneInside = startSketchOn(tableTopBody, 'END')
|
||||
sinkBodyInside = startProfileAt([sinkOffsetLeft+metalThickness, sinkOffsetFront+metalThickness], sinkPlaneInside)
|
||||
|> yLine(length=sinkLength-metalThickness*2)
|
||||
|> xLine(length=sinkWidth-metalThickness*2)
|
||||
|> yLine(length=-sinkLength+metalThickness*2)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = sinkCount, distance = sinkSpacing)
|
||||
|> extrude(length = -sinkDepth)
|
||||
|
||||
// door panels
|
||||
doorGap = 2
|
||||
doorWidth = blockSubdivisionWidth - profileThickness - doorGap*2
|
||||
doorStart = profileThickness+doorGap
|
||||
doorHeightAboveTheFloor = pillarHeightAboveTheFloor + doorGap
|
||||
doorHeight = blockHeight - doorHeightAboveTheFloor - profileThickness - doorGap
|
||||
doorCount = blockCount * blockSubdivisionCount
|
||||
|
||||
doorPlane = startSketchOn(offsetPlane(XY, offset = doorHeightAboveTheFloor))
|
||||
doorBody = startProfileAt([doorStart, 0], doorPlane)
|
||||
|> yLine(length=profileThickness)
|
||||
|> xLine(length=doorWidth)
|
||||
|> yLine(length=-profileThickness)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = doorCount, distance = blockSubdivisionWidth)
|
||||
|> extrude(length = doorHeight)
|
||||
|
||||
// side panels
|
||||
panelWidth = blockDepth - profileThickness - doorGap*2
|
||||
panelCount = doorCount + 1
|
||||
panelSpacing = tableWidth - profileThickness
|
||||
panelBody = startProfileAt([0, doorStart], doorPlane)
|
||||
|> yLine(length=panelWidth)
|
||||
|> xLine(length=profileThickness)
|
||||
|> yLine(length=-panelWidth)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternLinear2d(axis = [1, 0], instances = 2, distance = panelSpacing)
|
||||
|> extrude(length = doorHeight)
|
||||
|
||||
// handle
|
||||
handleDepth = 40
|
||||
handleWidth = 120
|
||||
handleFillet = 20
|
||||
handleHeightAboveTheFloor = 780
|
||||
handleOffset = doorStart + doorWidth / 2 - (handleWidth / 2)
|
||||
handleLengthSegmentA = handleDepth - handleFillet
|
||||
handleLengthSegmentB = handleWidth - (handleFillet * 2)
|
||||
|
||||
handlePlane = startSketchOn(offsetPlane(XY, offset = handleHeightAboveTheFloor))
|
||||
|
||||
handleProfilePath = startProfileAt([0 + handleOffset, 0], handlePlane)
|
||||
|> yLine(length=-handleLengthSegmentA)
|
||||
|> tangentialArcTo([
|
||||
handleFillet + handleOffset,
|
||||
-handleDepth
|
||||
], %)
|
||||
|> xLine(length=handleLengthSegmentB)
|
||||
|> tangentialArcTo([
|
||||
handleOffset + handleWidth,
|
||||
-handleLengthSegmentA
|
||||
], %)
|
||||
|> yLine(length=handleLengthSegmentA)
|
||||
handleSectionPlane = startSketchOn(XZ)
|
||||
handleProfileSection = circle(
|
||||
handleSectionPlane,
|
||||
center = [handleOffset, handleHeightAboveTheFloor],
|
||||
radius = 2)
|
||||
|
||||
handleBody = sweep(handleProfileSection, path = handleProfilePath)
|
||||
|> patternLinear3d(axis = [1, 0, 0], instances = doorCount, distance = blockSubdivisionWidth)
|
75
public/kcl-samples/makeup-mirror/main.kcl
Normal file
@ -0,0 +1,75 @@
|
||||
// Makeup Mirror
|
||||
// A circular vanity mirror mounted on a swiveling arm with pivot joints, used for personal grooming.
|
||||
|
||||
// Settings
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
// hinge
|
||||
hingeRadius = 8
|
||||
hingeHeight = hingeRadius * 3
|
||||
hingeGap = 0.5
|
||||
|
||||
// arm
|
||||
armLength = 170
|
||||
armRadius = 5
|
||||
|
||||
// mirror
|
||||
mirrorRadius = 170 / 2
|
||||
mirrorThickness = 10
|
||||
archToMirrorGap = 5
|
||||
archThickness = 1
|
||||
archRadius = mirrorRadius + archToMirrorGap
|
||||
|
||||
// Geometry
|
||||
// hinge
|
||||
fn hingeFn(x, y, z) {
|
||||
hingeBody = startSketchOn(offsetPlane(XY, offset = z))
|
||||
|> circle(center = [x, y], radius = hingeRadius)
|
||||
|> extrude(length = hingeHeight)
|
||||
return hingeBody
|
||||
}
|
||||
|
||||
hingePartA1 = hingeFn(0, 0, 0)
|
||||
hingePartA2 = hingeFn(0, 0, hingeHeight + hingeGap)
|
||||
hingePartA3 = hingeFn(0, 0, hingeHeight * 2 + hingeGap * 2)
|
||||
|
||||
hingePartB2 = hingeFn(armLength, 0, hingeHeight + hingeGap)
|
||||
hingePartB3 = hingeFn(armLength, 0, hingeHeight * 2 + hingeGap * 2)
|
||||
|
||||
hingePartC2 = hingeFn(armLength, -armLength, hingeHeight * 2 + hingeGap * 2)
|
||||
hingePartC3 = hingeFn(armLength, -armLength, hingeHeight * 3 + hingeGap * 3)
|
||||
|
||||
// arm
|
||||
fn armFn(plane, offset, altitude) {
|
||||
armBody = startSketchOn(plane)
|
||||
|> circle(center = [offset, altitude], radius = armRadius)
|
||||
|> extrude(length = armLength)
|
||||
return armBody
|
||||
}
|
||||
|
||||
armPartA = armFn(YZ, 0, hingeHeight * 1.5 + hingeGap)
|
||||
armPartB = armFn(XZ, armLength, hingeHeight * 2.5 + hingeGap * 2)
|
||||
|
||||
// mirror
|
||||
fn mirrorFn(plane, offsetX, offsetY, altitude, radius, tiefe, gestellR, gestellD) {
|
||||
armPlane = startSketchOn(offsetPlane(plane, offset = offsetY - (tiefe / 2)))
|
||||
armBody = circle(armPlane, center = [offsetX, altitude], radius = radius)
|
||||
|> extrude(length = tiefe)
|
||||
|
||||
archBody = startProfileAt([offsetX-gestellR, altitude], armPlane)
|
||||
|> xLine(length = gestellD)
|
||||
|> arcTo({
|
||||
interior = [offsetX, altitude-gestellR],
|
||||
end = [offsetX+gestellR, altitude]
|
||||
}, %)
|
||||
|> xLine(length = gestellD)
|
||||
|> arcTo({
|
||||
interior = [offsetX, altitude-gestellR-gestellD],
|
||||
end = [profileStartX(%), profileStartY(%)]
|
||||
}, %)
|
||||
|> close()
|
||||
|> extrude(length = tiefe)
|
||||
return armBody
|
||||
}
|
||||
|
||||
mirror = mirrorFn(XZ, armLength, armLength, hingeHeight * 4 + hingeGap * 3 + mirrorRadius+archToMirrorGap+archThickness, mirrorRadius, mirrorThickness, archRadius, archThickness)
|
@ -62,6 +62,13 @@
|
||||
"title": "Hollow Dodecahedron",
|
||||
"description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards."
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "dual-basin-utility-sink/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "Dual-Basin Utility Sink",
|
||||
"description": "A stainless steel sink unit with dual rectangular basins and six under-counter storage compartments."
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "enclosure/main.kcl",
|
||||
@ -181,6 +188,13 @@
|
||||
"title": "Lego Brick",
|
||||
"description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code."
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "makeup-mirror/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "Makeup Mirror",
|
||||
"description": "A circular vanity mirror mounted on a swiveling arm with pivot joints, used for personal grooming."
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "mounting-plate/main.kcl",
|
||||
|
@ -19,27 +19,27 @@ import pipe from "1120t74-pipe.kcl"
|
||||
flange()
|
||||
flange()
|
||||
|> rotate(axis = [0, 1, 0], angle = 180)
|
||||
|> translate(translate = [
|
||||
0,
|
||||
0,
|
||||
flangeBackHeight * 2 + gasketThickness
|
||||
])
|
||||
|> translate(
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = flangeBackHeight * 2 + gasketThickness,
|
||||
)
|
||||
|
||||
// place gasket between the flanges
|
||||
gasket()
|
||||
|> translate(translate = [
|
||||
0,
|
||||
0,
|
||||
-flangeBackHeight - gasketThickness
|
||||
])
|
||||
|> translate(
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = -flangeBackHeight - gasketThickness
|
||||
)
|
||||
|
||||
// place eight washers (four front, four back)
|
||||
washer()
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
flangeBaseThickness
|
||||
])
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = flangeBaseThickness
|
||||
)
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
instances = 4,
|
||||
@ -57,11 +57,11 @@ washer()
|
||||
|
||||
// place four bolts
|
||||
bolt()
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
flangeBaseThickness + washerThickness
|
||||
])
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = flangeBaseThickness + washerThickness,
|
||||
)
|
||||
|> rotate(roll = 90, pitch = 0, yaw = 0)
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
@ -74,11 +74,11 @@ bolt()
|
||||
|
||||
// place four hex nuts
|
||||
hexNut()
|
||||
|> translate(translate = [
|
||||
mountingHolePlacementDiameter / 2,
|
||||
0,
|
||||
-(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + washerThickness + hexNutThickness)
|
||||
])
|
||||
|> translate(
|
||||
x = mountingHolePlacementDiameter / 2,
|
||||
y = 0,
|
||||
z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + washerThickness + hexNutThickness),
|
||||
)
|
||||
|> patternCircular3d(
|
||||
%,
|
||||
instances = 4,
|
||||
@ -97,13 +97,11 @@ pipe()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
%,
|
||||
translate = [
|
||||
0,
|
||||
0,
|
||||
flangeBaseThickness + flangeFrontHeight - 0.5
|
||||
],
|
||||
global = true,
|
||||
%,
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = flangeBaseThickness + flangeFrontHeight - 0.5,
|
||||
global = true,
|
||||
)
|
||||
|
||||
pipe()
|
||||
@ -114,11 +112,9 @@ pipe()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
%,
|
||||
translate = [
|
||||
0,
|
||||
0,
|
||||
-(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + flangeFrontHeight - 0.5)
|
||||
],
|
||||
global = true,
|
||||
%,
|
||||
x = 0,
|
||||
y = 0,
|
||||
z = -(flangeBackHeight * 2 + gasketThickness + flangeBaseThickness + flangeFrontHeight - 0.5),
|
||||
global = true,
|
||||
)
|
||||
|
@ -14,7 +14,7 @@ radius = 10
|
||||
depth = 30
|
||||
distanceToInsideEdge = slateWidthHalf + templateThickness + templateGap
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfileAt([ZERO, depth + templateGap], %)
|
||||
|> startProfileAt([0, 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 = 0, 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 = 0, tag = $seg02)
|
||||
// |> line(end = [7.78, 11.16])
|
||||
|> xLine(length = -segLen(seg02))
|
||||
|> yLine(length = segLen(seg03))
|
||||
|
BIN
public/kcl-samples/screenshots/dual-basin-utility-sink.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
public/kcl-samples/screenshots/makeup-mirror.png
Normal file
After Width: | Height: | Size: 62 KiB |
@ -20,51 +20,36 @@ body()
|
||||
|
||||
// import the antenna
|
||||
antenna()
|
||||
|> translate(translate = [-width / 2 + .45, -0.10, height / 2])
|
||||
|> translate(x = -width / 2 + .45, y = -0.10, z = height / 2)
|
||||
|
||||
// import the case
|
||||
case()
|
||||
|> translate(translate = [0, -1, 0])
|
||||
|> translate(x = 0, y = -1, z = 0)
|
||||
|
||||
// import the talk button
|
||||
talkButton()
|
||||
|> translate(translate = [width / 2, -thickness / 2, .5])
|
||||
|> translate(x = width / 2, y = -thickness / 2, z = .5)
|
||||
|
||||
// import the frequency knob
|
||||
knob()
|
||||
|> translate(translate = [
|
||||
width / 2 - 0.70,
|
||||
-thickness / 2,
|
||||
height / 2
|
||||
])
|
||||
|> translate(
|
||||
x = width / 2 - 0.70,
|
||||
y = -thickness / 2,
|
||||
z = height / 2
|
||||
)
|
||||
|
||||
// import the buttons
|
||||
button()
|
||||
|> translate(translate = [
|
||||
-(screenWidth / 2 + tolerance),
|
||||
-1,
|
||||
screenYPosition
|
||||
])
|
||||
button()
|
||||
|> translate(translate = [
|
||||
-(screenWidth / 2 + tolerance),
|
||||
-1,
|
||||
screenYPosition - buttonHeight - (tolerance * 2)
|
||||
])
|
||||
button()
|
||||
|> rotate(
|
||||
%,
|
||||
roll = 0,
|
||||
pitch = 180,
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
translate = [
|
||||
screenWidth / 2 + tolerance,
|
||||
-1,
|
||||
screenYPosition - buttonHeight
|
||||
],
|
||||
global = true,
|
||||
x = -(screenWidth / 2 + tolerance),
|
||||
y = -1,
|
||||
z = screenYPosition
|
||||
)
|
||||
button()
|
||||
|> translate(
|
||||
x = -(screenWidth / 2 + tolerance),
|
||||
y = -1,
|
||||
z = screenYPosition - buttonHeight - (tolerance * 2)
|
||||
)
|
||||
button()
|
||||
|> rotate(
|
||||
@ -74,10 +59,21 @@ button()
|
||||
yaw = 0,
|
||||
)
|
||||
|> translate(
|
||||
translate = [
|
||||
screenWidth / 2 + tolerance,
|
||||
-1,
|
||||
screenYPosition - (buttonHeight * 2) - (tolerance * 2)
|
||||
],
|
||||
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),
|
||||
global = true,
|
||||
)
|
||||
|
24
rust/Cargo.lock
generated
@ -1780,7 +1780,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1791,7 +1791,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-derive-docs"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -1810,7 +1810,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-directory-test-macro"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1819,7 +1819,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server"
|
||||
version = "0.2.55"
|
||||
version = "0.2.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1840,7 +1840,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1860,7 +1860,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.55"
|
||||
version = "0.2.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1928,7 +1928,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-python-bindings"
|
||||
version = "0.3.55"
|
||||
version = "0.3.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kcl-lib",
|
||||
@ -1943,7 +1943,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
@ -1956,7 +1956,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-to-core"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1970,7 +1970,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-wasm-lib"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
dependencies = [
|
||||
"bson",
|
||||
"console_error_panic_hook",
|
||||
@ -1996,9 +1996,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.3.33"
|
||||
version = "0.3.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f6f65645cc07a8f43c34584e4979bf4da16c047cce50c4715fa9381227574d5"
|
||||
checksum = "0a345fd2a4cb16205f32bd1aa41715045830c59d78c59927fca6580e2a651ac9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
|
@ -35,7 +35,7 @@ clap = { version = "4.5.31", features = ["derive"] }
|
||||
dashmap = { version = "6.1.0" }
|
||||
http = "1"
|
||||
indexmap = "2.7.0"
|
||||
kittycad = { version = "0.3.33", default-features = false, features = ["js", "requests"] }
|
||||
kittycad = { version = "0.3.36", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.107", features = ["ts-rs", "websocket"] }
|
||||
lazy_static = "1.5.0"
|
||||
miette = "7.5.0"
|
||||
|
@ -43,6 +43,10 @@ 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,7 +1,7 @@
|
||||
|
||||
[package]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-api"
|
||||
rust-version = "1.76"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-directory-test-macro"
|
||||
description = "A tool for generating tests from a directory of kcl files"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.55"
|
||||
version = "0.1.56"
|
||||
edition = "2021"
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
publish = false
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "kcl-language-server"
|
||||
description = "A language server for KCL."
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
version = "0.2.55"
|
||||
version = "0.2.56"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -2194,9 +2194,9 @@ supports-color@^8.1.1:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
tar-fs@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
|
||||
integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
|
||||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.2.tgz#425f154f3404cb16cb8ff6e671d45ab2ed9596c5"
|
||||
integrity sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA==
|
||||
dependencies:
|
||||
chownr "^1.1.1"
|
||||
mkdirp-classic "^0.5.2"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.55"
|
||||
version = "0.2.56"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
@ -104,7 +104,7 @@ tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||
|
||||
[features]
|
||||
default = ["engine"]
|
||||
cli = ["dep:clap"]
|
||||
cli = ["dep:clap", "kittycad/clap"]
|
||||
dhat-heap = ["dep:dhat"]
|
||||
# For the lsp server, when run with stdout for rpc we want to disable println.
|
||||
# This is used for editor extensions that use the lsp server.
|
||||
|
@ -1,6 +1,8 @@
|
||||
//! Cache testing framework.
|
||||
|
||||
use kcl_lib::{bust_cache, ExecError, ExecOutcome};
|
||||
use kcmc::{each_cmd as mcmd, ModelingCmd};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Variation<'a> {
|
||||
@ -253,3 +255,71 @@ extrude(sketch001, length = 4)
|
||||
second.artifact_graph.len()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_cache_empty_file_pop_cache_empty_file_planes_work() {
|
||||
// Get the current working directory.
|
||||
let code = "";
|
||||
|
||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let program = kcl_lib::Program::parse_no_errs(code).unwrap();
|
||||
let outcome = ctx.run_with_caching(program).await.unwrap();
|
||||
|
||||
// Ensure nothing is left in the batch
|
||||
assert!(ctx.engine.batch().read().await.is_empty());
|
||||
assert!(ctx.engine.batch_end().read().await.is_empty());
|
||||
|
||||
// Ensure the planes work, and we can show or hide them.
|
||||
// Hide/show the grid.
|
||||
let default_planes = ctx.engine.get_default_planes().read().await.clone().unwrap();
|
||||
|
||||
// Assure the outcome is the same.
|
||||
assert_eq!(outcome.default_planes, Some(default_planes.clone()));
|
||||
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
Default::default(),
|
||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||
hidden: false,
|
||||
object_id: default_planes.xy,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Now simulate an engine pause/network disconnect.
|
||||
// Raw dog clear the scene entirely.
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
Default::default(),
|
||||
&ModelingCmd::from(mcmd::SceneClearAll {}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Bust the cache and reset the scene.
|
||||
let outcome = ctx.bust_cache_and_reset_scene().await.unwrap();
|
||||
// Get the default planes.
|
||||
let default_planes = ctx.engine.get_default_planes().read().await.clone().unwrap();
|
||||
|
||||
assert_eq!(outcome.default_planes, Some(default_planes.clone()));
|
||||
|
||||
// Ensure we can show a plane.
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
Default::default(),
|
||||
&ModelingCmd::from(mcmd::ObjectVisible {
|
||||
hidden: false,
|
||||
object_id: default_planes.xz,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
ctx.close().await;
|
||||
}
|
||||
|
77
rust/kcl-lib/e2e/executor/inputs/boolean-setup-with
Normal file
@ -0,0 +1,77 @@
|
||||
@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)
|
@ -53,7 +53,7 @@ pub trait CoreDump: Clone {
|
||||
.meta()
|
||||
.create_debug_uploads(vec![kittycad::types::multipart::Attachment {
|
||||
name: "".to_string(),
|
||||
filename: Some(format!(r#"modeling-app/coredump-{coredump_id}-screenshot.png"#)),
|
||||
filepath: Some(format!(r#"modeling-app/coredump-{coredump_id}-screenshot.png"#).into()),
|
||||
content_type: Some("image/png".to_string()),
|
||||
data,
|
||||
}])
|
||||
@ -101,7 +101,7 @@ pub trait CoreDump: Clone {
|
||||
.meta()
|
||||
.create_debug_uploads(vec![kittycad::types::multipart::Attachment {
|
||||
name: "".to_string(),
|
||||
filename: Some(format!(r#"modeling-app/coredump-{}.json"#, coredump_id)),
|
||||
filepath: Some(format!(r#"modeling-app/coredump-{}.json"#, coredump_id).into()),
|
||||
content_type: Some("application/json".to_string()),
|
||||
data,
|
||||
}])
|
||||
|
@ -339,9 +339,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,
|
||||
@ -580,6 +596,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 +615,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 +628,7 @@ impl TyData {
|
||||
}
|
||||
|
||||
TyData {
|
||||
preferred_name: format!("{preferred_prefix}{name}"),
|
||||
name,
|
||||
qual_name,
|
||||
properties: Properties {
|
||||
@ -641,7 +664,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 +681,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,
|
||||
|
@ -1141,7 +1141,7 @@ mod tests {
|
||||
let snippet = scale_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"scale(${0:%}, scale = [${1:3.14}, ${2:3.14}, ${3:3.14}])${}"#
|
||||
r#"scale(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
@ -1152,7 +1152,7 @@ mod tests {
|
||||
let snippet = translate_fn.to_autocomplete_snippet().unwrap();
|
||||
assert_eq!(
|
||||
snippet,
|
||||
r#"translate(${0:%}, translate = [${1:3.14}, ${2:3.14}, ${3:3.14}])${}"#
|
||||
r#"translate(${0:%}, x = ${1:3.14}, y = ${2:3.14}, z = ${3:3.14})${}"#
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,13 @@ 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);
|
||||
|
||||
|
@ -6,7 +6,7 @@ use itertools::{EitherOrBoth, Itertools};
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
use crate::{
|
||||
execution::{annotations, memory::Stack, EnvironmentRef, ExecState, ExecutorSettings},
|
||||
execution::{annotations, memory::Stack, state::ModuleInfoMap, EnvironmentRef, ExecState, ExecutorSettings},
|
||||
parsing::ast::types::{Annotation, Node, Program},
|
||||
walk::Node as WalkNode,
|
||||
};
|
||||
@ -15,7 +15,7 @@ lazy_static::lazy_static! {
|
||||
/// A static mutable lock for updating the last successful execution state for the cache.
|
||||
static ref OLD_AST: Arc<RwLock<Option<OldAstState>>> = Default::default();
|
||||
// The last successful run's memory. Not cleared after an unssuccessful run.
|
||||
static ref PREV_MEMORY: Arc<RwLock<Option<Stack>>> = Default::default();
|
||||
static ref PREV_MEMORY: Arc<RwLock<Option<(Stack, ModuleInfoMap)>>> = Default::default();
|
||||
}
|
||||
|
||||
/// Read the old ast memory from the lock.
|
||||
@ -29,12 +29,12 @@ pub(super) async fn write_old_ast(old_state: OldAstState) {
|
||||
*old_ast = Some(old_state);
|
||||
}
|
||||
|
||||
pub(crate) async fn read_old_memory() -> Option<Stack> {
|
||||
pub(crate) async fn read_old_memory() -> Option<(Stack, ModuleInfoMap)> {
|
||||
let old_mem = PREV_MEMORY.read().await;
|
||||
old_mem.clone()
|
||||
}
|
||||
|
||||
pub(super) async fn write_old_memory(mem: Stack) {
|
||||
pub(super) async fn write_old_memory(mem: (Stack, ModuleInfoMap)) {
|
||||
let mut old_mem = PREV_MEMORY.write().await;
|
||||
*old_mem = Some(mem);
|
||||
}
|
||||
|
@ -529,6 +529,18 @@ impl ExecutorContext {
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn bust_cache_and_reset_scene(&self) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||
cache::bust_cache().await;
|
||||
|
||||
// Execute an empty program to clear and reset the scene.
|
||||
// We specifically want to be returned the objects after the scene is reset.
|
||||
// Like the default planes so it is easier to just execute an empty program
|
||||
// after the cache is busted.
|
||||
let outcome = self.run_with_caching(crate::Program::empty()).await?;
|
||||
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
async fn prepare_mem(&self, exec_state: &mut ExecState) -> Result<(), KclErrorWithOutputs> {
|
||||
self.eval_prelude(exec_state, SourceRange::synthetic())
|
||||
.await
|
||||
@ -547,7 +559,10 @@ impl ExecutorContext {
|
||||
let mut exec_state = ExecState::new(self);
|
||||
if use_prev_memory {
|
||||
match cache::read_old_memory().await {
|
||||
Some(mem) => *exec_state.mut_stack() = mem,
|
||||
Some(mem) => {
|
||||
*exec_state.mut_stack() = mem.0;
|
||||
exec_state.global.module_infos = mem.1;
|
||||
}
|
||||
None => self.prepare_mem(&mut exec_state).await?,
|
||||
}
|
||||
} else {
|
||||
@ -565,10 +580,11 @@ impl ExecutorContext {
|
||||
// memory, not to the exec_state which is not cached for mock execution.
|
||||
|
||||
let mut mem = exec_state.stack().clone();
|
||||
let module_infos = exec_state.global.module_infos.clone();
|
||||
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
|
||||
|
||||
mem.squash_env(result.0);
|
||||
cache::write_old_memory(mem).await;
|
||||
cache::write_old_memory((mem, module_infos)).await;
|
||||
|
||||
Ok(outcome)
|
||||
}
|
||||
@ -758,7 +774,7 @@ impl ExecutorContext {
|
||||
if !self.is_mock() {
|
||||
let mut mem = exec_state.stack().deep_clone();
|
||||
mem.restore_env(env_ref);
|
||||
cache::write_old_memory(mem).await;
|
||||
cache::write_old_memory((mem, exec_state.global.module_infos.clone())).await;
|
||||
}
|
||||
let session_data = self.engine.get_session_data().await;
|
||||
Ok((env_ref, session_data))
|
||||
@ -2044,6 +2060,8 @@ let w = f() + f()
|
||||
|
||||
// Ensure the settings are as expected.
|
||||
assert_eq!(settings_state, ctx.settings);
|
||||
|
||||
ctx.close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2057,6 +2075,9 @@ 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")]
|
||||
|
@ -30,6 +30,8 @@ pub struct ExecState {
|
||||
pub(super) exec_context: Option<super::ExecutorContext>,
|
||||
}
|
||||
|
||||
pub type ModuleInfoMap = IndexMap<ModuleId, ModuleInfo>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct GlobalState {
|
||||
/// Map from source file absolute path to module ID.
|
||||
@ -37,7 +39,7 @@ pub(super) struct GlobalState {
|
||||
/// Map from module ID to source file.
|
||||
pub id_to_source: IndexMap<ModuleId, ModuleSource>,
|
||||
/// Map from module ID to module info.
|
||||
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
||||
pub module_infos: ModuleInfoMap,
|
||||
/// Output map of UUIDs to artifacts.
|
||||
pub artifacts: IndexMap<ArtifactId, Artifact>,
|
||||
/// Output commands to allow building the artifact graph by the caller.
|
||||
|
@ -97,7 +97,7 @@ pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||
pub use source_range::SourceRange;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
pub use unparser::recast_dir;
|
||||
pub use unparser::{recast_dir, walk_dir};
|
||||
|
||||
// Rather than make executor public and make lots of it pub(crate), just re-export into a new module.
|
||||
// Ideally we wouldn't export these things at all, they should only be used for testing.
|
||||
@ -211,6 +211,14 @@ impl Program {
|
||||
pub fn recast_with_options(&self, options: &FormatOptions) -> String {
|
||||
self.ast.recast(options, 0)
|
||||
}
|
||||
|
||||
/// Create an empty program.
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
ast: parsing::ast::types::Node::no_src(parsing::ast::types::Program::default()),
|
||||
original_file_contents: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -786,7 +786,7 @@ impl Backend {
|
||||
vec![kittycad::types::multipart::Attachment {
|
||||
// Clean the URI part.
|
||||
name: "attachment".to_string(),
|
||||
filename: Some("attachment.zip".to_string()),
|
||||
filepath: Some("attachment.zip".into()),
|
||||
content_type: Some("application/x-zip".to_string()),
|
||||
data: self.create_zip().await?,
|
||||
}],
|
||||
@ -1635,7 +1635,7 @@ fn position_to_char_index(position: Position, code: &str) -> usize {
|
||||
|
||||
async fn with_cached_var<T>(name: &str, f: impl Fn(&KclValue) -> T) -> Option<T> {
|
||||
let mem = cache::read_old_memory().await?;
|
||||
let value = mem.get(name, SourceRange::default()).ok()?;
|
||||
let value = mem.0.get(name, SourceRange::default()).ok()?;
|
||||
|
||||
Some(f(value))
|
||||
}
|
||||
|
@ -1016,6 +1016,8 @@ startSketchOn(XY)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2318,6 +2320,8 @@ async fn kcl_test_kcl_lsp_on_change_update_memory() {
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
@ -2390,6 +2394,8 @@ part001 = cube([0,0], 20)
|
||||
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::M);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2506,6 +2512,8 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() {
|
||||
|
||||
// Get the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2556,6 +2564,8 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(ast.ast, default_hashed);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2642,6 +2652,8 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2717,6 +2729,8 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2795,6 +2809,8 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2852,6 +2868,8 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2975,6 +2993,8 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3243,6 +3263,8 @@ part001 = startSketchOn(XY)
|
||||
|
||||
// Check the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 2);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3310,6 +3332,8 @@ NEW_LINT = 1"#
|
||||
|
||||
// Check the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 2);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3377,6 +3401,8 @@ NEW_LINT = 1"#
|
||||
|
||||
// Check the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3460,6 +3486,8 @@ NEW_LINT = 1"#
|
||||
|
||||
// Check the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3551,6 +3579,8 @@ part001 = startSketchOn(XY)
|
||||
|
||||
// Check the diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 2);
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3649,4 +3679,6 @@ async fn kcl_test_kcl_lsp_multi_file_error() {
|
||||
} else {
|
||||
panic!("Expected diagnostics");
|
||||
}
|
||||
|
||||
server.executor_ctx().await.clone().unwrap().close().await;
|
||||
}
|
||||
|
@ -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,6 +133,13 @@ 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)
|
||||
}
|
||||
@ -345,7 +352,10 @@ impl Node<Program> {
|
||||
let mut found = false;
|
||||
for node in &mut new_program.inner_attrs {
|
||||
if node.name() == Some(annotations::SETTINGS) {
|
||||
*node = Node::no_src(Annotation::new_from_meta_settings(&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();
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@ -1609,19 +1619,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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -4140,6 +4152,50 @@ 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 {
|
||||
|
@ -1823,14 +1823,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(
|
||||
@ -2341,21 +2333,6 @@ fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(suggestion) = super::deprecation(&result.name, DeprecationKind::Const) {
|
||||
ParseContext::warn(
|
||||
CompilationError::err(
|
||||
result.as_source_range(),
|
||||
format!("Using `{}` is deprecated, prefer using `{}`.", result.name, suggestion),
|
||||
)
|
||||
.with_suggestion(
|
||||
format!("Replace `{}` with `{}`", result.name, suggestion),
|
||||
suggestion,
|
||||
None,
|
||||
Tag::Deprecated,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
@ -2374,8 +2351,7 @@ fn name(i: &mut TokenSlice) -> PResult<Node<Name>> {
|
||||
let name = idents.pop().unwrap();
|
||||
let end = name.end;
|
||||
let module_id = name.module_id;
|
||||
|
||||
Ok(Node::new(
|
||||
let result = Node::new(
|
||||
Name {
|
||||
name,
|
||||
path: idents,
|
||||
@ -2385,7 +2361,24 @@ fn name(i: &mut TokenSlice) -> PResult<Node<Name>> {
|
||||
start,
|
||||
end,
|
||||
module_id,
|
||||
))
|
||||
);
|
||||
|
||||
if let Some(suggestion) = super::deprecation(&result.to_string(), DeprecationKind::Const) {
|
||||
ParseContext::warn(
|
||||
CompilationError::err(
|
||||
result.as_source_range(),
|
||||
format!("Using `{result}` is deprecated, prefer using `{suggestion}`."),
|
||||
)
|
||||
.with_suggestion(
|
||||
format!("Replace `{result}` with `{suggestion}`"),
|
||||
suggestion,
|
||||
None,
|
||||
Tag::Deprecated,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
impl TryFrom<Token> for Node<TagDeclarator> {
|
||||
|
@ -114,6 +114,8 @@ async fn unparse_test(test: &Test) {
|
||||
let kcl_files = kcl_files.into_iter().filter(|f| f != &entry_point);
|
||||
let futures = kcl_files
|
||||
.into_iter()
|
||||
.filter(|file| file.extension().is_some_and(|ext| ext == "kcl")) // We only care about kcl
|
||||
// files here.
|
||||
.map(|file| {
|
||||
let snap_path = Path::new("..").join(&test.output_dir);
|
||||
tokio::spawn(async move {
|
||||
@ -2379,3 +2381,66 @@ 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
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ pub async fn union(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "union",
|
||||
feature_tree_operation = false,
|
||||
feature_tree_operation = true,
|
||||
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 = false,
|
||||
feature_tree_operation = true,
|
||||
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 = false,
|
||||
feature_tree_operation = true,
|
||||
keywords = true,
|
||||
unlabeled_first = true,
|
||||
deprecated = true,
|
||||
|
@ -28,15 +28,22 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let scale = args.get_kw_arg("scale")?;
|
||||
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 global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let objects = inner_scale(objects, scale, global, exec_state, args).await?;
|
||||
let objects = inner_scale(objects, scale_x, scale_y, scale_z, 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
|
||||
@ -78,7 +85,9 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// |> scale(
|
||||
/// scale = [1.0, 1.0, 2.5],
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -89,7 +98,9 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
||||
///
|
||||
/// cube
|
||||
/// |> scale(
|
||||
/// scale = [1.0, 1.0, 2.5],
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -124,7 +135,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, scale = [1.0, 1.0, 0.5])
|
||||
/// scale(parts, x = 1.0, y = 1.0, z = 0.5)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "scale",
|
||||
@ -133,13 +144,17 @@ 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."},
|
||||
scale = {docs = "The scale factor for the x, y, and z axes."},
|
||||
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."},
|
||||
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,
|
||||
scale: [f64; 3],
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
@ -159,11 +174,7 @@ async fn inner_scale(
|
||||
object_id,
|
||||
transforms: vec![shared::ComponentTransform {
|
||||
scale: Some(shared::TransformBy::<Point3d<f64>> {
|
||||
property: Point3d {
|
||||
x: scale[0],
|
||||
y: scale[1],
|
||||
z: scale[2],
|
||||
},
|
||||
property: Point3d { x, y, z },
|
||||
set: false,
|
||||
is_local: !global.unwrap_or(false),
|
||||
}),
|
||||
@ -190,15 +201,23 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
]),
|
||||
exec_state,
|
||||
)?;
|
||||
let translate = args.get_kw_arg("translate")?;
|
||||
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 global = args.get_kw_arg_opt("global")?;
|
||||
|
||||
let objects = inner_translate(objects, translate, global, exec_state, args).await?;
|
||||
let objects = inner_translate(objects, translate_x, translate_y, translate_z, 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.
|
||||
///
|
||||
@ -232,7 +251,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// |> hole(pipeHole, %)
|
||||
/// |> sweep(path = sweepPath)
|
||||
/// |> translate(
|
||||
/// translate = [1.0, 1.0, 2.5],
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -243,7 +264,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
///
|
||||
/// cube
|
||||
/// |> translate(
|
||||
/// translate = [1.0, 1.0, 2.5],
|
||||
/// x = 1.0,
|
||||
/// y = 1.0,
|
||||
/// z = 2.5,
|
||||
/// )
|
||||
/// ```
|
||||
///
|
||||
@ -278,7 +301,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
///
|
||||
/// // Move the sweeps.
|
||||
/// translate(parts, translate = [1.0, 1.0, 2.5])
|
||||
/// translate(parts, x = 1.0, y = 1.0, z = 2.5)
|
||||
/// ```
|
||||
///
|
||||
/// ```no_run
|
||||
@ -301,7 +324,9 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
///
|
||||
/// square(10)
|
||||
/// |> translate(
|
||||
/// translate = [5, 5, 0],
|
||||
/// x = 5,
|
||||
/// y = 5,
|
||||
/// z = 0,
|
||||
/// )
|
||||
/// |> extrude(
|
||||
/// length = 10,
|
||||
@ -324,7 +349,7 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
||||
/// profile001 = square()
|
||||
///
|
||||
/// profile002 = square()
|
||||
/// |> translate(translate = [0, 0, 20])
|
||||
/// |> translate(x = 0, y = 0, z = 20)
|
||||
/// |> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
///
|
||||
/// loft([profile001, profile002])
|
||||
@ -336,13 +361,17 @@ 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."},
|
||||
translate = {docs = "The amount to move the solid or sketch in all three axes."},
|
||||
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."},
|
||||
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,
|
||||
translate: [f64; 3],
|
||||
x: f64,
|
||||
y: f64,
|
||||
z: f64,
|
||||
global: Option<bool>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
@ -363,9 +392,9 @@ async fn inner_translate(
|
||||
transforms: vec![shared::ComponentTransform {
|
||||
translate: Some(shared::TransformBy::<Point3d<LengthUnit>> {
|
||||
property: shared::Point3d {
|
||||
x: LengthUnit(translate[0]),
|
||||
y: LengthUnit(translate[1]),
|
||||
z: LengthUnit(translate[2]),
|
||||
x: LengthUnit(x),
|
||||
y: LengthUnit(y),
|
||||
z: LengthUnit(z),
|
||||
},
|
||||
set: false,
|
||||
is_local: !global.unwrap_or(false),
|
||||
@ -506,6 +535,11 @@ 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
|
||||
@ -667,7 +701,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// profile001 = square()
|
||||
///
|
||||
/// profile002 = square()
|
||||
/// |> translate(translate = [0, 0, 20])
|
||||
/// |> translate(x = 0, y = 0, z = 20)
|
||||
/// |> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
///
|
||||
/// loft([profile001, profile002])
|
||||
|
@ -1,5 +1,8 @@
|
||||
use std::fmt::Write;
|
||||
|
||||
#[cfg(feature = "cli")]
|
||||
use clap::ValueEnum;
|
||||
|
||||
use crate::parsing::{
|
||||
ast::types::{
|
||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||
@ -864,10 +867,33 @@ impl Parameter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect all the kcl files in a directory, recursively.
|
||||
lazy_static::lazy_static! {
|
||||
|
||||
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
|
||||
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
|
||||
#[cfg(feature = "cli")]
|
||||
let named_extensions = kittycad::types::FileImportFormat::value_variants()
|
||||
.iter()
|
||||
.map(|x| format!("{}", x))
|
||||
.collect::<Vec<String>>();
|
||||
#[cfg(not(feature = "cli"))]
|
||||
let named_extensions = vec![]; // We don't really need this outside of the CLI.
|
||||
// Add all the default import formats.
|
||||
import_file_extensions.extend_from_slice(&named_extensions);
|
||||
import_file_extensions
|
||||
};
|
||||
|
||||
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
|
||||
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
|
||||
relevant_extensions.push("kcl".to_string());
|
||||
relevant_extensions
|
||||
};
|
||||
}
|
||||
|
||||
/// Collect all the kcl (and other relevant) files in a directory, recursively.
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[async_recursion::async_recursion]
|
||||
pub(crate) async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf>, anyhow::Error> {
|
||||
pub async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf>, anyhow::Error> {
|
||||
// Make sure we actually have a directory.
|
||||
if !dir.is_dir() {
|
||||
anyhow::bail!("`{}` is not a directory", dir.display());
|
||||
@ -881,7 +907,10 @@ pub(crate) async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::
|
||||
|
||||
if path.is_dir() {
|
||||
files.extend(walk_dir(&path).await?);
|
||||
} else if path.extension().is_some_and(|ext| ext == "kcl") {
|
||||
} else if path
|
||||
.extension()
|
||||
.is_some_and(|ext| RELEVANT_EXTENSIONS.contains(&ext.to_string_lossy().to_string()))
|
||||
{
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
@ -901,6 +930,8 @@ pub async fn recast_dir(dir: &std::path::Path, options: &crate::FormatOptions) -
|
||||
|
||||
let futures = files
|
||||
.into_iter()
|
||||
.filter(|file| file.extension().is_some_and(|ext| ext == "kcl")) // We only care about kcl
|
||||
// files here.
|
||||
.map(|file| {
|
||||
let options = options.clone();
|
||||
tokio::spawn(async move {
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
export import * from "std::math"
|
||||
export import * from "std::sketch"
|
||||
export import "std::turns"
|
||||
|
||||
/// A number
|
||||
///
|
||||
@ -245,11 +246,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 },
|
||||
|
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]
|
||||
|
@ -5,10 +5,10 @@ description: Variables in memory after executing assembly_mixed_units_cubes.kcl
|
||||
{
|
||||
"cubeIn": {
|
||||
"type": "Module",
|
||||
"value": 4
|
||||
"value": 5
|
||||
},
|
||||
"cubeMm": {
|
||||
"type": "Module",
|
||||
"value": 5
|
||||
"value": 6
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[197, 232, 4]"]
|
||||
3["Segment<br>[197, 232, 4]"]
|
||||
2["Path<br>[197, 232, 5]"]
|
||||
3["Segment<br>[197, 232, 5]"]
|
||||
4[Solid2d]
|
||||
end
|
||||
subgraph path6 [Path]
|
||||
6["Path<br>[113, 148, 5]"]
|
||||
7["Segment<br>[113, 148, 5]"]
|
||||
6["Path<br>[113, 148, 6]"]
|
||||
7["Segment<br>[113, 148, 6]"]
|
||||
8[Solid2d]
|
||||
end
|
||||
1["Plane<br>[172, 191, 4]"]
|
||||
5["Plane<br>[88, 107, 5]"]
|
||||
1["Plane<br>[172, 191, 5]"]
|
||||
5["Plane<br>[88, 107, 6]"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
|
@ -5,10 +5,10 @@ description: Variables in memory after executing assembly_non_default_units.kcl
|
||||
{
|
||||
"other1": {
|
||||
"type": "Module",
|
||||
"value": 4
|
||||
"value": 5
|
||||
},
|
||||
"other2": {
|
||||
"type": "Module",
|
||||
"value": 5
|
||||
"value": 6
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ description: Variables in memory after executing import_foreign.kcl
|
||||
{
|
||||
"cube": {
|
||||
"type": "Module",
|
||||
"value": 4
|
||||
"value": 5
|
||||
},
|
||||
"model": {
|
||||
"type": "ImportedGeometry",
|
||||
|
@ -151,54 +151,65 @@ description: Result of parsing import_transform.kcl
|
||||
"label": {
|
||||
"commentStart": 159,
|
||||
"end": 0,
|
||||
"name": "translate",
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 171,
|
||||
"elements": [
|
||||
{
|
||||
"commentStart": 172,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commentStart": 178,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commentStart": 184,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commentStart": 163,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 169,
|
||||
"end": 0,
|
||||
"name": "y",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 173,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 179,
|
||||
"end": 0,
|
||||
"name": "z",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 183,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -235,65 +246,76 @@ description: Result of parsing import_transform.kcl
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 205,
|
||||
"commentStart": 203,
|
||||
"end": 0,
|
||||
"name": "scale",
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 213,
|
||||
"elements": [
|
||||
{
|
||||
"commentStart": 214,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commentStart": 220,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"commentStart": 226,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"commentStart": 207,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 213,
|
||||
"end": 0,
|
||||
"name": "y",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 217,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"commentStart": 223,
|
||||
"end": 0,
|
||||
"name": "z",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"arg": {
|
||||
"commentStart": 227,
|
||||
"end": 0,
|
||||
"raw": "3.14",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 3.14,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"abs_path": false,
|
||||
"commentStart": 196,
|
||||
"commentStart": 194,
|
||||
"end": 0,
|
||||
"name": {
|
||||
"commentStart": 196,
|
||||
"commentStart": 194,
|
||||
"end": 0,
|
||||
"name": "scale",
|
||||
"start": 0,
|
||||
@ -303,13 +325,13 @@ description: Result of parsing import_transform.kcl
|
||||
"start": 0,
|
||||
"type": "Name"
|
||||
},
|
||||
"commentStart": 196,
|
||||
"commentStart": 194,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": {
|
||||
"commentStart": 202,
|
||||
"commentStart": 200,
|
||||
"end": 0,
|
||||
"start": 0,
|
||||
"type": "PipeSubstitution",
|
||||
|
@ -7,5 +7,5 @@ screw
|
||||
pitch = 3.14,
|
||||
yaw = 3.14,
|
||||
)
|
||||
|> translate(%, translate = [3.14, 3.14, 3.14])
|
||||
|> scale(%, scale = [3.14, 3.14, 3.14])
|
||||
|> translate(%, x = 3.14, y = 3.14, z = 3.14)
|
||||
|> scale(%, x = 3.14, y = 3.14, z = 3.14)
|
||||
|
@ -5,6 +5,6 @@ description: Variables in memory after executing import_transform.kcl
|
||||
{
|
||||
"screw": {
|
||||
"type": "Module",
|
||||
"value": 4
|
||||
"value": 5
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,15 @@ screw
|
||||
pitch = 3.14,
|
||||
yaw = 3.14,
|
||||
)
|
||||
|> translate(%, translate = [3.14, 3.14, 3.14])
|
||||
|> scale(%, scale = [3.14, 3.14, 3.14])
|
||||
|> translate(
|
||||
%,
|
||||
x = 3.14,
|
||||
y = 3.14,
|
||||
z = 3.14,
|
||||
)
|
||||
|> scale(
|
||||
%,
|
||||
x = 3.14,
|
||||
y = 3.14,
|
||||
z = 3.14,
|
||||
)
|
||||
|
@ -1,12 +1,12 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[83, 119, 4]"]
|
||||
3["Segment<br>[83, 119, 4]"]
|
||||
2["Path<br>[83, 119, 5]"]
|
||||
3["Segment<br>[83, 119, 5]"]
|
||||
4[Solid2d]
|
||||
end
|
||||
1["Plane<br>[60, 77, 4]"]
|
||||
5["Sweep Extrusion<br>[125, 145, 4]"]
|
||||
1["Plane<br>[60, 77, 5]"]
|
||||
5["Sweep Extrusion<br>[125, 145, 5]"]
|
||||
6[Wall]
|
||||
7["Cap Start"]
|
||||
8["Cap End"]
|
||||
|
@ -111,6 +111,6 @@ description: Variables in memory after executing import_whole.kcl
|
||||
},
|
||||
"foo": {
|
||||
"type": "Module",
|
||||
"value": 4
|
||||
"value": 5
|
||||
}
|
||||
}
|
||||
|
554
rust/kcl-lib/tests/intersect_cubes/artifact_commands.snap
Normal file
@ -0,0 +1,554 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact commands intersect_cubes.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -10.0,
|
||||
"y": -10.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 10.0,
|
||||
"y": -10.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 10.0,
|
||||
"y": 10.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": -10.0,
|
||||
"y": 10.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extrude",
|
||||
"target": "[uuid]",
|
||||
"distance": 10.0,
|
||||
"faces": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"size": 60.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -2.0,
|
||||
"y": -2.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 18.0,
|
||||
"y": -2.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 18.0,
|
||||
"y": 18.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": -2.0,
|
||||
"y": 18.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "enable_sketch_mode",
|
||||
"entity_id": "[uuid]",
|
||||
"ortho": false,
|
||||
"animated": false,
|
||||
"adjust_camera": false,
|
||||
"planar_normal": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "extrude",
|
||||
"target": "[uuid]",
|
||||
"distance": 10.0,
|
||||
"faces": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "sketch_mode_disable"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart intersect_cubes.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,117 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[52, 103, 0]"]
|
||||
3["Segment<br>[111, 163, 0]"]
|
||||
4["Segment<br>[171, 223, 0]"]
|
||||
5["Segment<br>[231, 283, 0]"]
|
||||
6["Segment<br>[291, 298, 0]"]
|
||||
7[Solid2d]
|
||||
end
|
||||
subgraph path24 [Path]
|
||||
24["Path<br>[52, 103, 0]"]
|
||||
25["Segment<br>[111, 163, 0]"]
|
||||
26["Segment<br>[171, 223, 0]"]
|
||||
27["Segment<br>[231, 283, 0]"]
|
||||
28["Segment<br>[291, 298, 0]"]
|
||||
29[Solid2d]
|
||||
end
|
||||
1["Plane<br>[27, 44, 0]"]
|
||||
8["Sweep Extrusion<br>[306, 326, 0]"]
|
||||
9[Wall]
|
||||
10[Wall]
|
||||
11[Wall]
|
||||
12[Wall]
|
||||
13["Cap Start"]
|
||||
14["Cap End"]
|
||||
15["SweepEdge Opposite"]
|
||||
16["SweepEdge Adjacent"]
|
||||
17["SweepEdge Opposite"]
|
||||
18["SweepEdge Adjacent"]
|
||||
19["SweepEdge Opposite"]
|
||||
20["SweepEdge Adjacent"]
|
||||
21["SweepEdge Opposite"]
|
||||
22["SweepEdge Adjacent"]
|
||||
23["Plane<br>[27, 44, 0]"]
|
||||
30["Sweep Extrusion<br>[306, 326, 0]"]
|
||||
31[Wall]
|
||||
32[Wall]
|
||||
33[Wall]
|
||||
34[Wall]
|
||||
35["Cap Start"]
|
||||
36["Cap End"]
|
||||
37["SweepEdge Opposite"]
|
||||
38["SweepEdge Adjacent"]
|
||||
39["SweepEdge Opposite"]
|
||||
40["SweepEdge Adjacent"]
|
||||
41["SweepEdge Opposite"]
|
||||
42["SweepEdge Adjacent"]
|
||||
43["SweepEdge Opposite"]
|
||||
44["SweepEdge Adjacent"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
2 --- 4
|
||||
2 --- 5
|
||||
2 --- 6
|
||||
2 ---- 8
|
||||
2 --- 7
|
||||
3 --- 9
|
||||
3 --- 15
|
||||
3 --- 16
|
||||
4 --- 10
|
||||
4 --- 17
|
||||
4 --- 18
|
||||
5 --- 11
|
||||
5 --- 19
|
||||
5 --- 20
|
||||
6 --- 12
|
||||
6 --- 21
|
||||
6 --- 22
|
||||
8 --- 9
|
||||
8 --- 10
|
||||
8 --- 11
|
||||
8 --- 12
|
||||
8 --- 13
|
||||
8 --- 14
|
||||
8 --- 15
|
||||
8 --- 16
|
||||
8 --- 17
|
||||
8 --- 18
|
||||
8 --- 19
|
||||
8 --- 20
|
||||
8 --- 21
|
||||
8 --- 22
|
||||
23 --- 24
|
||||
24 --- 25
|
||||
24 --- 26
|
||||
24 --- 27
|
||||
24 --- 28
|
||||
24 ---- 30
|
||||
24 --- 29
|
||||
25 --- 31
|
||||
25 --- 37
|
||||
25 --- 38
|
||||
26 --- 32
|
||||
26 --- 39
|
||||
26 --- 40
|
||||
27 --- 33
|
||||
27 --- 41
|
||||
27 --- 42
|
||||
28 --- 34
|
||||
28 --- 43
|
||||
28 --- 44
|
||||
30 --- 31
|
||||
30 --- 32
|
||||
30 --- 33
|
||||
30 --- 34
|
||||
30 --- 35
|
||||
30 --- 36
|
||||
30 --- 37
|
||||
30 --- 38
|
||||
30 --- 39
|
||||
30 --- 40
|
||||
30 --- 41
|
||||
30 --- 42
|
||||
30 --- 43
|
||||
30 --- 44
|
||||
```
|
1011
rust/kcl-lib/tests/intersect_cubes/ast.snap
Normal file
14
rust/kcl-lib/tests/intersect_cubes/input.kcl
Normal file
@ -0,0 +1,14 @@
|
||||
fn cube(center) {
|
||||
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])
|
||||
|> line(endAbsolute = [center[0] - 10, center[1] + 10])
|
||||
|> close()
|
||||
|> extrude(length = 10)
|
||||
}
|
||||
|
||||
part001 = cube([0, 0])
|
||||
part002 = cube([8, 8])
|
||||
|
||||
fullPart = intersect([part001, part002])
|
158
rust/kcl-lib/tests/intersect_cubes/ops.snap
Normal file
@ -0,0 +1,158 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Operations executed intersect_cubes.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"type": "UserDefinedFunctionCall",
|
||||
"name": "cube",
|
||||
"functionSourceRange": [
|
||||
7,
|
||||
328,
|
||||
0
|
||||
],
|
||||
"unlabeledArg": null,
|
||||
"labeledArgs": {},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "Plane",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 10.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "extrude",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UserDefinedFunctionReturn"
|
||||
},
|
||||
{
|
||||
"type": "UserDefinedFunctionCall",
|
||||
"name": "cube",
|
||||
"functionSourceRange": [
|
||||
7,
|
||||
328,
|
||||
0
|
||||
],
|
||||
"unlabeledArg": null,
|
||||
"labeledArgs": {},
|
||||
"sourceRange": []
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "Plane",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 10.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "extrude",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UserDefinedFunctionReturn"
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"solids": {
|
||||
"value": {
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"name": "intersect",
|
||||
"sourceRange": [],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
}
|
||||
]
|
543
rust/kcl-lib/tests/intersect_cubes/program_memory.snap
Normal file
@ -0,0 +1,543 @@
|
||||
---
|
||||
source: kcl-lib/src/simulation_tests.rs
|
||||
description: Variables in memory after executing intersect_cubes.kcl
|
||||
---
|
||||
{
|
||||
"cube": {
|
||||
"type": "Function"
|
||||
},
|
||||
"fullPart": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
10.0,
|
||||
-10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
-10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
10.0,
|
||||
10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-10.0,
|
||||
10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"to": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 10.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"part001": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
10.0,
|
||||
-10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
-10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
10.0,
|
||||
10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
10.0,
|
||||
10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-10.0,
|
||||
10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-10.0,
|
||||
10.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"to": [
|
||||
-10.0,
|
||||
-10.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 10.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
},
|
||||
"part002": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
},
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [],
|
||||
"tag": null,
|
||||
"type": "extrudePlane"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-2.0,
|
||||
-2.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
18.0,
|
||||
-2.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
18.0,
|
||||
-2.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
18.0,
|
||||
18.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
18.0,
|
||||
18.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-2.0,
|
||||
18.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
},
|
||||
"from": [
|
||||
-2.0,
|
||||
18.0
|
||||
],
|
||||
"tag": null,
|
||||
"to": [
|
||||
-2.0,
|
||||
-2.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-2.0,
|
||||
-2.0
|
||||
],
|
||||
"to": [
|
||||
-2.0,
|
||||
-2.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": []
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"height": 10.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
BIN
rust/kcl-lib/tests/intersect_cubes/rendered_model.png
Normal file
After Width: | Height: | Size: 67 KiB |