Compare commits

...

33 Commits

Author SHA1 Message Date
d93a57d7bf more lsp tests / pass python a bool on parse (#6975)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* parse returns bool for python

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-15 11:04:53 -07:00
d34aea345b #6548 Fix autocomplete for offsetPlane (#6940)
* handle Plane in get_autocomplete_snippet to fix missing default plane value for offsetPlane

* adds test for offsetPlane default plane value

* cleanup test

* use XY instead of XZ for default plane

* add rust test for offsetplane autocomplete

* remove playwright test for offestPlane, as it has rust test now and there are autocomplete tests already
2025-05-15 19:53:35 +02:00
0b6102b0ac #6214 Fix center circle radius issues (#6971)
* add missing radius line for center circle

* remove some duplication by introducing createLineShape
2025-05-15 13:30:25 -04:00
9e0873ed84 Remove updater-test builds (#6962)
Remove updater-test
2025-05-15 13:25:40 -04:00
8587eb5fea Make idle sestting 30sec in nightly (#6972) 2025-05-15 17:17:59 +00:00
b898c27e74 Add KCL constants for sweep relativeTo strings (#6974)
* Add KCL constants for sweep relativeTo strings

Before:

```
sweep(pill, path = sweepPath, relativeTo = "trajectoryCurve")
```

After:

```
sweep(pill, path = sweepPath, relativeTo = sweep::TRAJECTORY)
```

* Update docs
2025-05-15 10:13:04 -07:00
3026866a16 Remove old dry-run validations on Sweep, Loft, Revolve, and Shell (#6969) 2025-05-15 12:11:40 -05:00
92fc294eae Update mesh clone test (#6597) 2025-05-15 16:46:37 +00:00
21e967ea7f More consistent error handling in modelingMachine codemods (#6910)
* First pass at consistency in modelingMachine codemods

* Add 'no kcl errors' guard instead of if check for all the existing ones

* Add more commands and improve consistency

* Add comments

* Fix test with old kcl that was showcasing the very behavior we're trying to fix
https://kittycadworkspace.slack.com/archives/C07A80B83FS/p1747231832870739?thread_ts=1747231178.515289&cid=C07A80B83FS

* Add test for sketch and helix

* Remove guard use and move hasErrors check closer to updateAst calls

* Revert "Remove guard use and move hasErrors check closer to updateAst calls"

This reverts commit 868ea4b605.

* Remove toasts from guards

* Remove some scene.settled calls

* Lint

* More shaky fixes

* Clean up and more test fixes

---------

Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
2025-05-15 12:26:20 -04:00
3f00e7186c Backoff Reconnect (#6358)
* Add a Stop command that does a bit of cleanup in the engineStateMachine

* Major network code startup refactor; backoff reconnect; many more logs for us

* Save camera state after every user interaction in case of disconnection and restoral

* Translate basic WebSocket error numbers into useful text

* Add a RestartRequest event to the engineCommandManager

* Adjust for a rebase

* Add E2E test for stream pause behavior

* Fix snapshot test

* fmt, lint

* small issue

* Fix tests

* Fix up idle test

* One last fix

* fixes

* Remove circ dep

* fix test

* use a const for time

* TEST -> isPlaywright instead

* whoops
2025-05-15 11:58:00 -04:00
d3a4fd8b55 Raw dog parse for python bindings (#6970)
* raw dog parse python

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* add tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-15 15:39:28 +00:00
2be7107cca External: Fix: symbol replace text box dark mode contrast (#6827) (#6927)
* fix: symbol replace text box dark mode contrast (#6827)

* fix: symbol replace text box dark mode contrast (#6827)

* correct fix for rename popup white text on white background #6827

* correct fix for rename popup white text on white background #6827

* added comments explaining the change

---------

Co-authored-by: rajgandhi1 <rajgandhi9952@gmail.com>
2025-05-15 12:57:39 +00:00
94f194a984 new sweep default (#6965)
snaps

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-15 11:50:55 +00:00
4fe880a970 Revert "fixes"
This reverts commit 8f5fbfc273.
2025-05-15 04:28:20 -07:00
8f5fbfc273 fixes
Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-15 04:27:59 -07:00
e660f52bb0 Restore automated snapshot commits (#6960)
* Restore automated snapshot commits

* Update snapshots

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-05-15 03:28:42 +00:00
d74fdd9369 Color picker goober works w single quotes (#6957)
* lsp stuffs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-15 12:37:14 +10:00
334145f0be fix sketch on face of union (#6949)
* fix sketch on face of union

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* rotate the model

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-14 23:25:12 +00:00
c24073b6ae Sensible parser error when using keyword as arg label (#6948)
Closes <https://github.com/KittyCAD/modeling-app/issues/6924>
2025-05-14 22:29:31 +00:00
078b7f3bf7 fix errors from the wasm side (#6939)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* playwright tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* change order of operations

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* lint

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* Revert "fixups"

This reverts commit c54fd71074.

* fixups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2025-05-14 13:20:46 -07:00
3d65676ccb Permit concurrent unit tests (#6938)
Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-15 07:43:03 +12:00
ce566fb6e5 Accept idents as KW args (#6644)
Support kw arg/local variable shorthand

Signed-off-by: Nick Cameron <nrc@ncameron.org>
2025-05-15 07:42:48 +12:00
b23fc9f623 Make the Reset View button do the same view_isometric behavior as load (#6934)
* Make the Reset View button do the same view_isometric behavior as load

Just copying some logic from the EngineStream code to make that button
behave the same way: old initial camera position while in Playwright,
isometric view for normal users.

* Move duplicate code into shared `resetCameraPosition` function

* Fix lints
2025-05-14 19:01:45 +00:00
5c2dfb8e40 Support new sweep flag (#6932)
Closes https://github.com/KittyCAD/engine/issues/3115
2025-05-14 13:54:10 -05:00
0e341d7863 #6202 Save input value before closing settings dialogue (#6931)
* call blur on current input before closing settings dialogue to save value

* separate esc handling is not needed

* lint
2025-05-14 14:16:23 -04:00
6a03ff9596 Stop checking for intermediate export toasts (#6935) 2025-05-14 17:53:44 +00:00
d7bd0c937d Keep test toast messages around for longer (#6930)
* Keep test toast messages around for longer

* Check for at least two locators

I wasn't able to reproduce, but it's possible one stuck around from a previous test.
2025-05-14 12:24:38 -04:00
d3b2483f4f Clear errors when leaving file to avoid seeing previous files errors (#6928)
Clear errors when leaving file to avoid seeing previous files errors when opening new project

Co-authored-by: Jace Browning <jacebrowning@gmail.com>
2025-05-14 17:20:04 +02:00
7838b7c9fd Fix "include settings" setting to have an effect (#6917)
* Fix "include settings" setting to have an effect

I'm not sold on if we should have this setting, but this fixes it for
now. The issue is was that the new callback actor approach was using a
stale version of the settings every time it received an "update" event:
JS closure problems. Now it receives the new settings as an event
payload.

* Update src/machines/settingsMachine.ts

---------

Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-05-14 15:14:33 +00:00
130ecf1f88 Shard macOS and Windows e2e tests (#6926) 2025-05-14 14:33:53 +00:00
550d8b3753 #6300 Fix wrong Feature Tree when switching to kcl file with errors (#6922)
* reset lastSuccessfulOperation, variables when switching to new kcl file, add test

* use scene.settled instead of random delay in test
2025-05-14 14:17:22 +00:00
696222a070 Change KCL error messages to display principal type of values (#6906) 2025-05-14 10:04:51 -04:00
edb424988d Restore the native file menu tests (#6279)
* Restore the native file menu tests

* fix: saving off progress

* chore: making progress cleaning up these verbose tests and improving app logic for e2e

* chore: rewriting tests

* fix: reworking application logic for file menu in the scene and e2e scene file menu test

* chore: updating more e2e tests

* fix: updated all the tests, auto fixers

* fix: trying to improve tests within E2E, they aren't failing locally even with --repeat-each=10

* fix: application logic has a bug that you can navigate instantly but the scroll to view code will not trigger which breaks end to end tests

* fix: improving E2E tests

* fix: fixing clipboard typo

* fix: porting test() for each native file menu to a test.step to speed it up

* fix: auto fixes and console log helper function for playwright runtimes

* fix: more cleanup

* fix: trying to fix these...

* fix: got the tests working

* fix: addressing PR comments

* fix: trying to stablize the tests

* fix: auto fixes

* fix: trying to make it the command name and not arg? could be a source of race condition if the input is not written fast enough?

* fix: maybe because this close locator was running too quickly?

* fix: panic timeout, classic

* fix: these are gone

* fix: shorter waits

---------

Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierrejacquier39@gmail.com>
2025-05-14 09:06:29 -04:00
135 changed files with 15150 additions and 51430 deletions

View File

@ -123,18 +123,6 @@ jobs:
- id: export_notes - id: export_notes
run: echo "notes=`cat release-notes.md`" >> "$GITHUB_OUTPUT" run: echo "notes=`cat release-notes.md`" >> "$GITHUB_OUTPUT"
- name: Prepare electron-builder.yml file for updater test
if: ${{ env.IS_RELEASE == 'true' }}
run: |
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: prepared-files-updater-test
path: |
electron-builder.yml
build-apps: build-apps:
needs: [prepare-files] needs: [prepare-files]
@ -259,49 +247,6 @@ jobs:
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back # TODO: add the 'Build for Mac TestFlight (nightly)' stage back
# The steps below are for updater-test builds, only on release
- uses: actions/download-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
name: prepared-files-updater-test
- name: Copy updated electron-builder.yml file for updater test
if: ${{ env.IS_RELEASE == 'true' }}
run: |
ls -R prepared-files-updater-test
cp prepared-files-updater-test/electron-builder.yml electron-builder.yml
- name: Build the app (updater-test)
if: ${{ env.IS_RELEASE == 'true' }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: npm run tronb:package:prod
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-arm64-${{ matrix.platform }}
path: |
out/*-arm64-win.exe
out/*-arm64-mac.dmg
out/*-arm64-linux.AppImage
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-x64-${{ matrix.platform }}
path: |
out/*-x64-win.exe
out/*-x64-mac.dmg
out/*-x86_64-linux.AppImage
upload-apps-release: upload-apps-release:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04

View File

@ -40,7 +40,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: npm install run: npm install
- name: Download Wasm Cache - name: Download Wasm cache
id: download-wasm id: download-wasm
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }} if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
uses: dawidd6/action-download-artifact@v7 uses: dawidd6/action-download-artifact@v7
@ -52,7 +52,7 @@ jobs:
branch: main branch: main
path: rust/kcl-wasm-lib/pkg path: rust/kcl-wasm-lib/pkg
- name: Build WASM condition - name: Build Wasm condition
id: wasm id: wasm
run: | run: |
set -euox pipefail set -euox pipefail
@ -70,7 +70,7 @@ jobs:
run: | run: |
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./ [ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
- name: Install rust - name: Install Rust
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
uses: actions-rust-lang/setup-rust-toolchain@v1 uses: actions-rust-lang/setup-rust-toolchain@v1
with: with:
@ -81,7 +81,7 @@ jobs:
with: with:
tool: wasm-pack tool: wasm-pack
- name: Rust Cache - name: Use Rust cache
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }} if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
@ -117,7 +117,7 @@ jobs:
- uses: actions/download-artifact@v4 - uses: actions/download-artifact@v4
name: prepared-wasm name: prepared-wasm
- name: Copy prepared wasm - name: Copy prepared Wasm
run: | run: |
ls -R prepared-wasm ls -R prepared-wasm
cp prepared-wasm/kcl_wasm_lib_bg.wasm public cp prepared-wasm/kcl_wasm_lib_bg.wasm public
@ -133,20 +133,17 @@ jobs:
id: deps-install id: deps-install
run: npm install run: npm install
- name: Cache Playwright Browsers - name: Cache browsers
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: | path: |
~/.cache/ms-playwright/ ~/.cache/ms-playwright/
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
- name: Install Playwright Browsers - name: Install browsers
run: npm run playwright install --with-deps run: npm run playwright install --with-deps
- name: build web - name: Capture snapshots
run: npm run tronb:vite:dev
- name: Run ubuntu/chrome snapshots
uses: nick-fields/retry@v3.0.2 uses: nick-fields/retry@v3.0.2
with: with:
shell: bash shell: bash
@ -170,7 +167,7 @@ jobs:
retention-days: 30 retention-days: 30
overwrite: true overwrite: true
- name: Check for changes - name: Check diff
if: ${{ github.ref != 'refs/heads/main' }} if: ${{ github.ref != 'refs/heads/main' }}
shell: bash shell: bash
id: git-check id: git-check
@ -181,9 +178,8 @@ jobs:
else echo "modified=false" >> $GITHUB_OUTPUT else echo "modified=false" >> $GITHUB_OUTPUT
fi fi
- name: Commit changes, if any - name: Commit changes
# TODO: find a more reliable way to detect visual changes if: ${{ steps.git-check.outputs.modified == 'true' }}
if: ${{ false && steps.git-check.outputs.modified == 'true' }}
shell: bash shell: bash
run: | run: |
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
@ -193,7 +189,7 @@ jobs:
git fetch origin git fetch origin
echo ${{ github.head_ref }} echo ${{ github.head_ref }}
git checkout ${{ github.head_ref }} git checkout ${{ github.head_ref }}
git commit -m "A snapshot a day keeps the bugs away! 📷🐛" || true git commit --message "Update snapshots" || true
git push git push
git push origin ${{ github.head_ref }} git push origin ${{ github.head_ref }}
@ -234,10 +230,16 @@ jobs:
shardTotal: 8 shardTotal: 8
- os: namespace-profile-macos-8-cores - os: namespace-profile-macos-8-cores
shardIndex: 1 shardIndex: 1
shardTotal: 1 shardTotal: 2
- os: namespace-profile-macos-8-cores
shardIndex: 2
shardTotal: 2
- os: windows-latest-8-cores - os: windows-latest-8-cores
shardIndex: 1 shardIndex: 1
shardTotal: 1 shardTotal: 2
- os: windows-latest-8-cores
shardIndex: 2
shardTotal: 2
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

View File

@ -130,7 +130,7 @@ git tag $VERSION
git push origin --tags git push origin --tags
``` ```
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts. This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files.
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)). The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
@ -142,13 +142,10 @@ The release builds can be found under the `out-{arch}-{platform}` zip files, at
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue. Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
##### Updater-test builds A prompt should show up asking for a downgrade to the last release version. Running through that at the end of testing
and making sure the current release candidate has the ability to be updated to what electron-updater points to is critical,
The other `build-apps` output in the release `build-apps` workflow (triggered by 2.) is `updater-test-{arch}-{platform}`. It's a semi-automated process: for macOS, Windows, and Linux, download the corresponding updater-test artifact file, install the app, run it, expect an updater prompt to a dummy v0.255.255, install it and check that the app comes back at that version. but what is actually being downloaded and installed isn't.
If the prompt doesn't show up, start the app in command line to grab the electron-updater logs. This is likely an issue with the current build that needs addressing.
The only difference with these builds is that they point to a different update location on the release bucket, with this dummy v0.255.255 always available. This helps ensuring that the version we release will be able to update to the next one available.
If the prompt doesn't show up, start the app in command line to grab the electron-updater logs. This is likely an issue with the current build that needs addressing (or the updater-test location in the storage bucket).
``` ```
# Windows (PowerShell) # Windows (PowerShell)

View File

@ -0,0 +1,16 @@
---
title: "sweep::SKETCH_PLANE"
subtitle: "Constant in std::sweep"
excerpt: "Local/relative to a position centered within the plane being sketched on"
layout: manual
---
Local/relative to a position centered within the plane being sketched on
```kcl
sweep::SKETCH_PLANE: string = 'sketchPlane'
```

View File

@ -0,0 +1,16 @@
---
title: "sweep::TRAJECTORY"
subtitle: "Constant in std::sweep"
excerpt: "Local/relative to the trajectory curve"
layout: manual
---
Local/relative to the trajectory curve
```kcl
sweep::TRAJECTORY: string = 'trajectoryCurve'
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -128,6 +128,9 @@ layout: manual
* [`E`](/docs/kcl-std/consts/std-math-E) * [`E`](/docs/kcl-std/consts/std-math-E)
* [`PI`](/docs/kcl-std/consts/std-math-PI) * [`PI`](/docs/kcl-std/consts/std-math-PI)
* [`TAU`](/docs/kcl-std/consts/std-math-TAU) * [`TAU`](/docs/kcl-std/consts/std-math-TAU)
* [**std::sweep**](/docs/kcl-std/modules/std-sweep)
* [`sweep::SKETCH_PLANE`](/docs/kcl-std/consts/std-sweep-SKETCH_PLANE)
* [`sweep::TRAJECTORY`](/docs/kcl-std/consts/std-sweep-TRAJECTORY)
* [**std::turns**](/docs/kcl-std/modules/std-turns) * [**std::turns**](/docs/kcl-std/modules/std-turns)
* [`turns::HALF_TURN`](/docs/kcl-std/consts/std-turns-HALF_TURN) * [`turns::HALF_TURN`](/docs/kcl-std/consts/std-turns-HALF_TURN)
* [`turns::QUARTER_TURN`](/docs/kcl-std/consts/std-turns-QUARTER_TURN) * [`turns::QUARTER_TURN`](/docs/kcl-std/consts/std-turns-QUARTER_TURN)

View File

@ -0,0 +1,17 @@
---
title: "sweep"
subtitle: "Module in std"
excerpt: ""
layout: manual
---
## Functions and constants
* [`sweep::SKETCH_PLANE`](/docs/kcl-std/consts/std-sweep-SKETCH_PLANE)
* [`sweep::TRAJECTORY`](/docs/kcl-std/consts/std-sweep-TRAJECTORY)

View File

@ -19,6 +19,7 @@ You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL gui
* [`math`](/docs/kcl-std/modules/std-math) * [`math`](/docs/kcl-std/modules/std-math)
* [`sketch`](/docs/kcl-std/modules/std-sketch) * [`sketch`](/docs/kcl-std/modules/std-sketch)
* [`solid`](/docs/kcl-std/modules/std-solid) * [`solid`](/docs/kcl-std/modules/std-solid)
* [`sweep::sweep`](/docs/kcl-std/modules/std-sweep)
* [`transform`](/docs/kcl-std/modules/std-transform) * [`transform`](/docs/kcl-std/modules/std-transform)
* [`turns::turns`](/docs/kcl-std/modules/std-turns) * [`turns::turns`](/docs/kcl-std/modules/std-turns)
* [`types`](/docs/kcl-std/modules/std-types) * [`types`](/docs/kcl-std/modules/std-types)

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -134,8 +134,6 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await page.waitForTimeout(1000)
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')
await expect(codePaneButtonHolder).toContainText('notification', { await expect(codePaneButtonHolder).toContainText('notification', {
@ -158,10 +156,14 @@ extrude001 = extrude(sketch001, length = 5)`
await expect( await expect(
page page
.getByText( .getByText(
'Modeling command failed: [ApiError { error_code: InternalEngine, message: "Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis" }]' 'Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis'
) )
.first() .first()
).toBeVisible() ).toBeVisible()
// Make sure ApiError is not on the page.
// This ensures we didn't nest the json
await expect(page.getByText('ApiError')).not.toBeVisible()
}) })
test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({ test('When error is not in view WITH LINTS you can click the badge to scroll to it', async ({
@ -179,7 +181,7 @@ extrude001 = extrude(sketch001, length = 5)`
await page.setBodyDimensions({ width: 1200, height: 500 }) await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.settled(cmdBar) // await scene.settled(cmdBar)
// Ensure badge is present // Ensure badge is present
const codePaneButtonHolder = page.locator('#code-button-holder') const codePaneButtonHolder = page.locator('#code-button-holder')

View File

@ -58,12 +58,6 @@ test(
await expect(submitButton).toBeVisible() await expect(submitButton).toBeVisible()
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
// Look out for the toast message
const exportingToastMessage = page.getByText(`Exporting...`)
const alreadyExportingToastMessage = page.getByText(`Already exporting`)
await expect(exportingToastMessage).toBeVisible()
await expect(alreadyExportingToastMessage).not.toBeVisible()
// Expect it to succeed // Expect it to succeed
const errorToastMessage = page.getByText(`Error while exporting`) const errorToastMessage = page.getByText(`Error while exporting`)
const engineErrorToastMessage = page.getByText(`Nothing to export`) const engineErrorToastMessage = page.getByText(`Nothing to export`)
@ -72,7 +66,6 @@ test(
const successToastMessage = page.getByText(`Exported successfully`) const successToastMessage = page.getByText(`Exported successfully`)
await expect(successToastMessage).toBeVisible() await expect(successToastMessage).toBeVisible()
await expect(exportingToastMessage).not.toBeVisible()
// Check for the exported file // Check for the exported file
const firstFileFullPath = path.resolve( const firstFileFullPath = path.resolve(

View File

@ -1533,7 +1533,6 @@ sketch001 = startSketchOn(XZ)
await homePage.goToModelingScene() await homePage.goToModelingScene()
await scene.connectionEstablished() await scene.connectionEstablished()
await scene.settled(cmdBar)
await scene.expectPixelColor( await scene.expectPixelColor(
TEST_COLORS.DARK_MODE_BKGD, TEST_COLORS.DARK_MODE_BKGD,

View File

@ -155,6 +155,12 @@ export class CmdBarFixture {
} }
} }
closeCmdBar = async () => {
const cmdBarCloseBtn = this.page.getByTestId('command-bar-close-button')
await cmdBarCloseBtn.click()
await expect(this.cmdBarElement).not.toBeVisible()
}
get cmdSearchInput() { get cmdSearchInput() {
return this.page.getByTestId('cmd-bar-search') return this.page.getByTestId('cmd-bar-search')
} }
@ -298,4 +304,27 @@ export class CmdBarFixture {
`Monitoring text-to-cad API requests. Output will be saved to: ${outputPath}` `Monitoring text-to-cad API requests. Output will be saved to: ${outputPath}`
) )
} }
async toBeOpened() {
// Check that the command bar is opened
await expect(this.cmdBarElement).toBeVisible({ timeout: 10_000 })
}
async expectArgValue(value: string) {
// Check the placeholder project name exists
const actualArgument = await this.cmdBarElement
.getByTestId('cmd-bar-arg-value')
.inputValue()
const expectedArgument = value
expect(actualArgument).toBe(expectedArgument)
}
async expectCommandName(value: string) {
// Check the placeholder project name exists
const actual = await this.cmdBarElement
.getByTestId('command-name')
.textContent()
const expected = value
expect(actual).toBe(expected)
}
} }

View File

@ -24,6 +24,7 @@ export class HomePageFixture {
projectTextName!: Locator projectTextName!: Locator
sortByDateBtn!: Locator sortByDateBtn!: Locator
sortByNameBtn!: Locator sortByNameBtn!: Locator
appHeader!: Locator
tutorialBtn!: Locator tutorialBtn!: Locator
constructor(page: Page) { constructor(page: Page) {
@ -44,6 +45,7 @@ export class HomePageFixture {
this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified') this.sortByDateBtn = this.page.getByTestId('home-sort-by-modified')
this.sortByNameBtn = this.page.getByTestId('home-sort-by-name') this.sortByNameBtn = this.page.getByTestId('home-sort-by-name')
this.appHeader = this.page.getByTestId('app-header')
this.tutorialBtn = this.page.getByTestId('home-tutorial-button') this.tutorialBtn = this.page.getByTestId('home-tutorial-button')
} }
@ -125,4 +127,11 @@ export class HomePageFixture {
await this.createAndGoToProject(name) await this.createAndGoToProject(name)
} }
isNativeFileMenuCreated = async () => {
await expect(this.appHeader).toHaveAttribute(
'data-native-file-menu',
'true'
)
}
} }

View File

@ -46,6 +46,7 @@ export class SceneFixture {
public networkToggleConnected!: Locator public networkToggleConnected!: Locator
public engineConnectionsSpinner!: Locator public engineConnectionsSpinner!: Locator
public startEditSketchBtn!: Locator public startEditSketchBtn!: Locator
public appHeader!: Locator
constructor(page: Page) { constructor(page: Page) {
this.page = page this.page = page
@ -57,6 +58,7 @@ export class SceneFixture {
this.startEditSketchBtn = page this.startEditSketchBtn = page
.getByRole('button', { name: 'Start Sketch' }) .getByRole('button', { name: 'Start Sketch' })
.or(page.getByRole('button', { name: 'Edit Sketch' })) .or(page.getByRole('button', { name: 'Edit Sketch' }))
this.appHeader = this.page.getByTestId('app-header')
} }
private _serialiseScene = async (): Promise<SceneSerialised> => { private _serialiseScene = async (): Promise<SceneSerialised> => {
const camera = await this.getCameraInfo() const camera = await this.getCameraInfo()
@ -280,6 +282,13 @@ export class SceneFixture {
await expect(buttonToTest).toBeVisible() await expect(buttonToTest).toBeVisible()
await buttonToTest.click() await buttonToTest.click()
} }
isNativeFileMenuCreated = async () => {
await expect(this.appHeader).toHaveAttribute(
'data-native-file-menu',
'true'
)
}
} }
function isColourArray( function isColourArray(

File diff suppressed because it is too large Load Diff

View File

@ -1931,84 +1931,6 @@ sketch002 = startSketchOn(XZ)
}) })
}) })
test(`Sweep point-and-click failing validation`, async ({
context,
page,
homePage,
scene,
toolbar,
cmdBar,
}) => {
const initialCode = `@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(YZ)
|> circle(
center = [0, 0],
radius = 500
)
sketch002 = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> xLine(length = -500)
|> line(endAbsolute = [-2000, 500])
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 250 }
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const [clickOnSketch2] = scene.makeMouseHelpers(
testPoint.x - 50,
testPoint.y
)
await test.step(`Look for sketch001`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
})
await test.step(`Go through the command bar flow and fail validation with a toast`, async () => {
await toolbar.sweepButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({
commandName: 'Sweep',
currentArgKey: 'sketches',
currentArgValue: '',
headerArguments: {
Sectional: '',
Sketches: '',
Path: '',
},
highlightedHeaderArg: 'sketches',
stage: 'arguments',
})
await clickOnSketch1()
await cmdBar.progressCmdBar()
await cmdBar.expectState({
commandName: 'Sweep',
currentArgKey: 'path',
currentArgValue: '',
headerArguments: {
Sectional: '',
Sketches: '1 face',
Path: '',
},
highlightedHeaderArg: 'path',
stage: 'arguments',
})
await clickOnSketch2()
await cmdBar.progressCmdBar()
await expect(
page.getByText('Unable to sweep with the current selection. Reason:')
).toBeVisible()
})
})
test(`Fillet point-and-click`, async ({ test(`Fillet point-and-click`, async ({
context, context,
page, page,
@ -3627,67 +3549,6 @@ profile001 = startProfile(sketch001, at = [-20, 20])
}) })
}) })
test(`Shell dry-run validation rejects sweeps`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const initialCode = `sketch001 = startSketchOn(YZ)
|> circle(
center = [0, 0],
radius = 500
)
sketch002 = startSketchOn(XZ)
|> startProfile(at = [0, 0])
|> xLine(length = -2000)
sweep001 = sweep(sketch001, path = sketch002)
`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, initialCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.settled(cmdBar)
// One dumb hardcoded screen pixel value
const testPoint = { x: 500, y: 250 }
const [clickOnSweep] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
await test.step(`Confirm sweep exists`, async () => {
await toolbar.closePane('code')
await scene.expectPixelColor([231, 231, 231], testPoint, 15)
})
await test.step(`Go through the Shell flow and fail validation with a toast`, async () => {
await toolbar.shellButton.click()
await expect
.poll(() => page.getByText('Please select one').count())
.toBe(1)
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'selection',
currentArgValue: '',
headerArguments: {
Selection: '',
Thickness: '',
},
highlightedHeaderArg: 'selection',
commandName: 'Shell',
})
await clickOnSweep()
await page.waitForTimeout(500)
await cmdBar.progressCmdBar()
await expect(
page.getByText('Unable to shell with the current selection. Reason:')
).toBeVisible()
await page.waitForTimeout(1000)
})
})
test.describe('Revolve point and click workflows', () => { test.describe('Revolve point and click workflows', () => {
test('Base case workflow, auto spam continue in command bar', async ({ test('Base case workflow, auto spam continue in command bar', async ({
context, context,
@ -4943,4 +4804,34 @@ path001 = startProfile(sketch001, at = [0, 0])
) )
}) })
}) })
test(`Point and click codemods can't run on KCL errors`, async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
const badCode = `sketch001 = startSketchOn(XZ)
profile001 = circle(sketch001, center = [0, 0], radius = 1)
extrude001 = extrude(profile001 length = 1)`
await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode)
}, badCode)
await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene()
await scene.connectionEstablished()
await test.step(`Start Sketch is disabled`, async () => {
await expect(toolbar.startSketchBtn).not.toBeEnabled()
await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
})
await test.step(`Helix is disabled`, async () => {
await expect(toolbar.helixButton).not.toBeEnabled()
await editor.expectEditor.toContain(badCode, { shouldNormalise: true })
})
})
}) })

View File

@ -19,11 +19,12 @@ test.describe('Regression tests', () => {
context, context,
page, page,
homePage, homePage,
scene,
}) => { }) => {
// because the model has `line([0,0]..` it is valid code, but the model is invalid // because the model has `line([0,0]..` it is valid code, but the model is invalid
// regression test for https://github.com/KittyCAD/modeling-app/issues/3251 // regression test for https://github.com/KittyCAD/modeling-app/issues/3251
// Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics // Since the bad model also found as issue with the artifact graph, which in tern blocked the editor diognostics
const u = await getUtils(page) // const u = await getUtils(page)
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -40,7 +41,8 @@ test.describe('Regression tests', () => {
await page.setBodyDimensions({ width: 1000, height: 500 }) await page.setBodyDimensions({ width: 1000, height: 500 })
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() await scene.connectionEstablished()
// await u.waitForPageLoad()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible() await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
@ -51,8 +53,11 @@ test.describe('Regression tests', () => {
// the close doesn't work // the close doesn't work
// when https://github.com/KittyCAD/modeling-app/issues/3268 is closed // when https://github.com/KittyCAD/modeling-app/issues/3268 is closed
// this test will need updating // this test will need updating
const crypticErrorText = `ApiError` const crypticErrorText = `Cannot close a path that is non-planar or with duplicate vertices.
Internal engine error on request`
await expect(page.getByText(crypticErrorText).first()).toBeVisible() await expect(page.getByText(crypticErrorText).first()).toBeVisible()
// Ensure we didn't nest the json.
await expect(page.getByText('ApiError')).not.toBeVisible()
}) })
test('user should not have to press down twice in cmdbar', async ({ test('user should not have to press down twice in cmdbar', async ({
page, page,
@ -185,8 +190,8 @@ extrude001 = extrude(sketch001, length = 50)
page.locator('.pretty-json-container >> text=myVar:"67') page.locator('.pretty-json-container >> text=myVar:"67')
).toBeVisible() ).toBeVisible()
}) })
test('ProgramMemory can be serialised', async ({ page, homePage }) => { test('ProgramMemory can be serialised', async ({ page, homePage, scene }) => {
const u = await getUtils(page) // const u = await getUtils(page)
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
@ -211,11 +216,12 @@ extrude001 = extrude(sketch001, length = 50)
// Listen for all console events and push the message text to an array // Listen for all console events and push the message text to an array
page.on('console', (message) => messages.push(message.text())) page.on('console', (message) => messages.push(message.text()))
await homePage.goToModelingScene() await homePage.goToModelingScene()
await u.waitForPageLoad() // await u.waitForPageLoad()
await scene.connectionEstablished()
// wait for execution done // wait for execution done
await u.openDebugPanel() // await u.openDebugPanel()
await u.expectCmdLog('[data-message-type="execution-done"]') // await u.expectCmdLog('[data-message-type="execution-done"]')
const forbiddenMessages = ['cannot serialize tagged newtype variant'] const forbiddenMessages = ['cannot serialize tagged newtype variant']
forbiddenMessages.forEach((forbiddenMessage) => { forbiddenMessages.forEach((forbiddenMessage) => {
@ -229,6 +235,7 @@ extrude001 = extrude(sketch001, length = 50)
context, context,
page, page,
homePage, homePage,
scene,
}) => { }) => {
const u = await getUtils(page) const u = await getUtils(page)
// const PUR = 400 / 37.5 //pixeltoUnitRatio // const PUR = 400 / 37.5 //pixeltoUnitRatio
@ -247,11 +254,10 @@ extrude001 = extrude(sketch001, length = 50)
shell(exampleSketch, faces = ['end'], thickness = 0.25)` shell(exampleSketch, faces = ['end'], thickness = 0.25)`
) )
}) })
await homePage.goToModelingScene()
await scene.connectionEstablished()
await expect(async () => { await expect(async () => {
await homePage.goToModelingScene()
await u.waitForPageLoad()
// error in guter // error in guter
await expect(page.locator('.cm-lint-marker-error')).toBeVisible({ await expect(page.locator('.cm-lint-marker-error')).toBeVisible({
timeout: 1_000, timeout: 1_000,
@ -545,7 +551,8 @@ extrude002 = extrude(profile002, length = 150)
expect(alreadyExportingToastMessage).not.toBeVisible(), expect(alreadyExportingToastMessage).not.toBeVisible(),
]) ])
await expect(successToastMessage).toHaveCount(2) const count = await successToastMessage.count()
await expect(count).toBeGreaterThanOrEqual(2)
}) })
}) })

View File

@ -1365,18 +1365,18 @@ solid001 = subtract([extrude001], tools = [extrude002])
await page.addInitScript(async () => { await page.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
'persistCode', 'persistCode',
`fn in2mm = (inches) => { `fn in2mm(@inches) {
return inches * 25.4 return inches * 25.4
} }
const railTop = in2mm(.748) railTop = in2mm(.748)
const railSide = in2mm(.024) railSide = in2mm(.024)
const railBaseWidth = in2mm(.612) railBaseWidth = in2mm(.612)
const railWideWidth = in2mm(.835) railWideWidth = in2mm(.835)
const railBaseLength = in2mm(.200) railBaseLength = in2mm(.200)
const railClampable = in2mm(.200) railClampable = in2mm(.200)
const rail = startSketchOn(XZ) rail = startSketchOn(XZ)
|> startProfile(at = [-railTop / 2, railClampable + railBaseLength]) |> startProfile(at = [-railTop / 2, railClampable + railBaseLength])
|> line(endAbsolute = [ |> line(endAbsolute = [
railTop / 2, railTop / 2,
@ -3496,6 +3496,69 @@ profile001 = startProfile(sketch001, at = [-102.72, 237.44])
).toBeVisible() ).toBeVisible()
}) })
// Ensure feature tree is not showing previous file's content when switching to a file with KCL errors.
test('Feature tree shows correct sketch count per file', async ({
context,
homePage,
scene,
toolbar,
cmdBar,
page,
}) => {
const u = await getUtils(page)
// Setup project with files.
const GOOD_KCL = `sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [220.81, 253.8])
|> line(end = [132.84, -151.31])
|> line(end = [25.51, 167.15])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()
sketch002 = startSketchOn(XZ)
profile002 = startProfile(sketch002, at = [158.35, -70.82])
|> line(end = [73.9, -152.19])
|> line(end = [85.33, 135.48])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close()`
const ERROR_KCL = `sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [127.56, 179.02])
|> line(end = [132.84, -112.6])
|> line(end = [85.33, 234.01])
|> line(enfd = [-137.23, -54.55])`
await context.folderSetupFn(async (dir) => {
const projectDir = path.join(dir, 'multi-file-sketch-test')
await fs.mkdir(projectDir, { recursive: true })
await Promise.all([
fs.writeFile(path.join(projectDir, 'good.kcl'), GOOD_KCL, 'utf-8'),
fs.writeFile(path.join(projectDir, 'error.kcl'), ERROR_KCL, 'utf-8'),
])
})
await page.setBodyDimensions({ width: 1000, height: 800 })
await homePage.openProject('multi-file-sketch-test')
await scene.connectionEstablished()
await u.closeDebugPanel()
await toolbar.openFeatureTreePane()
await toolbar.openPane('files')
await toolbar.openFile('good.kcl')
await expect(
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
).toHaveCount(2)
await toolbar.openFile('error.kcl')
await expect(
toolbar.featureTreePane.getByRole('button', { name: 'Sketch' })
).toHaveCount(0)
})
test('adding a syntax error, recovers after fixing', async ({ test('adding a syntax error, recovers after fixing', async ({
page, page,
homePage, homePage,

View File

@ -1,4 +1,3 @@
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture' import type { CmdBarFixture } from '@e2e/playwright/fixtures/cmdBarFixture'
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture' import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
@ -9,6 +8,7 @@ import {
settingsToToml, settingsToToml,
} from '@e2e/playwright/test-utils' } from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
import { KCL_DEFAULT_LENGTH } from '@src/lib/constants'
test.beforeEach(async ({ page, context }) => { test.beforeEach(async ({ page, context }) => {
// Make the user avatar image always 404 // Make the user avatar image always 404
@ -766,7 +766,7 @@ test.describe('Grid visibility', { tag: '@snapshot' }, () => {
}) })
}) })
test('theme persists', async ({ page, context }) => { test('theme persists', async ({ page, context, homePage }) => {
const u = await getUtils(page) const u = await getUtils(page)
await context.addInitScript(async () => { await context.addInitScript(async () => {
localStorage.setItem( localStorage.setItem(
@ -784,7 +784,7 @@ test('theme persists', async ({ page, context }) => {
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart() await homePage.goToModelingScene()
await page.waitForTimeout(500) await page.waitForTimeout(500)
// await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click() // await page.getByRole('link', { name: 'Settings Settings (tooltip)' }).click()
@ -812,7 +812,7 @@ test('theme persists', async ({ page, context }) => {
// Disconnect and reconnect to check the theme persists through a reload // Disconnect and reconnect to check the theme persists through a reload
// Expect the network to be down // Expect the network to be down
await expect(networkToggle).toContainText('Offline') await expect(networkToggle).toContainText('Problem')
// simulate network up // simulate network up
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
@ -873,6 +873,50 @@ sweepSketch = startSketchOn(XY)
mask: lowerRightMasks(page), mask: lowerRightMasks(page),
}) })
}) })
test('code color goober works with single quotes', async ({
page,
context,
scene,
cmdBar,
}) => {
const u = await getUtils(page)
await context.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`// Create a pipe using a sweep.
// Create a path for the sweep.
sweepPath = startSketchOn(XZ)
|> startProfile(at = [0.05, 0.05])
|> line(end = [0, 7])
|> tangentialArc(angle = 90, radius = 5)
|> line(end = [-3, 0])
|> tangentialArc(angle = -90, radius = 5)
|> line(end = [0, 7])
sweepSketch = startSketchOn(XY)
|> startProfile(at = [2, 0])
|> arc(angleStart = 0, angleEnd = 360, radius = 2)
|> sweep(path = sweepPath)
|> appearance(
color = '#bb00ff',
metalness = 90,
roughness = 90
)
`
)
})
await page.setViewportSize({ width: 1200, height: 1000 })
await u.waitForAuthSkipAppStart()
await scene.settled(cmdBar)
await expect(page, 'expect small color widget').toHaveScreenshot({
maxDiffPixels: 100,
mask: lowerRightMasks(page),
})
})
test('code color goober opening window', async ({ test('code color goober opening window', async ({
page, page,

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@ -1,236 +1,257 @@
import type { EngineCommand } from '@src/lang/std/artifactGraph' import type { EngineCommand } from '@src/lang/std/artifactGraph'
import { uuidv4 } from '@src/lib/utils' import { uuidv4 } from '@src/lib/utils'
import { commonPoints, getUtils } from '@e2e/playwright/test-utils' import {
commonPoints,
getUtils,
TEST_COLORS,
circleMove,
} from '@e2e/playwright/test-utils'
import { expect, test } from '@e2e/playwright/zoo-test' import { expect, test } from '@e2e/playwright/zoo-test'
test.describe( test.describe('Test network related behaviors', () => {
'Test network and connection issues', test(
{ 'simulate network down and network little widget',
tag: ['@macos', '@windows'], { tag: '@skipLocalEngine' },
}, async ({ page, homePage }) => {
() => { const networkToggleConnectedText = page.getByText('Connected')
test( const networkToggleWeakText = page.getByText('Network health (Weak)')
'simulate network down and network little widget',
{ tag: '@skipLocalEngine' },
async ({ page, homePage }) => {
const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
await homePage.goToModelingScene() const u = await getUtils(page)
await page.setBodyDimensions({ width: 1200, height: 500 })
const networkToggle = page.getByTestId('network-toggle') await homePage.goToModelingScene()
// This is how we wait until the stream is online const networkToggle = page.getByTestId('network-toggle')
await expect(
page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 })
const networkWidget = page.locator('[data-testid="network-toggle"]') // This is how we wait until the stream is online
await expect(networkWidget).toBeVisible() await expect(
await networkWidget.hover() page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 })
const networkPopover = page.locator('[data-testid="network-popover"]') const networkWidget = page.locator('[data-testid="network-toggle"]')
await expect(networkPopover).not.toBeVisible() await expect(networkWidget).toBeVisible()
await networkWidget.hover()
// (First check) Expect the network to be up const networkPopover = page.locator('[data-testid="network-popover"]')
await expect(networkToggle).toContainText('Connected') await expect(networkPopover).not.toBeVisible()
// Click the network widget // (First check) Expect the network to be up
await networkWidget.click() await expect(
networkToggleConnectedText.or(networkToggleWeakText)
).toBeVisible()
// Check the modal opened. // Click the network widget
await expect(networkPopover).toBeVisible() await networkWidget.click()
// Click off the modal. // Check the modal opened.
await page.mouse.click(100, 100) await expect(networkPopover).toBeVisible()
await expect(networkPopover).not.toBeVisible()
// Turn off the network // Click off the modal.
await u.emulateNetworkConditions({ await page.mouse.click(100, 100)
offline: true, await expect(networkPopover).not.toBeVisible()
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
// Expect the network to be down // Turn off the network
await expect(networkToggle).toContainText('Problem') await u.emulateNetworkConditions({
offline: true,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
// Click the network widget // Expect the network to be down
await networkWidget.click() await expect(networkToggle).toContainText('Problem')
// Check the modal opened. // Click the network widget
await expect(networkPopover).toBeVisible() await networkWidget.click()
// Click off the modal. // Check the modal opened.
await page.mouse.click(0, 0) await expect(networkPopover).toBeVisible()
await expect(networkPopover).not.toBeVisible()
// Turn back on the network // Click off the modal.
await u.emulateNetworkConditions({ await page.mouse.click(0, 0)
offline: false, await expect(networkPopover).not.toBeVisible()
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
await expect( // Turn back on the network
page.getByRole('button', { name: 'Start Sketch' }) await u.emulateNetworkConditions({
).not.toBeDisabled({ timeout: 15000 }) offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
// (Second check) expect the network to be up await expect(
await expect(networkToggle).toContainText('Connected') page.getByRole('button', { name: 'Start Sketch' })
} ).not.toBeDisabled({ timeout: 15000 })
)
test( // (Second check) expect the network to be up
'Engine disconnect & reconnect in sketch mode', await expect(
{ tag: '@skipLocalEngine' }, networkToggleConnectedText.or(networkToggleWeakText)
async ({ page, homePage, toolbar, scene, cmdBar }) => { ).toBeVisible()
const networkToggle = page.getByTestId('network-toggle') }
)
const u = await getUtils(page) test(
await page.setBodyDimensions({ width: 1200, height: 500 }) 'Engine disconnect & reconnect in sketch mode',
const PUR = 400 / 37.5 //pixeltoUnitRatio { tag: '@skipLocalEngine' },
async ({ page, homePage, toolbar, scene, cmdBar }) => {
const networkToggle = page.getByTestId('network-toggle')
const networkToggleConnectedText = page.getByText('Connected')
const networkToggleWeakText = page.getByText('Network health (Weak)')
await homePage.goToModelingScene() const u = await getUtils(page)
await u.waitForPageLoad() await page.setBodyDimensions({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio
await u.openDebugPanel() await homePage.goToModelingScene()
// click on "Start Sketch" button await u.waitForPageLoad()
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
// select a plane await u.openDebugPanel()
await page.mouse.click(700, 200) // click on "Start Sketch" button
await u.clearCommandLogs()
await page.getByRole('button', { name: 'Start Sketch' }).click()
await page.waitForTimeout(100)
await expect(page.locator('.cm-content')).toHaveText( // select a plane
`sketch001 = startSketchOn(XZ)` await page.mouse.click(700, 200)
)
await u.closeDebugPanel()
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation await expect(page.locator('.cm-content')).toHaveText(
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)`
)
await u.closeDebugPanel()
const startXPx = 600 await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')).toHaveText(
`sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})`
)
await page.waitForTimeout(100)
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10) const startXPx = 600
await page.waitForTimeout(100) await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
await expect(page.locator('.cm-content')).toHaveText(
`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})`
)
await page.waitForTimeout(100)
await expect( await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
page.locator('.cm-content') await page.waitForTimeout(100)
).toHaveText(`sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})
await expect(
page.locator('.cm-content')
).toHaveText(`@settings(defaultLengthUnit = in)sketch001 = startSketchOn(XZ)profile001 = startProfile(sketch001, at = ${commonPoints.startAt})
|> xLine(length = ${commonPoints.num1})`) |> xLine(length = ${commonPoints.num1})`)
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await networkToggle.hover()
await expect(
networkToggleConnectedText.or(networkToggleWeakText)
).toBeVisible()
// simulate network down // simulate network down
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
offline: true, offline: true,
// values of 0 remove any active throttling. crbug.com/456324#c9 // values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0, latency: 0,
downloadThroughput: -1, downloadThroughput: -1,
uploadThroughput: -1, uploadThroughput: -1,
}) })
// Expect the network to be down // Expect the network to be down
await expect(networkToggle).toContainText('Problem') await networkToggle.hover()
await expect(networkToggle).toContainText('Problem')
// Ensure we are not in sketch mode // Ensure we are not in sketch mode
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).not.toBeVisible() ).not.toBeVisible()
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).toBeVisible() ).toBeVisible()
// simulate network up // simulate network up
await u.emulateNetworkConditions({ await u.emulateNetworkConditions({
offline: false, offline: false,
// values of 0 remove any active throttling. crbug.com/456324#c9 // values of 0 remove any active throttling. crbug.com/456324#c9
latency: 0, latency: 0,
downloadThroughput: -1, downloadThroughput: -1,
uploadThroughput: -1, uploadThroughput: -1,
}) })
// Wait for the app to be ready for use // Wait for the app to be ready for use
await expect( await expect(
page.getByRole('button', { name: 'Start Sketch' }) page.getByRole('button', { name: 'Start Sketch' })
).not.toBeDisabled({ timeout: 15000 }) ).not.toBeDisabled({ timeout: 15000 })
// Expect the network to be up // Expect the network to be up
await expect(networkToggle).toContainText('Connected') await networkToggle.hover()
await scene.settled(cmdBar) await expect(
networkToggleConnectedText.or(networkToggleWeakText)
).toBeVisible()
// Click off the code pane. await scene.settled(cmdBar)
await page.mouse.click(100, 100)
// select a line // Click off the code pane.
await page await page.mouse.click(100, 100)
.getByText(`startProfile(sketch001, at = ${commonPoints.startAt})`)
.click()
// enter sketch again // select a line
await toolbar.editSketch() await page
.getByText(`startProfile(sketch001, at = ${commonPoints.startAt})`)
.click()
// Click the line tool // enter sketch again
await page await toolbar.editSketch()
.getByRole('button', { name: 'line Line', exact: true })
.click()
await page.waitForTimeout(150) // Click the line tool
await page.getByRole('button', { name: 'line Line', exact: true }).click()
const camCommand: EngineCommand = { await page.waitForTimeout(150)
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_look_at',
center: { x: 109, y: 0, z: -152 },
vantage: { x: 115, y: -505, z: -152 },
up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
await toolbar.openPane('debug')
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
await page.waitForTimeout(100)
// click to continue profile const camCommand: EngineCommand = {
await page.mouse.click(1007, 400) type: 'modeling_cmd_req',
await page.waitForTimeout(100) cmd_id: uuidv4(),
// Ensure we can continue sketching cmd: {
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) type: 'default_camera_look_at',
await expect center: { x: 109, y: 0, z: -152 },
.poll(u.normalisedEditorCode) vantage: { x: 115, y: -505, z: -152 },
.toBe(`sketch001 = startSketchOn(XZ) up: { x: 0, y: 0, z: 1 },
},
}
const updateCamCommand: EngineCommand = {
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'default_camera_get_settings',
},
}
await toolbar.openPane('debug')
await u.sendCustomCmd(camCommand)
await page.waitForTimeout(100)
await u.sendCustomCmd(updateCamCommand)
await page.waitForTimeout(100)
// click to continue profile
await page.mouse.click(1007, 400)
await page.waitForTimeout(100)
// Ensure we can continue sketching
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
await expect
.poll(u.normalisedEditorCode)
.toBe(`@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [12.34, -12.34]) profile001 = startProfile(sketch001, at = [12.34, -12.34])
|> xLine(length = 12.34) |> xLine(length = 12.34)
|> line(end = [-12.34, 12.34]) |> line(end = [-12.34, 12.34])
`) `)
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(startXPx, 500 - PUR * 20) await page.mouse.click(startXPx, 500 - PUR * 20)
await expect await expect
.poll(u.normalisedEditorCode) .poll(u.normalisedEditorCode)
.toBe(`sketch001 = startSketchOn(XZ) .toBe(`@settings(defaultLengthUnit = in)
sketch001 = startSketchOn(XZ)
profile001 = startProfile(sketch001, at = [12.34, -12.34]) profile001 = startProfile(sketch001, at = [12.34, -12.34])
|> xLine(length = 12.34) |> xLine(length = 12.34)
|> line(end = [-12.34, 12.34]) |> line(end = [-12.34, 12.34])
@ -238,22 +259,105 @@ profile001 = startProfile(sketch001, at = [12.34, -12.34])
`) `)
// Unequip line tool // Unequip line tool
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
// Make sure we didn't pop out of sketch mode. // Make sure we didn't pop out of sketch mode.
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).toBeVisible() ).toBeVisible()
await expect( await expect(
page.getByRole('button', { name: 'line Line', exact: true }) page.getByRole('button', { name: 'line Line', exact: true })
).not.toHaveAttribute('aria-pressed', 'true') ).not.toHaveAttribute('aria-pressed', 'true')
// Exit sketch // Exit sketch
await page.keyboard.press('Escape') await page.keyboard.press('Escape')
await expect( await expect(
page.getByRole('button', { name: 'Exit Sketch' }) page.getByRole('button', { name: 'Exit Sketch' })
).not.toBeVisible() ).not.toBeVisible()
}
)
test(
'Paused stream freezes view frame, unpause reconnect is seamless to user',
{ tag: ['@electron', '@skipLocalEngine'] },
async ({ page, homePage, scene, cmdBar, toolbar, tronApp }) => {
const networkToggle = page.getByTestId('network-toggle')
const networkToggleConnectedText = page.getByText('Connected')
const networkToggleWeakText = page.getByText('Network health (Weak)')
if (!tronApp) {
fail()
} }
)
} await tronApp.cleanProjectDir({
) app: {
stream_idle_mode: 5000,
},
})
await page.addInitScript(async () => {
localStorage.setItem(
'persistCode',
`sketch001 = startSketchOn(XY)
profile001 = startProfile(sketch001, at = [0.0, 0.0])
|> line(end = [10.0, 0])
|> line(end = [0, 10.0])
|> close()`
)
})
const dim = { width: 1200, height: 500 }
await page.setBodyDimensions(dim)
await test.step('Go to modeling scene', async () => {
await homePage.goToModelingScene()
await scene.settled(cmdBar)
})
await test.step('Verify pausing behavior', async () => {
// Wait 5s + 1s to pause.
await page.waitForTimeout(6000)
// We should now be paused. To the user, it should appear we're still
// connected.
await networkToggle.hover()
await expect(
networkToggleConnectedText.or(networkToggleWeakText)
).toBeVisible()
const center = {
x: dim.width / 2,
y: dim.height / 2,
}
let probe = { x: 0, y: 0 }
// ... and the model's still visibly there
probe.x = center.x + dim.width / 100
probe.y = center.y
await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
probe = { ...center }
// Now move the mouse around to unpause!
await circleMove(page, probe.x, probe.y, 20, 10)
// ONCE AGAIN! Check the view area hasn't changed at all.
// Check the pixel a couple times as it reconnects.
// NOTE: Remember, idle behavior is still on at this point -
// if this test takes longer than 5s shit WILL go south!
probe.x = center.x + dim.width / 100
probe.y = center.y
await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
await page.waitForTimeout(1000)
await scene.expectPixelColor(TEST_COLORS.GREY, probe, 15)
probe = { ...center }
// Ensure we're still connected
await networkToggle.hover()
await expect(
networkToggleConnectedText.or(networkToggleWeakText)
).toBeVisible()
})
}
)
})

View File

@ -21,6 +21,7 @@ export const token = process.env.token || ''
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration' import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
import type { ElectronZoo } from '@e2e/playwright/fixtures/fixtureSetup'
import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist' import { isErrorWhitelisted } from '@e2e/playwright/lib/console-error-whitelist'
import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates' import { TEST_SETTINGS, TEST_SETTINGS_KEY } from '@e2e/playwright/storageStates'
import { test } from '@e2e/playwright/zoo-test' import { test } from '@e2e/playwright/zoo-test'
@ -43,6 +44,8 @@ export const lowerRightMasks = (page: Page) => [
export type TestColor = [number, number, number] export type TestColor = [number, number, number]
export const TEST_COLORS: { [key: string]: TestColor } = { export const TEST_COLORS: { [key: string]: TestColor } = {
WHITE: [249, 249, 249], WHITE: [249, 249, 249],
OFFWHITE: [237, 237, 237],
GREY: [142, 142, 142],
YELLOW: [255, 255, 0], YELLOW: [255, 255, 0],
BLUE: [0, 0, 255], BLUE: [0, 0, 255],
DARK_MODE_BKGD: [27, 27, 27], DARK_MODE_BKGD: [27, 27, 27],
@ -1149,3 +1152,77 @@ export function perProjectSettingsToToml(
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
return TOML.stringify(settings as any) return TOML.stringify(settings as any)
} }
export async function clickElectronNativeMenuById(
tronApp: ElectronZoo,
menuId: string
) {
const clickWasTriggered = await tronApp.electron.evaluate(
async ({ app }, menuId) => {
if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById(menuId)
if (!menu) return false
menu.click()
return true
},
menuId
)
expect(clickWasTriggered).toBe(true)
}
export async function findElectronNativeMenuById(
tronApp: ElectronZoo,
menuId: string
) {
const found = await tronApp.electron.evaluate(async ({ app }, menuId) => {
if (!app || !app.applicationMenu) {
return false
}
const menu = app.applicationMenu.getMenuItemById(menuId)
if (!menu) return false
return true
}, menuId)
expect(found).toBe(true)
}
export async function openSettingsExpectText(page: Page, text: string) {
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
// You are viewing the user tab
const actualText = settings.getByText(text)
await expect(actualText).toBeVisible()
}
export async function openSettingsExpectLocator(page: Page, selector: string) {
const settings = page.getByTestId('settings-dialog-panel')
await expect(settings).toBeVisible()
// You are viewing the keybindings tab
const settingsLocator = settings.locator(selector)
await expect(settingsLocator).toBeVisible()
}
/**
* A developer helper function to make playwright send all the console logs to stdout
* Call this within your E2E test and pass in the page or the tronApp to get as many
* logs piped to stdout for debugging
*/
export async function enableConsoleLogEverything({
page,
tronApp,
}: { page?: Page; tronApp?: ElectronZoo }) {
page?.on('console', (msg) => {
console.log(`[Page-log]: ${msg.text()}`)
})
tronApp?.electron.on('window', async (electronPage) => {
electronPage.on('console', (msg) => {
console.log(`[Renderer] ${msg.type()}: ${msg.text()}`)
})
})
tronApp?.electron.on('console', (msg) => {
console.log(`[Main] ${msg.type()}: ${msg.text()}`)
})
}

View File

@ -57,9 +57,9 @@ fn connectorSketch(@plane, start) {
export fn connector(@plane, length) { export fn connector(@plane, length) {
connectorSketch(plane, start = [-12, 8]) connectorSketch(plane, start = [-12, 8])
|> extrude(length = length) |> extrude(length)
connectorSketch(plane, start = [16, 8]) connectorSketch(plane, start = [16, 8])
|> extrude(length = length) |> extrude(length)
return 0 return 0
} }
@ -79,7 +79,7 @@ fn seatSlatSketch(@plane) {
export fn seatSlats(@plane, length) { export fn seatSlats(@plane, length) {
seatSlatSketch(plane) seatSlatSketch(plane)
|> extrude(length = length) |> extrude(length)
return 0 return 0
} }
@ -99,7 +99,7 @@ fn backSlatsSketch(@plane) {
export fn backSlats(@plane, length) { export fn backSlats(@plane, length) {
b = backSlatsSketch(plane) b = backSlatsSketch(plane)
|> extrude(length = length) |> extrude(length)
return b return b
} }

View File

@ -15,7 +15,7 @@ holeDia = 4
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
|> startProfile(at = [0, 0]) |> startProfile(at = [0, 0])
|> angledLine(angle = 0, length = width, tag = $rectangleSegmentA001) |> angledLine(angle = 0, length = width, tag = $rectangleSegmentA001)
|> angledLine(angle = segAng(rectangleSegmentA001) + 90, length = length, tag = $rectangleSegmentB001) |> angledLine(angle = segAng(rectangleSegmentA001) + 90, length, tag = $rectangleSegmentB001)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD001)
|> close() |> close()
@ -74,7 +74,7 @@ function001([
sketch003 = startSketchOn(XY) sketch003 = startSketchOn(XY)
|> startProfile(at = [width * 1.2, 0]) |> startProfile(at = [width * 1.2, 0])
|> angledLine(angle = 0, length = width, tag = $rectangleSegmentA002) |> angledLine(angle = 0, length = width, tag = $rectangleSegmentA002)
|> angledLine(angle = segAng(rectangleSegmentA001) + 90, length = length, tag = $rectangleSegmentB002) |> angledLine(angle = segAng(rectangleSegmentA001) + 90, length, tag = $rectangleSegmentB002)
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC002) |> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $rectangleSegmentC002)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD002) |> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $rectangleSegmentD002)
|> close() |> close()

View File

@ -1,12 +1,12 @@
[test-groups] [test-groups]
# If a test uses the engine, we want to limit the number that can run in parallel. # If a test uses the engine, we want to limit the number that can run in parallel.
# This way we don't start and stop too many engine instances, putting pressure on our cloud. # This way we don't start and stop too many engine instances, putting pressure on our cloud.
uses-engine = { max-threads = 4 } uses-engine = { max-threads = 32 }
# If a test must run after the engine tests, we want to make sure the engine tests are done first. # If a test must run after the engine tests, we want to make sure the engine tests are done first.
after-engine = { max-threads = 12 } after-engine = { max-threads = 32 }
[profile.default] [profile.default]
slow-timeout = { period = "180s", terminate-after = 1 } slow-timeout = { period = "280s", terminate-after = 1 }
[profile.ci] [profile.ci]
slow-timeout = { period = "280s", terminate-after = 5 } slow-timeout = { period = "280s", terminate-after = 5 }

24
rust/Cargo.lock generated
View File

@ -1815,7 +1815,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1826,7 +1826,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-derive-docs" name = "kcl-derive-docs"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"anyhow", "anyhow",
@ -1845,7 +1845,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1854,7 +1854,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server" name = "kcl-language-server"
version = "0.2.69" version = "0.2.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1875,7 +1875,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"clap", "clap",
@ -1895,7 +1895,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.2.69" version = "0.2.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx 0.5.1", "approx 0.5.1",
@ -1971,7 +1971,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-python-bindings" name = "kcl-python-bindings"
version = "0.3.69" version = "0.3.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"kcl-lib", "kcl-lib",
@ -1986,7 +1986,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-test-server" name = "kcl-test-server"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"hyper 0.14.32", "hyper 0.14.32",
@ -1999,7 +1999,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-to-core" name = "kcl-to-core"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -2013,7 +2013,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-wasm-lib" name = "kcl-wasm-lib"
version = "0.1.69" version = "0.1.72"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bson", "bson",
@ -2080,9 +2080,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.120" version = "0.2.121"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48b71e06ee5d711d0085864a756fb6a304531246689ea00c6ef5d740670c3701" checksum = "94ba95c22493d79ec8a1faab963d8903f6de0e373efedf2bc3bb76a0ddbab036"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-bumper" name = "kcl-bumper"
version = "0.1.69" version = "0.1.72"
edition = "2021" edition = "2021"
repository = "https://github.com/KittyCAD/modeling-api" repository = "https://github.com/KittyCAD/modeling-api"
rust-version = "1.76" rust-version = "1.76"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-derive-docs" name = "kcl-derive-docs"
description = "A tool for generating documentation from Rust derive macros" description = "A tool for generating documentation from Rust derive macros"
version = "0.1.69" version = "0.1.72"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-directory-test-macro" name = "kcl-directory-test-macro"
description = "A tool for generating tests from a directory of kcl files" description = "A tool for generating tests from a directory of kcl files"
version = "0.1.69" version = "0.1.72"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-language-server-release" name = "kcl-language-server-release"
version = "0.1.69" version = "0.1.72"
edition = "2021" edition = "2021"
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
publish = false publish = false

View File

@ -2,7 +2,7 @@
name = "kcl-language-server" name = "kcl-language-server"
description = "A language server for KCL." description = "A language server for KCL."
authors = ["KittyCAD Inc <kcl@kittycad.io>"] authors = ["KittyCAD Inc <kcl@kittycad.io>"]
version = "0.2.69" version = "0.2.72"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-lib" name = "kcl-lib"
description = "KittyCAD Language implementation and tools" description = "KittyCAD Language implementation and tools"
version = "0.2.69" version = "0.2.72"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -953,36 +953,6 @@ sketch001 = startSketchOn(box, face = END)
assert_out("revolve_on_edge", &result); assert_out("revolve_on_edge", &result);
} }
#[tokio::test(flavor = "multi_thread")]
async fn kcl_test_revolve_on_edge_get_edge() {
let code = r#"box = startSketchOn(XY)
|> startProfile(at = [0, 0])
|> line(end = [0, 10])
|> line(end = [10, 0])
|> line(end = [0, -10], tag = $revolveAxis)
|> close()
|> extrude(length = 10)
sketch001 = startSketchOn(box, face = revolveAxis)
|> startProfile(at = [5, 10])
|> line(end = [0, -10])
|> line(end = [2, 0])
|> line(end = [0, 10])
|> close()
|> revolve(axis = revolveAxis, angle = 90)
"#;
let result = execute_and_snapshot(code, None).await;
result.unwrap_err();
//this fails right now, but slightly differently, lets just say its enough for it to fail - mike
//assert_eq!(
// result.err().unwrap().to_string(),
// r#"engine: KclErrorDetails { source_ranges: [SourceRange([346, 390, 0])], message: "Modeling command failed: [ApiError { error_code: InternalEngine, message: \"Solid3D revolve failed: sketch profile must lie entirely on one side of the revolution axis\" }]" }"#
//);
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn kcl_test_revolve_on_face_circle_edge() { async fn kcl_test_revolve_on_face_circle_edge() {
let code = r#"box = startSketchOn(XY) let code = r#"box = startSketchOn(XY)
@ -1249,7 +1219,7 @@ secondSketch = startSketchOn(part001, face = '')
let err = err.as_kcl_error().unwrap(); let err = err.as_kcl_error().unwrap();
assert_eq!( assert_eq!(
err.message(), err.message(),
"The arg face was given, but it was the wrong type. It should be type FaceTag but it was string (text)" "The arg face was given, but it was the wrong type. It should be type FaceTag but it was string"
); );
} }
@ -1882,7 +1852,7 @@ someFunction('INVALID')
assert!(result.is_err()); assert!(result.is_err());
assert_eq!( assert_eq!(
result.err().unwrap().to_string(), result.err().unwrap().to_string(),
r#"semantic: KclErrorDetails { source_ranges: [SourceRange([46, 55, 0]), SourceRange([60, 83, 0])], message: "This function expected the input argument to be Solid or Plane but it's actually of type string (text)" }"# r#"semantic: KclErrorDetails { source_ranges: [SourceRange([46, 55, 0]), SourceRange([60, 83, 0])], message: "This function expected the input argument to be Solid or Plane but it's actually of type string" }"#
); );
} }

View File

@ -788,6 +788,7 @@ impl ArgData {
Some("Axis2d | Edge") | Some("Axis3d | Edge") => Some((index, format!(r#"{label}${{{index}:X}}"#))), Some("Axis2d | Edge") | Some("Axis3d | Edge") => Some((index, format!(r#"{label}${{{index}:X}}"#))),
Some("Edge") => Some((index, format!(r#"{label}${{{index}:tag_or_edge_fn}}"#))), Some("Edge") => Some((index, format!(r#"{label}${{{index}:tag_or_edge_fn}}"#))),
Some("[Edge; 1+]") => Some((index, format!(r#"{label}[${{{index}:tag_or_edge_fn}}]"#))), Some("[Edge; 1+]") => Some((index, format!(r#"{label}[${{{index}:tag_or_edge_fn}}]"#))),
Some("Plane") => Some((index, format!(r#"{label}${{{}:XY}}"#, index))),
Some("string") => Some((index, format!(r#"{label}${{{}:"string"}}"#, index))), Some("string") => Some((index, format!(r#"{label}${{{}:"string"}}"#, index))),
Some("bool") => Some((index, format!(r#"{label}${{{}:false}}"#, index))), Some("bool") => Some((index, format!(r#"{label}${{{}:false}}"#, index))),

View File

@ -1167,6 +1167,16 @@ mod tests {
assert_eq!(snippet, r#"clone(${0:part001})"#); assert_eq!(snippet, r#"clone(${0:part001})"#);
} }
#[test]
fn get_autocomplete_snippet_offset_plane() {
let data = kcl_doc::walk_prelude();
let DocData::Fn(offset_plane_fn) = data.find_by_name("offsetPlane").unwrap() else {
panic!();
};
let snippet = offset_plane_fn.to_autocomplete_snippet();
assert_eq!(snippet, r#"offsetPlane(${0:XY}, offset = ${1:3.14})"#);
}
// We want to test the snippets we compile at lsp start. // We want to test the snippets we compile at lsp start.
#[test] #[test]
fn get_all_stdlib_autocomplete_snippets() { fn get_all_stdlib_autocomplete_snippets() {

View File

@ -223,6 +223,26 @@ impl EngineConnection {
message: errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"), message: errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
source_ranges: vec![source_range], source_ranges: vec![source_range],
}) })
} else if let Ok(data) =
serde_json::from_str::<Vec<kittycad_modeling_cmds::websocket::FailureWebSocketResponse>>(&err_str)
{
if let Some(data) = data.first() {
// It could also be an array of responses.
KclError::Engine(KclErrorDetails {
message: data
.errors
.iter()
.map(|e| e.message.clone())
.collect::<Vec<_>>()
.join("\n"),
source_ranges: vec![source_range],
})
} else {
KclError::Engine(KclErrorDetails {
message: "Received empty response from engine".into(),
source_ranges: vec![source_range],
})
}
} else { } else {
KclError::Engine(KclErrorDetails { KclError::Engine(KclErrorDetails {
message: format!("Failed to wait for promise from send modeling command: {:?}", e), message: format!("Failed to wait for promise from send modeling command: {:?}", e),

View File

@ -764,7 +764,12 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
WebSocketResponse::Failure(fail) => { WebSocketResponse::Failure(fail) => {
let _request_id = fail.request_id; let _request_id = fail.request_id;
Err(KclError::Engine(KclErrorDetails { Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", fail.errors), message: fail
.errors
.iter()
.map(|e| e.message.clone())
.collect::<Vec<_>>()
.join("\n"),
source_ranges: vec![source_range], source_ranges: vec![source_range],
})) }))
} }
@ -807,7 +812,7 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
}) })
})?; })?;
return Err(KclError::Engine(KclErrorDetails { return Err(KclError::Engine(KclErrorDetails {
message: format!("Modeling command failed: {:?}", errors), message: errors.iter().map(|e| e.message.clone()).collect::<Vec<_>>().join("\n"),
source_ranges: vec![source_range], source_ranges: vec![source_range],
})); }));
} }

View File

@ -913,11 +913,9 @@ impl Node<MemberExpression> {
}), }),
(being_indexed, _, _) => { (being_indexed, _, _) => {
let t = being_indexed.human_friendly_type(); let t = being_indexed.human_friendly_type();
let article = article_for(t); let article = article_for(&t);
Err(KclError::Semantic(KclErrorDetails { Err(KclError::Semantic(KclErrorDetails {
message: format!( message: format!("Only arrays can be indexed, but you're trying to index {article} {t}"),
"Only arrays and objects can be indexed, but you're trying to index {article} {t}"
),
source_ranges: vec![self.clone().into()], source_ranges: vec![self.clone().into()],
})) }))
} }
@ -1313,10 +1311,15 @@ impl Node<CallExpressionKw> {
Some(l) => { Some(l) => {
fn_args.insert(l.name.clone(), arg); fn_args.insert(l.name.clone(), arg);
} }
None => errors.push(arg), None => {
if let Some(id) = arg_expr.arg.ident_name() {
fn_args.insert(id.to_owned(), arg);
} else {
errors.push(arg);
}
}
} }
} }
let fn_args = fn_args; // remove mutability
// Evaluate the unlabeled first param, if any exists. // Evaluate the unlabeled first param, if any exists.
let unlabeled = if let Some(ref arg_expr) = self.unlabeled { let unlabeled = if let Some(ref arg_expr) = self.unlabeled {
@ -1325,12 +1328,15 @@ impl Node<CallExpressionKw> {
let value = ctx let value = ctx
.execute_expr(arg_expr, exec_state, &metadata, &[], StatementKind::Expression) .execute_expr(arg_expr, exec_state, &metadata, &[], StatementKind::Expression)
.await?; .await?;
Some(Arg::new(value, source_range))
let label = arg_expr.ident_name().map(str::to_owned);
Some((label, Arg::new(value, source_range)))
} else { } else {
None None
}; };
let args = Args::new_kw( let mut args = Args::new_kw(
KwArgs { KwArgs {
unlabeled, unlabeled,
labeled: fn_args, labeled: fn_args,
@ -1349,6 +1355,20 @@ impl Node<CallExpressionKw> {
)); ));
} }
let formals = func.args(false);
// If it's possible the input arg was meant to be labelled and we probably don't want to use
// it as the input arg, then treat it as labelled.
if let Some((Some(label), _)) = &args.kw_args.unlabeled {
if (formals.iter().all(|a| a.label_required) || exec_state.pipe_value().is_some())
&& formals.iter().any(|a| &a.name == label && a.label_required)
&& !args.kw_args.labeled.contains_key(label)
{
let (label, arg) = args.kw_args.unlabeled.take().unwrap();
args.kw_args.labeled.insert(label.unwrap(), arg);
}
}
#[cfg(feature = "artifact-graph")] #[cfg(feature = "artifact-graph")]
let op = if func.feature_tree_operation() { let op = if func.feature_tree_operation() {
let op_labeled_args = args let op_labeled_args = args
@ -1370,7 +1390,6 @@ impl Node<CallExpressionKw> {
None None
}; };
let formals = func.args(false);
for (label, arg) in &args.kw_args.labeled { for (label, arg) in &args.kw_args.labeled {
match formals.iter().find(|p| &p.name == label) { match formals.iter().find(|p| &p.name == label) {
Some(p) => { Some(p) => {
@ -1698,8 +1717,9 @@ impl Node<ObjectExpression> {
} }
} }
fn article_for(s: &str) -> &'static str { fn article_for<S: AsRef<str>>(s: S) -> &'static str {
if s.starts_with(['a', 'e', 'i', 'o', 'u']) { // '[' is included since it's an array.
if s.as_ref().starts_with(['a', 'e', 'i', 'o', 'u', '[']) {
"an" "an"
} else { } else {
"a" "a"
@ -1709,10 +1729,9 @@ fn article_for(s: &str) -> &'static str {
fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> { fn number_as_f64(v: &KclValue, source_range: SourceRange) -> Result<TyF64, KclError> {
v.as_ty_f64().ok_or_else(|| { v.as_ty_f64().ok_or_else(|| {
let actual_type = v.human_friendly_type(); let actual_type = v.human_friendly_type();
let article = article_for(actual_type);
KclError::Semantic(KclErrorDetails { KclError::Semantic(KclErrorDetails {
source_ranges: vec![source_range], source_ranges: vec![source_range],
message: format!("Expected a number, but found {article} {actual_type}",), message: format!("Expected a number, but found {actual_type}",),
}) })
}) })
} }
@ -1867,6 +1886,21 @@ fn type_check_params_kw(
args: &mut KwArgs, args: &mut KwArgs,
exec_state: &mut ExecState, exec_state: &mut ExecState,
) -> Result<(), KclError> { ) -> Result<(), KclError> {
// If it's possible the input arg was meant to be labelled and we probably don't want to use
// it as the input arg, then treat it as labelled.
if let Some((Some(label), _)) = &args.unlabeled {
if (function_expression.params.iter().all(|p| p.labeled) || exec_state.pipe_value().is_some())
&& function_expression
.params
.iter()
.any(|p| &p.identifier.name == label && p.labeled)
&& !args.labeled.contains_key(label)
{
let (label, arg) = args.unlabeled.take().unwrap();
args.labeled.insert(label.unwrap(), arg);
}
}
for (label, arg) in &mut args.labeled { for (label, arg) in &mut args.labeled {
match function_expression.params.iter().find(|p| &p.identifier.name == label) { match function_expression.params.iter().find(|p| &p.identifier.name == label) {
Some(p) => { Some(p) => {
@ -1961,10 +1995,11 @@ fn type_check_params_kw(
if let Some(arg) = &mut args.unlabeled { if let Some(arg) = &mut args.unlabeled {
if let Some(p) = function_expression.params.iter().find(|p| !p.labeled) { if let Some(p) = function_expression.params.iter().find(|p| !p.labeled) {
if let Some(ty) = &p.type_ { if let Some(ty) = &p.type_ {
arg.value = arg arg.1.value = arg
.1
.value .value
.coerce( .coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range) &RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.1.source_range)
.map_err(|e| KclError::Semantic(e.into()))?, .map_err(|e| KclError::Semantic(e.into()))?,
exec_state, exec_state,
) )
@ -1976,9 +2011,9 @@ fn type_check_params_kw(
.map(|n| format!("`{}`", n)) .map(|n| format!("`{}`", n))
.unwrap_or_else(|| "this function".to_owned()), .unwrap_or_else(|| "this function".to_owned()),
ty.inner, ty.inner,
arg.value.human_friendly_type() arg.1.value.human_friendly_type()
), ),
source_ranges: vec![arg.source_range], source_ranges: vec![arg.1.source_range],
}) })
})?; })?;
} }
@ -2141,10 +2176,11 @@ impl FunctionSource {
if let Some(arg) = &mut args.kw_args.unlabeled { if let Some(arg) = &mut args.kw_args.unlabeled {
if let Some(p) = ast.params.iter().find(|p| !p.labeled) { if let Some(p) = ast.params.iter().find(|p| !p.labeled) {
if let Some(ty) = &p.type_ { if let Some(ty) = &p.type_ {
arg.value = arg arg.1.value = arg
.1
.value .value
.coerce( .coerce(
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range) &RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.1.source_range)
.map_err(|e| KclError::Semantic(e.into()))?, .map_err(|e| KclError::Semantic(e.into()))?,
exec_state, exec_state,
) )
@ -2154,7 +2190,7 @@ impl FunctionSource {
"The input argument of {} requires a value with type `{}`, but found {}", "The input argument of {} requires a value with type `{}`, but found {}",
props.name, props.name,
ty.inner, ty.inner,
arg.value.human_friendly_type(), arg.1.value.human_friendly_type(),
), ),
source_ranges: vec![callsite], source_ranges: vec![callsite],
}) })
@ -2226,7 +2262,7 @@ impl FunctionSource {
.kw_args .kw_args
.unlabeled .unlabeled
.as_ref() .as_ref()
.map(|arg| OpArg::new(OpKclValue::from(&arg.value), arg.source_range)), .map(|arg| OpArg::new(OpKclValue::from(&arg.1.value), arg.1.source_range)),
labeled_args: op_labeled_args, labeled_args: op_labeled_args,
}, },
source_range: callsite, source_range: callsite,
@ -2446,19 +2482,23 @@ arr1 = [42]: [number(cm)]
a = 42: string a = 42: string
"#; "#;
let result = parse_execute(program).await; let result = parse_execute(program).await;
assert!(result let err = result.unwrap_err();
.unwrap_err() assert!(
.to_string() err.to_string()
.contains("could not coerce number value to type string")); .contains("could not coerce number(default units) value to type string"),
"Expected error but found {err:?}"
);
let program = r#" let program = r#"
a = 42: Plane a = 42: Plane
"#; "#;
let result = parse_execute(program).await; let result = parse_execute(program).await;
assert!(result let err = result.unwrap_err();
.unwrap_err() assert!(
.to_string() err.to_string()
.contains("could not coerce number value to type Plane")); .contains("could not coerce number(default units) value to type Plane"),
"Expected error but found {err:?}"
);
let program = r#" let program = r#"
arr = [0]: [string] arr = [0]: [string]
@ -2467,7 +2507,7 @@ arr = [0]: [string]
let err = result.unwrap_err(); let err = result.unwrap_err();
assert!( assert!(
err.to_string() err.to_string()
.contains("could not coerce array (list) value to type [string]"), .contains("could not coerce [any; 1] value to type [string]"),
"Expected error but found {err:?}" "Expected error but found {err:?}"
); );
@ -2478,7 +2518,7 @@ mixedArr = [0, "a"]: [number(mm)]
let err = result.unwrap_err(); let err = result.unwrap_err();
assert!( assert!(
err.to_string() err.to_string()
.contains("could not coerce array (list) value to type [number(mm)]"), .contains("could not coerce [any; 2] value to type [number(mm)]"),
"Expected error but found {err:?}" "Expected error but found {err:?}"
); );
} }
@ -2663,13 +2703,12 @@ a = foo()
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn test_sensible_error_when_missing_equals_in_kwarg() { async fn test_sensible_error_when_missing_equals_in_kwarg() {
for (i, call) in ["f(x=1,y)", "f(x=1,3,z)", "f(x=1,y,z=1)", "f(x=1, 3 + 4, z=1)"] for (i, call) in ["f(x=1,3,0)", "f(x=1,3,z)", "f(x=1,0,z=1)", "f(x=1, 3 + 4, z)"]
.into_iter() .into_iter()
.enumerate() .enumerate()
{ {
let program = format!( let program = format!(
"fn foo() {{ return 0 }} "fn foo() {{ return 0 }}
y = 42
z = 0 z = 0
fn f(x, y, z) {{ return 0 }} fn f(x, y, z) {{ return 0 }}
{call}" {call}"
@ -2689,9 +2728,10 @@ fn f(x, y, z) {{ return 0 }}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn default_param_for_unlabeled() { async fn default_param_for_unlabeled() {
// Tests that the input param for myExtrude is taken from the pipeline value. // Tests that the input param for myExtrude is taken from the pipeline value and same-name
// keyword args.
let ast = r#"fn myExtrude(@sk, length) { let ast = r#"fn myExtrude(@sk, length) {
return extrude(sk, length = length) return extrude(sk, length)
} }
sketch001 = startSketchOn(XY) sketch001 = startSketchOn(XY)
|> circle(center = [0, 0], radius = 93.75) |> circle(center = [0, 0], radius = 93.75)
@ -2701,6 +2741,18 @@ sketch001 = startSketchOn(XY)
parse_execute(ast).await.unwrap(); parse_execute(ast).await.unwrap();
} }
#[tokio::test(flavor = "multi_thread")]
async fn dont_use_unlabelled_as_input() {
// `length` should be used as the `length` argument to extrude, not the unlabelled input
let ast = r#"length = 10
startSketchOn(XY)
|> circle(center = [0, 0], radius = 93.75)
|> extrude(length)
"#;
parse_execute(ast).await.unwrap();
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn ascription_in_binop() { async fn ascription_in_binop() {
let ast = r#"foo = tan(0): number(rad) - 4deg"#; let ast = r#"foo = tan(0): number(rad) - 4deg"#;

View File

@ -280,7 +280,10 @@ impl KclValue {
/// Human readable type name used in error messages. Should not be relied /// Human readable type name used in error messages. Should not be relied
/// on for program logic. /// on for program logic.
pub(crate) fn human_friendly_type(&self) -> &'static str { pub(crate) fn human_friendly_type(&self) -> String {
if let Some(t) = self.principal_type() {
return t.to_string();
}
match self { match self {
KclValue::Uuid { .. } => "Unique ID (uuid)", KclValue::Uuid { .. } => "Unique ID (uuid)",
KclValue::TagDeclarator(_) => "TagDeclarator", KclValue::TagDeclarator(_) => "TagDeclarator",
@ -314,6 +317,7 @@ impl KclValue {
KclValue::Type { .. } => "type", KclValue::Type { .. } => "type",
KclValue::KclNone { .. } => "None", KclValue::KclNone { .. } => "None",
} }
.to_owned()
} }
pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self { pub(crate) fn from_literal(literal: Node<Literal>, exec_state: &mut ExecState) -> Self {

View File

@ -1910,13 +1910,13 @@ notNull = !myNull
"#; "#;
assert_eq!( assert_eq!(
parse_execute(code1).await.unwrap_err().message(), parse_execute(code1).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: number", "Cannot apply unary operator ! to non-boolean value: number(default units)",
); );
let code2 = "notZero = !0"; let code2 = "notZero = !0";
assert_eq!( assert_eq!(
parse_execute(code2).await.unwrap_err().message(), parse_execute(code2).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: number", "Cannot apply unary operator ! to non-boolean value: number(default units)",
); );
let code3 = r#" let code3 = r#"
@ -1924,7 +1924,7 @@ notEmptyString = !""
"#; "#;
assert_eq!( assert_eq!(
parse_execute(code3).await.unwrap_err().message(), parse_execute(code3).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: string (text)", "Cannot apply unary operator ! to non-boolean value: string",
); );
let code4 = r#" let code4 = r#"
@ -1933,7 +1933,7 @@ notMember = !obj.a
"#; "#;
assert_eq!( assert_eq!(
parse_execute(code4).await.unwrap_err().message(), parse_execute(code4).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: number", "Cannot apply unary operator ! to non-boolean value: number(default units)",
); );
let code5 = " let code5 = "
@ -1941,7 +1941,7 @@ a = []
notArray = !a"; notArray = !a";
assert_eq!( assert_eq!(
parse_execute(code5).await.unwrap_err().message(), parse_execute(code5).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: array (list)", "Cannot apply unary operator ! to non-boolean value: [any; 0]",
); );
let code6 = " let code6 = "
@ -1949,7 +1949,7 @@ x = {}
notObject = !x"; notObject = !x";
assert_eq!( assert_eq!(
parse_execute(code6).await.unwrap_err().message(), parse_execute(code6).await.unwrap_err().message(),
"Cannot apply unary operator ! to non-boolean value: object", "Cannot apply unary operator ! to non-boolean value: { }",
); );
let code7 = " let code7 = "
@ -1975,7 +1975,7 @@ notTagDeclarator = !myTagDeclarator";
assert!( assert!(
tag_declarator_err tag_declarator_err
.message() .message()
.starts_with("Cannot apply unary operator ! to non-boolean value: TagDeclarator"), .starts_with("Cannot apply unary operator ! to non-boolean value: tag"),
"Actual error: {:?}", "Actual error: {:?}",
tag_declarator_err tag_declarator_err
); );
@ -1989,7 +1989,7 @@ notTagIdentifier = !myTag";
assert!( assert!(
tag_identifier_err tag_identifier_err
.message() .message()
.starts_with("Cannot apply unary operator ! to non-boolean value: TagIdentifier"), .starts_with("Cannot apply unary operator ! to non-boolean value: tag"),
"Actual error: {:?}", "Actual error: {:?}",
tag_identifier_err tag_identifier_err
); );

View File

@ -4220,8 +4220,8 @@ sketch001 = startSketchOn(XY)
result, result,
vec![tower_lsp::lsp_types::ColorInformation { vec![tower_lsp::lsp_types::ColorInformation {
range: tower_lsp::lsp_types::Range { range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, start: tower_lsp::lsp_types::Position { line: 4, character: 25 },
end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, end: tower_lsp::lsp_types::Position { line: 4, character: 32 },
}, },
color: tower_lsp::lsp_types::Color { color: tower_lsp::lsp_types::Color {
red: 1.0, red: 1.0,
@ -4272,8 +4272,8 @@ sketch001 = startSketchOn(XY)
result, result,
vec![tower_lsp::lsp_types::ColorInformation { vec![tower_lsp::lsp_types::ColorInformation {
range: tower_lsp::lsp_types::Range { range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, start: tower_lsp::lsp_types::Position { line: 4, character: 25 },
end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, end: tower_lsp::lsp_types::Position { line: 4, character: 32 },
}, },
color: tower_lsp::lsp_types::Color { color: tower_lsp::lsp_types::Color {
red: 1.0, red: 1.0,
@ -4291,8 +4291,8 @@ sketch001 = startSketchOn(XY)
uri: "file:///test.kcl".try_into().unwrap(), uri: "file:///test.kcl".try_into().unwrap(),
}, },
range: tower_lsp::lsp_types::Range { range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position { line: 4, character: 24 }, start: tower_lsp::lsp_types::Position { line: 4, character: 25 },
end: tower_lsp::lsp_types::Position { line: 4, character: 33 }, end: tower_lsp::lsp_types::Position { line: 4, character: 32 },
}, },
color: tower_lsp::lsp_types::Color { color: tower_lsp::lsp_types::Color {
red: 1.0, red: 1.0,
@ -4316,3 +4316,64 @@ sketch001 = startSketchOn(XY)
}] }]
); );
} }
#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_diagnostic_compilation_warnings() {
let server = kcl_lsp_server(false).await.unwrap();
// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"foo = 42
@settings(defaultLengthUnit = mm)"#
.to_string(),
},
})
.await;
// Send diagnostics request.
let diagnostics = server
.diagnostic(tower_lsp::lsp_types::DocumentDiagnosticParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
partial_result_params: Default::default(),
work_done_progress_params: Default::default(),
identifier: None,
previous_result_id: None,
})
.await
.unwrap();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReportResult::Report(diagnostics) = diagnostics {
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
assert_eq!(
diagnostics.full_document_diagnostic_report.items[0],
tower_lsp::lsp_types::Diagnostic {
range: tower_lsp::lsp_types::Range {
start: tower_lsp::lsp_types::Position { line: 0, character: 8 },
end: tower_lsp::lsp_types::Position { line: 1, character: 33 },
},
severity: Some(tower_lsp::lsp_types::DiagnosticSeverity::WARNING),
code: None,
source: Some("kcl".to_string()),
message: "Named attributes should appear before any declarations or expressions.\n\nBecause named attributes apply to the whole function or module, including code written before them, it can be confusing for readers to not have these attributes at the top of code blocks.".to_string(),
related_information: None,
tags: None,
data: None,
code_description: None,
}
);
} else {
panic!("Expected full diagnostics");
}
} else {
panic!("Expected diagnostics");
}
}

View File

@ -96,6 +96,7 @@ pub(crate) fn read_std(mod_name: &str) -> Option<&'static str> {
"solid" => Some(include_str!("../std/solid.kcl")), "solid" => Some(include_str!("../std/solid.kcl")),
"units" => Some(include_str!("../std/units.kcl")), "units" => Some(include_str!("../std/units.kcl")),
"array" => Some(include_str!("../std/array.kcl")), "array" => Some(include_str!("../std/array.kcl")),
"sweep" => Some(include_str!("../std/sweep.kcl")),
"transform" => Some(include_str!("../std/transform.kcl")), "transform" => Some(include_str!("../std/transform.kcl")),
_ => None, _ => None,
} }

View File

@ -186,7 +186,7 @@ impl<T> Node<T> {
self.comment_start = start; self.comment_start = start;
} }
pub fn map_ref<'a, U: 'a>(&'a self, f: fn(&'a T) -> U) -> Node<U> { pub fn map_ref<'a, U: 'a>(&'a self, f: impl Fn(&'a T) -> U) -> Node<U> {
Node { Node {
inner: f(&self.inner), inner: f(&self.inner),
start: self.start, start: self.start,
@ -438,8 +438,15 @@ impl Node<Program> {
let add_color = |literal: &Node<Literal>| { let add_color = |literal: &Node<Literal>| {
// Check if the string is a color. // Check if the string is a color.
if let Some(c) = literal.value.is_color() { if let Some(c) = literal.value.is_color() {
let source_range = literal.as_source_range();
// We subtract 1 from either side because of the "'s in the literal.
let fixed_source_range = SourceRange::new(
source_range.start() + 1,
source_range.end() - 1,
source_range.module_id(),
);
let color = ColorInformation { let color = ColorInformation {
range: literal.as_source_range().to_lsp_range(code), range: fixed_source_range.to_lsp_range(code),
color: tower_lsp::lsp_types::Color { color: tower_lsp::lsp_types::Color {
red: c.r, red: c.r,
green: c.g, green: c.g,
@ -498,7 +505,11 @@ impl Node<Program> {
crate::walk::walk(self, |node: crate::walk::Node<'a>| { crate::walk::walk(self, |node: crate::walk::Node<'a>| {
match node { match node {
crate::walk::Node::Literal(literal) => { crate::walk::Node::Literal(literal) => {
if literal.start == pos_start && literal.end == pos_end && literal.value.is_color().is_some() { // Account for the quotes in the literal.
if (literal.start + 1) == pos_start
&& (literal.end - 1) == pos_end
&& literal.value.is_color().is_some()
{
found.replace(true); found.replace(true);
return Ok(true); return Ok(true);
} }
@ -1187,7 +1198,7 @@ impl Expr {
pub fn ident_name(&self) -> Option<&str> { pub fn ident_name(&self) -> Option<&str> {
match self { match self {
Expr::Name(ident) => Some(&ident.name.name), Expr::Name(name) => name.local_ident().map(|n| n.inner),
_ => None, _ => None,
} }
} }
@ -2371,7 +2382,7 @@ impl Name {
pub fn local_ident(&self) -> Option<Node<&str>> { pub fn local_ident(&self) -> Option<Node<&str>> {
if self.path.is_empty() && !self.abs_path { if self.path.is_empty() && !self.abs_path {
Some(self.name.map_ref(|n| &n.name)) Some(self.name.map_ref(|n| &*n.name))
} else { } else {
None None
} }

View File

@ -2729,6 +2729,17 @@ fn ty(i: &mut TokenSlice) -> PResult<Token> {
keyword(i, "type") keyword(i, "type")
} }
fn any_keyword(i: &mut TokenSlice) -> PResult<Token> {
any.try_map(|token: Token| match token.token_type {
TokenType::Keyword => Ok(token),
_ => Err(CompilationError::fatal(
token.as_source_range(),
"expected some reserved keyword".to_owned(),
)),
})
.parse_next(i)
}
fn keyword(i: &mut TokenSlice, expected: &str) -> PResult<Token> { fn keyword(i: &mut TokenSlice, expected: &str) -> PResult<Token> {
any.try_map(|token: Token| match token.token_type { any.try_map(|token: Token| match token.token_type {
TokenType::Keyword if token.value == expected => Ok(token), TokenType::Keyword if token.value == expected => Ok(token),
@ -3143,12 +3154,14 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
NonCode(Node<NonCodeNode>), NonCode(Node<NonCodeNode>),
LabeledArg(LabeledArg), LabeledArg(LabeledArg),
UnlabeledArg(Expr), UnlabeledArg(Expr),
Keyword(Token),
} }
let initial_unlabeled_arg = opt((expression, comma, opt(whitespace)).map(|(arg, _, _)| arg)).parse_next(i)?; let initial_unlabeled_arg = opt((expression, comma, opt(whitespace)).map(|(arg, _, _)| arg)).parse_next(i)?;
let args: Vec<_> = repeat( let args: Vec<_> = repeat(
0.., 0..,
alt(( alt((
terminated(non_code_node.map(ArgPlace::NonCode), whitespace), terminated(non_code_node.map(ArgPlace::NonCode), whitespace),
terminated(any_keyword.map(ArgPlace::Keyword), whitespace),
terminated(labeled_argument, labeled_arg_separator).map(ArgPlace::LabeledArg), terminated(labeled_argument, labeled_arg_separator).map(ArgPlace::LabeledArg),
expression.map(ArgPlace::UnlabeledArg), expression.map(ArgPlace::UnlabeledArg),
)), )),
@ -3164,6 +3177,18 @@ fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
ArgPlace::LabeledArg(x) => { ArgPlace::LabeledArg(x) => {
args.push(x); args.push(x);
} }
ArgPlace::Keyword(kw) => {
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::from(kw.clone()),
format!(
"`{}` is not the name of an argument (it's a reserved keyword)",
kw.value
),
)
.into(),
));
}
ArgPlace::UnlabeledArg(arg) => { ArgPlace::UnlabeledArg(arg) => {
let followed_by_equals = peek((opt(whitespace), equals)).parse_next(i).is_ok(); let followed_by_equals = peek((opt(whitespace), equals)).parse_next(i).is_ok();
if followed_by_equals { if followed_by_equals {
@ -5055,6 +5080,30 @@ bar = 1
} }
} }
#[test]
fn test_sensible_error_when_using_keyword_as_arg_label() {
for (i, program) in ["pow(2, fn = 8)"].into_iter().enumerate() {
let tokens = crate::parsing::token::lex(program, ModuleId::default()).unwrap();
let err = match fn_call_kw.parse(tokens.as_slice()) {
Err(e) => e,
Ok(ast) => {
eprintln!("{ast:#?}");
panic!("Expected this to error but it didn't");
}
};
let cause = err.inner().cause.as_ref().unwrap();
assert_eq!(
cause.message, "`fn` is not the name of an argument (it's a reserved keyword)",
"failed test {i}: {program}"
);
assert_eq!(
cause.source_range.start(),
program.find("fn").unwrap(),
"failed test {i}: {program}"
);
}
}
#[test] #[test]
fn test_sensible_error_when_missing_rhs_of_obj_property() { fn test_sensible_error_when_missing_rhs_of_obj_property() {
for (i, program) in ["{x = 1, y =}"].into_iter().enumerate() { for (i, program) in ["{x = 1, y =}"].into_iter().enumerate() {

View File

@ -1669,7 +1669,6 @@ mod mike_stress_test {
/// Test that KCL is executed correctly. /// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
#[ignore = "when kurt made the artifact graph lots of commands, this became super slow and sometimes the engine will just die, turn this back on when we can parallelize the simulation tests with snapshots deterministically"]
async fn kcl_test_execute() { async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
@ -2749,7 +2748,6 @@ mod import_mesh_clone {
/// Test that KCL is executed correctly. /// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
#[ignore = "turn on when katie fixes the mesh import"]
async fn kcl_test_execute() { async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
@ -2772,6 +2770,7 @@ mod clone_w_fillets {
/// Test that KCL is executed correctly. /// Test that KCL is executed correctly.
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
#[ignore] // turn on when https://github.com/KittyCAD/engine/pull/3380 is merged #[ignore] // turn on when https://github.com/KittyCAD/engine/pull/3380 is merged
// Theres also a test in clone.rs you need to turn too
async fn kcl_test_execute() { async fn kcl_test_execute() {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
@ -3071,3 +3070,45 @@ mod error_inside_fn_also_has_source_range_of_call_site_recursive {
super::execute(TEST_NAME, true).await super::execute(TEST_NAME, true).await
} }
} }
mod error_revolve_on_edge_get_edge {
const TEST_NAME: &str = "error_revolve_on_edge_get_edge";
/// 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 sketch_on_face_union {
const TEST_NAME: &str = "sketch_on_face_union";
/// 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
}
}

View File

@ -59,7 +59,9 @@ impl Arg {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct KwArgs { pub struct KwArgs {
/// Unlabeled keyword args. Currently only the first arg can be unlabeled. /// Unlabeled keyword args. Currently only the first arg can be unlabeled.
pub unlabeled: Option<Arg>, /// If the argument was a local variable, then the first element of the tuple is its name
/// which may be used to treat this arg as a labelled arg.
pub unlabeled: Option<(Option<String>, Arg)>,
/// Labeled args. /// Labeled args.
pub labeled: IndexMap<String, Arg>, pub labeled: IndexMap<String, Arg>,
pub errors: Vec<Arg>, pub errors: Vec<Arg>,
@ -257,14 +259,22 @@ impl Args {
}; };
let arg = arg.value.coerce(ty, exec_state).map_err(|_| { let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
let actual_type_name = arg.value.human_friendly_type(); let actual_type = arg.value.principal_type();
let actual_type_name = actual_type
.as_ref()
.map(|t| t.to_string())
.unwrap_or_else(|| arg.value.human_friendly_type().to_owned());
let msg_base = format!( let msg_base = format!(
"This function expected the input argument to be {} but it's actually of type {actual_type_name}", "This function expected the input argument to be {} but it's actually of type {actual_type_name}",
ty.human_friendly_type(), ty.human_friendly_type(),
); );
let suggestion = match (ty, actual_type_name) { let suggestion = match (ty, actual_type) {
(RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") => Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER), (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
(RuntimeType::Array(t, _), "Sketch") if **t == RuntimeType::Primitive(PrimitiveType::Solid) => { Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
}
(RuntimeType::Array(t, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
if **t == RuntimeType::Primitive(PrimitiveType::Solid) =>
{
Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER) Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
} }
_ => None, _ => None,
@ -334,6 +344,7 @@ impl Args {
self.kw_args self.kw_args
.unlabeled .unlabeled
.as_ref() .as_ref()
.map(|(_, a)| a)
.or(self.args.first()) .or(self.args.first())
.or(self.pipe_value.as_ref()) .or(self.pipe_value.as_ref())
} }
@ -381,14 +392,22 @@ impl Args {
}))?; }))?;
let arg = arg.value.coerce(ty, exec_state).map_err(|_| { let arg = arg.value.coerce(ty, exec_state).map_err(|_| {
let actual_type_name = arg.value.human_friendly_type(); let actual_type = arg.value.principal_type();
let actual_type_name = actual_type
.as_ref()
.map(|t| t.to_string())
.unwrap_or_else(|| arg.value.human_friendly_type().to_owned());
let msg_base = format!( let msg_base = format!(
"This function expected the input argument to be {} but it's actually of type {actual_type_name}", "This function expected the input argument to be {} but it's actually of type {actual_type_name}",
ty.human_friendly_type(), ty.human_friendly_type(),
); );
let suggestion = match (ty, actual_type_name) { let suggestion = match (ty, actual_type) {
(RuntimeType::Primitive(PrimitiveType::Solid), "Sketch") => Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER), (RuntimeType::Primitive(PrimitiveType::Solid), Some(RuntimeType::Primitive(PrimitiveType::Sketch))) => {
(RuntimeType::Array(ty, _), "Sketch") if **ty == RuntimeType::Primitive(PrimitiveType::Solid) => { Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
}
(RuntimeType::Array(ty, _), Some(RuntimeType::Primitive(PrimitiveType::Sketch)))
if **ty == RuntimeType::Primitive(PrimitiveType::Solid) =>
{
Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER) Some(ERROR_STRING_SKETCH_TO_SOLID_HELPER)
} }
_ => None, _ => None,

View File

@ -48,7 +48,7 @@ async fn call_map_closure(
ctxt: &ExecutorContext, ctxt: &ExecutorContext,
) -> Result<KclValue, KclError> { ) -> Result<KclValue, KclError> {
let kw_args = KwArgs { let kw_args = KwArgs {
unlabeled: Some(Arg::new(input, source_range)), unlabeled: Some((None, Arg::new(input, source_range))),
labeled: Default::default(), labeled: Default::default(),
errors: Vec::new(), errors: Vec::new(),
}; };
@ -104,7 +104,7 @@ async fn call_reduce_closure(
let mut labeled = IndexMap::with_capacity(1); let mut labeled = IndexMap::with_capacity(1);
labeled.insert("accum".to_string(), Arg::new(accum, source_range)); labeled.insert("accum".to_string(), Arg::new(accum, source_range));
let kw_args = KwArgs { let kw_args = KwArgs {
unlabeled: Some(Arg::new(elem, source_range)), unlabeled: Some((None, Arg::new(elem, source_range))),
labeled, labeled,
errors: Vec::new(), errors: Vec::new(),
}; };

View File

@ -177,25 +177,6 @@ async fn get_old_new_child_map(
exec_state: &mut ExecState, exec_state: &mut ExecState,
args: &Args, args: &Args,
) -> Result<HashMap<uuid::Uuid, uuid::Uuid>> { ) -> Result<HashMap<uuid::Uuid, uuid::Uuid>> {
// Get the new geometries entity ids.
let response = args
.send_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::EntityGetAllChildUuids {
entity_id: new_geometry_id,
}),
)
.await?;
let OkWebSocketResponseData::Modeling {
modeling_response:
OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids {
entity_ids: new_entity_ids,
}),
} = response
else {
anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response);
};
// Get the old geometries entity ids. // Get the old geometries entity ids.
let response = args let response = args
.send_modeling_cmd( .send_modeling_cmd(
@ -215,6 +196,25 @@ async fn get_old_new_child_map(
anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response); anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response);
}; };
// Get the new geometries entity ids.
let response = args
.send_modeling_cmd(
exec_state.next_uuid(),
ModelingCmd::from(mcmd::EntityGetAllChildUuids {
entity_id: new_geometry_id,
}),
)
.await?;
let OkWebSocketResponseData::Modeling {
modeling_response:
OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids {
entity_ids: new_entity_ids,
}),
} = response
else {
anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response);
};
// Create a map of old entity ids to new entity ids. // Create a map of old entity ids to new entity ids.
Ok(HashMap::from_iter( Ok(HashMap::from_iter(
old_entity_ids old_entity_ids

View File

@ -14,7 +14,7 @@ use super::{args::TyF64, DEFAULT_TOLERANCE};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
execution::{types::RuntimeType, ExecState, KclValue, Solid}, execution::{types::RuntimeType, ExecState, KclValue, Solid},
std::Args, std::{patterns::GeometryTrait, Args},
}; };
/// Union two or more solids into a single solid. /// Union two or more solids into a single solid.
@ -123,7 +123,7 @@ pub(crate) async fn inner_union(
let solid_out_id = exec_state.next_uuid(); let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone(); let mut solid = solids[0].clone();
solid.id = solid_out_id; solid.set_id(solid_out_id);
let mut new_solids = vec![solid.clone()]; let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await { if args.ctx.no_engine_commands().await {
@ -155,7 +155,7 @@ pub(crate) async fn inner_union(
// If we have more solids, set those as well. // If we have more solids, set those as well.
if !extra_solid_ids.is_empty() { if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0]; solid.set_id(extra_solid_ids[0]);
new_solids.push(solid.clone()); new_solids.push(solid.clone());
} }
@ -249,7 +249,7 @@ pub(crate) async fn inner_intersect(
let solid_out_id = exec_state.next_uuid(); let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone(); let mut solid = solids[0].clone();
solid.id = solid_out_id; solid.set_id(solid_out_id);
let mut new_solids = vec![solid.clone()]; let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await { if args.ctx.no_engine_commands().await {
@ -281,7 +281,7 @@ pub(crate) async fn inner_intersect(
// If we have more solids, set those as well. // If we have more solids, set those as well.
if !extra_solid_ids.is_empty() { if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0]; solid.set_id(extra_solid_ids[0]);
new_solids.push(solid.clone()); new_solids.push(solid.clone());
} }
@ -385,7 +385,7 @@ pub(crate) async fn inner_subtract(
let solid_out_id = exec_state.next_uuid(); let solid_out_id = exec_state.next_uuid();
let mut solid = solids[0].clone(); let mut solid = solids[0].clone();
solid.id = solid_out_id; solid.set_id(solid_out_id);
let mut new_solids = vec![solid.clone()]; let mut new_solids = vec![solid.clone()];
if args.ctx.no_engine_commands().await { if args.ctx.no_engine_commands().await {
@ -419,7 +419,7 @@ pub(crate) async fn inner_subtract(
// If we have more solids, set those as well. // If we have more solids, set those as well.
if !extra_solid_ids.is_empty() { if !extra_solid_ids.is_empty() {
solid.id = extra_solid_ids[0]; solid.set_id(extra_solid_ids[0]);
new_solids.push(solid.clone()); new_solids.push(solid.clone());
} }

View File

@ -424,7 +424,7 @@ async fn make_transform<T: GeometryTrait>(
meta: vec![source_range.into()], meta: vec![source_range.into()],
}; };
let kw_args = KwArgs { let kw_args = KwArgs {
unlabeled: Some(Arg::new(repetition_num, source_range)), unlabeled: Some((None, Arg::new(repetition_num, source_range))),
labeled: Default::default(), labeled: Default::default(),
errors: Vec::new(), errors: Vec::new(),
}; };
@ -598,7 +598,7 @@ fn array_to_point2d(
.map(|val| val.as_point2d().unwrap()) .map(|val| val.as_point2d().unwrap())
} }
trait GeometryTrait: Clone { pub trait GeometryTrait: Clone {
type Set: Into<Vec<Self>> + Clone; type Set: Into<Vec<Self>> + Clone;
fn id(&self) -> Uuid; fn id(&self) -> Uuid;
fn original_id(&self) -> Uuid; fn original_id(&self) -> Uuid;
@ -608,6 +608,7 @@ trait GeometryTrait: Clone {
source_ranges: Vec<SourceRange>, source_ranges: Vec<SourceRange>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
) -> Result<[TyF64; 3], KclError>; ) -> Result<[TyF64; 3], KclError>;
#[allow(async_fn_in_trait)]
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: &Self::Set) -> Result<(), KclError>; async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: &Self::Set) -> Result<(), KclError>;
} }
@ -641,6 +642,8 @@ impl GeometryTrait for Solid {
type Set = Vec<Solid>; type Set = Vec<Solid>;
fn set_id(&mut self, id: Uuid) { fn set_id(&mut self, id: Uuid) {
self.id = id; self.id = id;
// We need this for in extrude.rs when you sketch on face.
self.sketch.id = id;
} }
fn id(&self) -> Uuid { fn id(&self) -> Uuid {

View File

@ -3,7 +3,7 @@
use anyhow::Result; use anyhow::Result;
use kcl_derive_docs::stdlib; use kcl_derive_docs::stdlib;
use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd}; use kcmc::{each_cmd as mcmd, length_unit::LengthUnit, ModelingCmd};
use kittycad_modeling_cmds::{self as kcmc}; use kittycad_modeling_cmds::{self as kcmc, shared::RelativeTo};
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Serialize; use serde::Serialize;
@ -37,11 +37,20 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
)?; )?;
let sectional = args.get_kw_arg_opt("sectional")?; let sectional = args.get_kw_arg_opt("sectional")?;
let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?; let tolerance: Option<TyF64> = args.get_kw_arg_opt_typed("tolerance", &RuntimeType::length(), exec_state)?;
let relative_to: Option<String> = args.get_kw_arg_opt_typed("relativeTo", &RuntimeType::string(), exec_state)?;
let tag_start = args.get_kw_arg_opt("tagStart")?; let tag_start = args.get_kw_arg_opt("tagStart")?;
let tag_end = args.get_kw_arg_opt("tagEnd")?; let tag_end = args.get_kw_arg_opt("tagEnd")?;
let value = inner_sweep( let value = inner_sweep(
sketches, path, sectional, tolerance, tag_start, tag_end, exec_state, args, sketches,
path,
sectional,
tolerance,
relative_to,
tag_start,
tag_end,
exec_state,
args,
) )
.await?; .await?;
Ok(value.into()) Ok(value.into())
@ -103,7 +112,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ) /// springSketch = startSketchOn(YZ)
/// |> circle( center = [0, 0], radius = 1) /// |> circle( center = [0, 0], radius = 1)
/// |> sweep(path = helixPath) /// |> sweep(path = helixPath, relativeTo = "sketchPlane")
/// ``` /// ```
/// ///
/// ```no_run /// ```no_run
@ -158,6 +167,7 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
path = { docs = "The path to sweep the sketch along" }, path = { docs = "The path to sweep the sketch along" },
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." }, sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
tolerance = { docs = "Tolerance for this operation" }, tolerance = { docs = "Tolerance for this operation" },
relative_to = { docs = "What is the sweep relative to? Can be either 'sketchPlane' or 'trajectoryCurve'. Defaults to trajectoryCurve."},
tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" }, tag_start = { docs = "A named tag for the face at the start of the sweep, i.e. the original sketch" },
tag_end = { docs = "A named tag for the face at the end of the sweep" }, tag_end = { docs = "A named tag for the face at the end of the sweep" },
}, },
@ -169,6 +179,7 @@ async fn inner_sweep(
path: SweepPath, path: SweepPath,
sectional: Option<bool>, sectional: Option<bool>,
tolerance: Option<TyF64>, tolerance: Option<TyF64>,
relative_to: Option<String>,
tag_start: Option<TagNode>, tag_start: Option<TagNode>,
tag_end: Option<TagNode>, tag_end: Option<TagNode>,
exec_state: &mut ExecState, exec_state: &mut ExecState,
@ -178,6 +189,16 @@ async fn inner_sweep(
SweepPath::Sketch(sketch) => sketch.id.into(), SweepPath::Sketch(sketch) => sketch.id.into(),
SweepPath::Helix(helix) => helix.value.into(), SweepPath::Helix(helix) => helix.value.into(),
}; };
let relative_to = match relative_to.as_deref() {
Some("sketchPlane") => RelativeTo::SketchPlane,
Some("trajectoryCurve") | None => RelativeTo::TrajectoryCurve,
Some(_) => {
return Err(KclError::Syntax(crate::errors::KclErrorDetails {
source_ranges: vec![args.source_range],
message: "If you provide relativeTo, it must either be 'sketchPlane' or 'trajectoryCurve'".to_owned(),
}))
}
};
let mut solids = Vec::new(); let mut solids = Vec::new();
for sketch in &sketches { for sketch in &sketches {
@ -189,6 +210,7 @@ async fn inner_sweep(
trajectory, trajectory,
sectional: sectional.unwrap_or(false), sectional: sectional.unwrap_or(false),
tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)), tolerance: LengthUnit(tolerance.as_ref().map(|t| t.to_mm()).unwrap_or(DEFAULT_TOLERANCE)),
relative_to,
}), }),
) )
.await?; .await?;

View File

@ -21,6 +21,7 @@ export import * from "std::sketch"
export import * from "std::solid" export import * from "std::solid"
export import * from "std::transform" export import * from "std::transform"
export import "std::turns" export import "std::turns"
export import "std::sweep"
/// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis. /// An abstract 3d plane aligned with the X and Y axes. Its normal is the positive Z axis.
export XY = { export XY = {
@ -83,7 +84,7 @@ export END = 'end'
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(YZ) /// springSketch = startSketchOn(YZ)
/// |> circle( center = [0, 0], radius = 0.5) /// |> circle( center = [0, 0], radius = 0.5)
/// |> sweep(path = helixPath) /// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -104,7 +105,7 @@ export END = 'end'
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY) /// springSketch = startSketchOn(XY)
/// |> circle( center = [0, 0], radius = 0.5 ) /// |> circle( center = [0, 0], radius = 0.5 )
/// |> sweep(path = helixPath) /// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -124,7 +125,7 @@ export END = 'end'
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// springSketch = startSketchOn(XY) /// springSketch = startSketchOn(XY)
/// |> circle( center = [0, 0], radius = 1 ) /// |> circle( center = [0, 0], radius = 1 )
/// |> sweep(path = helixPath) /// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// ``` /// ```
/// ///
/// ``` /// ```
@ -413,7 +414,7 @@ export fn offsetPlane(
/// // Create a spring by sweeping around the helix path. /// // Create a spring by sweeping around the helix path.
/// sweepedSpring = clone(springSketch) /// sweepedSpring = clone(springSketch)
/// |> translate(x=100) /// |> translate(x=100)
/// |> sweep(path = helixPath) /// |> sweep(path = helixPath, relativeTo = sweep::SKETCH_PLANE)
/// ``` /// ```
/// ///
/// ```kcl /// ```kcl

View File

@ -0,0 +1,5 @@
/// Local/relative to the trajectory curve
export TRAJECTORY = 'trajectoryCurve'
/// Local/relative to a position centered within the plane being sketched on
export SKETCH_PLANE = 'sketchPlane'

View File

@ -4,8 +4,7 @@ description: Error from executing argument_error.kcl
--- ---
KCL Semantic error KCL Semantic error
× semantic: f requires a value with type `fn(any): any`, but found array × semantic: f requires a value with type `fn(any): any`, but found [any; 2]
│ (list)
╭─[5:1] ╭─[5:1]
4 │ 4 │
5 │ map(f, f = [0, 1]) 5 │ map(f, f = [0, 1])
@ -16,7 +15,7 @@ KCL Semantic error
╰─▶ KCL Semantic error ╰─▶ KCL Semantic error
× semantic: f requires a value with type `fn(any): any`, but found × semantic: f requires a value with type `fn(any): any`, but found
array (list) [any; 2]
╭─[5:12] ╭─[5:12]
4 │ 4 │
5 │ map(f, f = [0, 1]) 5 │ map(f, f = [0, 1])

View File

@ -5,7 +5,7 @@ description: Error from executing array_elem_pop_empty_fail.kcl
KCL Semantic error KCL Semantic error
× semantic: The input argument of `std::array::pop` requires a value with × semantic: The input argument of `std::array::pop` requires a value with
│ type `[any; 1+]`, but found array (list) │ type `[any; 1+]`, but found [any; 0]
╭─[2:8] ╭─[2:8]
1 │ arr = [] 1 │ arr = []
2 │ fail = pop(arr) 2 │ fail = pop(arr)
@ -16,7 +16,7 @@ KCL Semantic error
╰─▶ KCL Semantic error ╰─▶ KCL Semantic error
× semantic: The input argument of `std::array::pop` requires a value × semantic: The input argument of `std::array::pop` requires a value
│ with type `[any; 1+]`, but found array (list) │ with type `[any; 1+]`, but found [any; 0]
╭─[2:12] ╭─[2:12]
1 │ arr = [] 1 │ arr = []
2 │ fail = pop(arr) 2 │ fail = pop(arr)

View File

@ -4,7 +4,7 @@ description: Error from executing comparisons_multiple.kcl
--- ---
KCL Semantic error KCL Semantic error
× semantic: Expected a number, but found a boolean (true/false value) × semantic: Expected a number, but found bool
╭──── ╭────
1 │ assert(3 == 3 == 3, error = "this should not compile") 1 │ assert(3 == 3 == 3, error = "this should not compile")
· ───┬── · ───┬──

View File

@ -5,7 +5,7 @@ description: Error from executing error_inside_fn_also_has_source_range_of_call_
KCL Semantic error KCL Semantic error
× semantic: This function expected the input argument to be Solid or Plane × semantic: This function expected the input argument to be Solid or Plane
│ but it's actually of type string (text) │ but it's actually of type string
╭─[3:23] ╭─[3:23]
2 │ fn someNestedFunction(@something2) { 2 │ fn someNestedFunction(@something2) {
3 │ startSketchOn(something2) 3 │ startSketchOn(something2)
@ -25,7 +25,7 @@ KCL Semantic error
├─▶ KCL Semantic error ├─▶ KCL Semantic error
× semantic: This function expected the input argument to be Solid or × semantic: This function expected the input argument to be Solid or
│ │ Plane but it's actually of type string (text) │ │ Plane but it's actually of type string
│ ╭─[3:23] │ ╭─[3:23]
│ 2 │ fn someNestedFunction(@something2) { │ 2 │ fn someNestedFunction(@something2) {
│ 3 │ startSketchOn(something2) │ 3 │ startSketchOn(something2)
@ -37,7 +37,7 @@ KCL Semantic error
╰─▶ KCL Semantic error ╰─▶ KCL Semantic error
× semantic: This function expected the input argument to be Solid or × semantic: This function expected the input argument to be Solid or
│ Plane but it's actually of type string (text) │ Plane but it's actually of type string
╭─[6:5] ╭─[6:5]
5 │ 5 │
6 │ someNestedFunction(something) 6 │ someNestedFunction(something)

View File

@ -0,0 +1,349 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Artifact commands error_revolve_on_edge_get_edge.kcl
---
[
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "edge_lines_visible",
"hidden": false
}
},
{
"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": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 0.0,
"y": 0.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 0.0,
"y": 10.0,
"z": 0.0
},
"relative": true
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 10.0,
"y": 0.0,
"z": 0.0
},
"relative": true
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 0.0,
"y": -10.0,
"z": 0.0
},
"relative": true
}
}
},
{
"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,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_adjacency_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "enable_sketch_mode",
"entity_id": "[uuid]",
"ortho": false,
"animated": false,
"adjust_camera": false,
"planar_normal": null
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "move_path_pen",
"path": "[uuid]",
"to": {
"x": 5.0,
"y": 10.0,
"z": 0.0
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "sketch_mode_disable"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "start_path"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 0.0,
"y": -10.0,
"z": 0.0
},
"relative": true
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 2.0,
"y": 0.0,
"z": 0.0
},
"relative": true
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "extend_path",
"path": "[uuid]",
"segment": {
"type": "line",
"end": {
"x": 0.0,
"y": 10.0,
"z": 0.0
},
"relative": true
}
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "close_path",
"path_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_bring_to_front",
"object_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "revolve_about_edge",
"target": "[uuid]",
"edge_id": "[uuid]",
"angle": {
"unit": "degrees",
"value": 90.0
},
"tolerance": 0.0000001,
"opposite": "None"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "solid3d_get_extrusion_face_info",
"object_id": "[uuid]",
"edge_id": "[uuid]"
}
}
]

View File

@ -0,0 +1,937 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of parsing error_revolve_on_edge_get_edge.kcl
---
{
"Ok": {
"body": [
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "box",
"start": 0,
"type": "Identifier"
},
"init": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "startSketchOn",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "XY",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "at",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "startProfile",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"argument": {
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"operator": "-",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "tag",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"start": 0,
"type": "TagDeclarator",
"type": "TagDeclarator",
"value": "revolveAxis"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "close",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "length",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "extrude",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
}
],
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "sketch001",
"start": 0,
"type": "Identifier"
},
"init": {
"body": [
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "face",
"start": 0,
"type": "Identifier"
},
"arg": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "revolveAxis",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "startSketchOn",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "box",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "at",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "5",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 5.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "startProfile",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"argument": {
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"operator": "-",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "2",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "end",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"elements": [
{
"commentStart": 0,
"end": 0,
"raw": "0",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 0.0,
"suffix": "None"
}
},
{
"commentStart": 0,
"end": 0,
"raw": "10",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 10.0,
"suffix": "None"
}
}
],
"end": 0,
"start": 0,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "line",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "close",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "axis",
"start": 0,
"type": "Identifier"
},
"arg": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "revolveAxis",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "angle",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "90",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 90.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "revolve",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
}
],
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
}
],
"commentStart": 0,
"end": 0,
"nonCodeMeta": {
"nonCodeNodes": {
"0": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,26 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing error_revolve_on_edge_get_edge.kcl
---
KCL Engine error
× engine: Solid3D revolve failed: sketch profile must lie entirely on one
│ side of the revolution axis
╭─[15:6]
14 │ |> close()
15 │ |> revolve(axis = revolveAxis, angle = 90)
· ───────────────────┬───────────────────┬
· ╰── tests/error_revolve_on_edge_get_edge/input.kcl
· ╰── tests/error_revolve_on_edge_get_edge/input.kcl
╰────
╰─▶ KCL Engine error
× engine: Solid3D revolve failed: sketch profile must lie entirely on
│ one side of the revolution axis
╭─[15:6]
14 │ |> close()
15 │ |> revolve(axis = revolveAxis, angle = 90)
· ───────────────────┬───────────────────
· ╰── tests/error_revolve_on_edge_get_edge/
input.kcl
╰────

View File

@ -0,0 +1,15 @@
box = startSketchOn(XY)
|> startProfile(at = [0, 0])
|> line(end = [0, 10])
|> line(end = [10, 0])
|> line(end = [0, -10], tag = $revolveAxis)
|> close()
|> extrude(length = 10)
sketch001 = startSketchOn(box, face = revolveAxis)
|> startProfile(at = [5, 10])
|> line(end = [0, -10])
|> line(end = [2, 0])
|> line(end = [0, 10])
|> close()
|> revolve(axis = revolveAxis, angle = 90)

View File

@ -0,0 +1,116 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed error_revolve_on_edge_get_edge.kcl
---
[
{
"labeledArgs": {},
"name": "startSketchOn",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Plane",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
{
"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": []
}
},
{
"labeledArgs": {
"face": {
"value": {
"type": "TagIdentifier",
"value": "revolveAxis",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
"name": "startSketchOn",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Solid",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"type": "KclStdLibCall",
"name": "revolve",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
},
"labeledArgs": {
"angle": {
"value": {
"type": "Number",
"value": 90.0,
"ty": {
"type": "Default",
"len": {
"type": "Mm"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
},
"axis": {
"value": {
"type": "TagIdentifier",
"value": "revolveAxis",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
"sourceRange": [],
"isError": true
}
]

View File

@ -0,0 +1,19 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing error_revolve_on_edge_get_edge.kcl
---
box = startSketchOn(XY)
|> startProfile(at = [0, 0])
|> line(end = [0, 10])
|> line(end = [10, 0])
|> line(end = [0, -10], tag = $revolveAxis)
|> close()
|> extrude(length = 10)
sketch001 = startSketchOn(box, face = revolveAxis)
|> startProfile(at = [5, 10])
|> line(end = [0, -10])
|> line(end = [2, 0])
|> line(end = [0, 10])
|> close()
|> revolve(axis = revolveAxis, angle = 90)

View File

@ -4,9 +4,8 @@ description: Error from executing execute_engine_error_return.kcl
--- ---
KCL Engine error KCL Engine error
× engine: Modeling command failed: [ApiError { error_code: BadRequest, × engine: The path is not closed. Solid2D construction requires a closed
message: "The path is not closed. Solid2D construction requires a closed path!
│ path!" }]
╭─[7:6] ╭─[7:6]
6 │ |> line(end = [-11.53311, 2.81559]) 6 │ |> line(end = [-11.53311, 2.81559])
7 │ |> extrude(length = 4) 7 │ |> extrude(length = 4)

View File

@ -996,10 +996,51 @@ description: Artifact commands import_mesh_clone.kcl
"direction": "positive" "direction": "positive"
} }
}, },
"units": "mm" "units": "m"
} }
} }
}, },
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": -2000.0,
"y": -2000.0,
"z": 0.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_set_material_params_pbr",
"object_id": "[uuid]",
"color": {
"r": 1.0,
"g": 1.0,
"b": 0.0,
"a": 100.0
},
"metalness": 0.0,
"roughness": 0.0,
"ambient_occlusion": 0.0
}
},
{ {
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],
@ -1015,5 +1056,184 @@ description: Artifact commands import_mesh_clone.kcl
"type": "entity_get_all_child_uuids", "type": "entity_get_all_child_uuids",
"entity_id": "[uuid]" "entity_id": "[uuid]"
} }
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_get_all_child_uuids",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 4000.0,
"y": 0.0,
"z": 0.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_set_material_params_pbr",
"object_id": "[uuid]",
"color": {
"r": 1.0,
"g": 0.0,
"b": 0.0,
"a": 100.0
},
"metalness": 0.0,
"roughness": 0.0,
"ambient_occlusion": 0.0
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_clone",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_get_all_child_uuids",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_get_all_child_uuids",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 0.0,
"y": 4000.0,
"z": 0.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_set_material_params_pbr",
"object_id": "[uuid]",
"color": {
"r": 0.0,
"g": 1.0,
"b": 0.0,
"a": 100.0
},
"metalness": 0.0,
"roughness": 0.0,
"ambient_occlusion": 0.0
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_clone",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_get_all_child_uuids",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "entity_get_all_child_uuids",
"entity_id": "[uuid]"
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "set_object_transform",
"object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 0.0,
"y": 4000.0,
"z": 0.0
},
"set": false,
"is_local": true
},
"rotate_rpy": null,
"rotate_angle_axis": null,
"scale": null
}
]
}
},
{
"cmdId": "[uuid]",
"range": [],
"command": {
"type": "object_set_material_params_pbr",
"object_id": "[uuid]",
"color": {
"r": 0.0,
"g": 0.0,
"b": 1.0,
"a": 100.0
},
"metalness": 0.0,
"roughness": 0.0,
"ambient_occlusion": 0.0
}
} }
] ]

View File

@ -8,6 +8,46 @@ description: Result of parsing import_mesh_clone.kcl
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"outerAttrs": [
{
"commentStart": 0,
"end": 0,
"name": null,
"properties": [
{
"commentStart": 0,
"end": 0,
"key": {
"commentStart": 0,
"end": 0,
"name": "lengthUnit",
"start": 0,
"type": "Identifier"
},
"start": 0,
"type": "ObjectProperty",
"value": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "m",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
}
],
"start": 0,
"type": "Annotation"
}
],
"path": { "path": {
"type": "Foreign", "type": "Foreign",
"path": "../inputs/cube.obj" "path": "../inputs/cube.obj"
@ -17,7 +57,7 @@ description: Result of parsing import_mesh_clone.kcl
"alias": { "alias": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "cube", "name": "yellow",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
} }
@ -28,24 +68,180 @@ description: Result of parsing import_mesh_clone.kcl
}, },
{ {
"commentStart": 0, "commentStart": 0,
"declaration": { "end": 0,
"expression": {
"body": [
{
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "yellow",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "x",
"start": 0,
"type": "Identifier"
},
"arg": {
"argument": {
"commentStart": 0,
"end": 0,
"raw": "2000",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2000.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"operator": "-",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "y",
"start": 0,
"type": "Identifier"
},
"arg": {
"argument": {
"commentStart": 0,
"end": 0,
"raw": "2000",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 2000.0,
"suffix": "None"
}
},
"commentStart": 0,
"end": 0,
"operator": "-",
"start": 0,
"type": "UnaryExpression",
"type": "UnaryExpression"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "translate",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
}
],
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"id": { "start": 0,
"commentStart": 0, "type": "PipeExpression",
"end": 0, "type": "PipeExpression"
"name": "model", },
"start": 0, "start": 0,
"type": "Identifier" "type": "ExpressionStatement",
}, "type": "ExpressionStatement"
"init": { },
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "color",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"#ffff00\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "#ffff00"
}
}
],
"callee": {
"abs_path": false, "abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "cube", "name": "appearance",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "yellow",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -53,15 +249,11 @@ description: Result of parsing import_mesh_clone.kcl
"start": 0, "start": 0,
"type": "Name", "type": "Name",
"type": "Name" "type": "Name"
}, }
"start": 0,
"type": "VariableDeclarator"
}, },
"end": 0,
"kind": "const",
"start": 0, "start": 0,
"type": "VariableDeclaration", "type": "ExpressionStatement",
"type": "VariableDeclaration" "type": "ExpressionStatement"
}, },
{ {
"commentStart": 0, "commentStart": 0,
@ -71,7 +263,7 @@ description: Result of parsing import_mesh_clone.kcl
"id": { "id": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "model2", "name": "red",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -105,7 +297,7 @@ description: Result of parsing import_mesh_clone.kcl
"name": { "name": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": "model", "name": "yellow",
"start": 0, "start": 0,
"type": "Identifier" "type": "Identifier"
}, },
@ -129,12 +321,12 @@ description: Result of parsing import_mesh_clone.kcl
"arg": { "arg": {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"raw": "1020", "raw": "4000",
"start": 0, "start": 0,
"type": "Literal", "type": "Literal",
"type": "Literal", "type": "Literal",
"value": { "value": {
"value": 1020.0, "value": 4000.0,
"suffix": "None" "suffix": "None"
} }
} }
@ -160,95 +352,13 @@ description: Result of parsing import_mesh_clone.kcl
"start": 0, "start": 0,
"type": "CallExpressionKw", "type": "CallExpressionKw",
"type": "CallExpressionKw", "type": "CallExpressionKw",
"unlabeled": null "unlabeled": {
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "color",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"#ff0000\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "#ff0000"
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "metalness",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "50",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 50.0,
"suffix": "None"
}
}
},
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "roughness",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "50",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 50.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "appearance",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0, "start": 0,
"type": "Name" "type": "PipeSubstitution",
}, "type": "PipeSubstitution"
"commentStart": 0, }
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": null
} }
], ],
"commentStart": 0, "commentStart": 0,
@ -265,6 +375,446 @@ description: Result of parsing import_mesh_clone.kcl
"start": 0, "start": 0,
"type": "VariableDeclaration", "type": "VariableDeclaration",
"type": "VariableDeclaration" "type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "color",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"#ff0000\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "#ff0000"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "appearance",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "red",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "green",
"start": 0,
"type": "Identifier"
},
"init": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "clone",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "yellow",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "y",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "4000",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 4000.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "translate",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
}
],
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "color",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"#00ff00\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "#00ff00"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "appearance",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "green",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
},
{
"commentStart": 0,
"declaration": {
"commentStart": 0,
"end": 0,
"id": {
"commentStart": 0,
"end": 0,
"name": "blue",
"start": 0,
"type": "Identifier"
},
"init": {
"body": [
{
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "clone",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "red",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
{
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "y",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "4000",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": {
"value": 4000.0,
"suffix": "None"
}
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "translate",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
}
],
"commentStart": 0,
"end": 0,
"start": 0,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 0,
"type": "VariableDeclarator"
},
"end": 0,
"kind": "const",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"commentStart": 0,
"end": 0,
"expression": {
"arguments": [
{
"type": "LabeledArg",
"label": {
"commentStart": 0,
"end": 0,
"name": "color",
"start": 0,
"type": "Identifier"
},
"arg": {
"commentStart": 0,
"end": 0,
"raw": "\"#0000ff\"",
"start": 0,
"type": "Literal",
"type": "Literal",
"value": "#0000ff"
}
}
],
"callee": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "appearance",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name"
},
"commentStart": 0,
"end": 0,
"start": 0,
"type": "CallExpressionKw",
"type": "CallExpressionKw",
"unlabeled": {
"abs_path": false,
"commentStart": 0,
"end": 0,
"name": {
"commentStart": 0,
"end": 0,
"name": "blue",
"start": 0,
"type": "Identifier"
},
"path": [],
"start": 0,
"type": "Name",
"type": "Name"
}
},
"start": 0,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
} }
], ],
"commentStart": 0, "commentStart": 0,
@ -282,7 +832,29 @@ description: Result of parsing import_mesh_clone.kcl
} }
} }
], ],
"1": [ "2": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"4": [
{
"commentStart": 0,
"end": 0,
"start": 0,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
],
"6": [
{ {
"commentStart": 0, "commentStart": 0,
"end": 0, "end": 0,

View File

@ -1,19 +0,0 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Error from executing import_mesh_clone.kcl
---
KCL Internal error
× internal: failed to fix tags and references: engine: KclErrorDetails
│ { source_ranges: [SourceRange([60, 72, 0])], message: "Modeling command
│ failed: [ApiError { error_code: BadRequest, message: \"Entity type does
│ not currently support transform patterns.\" }, ApiError { error_code:
│ InternalEngine, message: \"Failed to clone entity.\" }, ApiError
│ { error_code: InternalEngine, message: \"Failed to clone entity\" }]" }
╭─[5:10]
4 │
5 │ model2 = clone(model)
· ──────┬─────
· ╰── tests/import_mesh_clone/input.kcl
6 │ |> translate(
╰────

View File

@ -1,13 +1,18 @@
import "../inputs/cube.obj" as cube @(lengthUnit = m)
import "../inputs/cube.obj" as yellow
model = cube yellow
|> translate(%, x = -2000, y = -2000)
appearance(yellow, color = "#ffff00")
model2 = clone(model) red = clone(yellow)
|> translate( |> translate(%, x = 4000)
x = 1020, appearance(red, color = "#ff0000")
)
|> appearance( green = clone(yellow)
color = "#ff0000", |> translate(%, y = 4000)
metalness = 50, appearance(green, color = "#00ff00")
roughness = 50
) blue = clone(red)
|> translate(%, y = 4000)
appearance(blue, color = "#0000ff")

View File

@ -7,28 +7,51 @@ description: Operations executed import_mesh_clone.kcl
"type": "GroupBegin", "type": "GroupBegin",
"group": { "group": {
"type": "ModuleInstance", "type": "ModuleInstance",
"name": "cube", "name": "cube.obj",
"moduleId": 6 "moduleId": 0
}, },
"sourceRange": [] "sourceRange": []
}, },
{ {
"type": "GroupEnd" "type": "KclStdLibCall",
"name": "clone",
"unlabeledArg": {
"value": {
"type": "ImportedGeometry",
"artifact_id": "[uuid]"
},
"sourceRange": []
},
"labeledArgs": {},
"sourceRange": []
}, },
{ {
"isError": true, "type": "KclStdLibCall",
"labeledArgs": {
"geometry": {
"value": {
"type": "ImportedGeometry",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
"name": "clone", "name": "clone",
"sourceRange": [], "unlabeledArg": {
"type": "StdLibCall", "value": {
"unlabeledArg": null "type": "ImportedGeometry",
"artifact_id": "[uuid]"
},
"sourceRange": []
},
"labeledArgs": {},
"sourceRange": []
},
{
"type": "KclStdLibCall",
"name": "clone",
"unlabeledArg": {
"value": {
"type": "ImportedGeometry",
"artifact_id": "[uuid]"
},
"sourceRange": []
},
"labeledArgs": {},
"sourceRange": []
},
{
"type": "GroupEnd"
} }
] ]

View File

@ -0,0 +1,31 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Variables in memory after executing import_mesh_clone.kcl
---
{
"blue": {
"type": "ImportedGeometry",
"id": "[uuid]",
"value": [
"cube.obj"
]
},
"green": {
"type": "ImportedGeometry",
"id": "[uuid]",
"value": [
"cube.obj"
]
},
"red": {
"type": "ImportedGeometry",
"id": "[uuid]",
"value": [
"cube.obj"
]
},
"yellow": {
"type": "Module",
"value": 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -2,10 +2,21 @@
source: kcl-lib/src/simulation_tests.rs source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing import_mesh_clone.kcl description: Result of unparsing import_mesh_clone.kcl
--- ---
import "../inputs/cube.obj" as cube @(lengthUnit = m)
import "../inputs/cube.obj" as yellow
model = cube yellow
|> translate(%, x = -2000, y = -2000)
appearance(yellow, color = "#ffff00")
model2 = clone(model) red = clone(yellow)
|> translate(x = 1020) |> translate(%, x = 4000)
|> appearance(color = "#ff0000", metalness = 50, roughness = 50) appearance(red, color = "#ff0000")
green = clone(yellow)
|> translate(%, y = 4000)
appearance(green, color = "#00ff00")
blue = clone(red)
|> translate(%, y = 4000)
appearance(blue, color = "#0000ff")

View File

@ -1,11 +1,11 @@
--- ---
source: kcl/src/simulation_tests.rs source: kcl-lib/src/simulation_tests.rs
description: Error from executing invalid_member_object.kcl description: Error from executing invalid_member_object.kcl
--- ---
KCL Semantic error KCL Semantic error
× semantic: Only arrays and objects can be indexed, but you're trying to × semantic: Only arrays can be indexed, but you're trying to index a
index a number number(default units)
╭─[2:5] ╭─[2:5]
1 │ num = 999 1 │ num = 999
2 │ x = num[3] 2 │ x = num[3]

View File

@ -5121,7 +5121,8 @@ description: Artifact commands bench.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -5132,7 +5133,8 @@ description: Artifact commands bench.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
} }
] ]

View File

@ -905,7 +905,8 @@ description: Artifact commands cold-plate.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -5575,7 +5575,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -6109,7 +6110,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -9466,7 +9468,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -9597,7 +9600,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -10115,7 +10119,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -10246,7 +10251,8 @@ description: Artifact commands cpu-cooler.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -456,13 +456,7 @@ description: Result of parsing enclosure.kcl
}, },
{ {
"type": "LabeledArg", "type": "LabeledArg",
"label": { "label": null,
"commentStart": 0,
"end": 0,
"name": "length",
"start": 0,
"type": "Identifier"
},
"arg": { "arg": {
"abs_path": false, "abs_path": false,
"commentStart": 0, "commentStart": 0,
@ -3178,13 +3172,7 @@ description: Result of parsing enclosure.kcl
}, },
{ {
"type": "LabeledArg", "type": "LabeledArg",
"label": { "label": null,
"commentStart": 0,
"end": 0,
"name": "length",
"start": 0,
"type": "Identifier"
},
"arg": { "arg": {
"abs_path": false, "abs_path": false,
"commentStart": 0, "commentStart": 0,

View File

@ -28,9 +28,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 485, "commentStart": 476,
"end": 506, "end": 497,
"start": 485, "start": 476,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB001" "value": "rectangleSegmentB001"
}, },
@ -41,9 +41,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 608, "commentStart": 599,
"end": 629, "end": 620,
"start": 608, "start": 599,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC001" "value": "rectangleSegmentC001"
}, },
@ -54,9 +54,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 699, "commentStart": 690,
"end": 720, "end": 711,
"start": 699, "start": 690,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD001" "value": "rectangleSegmentD001"
}, },
@ -102,9 +102,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 485, "commentStart": 476,
"end": 506, "end": 497,
"start": 485, "start": 476,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB001" "value": "rectangleSegmentB001"
}, },
@ -127,9 +127,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 608, "commentStart": 599,
"end": 629, "end": 620,
"start": 608, "start": 599,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC001" "value": "rectangleSegmentC001"
}, },
@ -152,9 +152,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 699, "commentStart": 690,
"end": 720, "end": 711,
"start": 699, "start": 690,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD001" "value": "rectangleSegmentD001"
}, },
@ -354,9 +354,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -367,9 +367,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -380,9 +380,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -393,9 +393,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -416,9 +416,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -441,9 +441,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -466,9 +466,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -491,9 +491,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -693,9 +693,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 4147, "commentStart": 4129,
"end": 4168, "end": 4150,
"start": 4147, "start": 4129,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA003" "value": "rectangleSegmentA003"
}, },
@ -706,9 +706,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 4274, "commentStart": 4256,
"end": 4295, "end": 4277,
"start": 4274, "start": 4256,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB003" "value": "rectangleSegmentB003"
}, },
@ -719,9 +719,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 4397, "commentStart": 4379,
"end": 4418, "end": 4400,
"start": 4397, "start": 4379,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC003" "value": "rectangleSegmentC003"
}, },
@ -732,9 +732,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 4488, "commentStart": 4470,
"end": 4509, "end": 4491,
"start": 4488, "start": 4470,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD003" "value": "rectangleSegmentD003"
}, },
@ -755,9 +755,9 @@ description: Variables in memory after executing enclosure.kcl
3.0 3.0
], ],
"tag": { "tag": {
"commentStart": 4147, "commentStart": 4129,
"end": 4168, "end": 4150,
"start": 4147, "start": 4129,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA003" "value": "rectangleSegmentA003"
}, },
@ -780,9 +780,9 @@ description: Variables in memory after executing enclosure.kcl
3.0 3.0
], ],
"tag": { "tag": {
"commentStart": 4274, "commentStart": 4256,
"end": 4295, "end": 4277,
"start": 4274, "start": 4256,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB003" "value": "rectangleSegmentB003"
}, },
@ -805,9 +805,9 @@ description: Variables in memory after executing enclosure.kcl
172.0 172.0
], ],
"tag": { "tag": {
"commentStart": 4397, "commentStart": 4379,
"end": 4418, "end": 4400,
"start": 4397, "start": 4379,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC003" "value": "rectangleSegmentC003"
}, },
@ -830,9 +830,9 @@ description: Variables in memory after executing enclosure.kcl
172.0 172.0
], ],
"tag": { "tag": {
"commentStart": 4488, "commentStart": 4470,
"end": 4509, "end": 4491,
"start": 4488, "start": 4470,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD003" "value": "rectangleSegmentD003"
}, },
@ -896,9 +896,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -909,9 +909,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -922,9 +922,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -935,9 +935,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -958,9 +958,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -983,9 +983,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -1008,9 +1008,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -1033,9 +1033,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -1495,9 +1495,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 485, "commentStart": 476,
"end": 506, "end": 497,
"start": 485, "start": 476,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB001" "value": "rectangleSegmentB001"
}, },
@ -1520,9 +1520,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 608, "commentStart": 599,
"end": 629, "end": 620,
"start": 608, "start": 599,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC001" "value": "rectangleSegmentC001"
}, },
@ -1545,9 +1545,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 699, "commentStart": 690,
"end": 720, "end": 711,
"start": 699, "start": 690,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD001" "value": "rectangleSegmentD001"
}, },
@ -1669,9 +1669,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -1694,9 +1694,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -1719,9 +1719,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -1744,9 +1744,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -1868,9 +1868,9 @@ description: Variables in memory after executing enclosure.kcl
3.0 3.0
], ],
"tag": { "tag": {
"commentStart": 4147, "commentStart": 4129,
"end": 4168, "end": 4150,
"start": 4147, "start": 4129,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA003" "value": "rectangleSegmentA003"
}, },
@ -1893,9 +1893,9 @@ description: Variables in memory after executing enclosure.kcl
3.0 3.0
], ],
"tag": { "tag": {
"commentStart": 4274, "commentStart": 4256,
"end": 4295, "end": 4277,
"start": 4274, "start": 4256,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB003" "value": "rectangleSegmentB003"
}, },
@ -1918,9 +1918,9 @@ description: Variables in memory after executing enclosure.kcl
172.0 172.0
], ],
"tag": { "tag": {
"commentStart": 4397, "commentStart": 4379,
"end": 4418, "end": 4400,
"start": 4397, "start": 4379,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC003" "value": "rectangleSegmentC003"
}, },
@ -1943,9 +1943,9 @@ description: Variables in memory after executing enclosure.kcl
172.0 172.0
], ],
"tag": { "tag": {
"commentStart": 4488, "commentStart": 4470,
"end": 4509, "end": 4491,
"start": 4488, "start": 4470,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD003" "value": "rectangleSegmentD003"
}, },
@ -2009,9 +2009,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -2022,9 +2022,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -2035,9 +2035,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -2048,9 +2048,9 @@ description: Variables in memory after executing enclosure.kcl
"id": "[uuid]", "id": "[uuid]",
"sourceRange": [], "sourceRange": [],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },
@ -2071,9 +2071,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2445, "commentStart": 2436,
"end": 2466, "end": 2457,
"start": 2445, "start": 2436,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentA002" "value": "rectangleSegmentA002"
}, },
@ -2096,9 +2096,9 @@ description: Variables in memory after executing enclosure.kcl
0.0 0.0
], ],
"tag": { "tag": {
"commentStart": 2550, "commentStart": 2532,
"end": 2571, "end": 2553,
"start": 2550, "start": 2532,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentB002" "value": "rectangleSegmentB002"
}, },
@ -2121,9 +2121,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2673, "commentStart": 2655,
"end": 2694, "end": 2676,
"start": 2673, "start": 2655,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentC002" "value": "rectangleSegmentC002"
}, },
@ -2146,9 +2146,9 @@ description: Variables in memory after executing enclosure.kcl
175.0 175.0
], ],
"tag": { "tag": {
"commentStart": 2764, "commentStart": 2746,
"end": 2785, "end": 2767,
"start": 2764, "start": 2746,
"type": "TagDeclarator", "type": "TagDeclarator",
"value": "rectangleSegmentD002" "value": "rectangleSegmentD002"
}, },

View File

@ -1597,7 +1597,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1608,7 +1609,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1619,7 +1621,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {
@ -1630,7 +1633,8 @@ description: Artifact commands exhaust-manifold.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -4490,7 +4490,8 @@ description: Artifact commands utility-sink.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

File diff suppressed because it is too large Load Diff

View File

@ -4,19 +4,17 @@ description: Operations executed mike_stress_test.kcl
--- ---
[ [
{ {
"labeledArgs": { "labeledArgs": {},
"planeOrSolid": {
"value": {
"type": "String",
"value": "XY"
},
"sourceRange": []
}
},
"name": "startSketchOn", "name": "startSketchOn",
"sourceRange": [], "sourceRange": [],
"type": "StdLibCall", "type": "StdLibCall",
"unlabeledArg": null "unlabeledArg": {
"value": {
"type": "Plane",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
}, },
{ {
"labeledArgs": { "labeledArgs": {

View File

@ -26043,10 +26043,8 @@ description: Variables in memory after executing mike_stress_test.kcl
} }
], ],
"on": { "on": {
"type": "plane",
"id": "[uuid]",
"artifactId": "[uuid]", "artifactId": "[uuid]",
"value": "XY", "id": "[uuid]",
"origin": { "origin": {
"x": 0.0, "x": 0.0,
"y": 0.0, "y": 0.0,
@ -26055,12 +26053,14 @@ description: Variables in memory after executing mike_stress_test.kcl
"type": "Mm" "type": "Mm"
} }
}, },
"type": "plane",
"value": "XY",
"xAxis": { "xAxis": {
"x": 1.0, "x": 1.0,
"y": 0.0, "y": 0.0,
"z": 0.0, "z": 0.0,
"units": { "units": {
"type": "Mm" "type": "Unknown"
} }
}, },
"yAxis": { "yAxis": {
@ -26068,7 +26068,7 @@ description: Variables in memory after executing mike_stress_test.kcl
"y": 1.0, "y": 1.0,
"z": 0.0, "z": 0.0,
"units": { "units": {
"type": "Mm" "type": "Unknown"
} }
} }
}, },

View File

@ -5,7 +5,7 @@ description: Error from executing panic_repro_cube.kcl
KCL Semantic error KCL Semantic error
× semantic: This function expected the input argument to be tag identifier × semantic: This function expected the input argument to be tag identifier
│ but it's actually of type Unique ID (uuid) │ but it's actually of type tag
╭─[43:25] ╭─[43:25]
42 │ // these double wrapped functions are the point of this test 42 │ // these double wrapped functions are the point of this test
43 │ getNextAdjacentEdge(getNextAdjacentEdge(seg01)), 43 │ getNextAdjacentEdge(getNextAdjacentEdge(seg01)),

View File

@ -4,8 +4,7 @@ description: Error from executing pattern_into_union.kcl
--- ---
KCL Engine error KCL Engine error
× engine: Modeling command failed: [ApiError { error_code: InternalEngine, × engine: More than 2 solids were passed to the low-level CSG method
│ message: "More than 2 solids were passed to the low-level CSG method" }]
╭─[67:1] ╭─[67:1]
66 │ 66 │
67 │ union([base,endTabs]) 67 │ union([base,endTabs])

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
@settings(defaultLengthUnit = in)
// Define parameters
trussSupportAngle = 15
height = 120
thickness = 4
sketch001 = startSketchOn(YZ)
profile001 = startProfile(sketch001, at = [60, 0])
|> xLine(length = -120, tag = $bottomFace)
|> yLine(length = 12)
|> angledLine(angle = 25, endAbsoluteX = 0, tag = $tag001)
|> angledLine(angle = -25, endAbsoluteX = 60)
|> close()
profile002 = startProfile(sketch001, at = [60-thickness, thickness])
|> xLine(endAbsolute = thickness/2)
|> yLine(endAbsolute = segEndY(tag001)-thickness) // update
|> angledLine(endAbsoluteX = profileStartX(%), angle = -25)
|> close(%)
profile003 = startProfile(sketch001, at = [-60+thickness, thickness])
|> xLine(endAbsolute = -thickness/2)
|> yLine(endAbsolute = segEndY(tag001)-thickness) // update
|> angledLine(endAbsoluteX = profileStartX(%), angle = 205)
|> close(%)
profile004 = subtract2d(profile001, tool = profile002)
subtract2d(profile001, tool = profile003)
body001 = extrude(profile001, length = 2)
sketch002 = startSketchOn(offsetPlane(YZ, offset = .1))
profile006 = startProfile(sketch002, at = [thickness/2-1, 14])
|> angledLine(angle = 30, length = 25)
|> angledLine(angle = -25, length = 5)
|> angledLine(angle = 210, endAbsoluteX = profileStartX(%))
|> close(%)
|> extrude(%, length = 1.8)
profile007 = startProfile(sketch002, at = [-thickness/2+1, 14])
|> angledLine(angle = 150, length = 25)
|> angledLine(angle = 205, length = 5)
|> angledLine(angle = -30, endAbsoluteX = profileStartX(%))
|> close(%)
|> extrude(%, length = 1.8)
newSketch = body001 + profile006 + profile007
leg001Sketch = startSketchOn(newSketch, face = bottomFace)
legProfile001 = startProfile(leg001Sketch, at = [-60, 0])
|> xLine(%, length = 4)
|> yLine(%, length = 2)
|> xLine(%, endAbsolute = profileStartX(%))
|> close(%)
leg001 = extrude(legProfile001, length = 48)
|> rotate(axis = [0, 0, 1.0], angle = -90)

View File

@ -0,0 +1,264 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Operations executed sketch_on_face_union.kcl
---
[
{
"labeledArgs": {},
"name": "startSketchOn",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Plane",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
{
"labeledArgs": {
"tool": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
"name": "subtract2d",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {
"tool": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
"name": "subtract2d",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {
"length": {
"value": {
"type": "Number",
"value": 2.0,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
},
"name": "extrude",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {},
"name": "startSketchOn",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Plane",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
{
"type": "KclStdLibCall",
"name": "offsetPlane",
"unlabeledArg": {
"value": {
"type": "Plane",
"artifact_id": "[uuid]"
},
"sourceRange": []
},
"labeledArgs": {
"offset": {
"value": {
"type": "Number",
"value": 0.1,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
},
"sourceRange": []
},
{
"labeledArgs": {
"length": {
"value": {
"type": "Number",
"value": 1.8,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
},
"name": "extrude",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {
"length": {
"value": {
"type": "Number",
"value": 1.8,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
},
"name": "extrude",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {
"face": {
"value": {
"type": "TagIdentifier",
"value": "bottomFace",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
"name": "startSketchOn",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Solid",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
},
{
"labeledArgs": {
"length": {
"value": {
"type": "Number",
"value": 48.0,
"ty": {
"type": "Default",
"len": {
"type": "Inches"
},
"angle": {
"type": "Degrees"
}
}
},
"sourceRange": []
}
},
"name": "extrude",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": {
"value": {
"type": "Sketch",
"value": {
"artifactId": "[uuid]"
}
},
"sourceRange": []
}
}
]

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

View File

@ -0,0 +1,62 @@
---
source: kcl-lib/src/simulation_tests.rs
description: Result of unparsing sketch_on_face_union.kcl
---
@settings(defaultLengthUnit = in)
// Define parameters
trussSupportAngle = 15
height = 120
thickness = 4
sketch001 = startSketchOn(YZ)
profile001 = startProfile(sketch001, at = [60, 0])
|> xLine(length = -120, tag = $bottomFace)
|> yLine(length = 12)
|> angledLine(angle = 25, endAbsoluteX = 0, tag = $tag001)
|> angledLine(angle = -25, endAbsoluteX = 60)
|> close()
profile002 = startProfile(sketch001, at = [60 - thickness, thickness])
|> xLine(endAbsolute = thickness / 2)
|> yLine(endAbsolute = segEndY(tag001) - thickness) // update
|> angledLine(endAbsoluteX = profileStartX(%), angle = -25)
|> close(%)
profile003 = startProfile(sketch001, at = [-60 + thickness, thickness])
|> xLine(endAbsolute = -thickness / 2)
|> yLine(endAbsolute = segEndY(tag001) - thickness) // update
|> angledLine(endAbsoluteX = profileStartX(%), angle = 205)
|> close(%)
profile004 = subtract2d(profile001, tool = profile002)
subtract2d(profile001, tool = profile003)
body001 = extrude(profile001, length = 2)
sketch002 = startSketchOn(offsetPlane(YZ, offset = .1))
profile006 = startProfile(sketch002, at = [thickness / 2 - 1, 14])
|> angledLine(angle = 30, length = 25)
|> angledLine(angle = -25, length = 5)
|> angledLine(angle = 210, endAbsoluteX = profileStartX(%))
|> close(%)
|> extrude(%, length = 1.8)
profile007 = startProfile(sketch002, at = [-thickness / 2 + 1, 14])
|> angledLine(angle = 150, length = 25)
|> angledLine(angle = 205, length = 5)
|> angledLine(angle = -30, endAbsoluteX = profileStartX(%))
|> close(%)
|> extrude(%, length = 1.8)
newSketch = body001 + profile006 + profile007
leg001Sketch = startSketchOn(newSketch, face = bottomFace)
legProfile001 = startProfile(leg001Sketch, at = [-60, 0])
|> xLine(%, length = 4)
|> yLine(%, length = 2)
|> xLine(%, endAbsolute = profileStartX(%))
|> close(%)
leg001 = extrude(legProfile001, length = 48)
|> rotate(axis = [0, 0, 1.0], angle = -90)

View File

@ -417,7 +417,8 @@ description: Artifact commands subtract_regression03.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -394,7 +394,8 @@ description: Artifact commands subtract_regression05.kcl
"target": "[uuid]", "target": "[uuid]",
"trajectory": "[uuid]", "trajectory": "[uuid]",
"sectional": false, "sectional": false,
"tolerance": 0.0000001 "tolerance": 0.0000001,
"relative_to": "trajectory_curve"
} }
}, },
{ {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "kcl-python-bindings" name = "kcl-python-bindings"
version = "0.3.69" version = "0.3.72"
edition = "2021" edition = "2021"
repository = "https://github.com/kittycad/modeling-app" repository = "https://github.com/kittycad/modeling-app"
exclude = ["tests/*", "files/*", "venv/*"] exclude = ["tests/*", "files/*", "venv/*"]

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