Compare commits

...

19 Commits

Author SHA1 Message Date
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
81 changed files with 11730 additions and 47512 deletions

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 }}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -158,10 +158,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 ({

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

@ -51,8 +51,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,
@ -545,7 +548,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

@ -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
@ -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,

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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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.71"
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)

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

@ -1311,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 {
@ -1323,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,
@ -1347,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
@ -1368,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) => {
@ -1865,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) => {
@ -1959,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,
) )
@ -1974,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],
}) })
})?; })?;
} }
@ -2139,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,
) )
@ -2152,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],
}) })
@ -2224,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,
@ -2665,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}"
@ -2691,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)
@ -2703,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

@ -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,

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
} }
@ -2772,6 +2771,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 +3071,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>,
@ -342,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())
} }

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

@ -83,7 +83,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 = "sketchPlane")
/// ``` /// ```
/// ///
/// ``` /// ```
@ -104,7 +104,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 = "sketchPlane")
/// ``` /// ```
/// ///
/// ``` /// ```
@ -124,7 +124,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 = "sketchPlane")
/// ``` /// ```
/// ///
/// ``` /// ```
@ -413,7 +413,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 = "sketchPlane")
/// ``` /// ```
/// ///
/// ```kcl /// ```kcl

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

@ -1012,8 +1012,41 @@ description: Artifact commands import_mesh_clone.kcl
"cmdId": "[uuid]", "cmdId": "[uuid]",
"range": [], "range": [],
"command": { "command": {
"type": "entity_get_all_child_uuids", "type": "set_object_transform",
"entity_id": "[uuid]" "object_id": "[uuid]",
"transforms": [
{
"translate": {
"property": {
"x": 1020.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.5,
"roughness": 0.5,
"ambient_occlusion": 0.0
} }
} }
] ]

View File

@ -2,18 +2,9 @@
source: kcl-lib/src/simulation_tests.rs source: kcl-lib/src/simulation_tests.rs
description: Error from executing import_mesh_clone.kcl description: Error from executing import_mesh_clone.kcl
--- ---
KCL Internal error KCL Engine error
× internal: failed to fix tags and references: engine: KclErrorDetails × engine: Modeling command failed: websocket closed early
│ { source_ranges: [SourceRange([60, 72, 0])], message: "Modeling command ╭────
failed: [ApiError { error_code: BadRequest, message: \"Entity type does 13 )
│ 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

@ -7,28 +7,25 @@ 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": "GroupEnd"
"labeledArgs": {
"geometry": {
"value": {
"type": "ImportedGeometry",
"artifact_id": "[uuid]"
},
"sourceRange": []
}
},
"name": "clone",
"sourceRange": [],
"type": "StdLibCall",
"unlabeledArg": null
} }
] ]

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

@ -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.71"
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/*"]

View File

@ -227,6 +227,31 @@ async fn new_context_state(current_file: Option<std::path::PathBuf>) -> Result<(
Ok((ctx, state)) Ok((ctx, state))
} }
/// Parse the kcl code from a file path.
#[pyfunction]
async fn parse(path: String) -> PyResult<()> {
tokio()
.spawn(async move {
let (code, path) = get_code_and_file_path(&path)
.await
.map_err(|err| pyo3::exceptions::PyException::new_err(err.to_string()))?;
let _program = kcl_lib::Program::parse_no_errs(&code)
.map_err(|err| into_miette_for_parse(&path.display().to_string(), &code, err))?;
Ok(())
})
.await
.map_err(|err| pyo3::exceptions::PyException::new_err(err.to_string()))?
}
/// Parse the kcl code.
#[pyfunction]
fn parse_code(code: String) -> PyResult<()> {
let _program = kcl_lib::Program::parse_no_errs(&code).map_err(|err| into_miette_for_parse("", &code, err))?;
Ok(())
}
/// Execute the kcl code from a file path. /// Execute the kcl code from a file path.
#[pyfunction] #[pyfunction]
async fn execute(path: String) -> PyResult<()> { async fn execute(path: String) -> PyResult<()> {
@ -534,6 +559,8 @@ fn kcl(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Discovered>()?; m.add_class::<Discovered>()?;
// Add our functions to the module. // Add our functions to the module.
m.add_function(wrap_pyfunction!(parse, m)?)?;
m.add_function(wrap_pyfunction!(parse_code, m)?)?;
m.add_function(wrap_pyfunction!(execute, m)?)?; m.add_function(wrap_pyfunction!(execute, m)?)?;
m.add_function(wrap_pyfunction!(execute_code, m)?)?; m.add_function(wrap_pyfunction!(execute_code, m)?)?;
m.add_function(wrap_pyfunction!(execute_and_snapshot, m)?)?; m.add_function(wrap_pyfunction!(execute_and_snapshot, m)?)?;

View File

@ -39,6 +39,33 @@ async def test_kcl_execute():
await kcl.execute(lego_file) await kcl.execute(lego_file)
@pytest.mark.asyncio
async def test_kcl_parse_with_exception():
# Read from a file.
try:
await kcl.parse(os.path.join(files_dir, "parse_file_error"))
except Exception as e:
assert e is not None
assert len(str(e)) > 0
assert "lksjndflsskjfnak;jfna##" in str(e)
@pytest.mark.asyncio
async def test_kcl_parse():
# Read from a file.
await kcl.parse(lego_file)
@pytest.mark.asyncio
async def test_kcl_parse_code():
# Read from a file.
with open(lego_file, "r") as f:
code = str(f.read())
assert code is not None
assert len(code) > 0
kcl.parse_code(code)
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_kcl_execute_code(): async def test_kcl_execute_code():
# Read from a file. # Read from a file.
@ -97,9 +124,7 @@ async def test_kcl_execute_and_snapshot():
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_kcl_execute_and_snapshot_dir(): async def test_kcl_execute_and_snapshot_dir():
# Read from a file. # Read from a file.
image_bytes = await kcl.execute_and_snapshot( image_bytes = await kcl.execute_and_snapshot(car_wheel_dir, kcl.ImageFormat.Jpeg)
car_wheel_dir, kcl.ImageFormat.Jpeg
)
assert image_bytes is not None assert image_bytes is not None
assert len(image_bytes) > 0 assert len(image_bytes) > 0
@ -129,10 +154,12 @@ def test_kcl_format():
assert formatted_code is not None assert formatted_code is not None
assert len(formatted_code) > 0 assert len(formatted_code) > 0
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_kcl_format_dir(): async def test_kcl_format_dir():
await kcl.format_dir(car_wheel_dir) await kcl.format_dir(car_wheel_dir)
def test_kcl_lint(): def test_kcl_lint():
# Read from a file. # Read from a file.
with open(os.path.join(files_dir, "box_with_linter_errors.kcl"), "r") as f: with open(os.path.join(files_dir, "box_with_linter_errors.kcl"), "r") as f:

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-test-server" name = "kcl-test-server"
description = "A test server for KCL" description = "A test server for KCL"
version = "0.1.69" version = "0.1.71"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"

View File

@ -1,7 +1,7 @@
[package] [package]
name = "kcl-to-core" name = "kcl-to-core"
description = "Utility methods to convert kcl to engine core executable tests" description = "Utility methods to convert kcl to engine core executable tests"
version = "0.1.69" version = "0.1.71"
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-wasm-lib" name = "kcl-wasm-lib"
version = "0.1.69" version = "0.1.71"
edition = "2021" edition = "2021"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"
rust-version = "1.83" rust-version = "1.83"

View File

@ -29,15 +29,9 @@ import { useSelector } from '@xstate/react'
import type { MouseEventHandler } from 'react' import type { MouseEventHandler } from 'react'
import { useEffect, useRef, useState } from 'react' import { useEffect, useRef, useState } from 'react'
import { useRouteLoaderData } from 'react-router-dom' import { useRouteLoaderData } from 'react-router-dom'
import { isPlaywright } from '@src/lib/isPlaywright'
import {
engineStreamZoomToFit,
engineViewIsometricWithGeometryPresent,
engineViewIsometricWithoutGeometryPresent,
} from '@src/lib/utils'
import { DEFAULT_DEFAULT_LENGTH_UNIT } from '@src/lib/constants'
import { createThumbnailPNGOnDesktop } from '@src/lib/screenshot' import { createThumbnailPNGOnDesktop } from '@src/lib/screenshot'
import type { SettingsViaQueryString } from '@src/lib/settings/settingsTypes' import type { SettingsViaQueryString } from '@src/lib/settings/settingsTypes'
import { resetCameraPosition } from '@src/lib/resetCameraPosition'
export const EngineStream = (props: { export const EngineStream = (props: {
pool: string | null pool: string | null
@ -104,31 +98,7 @@ export const EngineStream = (props: {
kmp kmp
.then(async () => { .then(async () => {
// Gotcha: Playwright E2E tests will be zoom_to_fit, when you try to recreate the e2e test manually await resetCameraPosition()
// your localhost will do view_isometric. Turn this boolean on to have the same experience when manually
// debugging e2e tests
// We need a padding of 0.1 for zoom_to_fit for all E2E tests since they were originally
// written with zoom_to_fit with padding 0.1
const padding = 0.1
if (isPlaywright()) {
await engineStreamZoomToFit({ engineCommandManager, padding })
} else {
// If the scene is empty you cannot use view_isometric, it will not move the camera
if (kclManager.isAstBodyEmpty(kclManager.ast)) {
await engineViewIsometricWithoutGeometryPresent({
engineCommandManager,
unit:
kclManager.fileSettings.defaultLengthUnit ||
DEFAULT_DEFAULT_LENGTH_UNIT,
})
} else {
await engineViewIsometricWithGeometryPresent({
engineCommandManager,
padding,
})
}
}
if (project && project.path) { if (project && project.path) {
createThumbnailPNGOnDesktop({ createThumbnailPNGOnDesktop({

View File

@ -13,6 +13,7 @@ import { VIEW_NAMES_SEMANTIC } from '@src/lib/constants'
import { sceneInfra } from '@src/lib/singletons' import { sceneInfra } from '@src/lib/singletons'
import { reportRejection } from '@src/lib/trap' import { reportRejection } from '@src/lib/trap'
import { useSettings } from '@src/lib/singletons' import { useSettings } from '@src/lib/singletons'
import { resetCameraPosition } from '@src/lib/resetCameraPosition'
export function useViewControlMenuItems() { export function useViewControlMenuItems() {
const { state: modelingState, send: modelingSend } = useModelingContext() const { state: modelingState, send: modelingSend } = useModelingContext()
@ -38,7 +39,7 @@ export function useViewControlMenuItems() {
<ContextMenuDivider />, <ContextMenuDivider />,
<ContextMenuItem <ContextMenuItem
onClick={() => { onClick={() => {
sceneInfra.camControls.resetCameraPosition().catch(reportRejection) resetCameraPosition().catch(reportRejection)
}} }}
disabled={shouldLockView} disabled={shouldLockView}
> >

View File

@ -111,7 +111,7 @@ function discoverColorsInKCL(
} }
export function parseColorLiteral(colorLiteral: string): ColorData | null { export function parseColorLiteral(colorLiteral: string): ColorData | null {
const literal = colorLiteral.replace(/"/g, '') const literal = colorLiteral.replace(/"/g, '').replace(/'/g, '')
const match = hexRegex.exec(literal) const match = hexRegex.exec(literal)
if (!match) { if (!match) {
return null return null

View File

@ -212,6 +212,12 @@ code {
z-index: 99999999999 !important; z-index: 99999999999 !important;
} }
.cm-rename-popup input {
/* use black text on white background in both light and dark mode */
color: black !important;
background: white !important;
}
@keyframes blink { @keyframes blink {
0%, 0%,
100% { 100% {

View File

@ -151,6 +151,10 @@ export class KclManager {
// These belonged to the previous file // These belonged to the previous file
this.lastSuccessfulOperations = [] this.lastSuccessfulOperations = []
this.lastSuccessfulVariables = {} this.lastSuccessfulVariables = {}
// Without this, when leaving a project which has errors and opening another project which doesn't,
// you'd see the errors from the previous project for a short time until the new code is executed.
this._errors = []
} }
get variables() { get variables() {

View File

@ -23,7 +23,7 @@ import {
getThemeColorForEngine, getThemeColorForEngine,
} from '@src/lib/theme' } from '@src/lib/theme'
import { reportRejection } from '@src/lib/trap' import { reportRejection } from '@src/lib/trap'
import { binaryToUuid, uuidv4 } from '@src/lib/utils' import { binaryToUuid, isArray, uuidv4 } from '@src/lib/utils'
const pingIntervalMs = 1_000 const pingIntervalMs = 1_000
@ -2010,12 +2010,20 @@ export class EngineCommandManager extends EventTarget {
return Promise.reject(EXECUTE_AST_INTERRUPT_ERROR_MESSAGE) return Promise.reject(EXECUTE_AST_INTERRUPT_ERROR_MESSAGE)
} }
const resp = await this.sendCommand(id, { try {
command, const resp = await this.sendCommand(id, {
range, command,
idToRangeMap, range,
}) idToRangeMap,
return BSON.serialize(resp[0]) })
return BSON.serialize(resp[0])
} catch (e) {
if (isArray(e) && e.length > 0) {
return Promise.reject(JSON.stringify(e[0]))
}
return Promise.reject(JSON.stringify(e))
}
} }
/** /**
* Common send command function used for both modeling and scene commands * Common send command function used for both modeling and scene commands

View File

@ -31,7 +31,7 @@ const save_ = async (file: ModelingAppFile, toastId: string) => {
) )
toast.success(EXPORT_TOAST_MESSAGES.SUCCESS + ' [TEST]', { toast.success(EXPORT_TOAST_MESSAGES.SUCCESS + ' [TEST]', {
id: toastId, id: toastId,
duration: 5_000, duration: 10_000,
}) })
return return
} }

View File

@ -0,0 +1,40 @@
import { DEFAULT_DEFAULT_LENGTH_UNIT } from '@src/lib/constants'
import { isPlaywright } from '@src/lib/isPlaywright'
import { engineCommandManager, kclManager } from '@src/lib/singletons'
import {
engineStreamZoomToFit,
engineViewIsometricWithoutGeometryPresent,
engineViewIsometricWithGeometryPresent,
} from '@src/lib/utils'
/**
* Reset the camera position to a baseline, which is isometric for
* normal users and a deprecated "front-down" view for playwright tests.
*
* Gotcha: Playwright E2E tests will be zoom_to_fit, when you try to recreate the e2e test manually
* your localhost will do view_isometric. Turn this boolean on to have the same experience when manually
* debugging e2e tests
*/
export async function resetCameraPosition() {
// We need a padding of 0.1 for zoom_to_fit for all E2E tests since they were originally
// written with zoom_to_fit with padding 0.1
const padding = 0.1
if (isPlaywright()) {
await engineStreamZoomToFit({ engineCommandManager, padding })
} else {
// If the scene is empty you cannot use view_isometric, it will not move the camera
if (kclManager.isAstBodyEmpty(kclManager.ast)) {
await engineViewIsometricWithoutGeometryPresent({
engineCommandManager,
unit:
kclManager.fileSettings.defaultLengthUnit ||
DEFAULT_DEFAULT_LENGTH_UNIT,
})
} else {
await engineViewIsometricWithGeometryPresent({
engineCommandManager,
padding,
})
}
}
}

View File

@ -139,18 +139,20 @@ export const settingsMachine = setup({
return () => darkModeMatcher?.removeEventListener('change', listener) return () => darkModeMatcher?.removeEventListener('change', listener)
}), }),
registerCommands: fromCallback< registerCommands: fromCallback<
{ type: 'update' }, { type: 'update'; settings: SettingsType },
{ settings: SettingsType; actor: AnyActorRef } { settings: SettingsType; actor: AnyActorRef }
>(({ input, receive, system }) => { >(({ input, receive, system }) => {
// This assumes this actor is running in a system with a command palette // This assumes this actor is running in a system with a command palette
const commandBarActor = system.get(ACTOR_IDS.COMMAND_BAR) const commandBarActor = system.get(ACTOR_IDS.COMMAND_BAR)
// If the user wants to hide the settings commands // If the user wants to hide the settings commands
//from the command bar don't add them. //from the command bar don't add them.
if (settings.commandBar.includeSettings.current === false) return if (settings.commandBar.includeSettings.current === false) {
return
}
let commands: Command[] = [] let commands: Command[] = []
const updateCommands = () => const updateCommands = (newSettings: SettingsType) =>
settingsWithCommandConfigs(input.settings) settingsWithCommandConfigs(newSettings)
.map((type) => .map((type) =>
createSettingsCommand({ createSettingsCommand({
type, type,
@ -175,14 +177,19 @@ export const settingsMachine = setup({
data: { commands: commands }, data: { commands: commands },
}) })
receive((event) => { receive(({ type, settings: newSettings }) => {
if (event.type !== 'update') return if (type !== 'update') {
return
}
removeCommands() removeCommands()
commands = updateCommands() commands =
newSettings.commandBar.includeSettings.current === false
? []
: updateCommands(newSettings)
addCommands() addCommands()
}) })
commands = updateCommands() commands = updateCommands(settings)
addCommands() addCommands()
return () => { return () => {
@ -205,7 +212,9 @@ export const settingsMachine = setup({
const sceneInfra = rootContext.sceneInfra const sceneInfra = rootContext.sceneInfra
const sceneEntitiesManager = rootContext.sceneEntitiesManager const sceneEntitiesManager = rootContext.sceneEntitiesManager
if (!sceneInfra || !sceneEntitiesManager) return if (!sceneInfra || !sceneEntitiesManager) {
return
}
const opposingTheme = getOppositeTheme(context.app.theme.current) const opposingTheme = getOppositeTheme(context.app.theme.current)
sceneInfra.theme = opposingTheme sceneInfra.theme = opposingTheme
sceneEntitiesManager.updateSegmentBaseColor(opposingTheme) sceneEntitiesManager.updateSegmentBaseColor(opposingTheme)
@ -213,13 +222,17 @@ export const settingsMachine = setup({
setAllowOrbitInSketchMode: ({ context, self }) => { setAllowOrbitInSketchMode: ({ context, self }) => {
const rootContext = self.system.get('root').getSnapshot().context const rootContext = self.system.get('root').getSnapshot().context
const sceneInfra = rootContext.sceneInfra const sceneInfra = rootContext.sceneInfra
if (!sceneInfra.camControls) return if (!sceneInfra.camControls) {
return
}
sceneInfra.camControls._setting_allowOrbitInSketchMode = sceneInfra.camControls._setting_allowOrbitInSketchMode =
context.app.allowOrbitInSketchMode.current context.app.allowOrbitInSketchMode.current
// ModelingMachineProvider will do a use effect to trigger the camera engine sync // ModelingMachineProvider will do a use effect to trigger the camera engine sync
}, },
toastSuccess: ({ event }) => { toastSuccess: ({ event }) => {
if (!('data' in event)) return if (!('data' in event)) {
return
}
const eventParts = event.type.replace(/^set./, '').split('.') as [ const eventParts = event.type.replace(/^set./, '').split('.') as [
keyof typeof settings, keyof typeof settings,
string, string,
@ -435,6 +448,22 @@ export const settingsMachine = setup({
actions: ['setSettingAtLevel', 'setThemeColor'], actions: ['setSettingAtLevel', 'setThemeColor'],
}, },
'set.commandBar.includeSettings': {
target: 'persisting settings',
actions: [
'setSettingAtLevel',
'toastSuccess',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
],
},
'set.modeling.defaultUnit': { 'set.modeling.defaultUnit': {
target: 'persisting settings', target: 'persisting settings',
@ -497,6 +526,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
@ -510,6 +546,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
@ -529,7 +572,13 @@ export const settingsMachine = setup({
'clearProjectSettings', 'clearProjectSettings',
'clearCurrentProject', 'clearCurrentProject',
'setThemeColor', 'setThemeColor',
sendTo('registerCommands', { type: 'update' }), sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
}, },
@ -582,6 +631,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
onError: { onError: {
@ -612,7 +668,13 @@ export const settingsMachine = setup({
'setClientTheme', 'setClientTheme',
'setAllowOrbitInSketchMode', 'setAllowOrbitInSketchMode',
'sendThemeToWatcher', 'sendThemeToWatcher',
sendTo('registerCommands', { type: 'update' }), sendTo(
'registerCommands',
({ context: { currentProject: _, ...settings } }) => ({
type: 'update',
settings,
})
),
], ],
}, },
onError: 'idle', onError: 'idle',

View File

@ -1,6 +1,5 @@
import { Dialog, Transition } from '@headlessui/react' import { Dialog, Transition } from '@headlessui/react'
import { Fragment, useEffect, useRef } from 'react' import { Fragment, useEffect, useRef } from 'react'
import { useHotkeys } from 'react-hotkeys-hook'
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom' import { useLocation, useNavigate, useSearchParams } from 'react-router-dom'
import { CustomIcon } from '@src/components/CustomIcon' import { CustomIcon } from '@src/components/CustomIcon'
@ -10,14 +9,19 @@ import { KeybindingsSectionsList } from '@src/components/Settings/KeybindingsSec
import { SettingsSearchBar } from '@src/components/Settings/SettingsSearchBar' import { SettingsSearchBar } from '@src/components/Settings/SettingsSearchBar'
import { SettingsSectionsList } from '@src/components/Settings/SettingsSectionsList' import { SettingsSectionsList } from '@src/components/Settings/SettingsSectionsList'
import { SettingsTabs } from '@src/components/Settings/SettingsTabs' import { SettingsTabs } from '@src/components/Settings/SettingsTabs'
import { useDotDotSlash } from '@src/hooks/useDotDotSlash'
import { PATHS } from '@src/lib/paths' import { PATHS } from '@src/lib/paths'
import type { SettingsLevel } from '@src/lib/settings/settingsTypes' import type { SettingsLevel } from '@src/lib/settings/settingsTypes'
export const Settings = () => { export const Settings = () => {
const navigate = useNavigate() const navigate = useNavigate()
const [searchParams, setSearchParams] = useSearchParams() const [searchParams, setSearchParams] = useSearchParams()
const close = () => navigate(location.pathname.replace(PATHS.SETTINGS, '')) const close = () => {
// This makes sure input texts are saved before closing the dialog (eg. default project name).
if (document.activeElement instanceof HTMLInputElement) {
document.activeElement.blur()
}
navigate(location.pathname.replace(PATHS.SETTINGS, ''))
}
const location = useLocation() const location = useLocation()
const isFileSettings = location.pathname.includes(PATHS.FILE) const isFileSettings = location.pathname.includes(PATHS.FILE)
const searchParamTab = const searchParamTab =
@ -25,8 +29,6 @@ export const Settings = () => {
(isFileSettings ? 'project' : 'user') (isFileSettings ? 'project' : 'user')
const scrollRef = useRef<HTMLDivElement>(null) const scrollRef = useRef<HTMLDivElement>(null)
const dotDotSlash = useDotDotSlash()
useHotkeys('esc', () => navigate(dotDotSlash()))
// Scroll to the hash on load if it exists // Scroll to the hash on load if it exists
useEffect(() => { useEffect(() => {