Compare commits
14 Commits
pierremtb/
...
jtran/fix-
Author | SHA1 | Date | |
---|---|---|---|
cbfe3db203 | |||
615b7feabb | |||
5743b9ced0 | |||
8896d06028 | |||
1f217ef50b | |||
5ef5c6280c | |||
aac95e1e2e | |||
18f4a1303c | |||
ded97eda61 | |||
f6b06520ee | |||
dcfcdc98ce | |||
9ab3325580 | |||
cb5ad3ab27 | |||
1e539cc134 |
36
.github/workflows/build-apps.yml
vendored
@ -7,14 +7,11 @@ on:
|
||||
- main
|
||||
tags:
|
||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
# Daily at 04:00 AM UTC
|
||||
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
||||
- 'nightly-v[0-9]+.[0-9]+.[0-9]+'
|
||||
|
||||
env:
|
||||
IS_RELEASE: ${{ github.ref_type == 'tag' }}
|
||||
IS_NIGHTLY: ${{ github.event_name == 'schedule' }}
|
||||
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
|
||||
IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
@ -32,6 +29,7 @@ jobs:
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn'
|
||||
|
||||
- run: yarn install
|
||||
|
||||
@ -52,7 +50,10 @@ jobs:
|
||||
|
||||
- name: Set nightly version, product name, release notes, and icons
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
run: yarn files:flip-to-nightly
|
||||
run: |
|
||||
export VERSION=${GITHUB_REF_NAME#nightly-v}
|
||||
yarn files:set-version
|
||||
yarn files:flip-to-nightly
|
||||
|
||||
- name: Set release version
|
||||
if: ${{ env.IS_RELEASE == 'true' }}
|
||||
@ -123,9 +124,11 @@ jobs:
|
||||
cp prepared-files/assets/icon.ico assets/icon.ico
|
||||
cp prepared-files/assets/icon.png assets/icon.png
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Sync node version and setup cache
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'yarn' # Set this to npm, yarn or pnpm.
|
||||
|
||||
- name: yarn install
|
||||
# Windows is picky sometimes and fails on fetch. Step takes about ~30s
|
||||
@ -270,7 +273,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
if: ${{ github.ref_type == 'tag' || github.event_name == 'schedule' }}
|
||||
if: ${{ github.ref_type == 'tag' }}
|
||||
env:
|
||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||
VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }}
|
||||
@ -327,8 +330,8 @@ jobs:
|
||||
env:
|
||||
NOTES: ${{ needs.prepare-files.outputs.notes }}
|
||||
PUB_DATE: ${{ github.event.repository.updated_at }}
|
||||
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
||||
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
||||
WEBSITE_DIR: ${{ env.IS_NIGHTLY == 'true' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
||||
URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
||||
run: |
|
||||
RELEASE_DIR=https://${WEBSITE_DIR}
|
||||
jq --null-input \
|
||||
@ -411,14 +414,3 @@ jobs:
|
||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
run: yarn files:invalidate-bucket:nightly
|
||||
|
||||
- name: Tag nightly commit
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
script: |
|
||||
const { VERSION } = process.env
|
||||
const { owner, repo } = context.repo
|
||||
const { sha } = context
|
||||
const ref = `refs/tags/nightly-${VERSION}`
|
||||
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||
|
4
.github/workflows/e2e-tests.yml
vendored
@ -142,7 +142,7 @@ jobs:
|
||||
# TODO: break this in its own job, for now it's not slowing down the overall execution as ubuntu is the quickest,
|
||||
# but we could do better. This forces a large 1/1 shard of all 20 snapshot tests that runs in about 3 minutes.
|
||||
run: |
|
||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --shard=1/1
|
||||
PLATFORM=web yarn playwright test --config=playwright.config.ts --retries="3" --update-snapshots --grep=@snapshot --trace=on --shard=1/1
|
||||
env:
|
||||
CI: true
|
||||
NODE_ENV: development
|
||||
@ -153,7 +153,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
name: playwright-report-snapshots-${{ matrix.os }}-snapshot-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
|
39
.github/workflows/tag-nightly.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: tag-nightly
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 4 * * *'
|
||||
# Daily at 04:00 AM UTC
|
||||
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
||||
|
||||
jobs:
|
||||
tag-nightly:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/create-github-app-token@v1
|
||||
id: app-token
|
||||
with:
|
||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
|
||||
owner: ${{ github.repository_owner }}
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ steps.app-token.outputs.token }}
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- run: yarn install
|
||||
|
||||
- name: Push tag
|
||||
run: |
|
||||
VERSION_NO_V=$(date +'%-y.%-m.%-d')
|
||||
TAG="nightly-v$VERSION_NO_V"
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git tag $TAG
|
||||
git push origin tag $TAG
|
2
.gitignore
vendored
@ -24,7 +24,7 @@ yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.idea
|
||||
# .vscode
|
||||
.vscode
|
||||
.helix
|
||||
src/wasm-lib/.idea
|
||||
src/wasm-lib/.vscode
|
||||
|
5
.vscode/settings.json
vendored
@ -1,5 +0,0 @@
|
||||
{
|
||||
"rust-analyzer.linkedProjects": [
|
||||
"src/wasm-lib/Cargo.toml"
|
||||
]
|
||||
}
|
@ -171,4 +171,22 @@ export class EditorFixture {
|
||||
{ text, placeCursor }
|
||||
)
|
||||
}
|
||||
async selectText(text: string) {
|
||||
// First make sure the code pane is open
|
||||
const wasPaneOpen = await this.checkIfPaneIsOpen()
|
||||
if (!wasPaneOpen) {
|
||||
await this.openPane()
|
||||
}
|
||||
|
||||
// Use Playwright's built-in text selection on the code content
|
||||
// it seems to only select whole divs, which works out to align with syntax highlighting
|
||||
// for code mirror, so you can probably select "sketch002 = startSketchOn('XZ')"
|
||||
// but less so for exactly "sketch002 = startS"
|
||||
await this.codeContent.getByText(text).first().selectText()
|
||||
|
||||
// Reset pane state if needed
|
||||
if (!wasPaneOpen) {
|
||||
await this.closePane()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,16 @@ export class ToolbarFixture {
|
||||
startSketchPlaneSelection = async () =>
|
||||
doAndWaitForImageDiff(this.page, () => this.startSketchBtn.click(), 500)
|
||||
|
||||
exitSketch = async () => {
|
||||
await this.exitSketchBtn.click()
|
||||
await expect(
|
||||
this.page.getByRole('button', { name: 'Start Sketch' })
|
||||
).toBeVisible()
|
||||
await expect(
|
||||
this.page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
}
|
||||
|
||||
editSketch = async () => {
|
||||
await this.editSketchBtn.first().click()
|
||||
// One of the rare times we want to allow a arbitrary wait
|
||||
|
@ -170,8 +170,7 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
})
|
||||
|
||||
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
||||
await toolbar.exitSketchBtn.click()
|
||||
await scene.waitForExecutionDone()
|
||||
await toolbar.exitSketch()
|
||||
})
|
||||
await test.step('Check there is no errors after code created in previous steps executes', async () => {
|
||||
await editor.expectState({
|
||||
@ -202,7 +201,9 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
await expect(
|
||||
page.getByTestId('model-state-indicator-receive-reliable')
|
||||
).toBeVisible()
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
||||
@ -390,6 +391,7 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||
}, file)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||
|
@ -36,7 +36,7 @@ extrude003 = extrude(sketch003, length = 20)
|
||||
`
|
||||
|
||||
test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
test.fixme('Check the happy path, for basic changing color', () => {
|
||||
test.describe('Check the happy path, for basic changing color', () => {
|
||||
const cases = [
|
||||
{
|
||||
desc: 'User accepts change',
|
||||
@ -70,7 +70,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
body1CapCoords.y
|
||||
)
|
||||
const yellow: [number, number, number] = [179, 179, 131]
|
||||
const green: [number, number, number] = [108, 152, 75]
|
||||
const green: [number, number, number] = [128, 194, 88]
|
||||
const notGreen: [number, number, number] = [132, 132, 132]
|
||||
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
||||
const submittingToast = page.getByText(
|
||||
@ -109,7 +109,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
})
|
||||
|
||||
await test.step('verify initial change', async () => {
|
||||
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
||||
await scene.expectPixelColor(green, greenCheckCoords, 20)
|
||||
await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15)
|
||||
await editor.expectEditor.toContain('appearance(')
|
||||
})
|
||||
@ -142,7 +142,7 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
}
|
||||
})
|
||||
|
||||
test(`bad edit prompt`, async ({
|
||||
test('bad edit prompt', async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
@ -195,4 +195,150 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
||||
await expect(failToast).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test(`manual code selection rename`, async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
page,
|
||||
scene,
|
||||
}) => {
|
||||
const body1CapCoords = { x: 571, y: 351 }
|
||||
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
||||
const successToast = page.getByText('Prompt to edit successful')
|
||||
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
||||
|
||||
await test.step('wait for scene to load and select code in editor', async () => {
|
||||
// Find and select the text "sketch002" in the editor
|
||||
await editor.selectText('sketch002')
|
||||
|
||||
// Verify the selection was made
|
||||
await editor.expectState({
|
||||
highlightedCode: '',
|
||||
activeLines: ["sketch002 = startSketchOn('XZ')"],
|
||||
diagnostics: [],
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('fire off edit prompt', async () => {
|
||||
await scene.expectPixelColor([134, 134, 134], body1CapCoords, 15)
|
||||
await cmdBar.openCmdBar('promptToEdit')
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.fill('Please rename to mySketch')
|
||||
await page.waitForTimeout(100)
|
||||
await cmdBar.progressCmdBar()
|
||||
await expect(submittingToast).toBeVisible()
|
||||
await expect(submittingToast).not.toBeVisible({
|
||||
timeout: 2 * 60_000,
|
||||
})
|
||||
await expect(successToast).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('verify rename change and accept it', async () => {
|
||||
await editor.expectEditor.toContain('mySketch = startSketchOn')
|
||||
await editor.expectEditor.not.toContain('sketch002 = startSketchOn')
|
||||
await editor.expectEditor.toContain(
|
||||
'extrude002 = extrude(mySketch, length = 50)'
|
||||
)
|
||||
|
||||
await acceptBtn.click()
|
||||
await expect(successToast).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
|
||||
test('multiple body selections', async ({
|
||||
context,
|
||||
homePage,
|
||||
cmdBar,
|
||||
editor,
|
||||
page,
|
||||
scene,
|
||||
}) => {
|
||||
const body1CapCoords = { x: 571, y: 351 }
|
||||
const body2WallCoords = { x: 620, y: 152 }
|
||||
const [clickBody1Cap] = scene.makeMouseHelpers(
|
||||
body1CapCoords.x,
|
||||
body1CapCoords.y
|
||||
)
|
||||
const [clickBody2Cap] = scene.makeMouseHelpers(
|
||||
body2WallCoords.x,
|
||||
body2WallCoords.y
|
||||
)
|
||||
const grey: [number, number, number] = [132, 132, 132]
|
||||
|
||||
await context.addInitScript((file) => {
|
||||
localStorage.setItem('persistCode', file)
|
||||
}, file)
|
||||
await homePage.goToModelingScene()
|
||||
await scene.waitForExecutionDone()
|
||||
|
||||
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
||||
const successToast = page.getByText('Prompt to edit successful')
|
||||
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
||||
|
||||
await test.step('select multiple bodies and fire prompt', async () => {
|
||||
// Initial color check
|
||||
await scene.expectPixelColor(grey, body1CapCoords, 15)
|
||||
|
||||
// Open command bar first (without selection)
|
||||
await cmdBar.openCmdBar('promptToEdit')
|
||||
|
||||
// Select first body
|
||||
await page.waitForTimeout(100)
|
||||
await clickBody1Cap()
|
||||
|
||||
// Hold shift and select second body
|
||||
await editor.expectState({
|
||||
highlightedCode: '',
|
||||
activeLines: ['|>startProfileAt([-73.64,-42.89],%)'],
|
||||
diagnostics: [],
|
||||
})
|
||||
await page.keyboard.down('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await clickBody2Cap()
|
||||
await editor.expectState({
|
||||
highlightedCode:
|
||||
'line(end=[121.13,56.63],tag=$seg02)extrude(profile001,length=200)',
|
||||
activeLines: [
|
||||
'|>line(end=[121.13,56.63],tag=$seg02)',
|
||||
'|>startProfileAt([-73.64,-42.89],%)',
|
||||
],
|
||||
diagnostics: [],
|
||||
})
|
||||
await page.keyboard.up('Shift')
|
||||
await page.waitForTimeout(100)
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
// Enter prompt and submit
|
||||
await page
|
||||
.getByTestId('cmd-bar-arg-value')
|
||||
.fill('make these neon green please, use #39FF14')
|
||||
await page.waitForTimeout(100)
|
||||
await cmdBar.progressCmdBar()
|
||||
|
||||
// Wait for API response
|
||||
await expect(submittingToast).toBeVisible()
|
||||
await expect(submittingToast).not.toBeVisible({
|
||||
timeout: 2 * 60_000,
|
||||
})
|
||||
await expect(successToast).toBeVisible()
|
||||
})
|
||||
|
||||
await test.step('verify code changed', async () => {
|
||||
await editor.expectEditor.toContain('appearance(')
|
||||
|
||||
// Accept changes
|
||||
await acceptBtn.click()
|
||||
await expect(successToast).not.toBeVisible()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
@ -775,7 +775,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||
)
|
||||
`)
|
||||
await expect(
|
||||
page.getByTestId('model-state-indicator-execution-done')
|
||||
page.getByTestId('model-state-indicator-receive-reliable')
|
||||
).toBeVisible()
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
|
1
exp
@ -1 +0,0 @@
|
||||
sketch001=startSketchOn('XZ')|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]|>angledLine([0,268.43],%,$rectangleSegmentA001)|>angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)|>angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)|>line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)|>close()extrude001=extrude(sketch001,length=100)|>chamfer(length=30,tags=[getOppositeEdge(seg01)],tag=$seg03)|>chamfer(length=30,tags=[seg01],tag=$seg04)|>chamfer(length=30,tags=[getNextAdjacentEdge(seg02)],tag=$seg05)|>chamfer(length=30,tags=[getNextAdjacentEdge(yo)],tag=$seg06)sketch004=startSketchOn(extrude001,seg05)profile003=startProfileAt([82.57,322.96],sketch004)|>angledLine([0,11.16],%,$rectangleSegmentA004)|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch003=startSketchOn(extrude001,seg04)profile002=startProfileAt([-209.64,255.28],sketch003)|>angledLine([0,11.56],%,$rectangleSegmentA003)|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch002=startSketchOn(extrude001,seg03)profile001=startProfileAt([205.96,254.59],sketch002)|>angledLine([0,11.39],%,$rectangleSegmentA002)|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()
|
1
got
@ -1 +0,0 @@
|
||||
sketch001=startSketchOn('XZ')|>startProfileAt([75.8,317.2],%)//[$startCapTag,$EndCapTag]|>angledLine([0,268.43],%,$rectangleSegmentA001)|>angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)|>angledLine([segAng(rectangleSegmentA001),-segLen(rectangleSegmentA001)],%,$yo)|>line(endAbsolute=[profileStartX(%),profileStartY(%)],tag=$seg02)|>close()extrude001=extrude(sketch001,length=100)|>chamfer(length=30,tags=[getOppositeEdge(seg01)],tag=$seg03)|>chamfer(length=30,tags=[seg01],tag=$seg04)|>chamfer(length=30,tags=[getNextAdjacentEdge(seg02)],tag=$seg05)|>chamfer(length=30,tags=[getNextAdjacentEdge(yo)],tag=$seg06)sketch005=startSketchOn(extrude001,seg06)profile004=startProfileAt([-23.43,19.69],sketch005)|>angledLine([0,9.1],%,$rectangleSegmentA005)|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch004=startSketchOn(extrude001,seg05)profile003=startProfileAt([82.57,322.96],sketch004)|>angledLine([0,11.16],%,$rectangleSegmentA004)|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch003=startSketchOn(extrude001,seg04)profile002=startProfileAt([-209.64,255.28],sketch003)|>angledLine([0,11.56],%,$rectangleSegmentA003)|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()sketch002=startSketchOn(extrude001,seg03)profile001=startProfileAt([205.96,254.59],sketch002)|>angledLine([0,11.39],%,$rectangleSegmentA002)|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)|>line(endAbsolute=[profileStartX(%),profileStartY(%)])|>close()
|
@ -91,11 +91,11 @@
|
||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||
"fetch:wasm:windows": "./scripts/get-latest-wasm-bundle.ps1",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/next/manifest.json",
|
||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/next/manifest.json",
|
||||
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||
"build:wasm:nocopy": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings",
|
||||
"build:wasm": "yarn build:wasm:nocopy && cp src/wasm-lib/pkg/wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm:windows": "yarn install:wasm-pack:cargo && yarn build:wasm:nocopy && copy src\\wasm-lib\\pkg\\wasm_lib_bg.wasm public && yarn fmt",
|
||||
"build:wasm:windows": "yarn install:wasm-pack:cargo && yarn build:wasm:nocopy && ./scripts/copy-wasm.ps1 && yarn fmt",
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||
|
1
scripts/copy-wasm.ps1
Normal file
@ -0,0 +1 @@
|
||||
copy src\wasm-lib\pkg\wasm_lib_bg.wasm public
|
@ -1,10 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
export VERSION=$(date +'%-y.%-m.%-d')
|
||||
export COMMIT=$(git rev-parse --short HEAD)
|
||||
|
||||
# package.json
|
||||
yarn files:set-version
|
||||
PACKAGE=$(jq '.productName="Zoo Modeling App (Nightly)" | .name="zoo-modeling-app-nightly"' package.json --indent 2)
|
||||
echo "$PACKAGE" > package.json
|
||||
|
||||
@ -14,7 +12,7 @@ yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
||||
yq -i '.nsis.include = "./scripts/installer-nightly.nsh"' electron-builder.yml
|
||||
|
||||
# Release notes
|
||||
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
||||
echo "Nightly build (commit $COMMIT)" > release-notes.md
|
||||
|
||||
# icons
|
||||
cp assets/icon-nightly.png assets/icon.png
|
||||
|
@ -24,12 +24,7 @@ import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import FileMachineProvider from 'components/FileMachineProvider'
|
||||
import { MachineManagerProvider } from 'components/MachineManagerProvider'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import {
|
||||
fileLoader,
|
||||
homeLoader,
|
||||
onboardingRedirectLoader,
|
||||
telemetryLoader,
|
||||
} from 'lib/routeLoaders'
|
||||
import { fileLoader, homeLoader, telemetryLoader } from 'lib/routeLoaders'
|
||||
import LspProvider from 'components/LspProvider'
|
||||
import { KclContextProvider } from 'lang/KclProvider'
|
||||
import { ASK_TO_OPEN_QUERY_PARAM, BROWSER_PROJECT_NAME } from 'lib/constants'
|
||||
@ -113,11 +108,6 @@ const router = createRouter([
|
||||
{
|
||||
id: PATHS.FILE + 'SETTINGS',
|
||||
children: [
|
||||
{
|
||||
loader: onboardingRedirectLoader,
|
||||
index: true,
|
||||
element: <></>,
|
||||
},
|
||||
{
|
||||
path: makeUrlPathRelative(PATHS.SETTINGS),
|
||||
element: <Settings />,
|
||||
|
@ -22,7 +22,7 @@ import {
|
||||
UnreliableSubscription,
|
||||
} from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
import { toSync, uuidv4 } from 'lib/utils'
|
||||
import { toSync, uuidv4, getNormalisedCoordinates } from 'lib/utils'
|
||||
import { deg2Rad } from 'lib/utils2d'
|
||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
||||
import * as TWEEN from '@tweenjs/tween.js'
|
||||
@ -109,6 +109,7 @@ export class CameraControls {
|
||||
interactionGuards: MouseGuard = cameraMouseDragGuards.Zoo
|
||||
isFovAnimationInProgress = false
|
||||
perspectiveFovBeforeOrtho = 45
|
||||
|
||||
// NOTE: Duplicated state across Provider and singleton. Mapped from settingsMachine
|
||||
_setting_allowOrbitInSketchMode = false
|
||||
get isPerspective() {
|
||||
@ -456,11 +457,19 @@ export class CameraControls {
|
||||
if (this.syncDirection === 'engineToClient') {
|
||||
const newCmdId = uuidv4()
|
||||
|
||||
// Nonsense to do anything until the video stream is established.
|
||||
if (!this.engineCommandManager.elVideo) return
|
||||
|
||||
const { x, y } = getNormalisedCoordinates(
|
||||
event,
|
||||
this.engineCommandManager.elVideo,
|
||||
this.engineCommandManager.streamDimensions
|
||||
)
|
||||
this.throttledEngCmd({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'highlight_set_entity',
|
||||
selected_at_window: { x: event.clientX, y: event.clientY },
|
||||
selected_at_window: { x, y },
|
||||
},
|
||||
cmd_id: newCmdId,
|
||||
})
|
||||
|
@ -17,7 +17,9 @@ export const CommandBar = () => {
|
||||
const {
|
||||
context: { selectedCommand, currentArgument, commands },
|
||||
} = commandBarState
|
||||
const isSelectionArgument = currentArgument?.inputType === 'selection'
|
||||
const isSelectionArgument =
|
||||
currentArgument?.inputType === 'selection' ||
|
||||
currentArgument?.inputType === 'selectionMixed'
|
||||
const WrapperComponent = isSelectionArgument ? Popover : Dialog
|
||||
|
||||
// Close the command bar when navigating
|
||||
|
@ -1,6 +1,7 @@
|
||||
import CommandArgOptionInput from './CommandArgOptionInput'
|
||||
import CommandBarBasicInput from './CommandBarBasicInput'
|
||||
import CommandBarSelectionInput from './CommandBarSelectionInput'
|
||||
import CommandBarSelectionMixedInput from './CommandBarSelectionMixedInput'
|
||||
import { CommandArgument } from 'lib/commandTypes'
|
||||
import CommandBarHeader from './CommandBarHeader'
|
||||
import CommandBarKclInput from './CommandBarKclInput'
|
||||
@ -84,6 +85,14 @@ function ArgumentInput({
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
case 'selectionMixed':
|
||||
return (
|
||||
<CommandBarSelectionMixedInput
|
||||
arg={arg}
|
||||
stepBack={stepBack}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)
|
||||
case 'kcl':
|
||||
return (
|
||||
<CommandBarKclInput arg={arg} stepBack={stepBack} onSubmit={onSubmit} />
|
||||
|
@ -124,7 +124,8 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
||||
<span className="sr-only">: </span>
|
||||
<span data-testid="header-arg-value">
|
||||
{argValue ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
arg.inputType === 'selection' ||
|
||||
arg.inputType === 'selectionMixed' ? (
|
||||
getSelectionTypeDisplayText(argValue as Selections)
|
||||
) : arg.inputType === 'kcl' ? (
|
||||
roundOff(
|
||||
|
135
src/components/CommandBar/CommandBarSelectionMixedInput.tsx
Normal file
@ -0,0 +1,135 @@
|
||||
import { useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { CommandArgument } from 'lib/commandTypes'
|
||||
import {
|
||||
Selections,
|
||||
canSubmitSelectionArg,
|
||||
getSelectionCountByType,
|
||||
getSelectionTypeDisplayText,
|
||||
} from 'lib/selections'
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
||||
|
||||
const selectionSelector = (snapshot: any) => snapshot?.context.selectionRanges
|
||||
|
||||
export default function CommandBarSelectionMixedInput({
|
||||
arg,
|
||||
stepBack,
|
||||
onSubmit,
|
||||
}: {
|
||||
arg: CommandArgument<unknown> & { inputType: 'selectionMixed'; name: string }
|
||||
stepBack: () => void
|
||||
onSubmit: (data: unknown) => void
|
||||
}) {
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const commandBarState = useCommandBarState()
|
||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||
const [hasAutoSkipped, setHasAutoSkipped] = useState(false)
|
||||
const selection: Selections = useSelector(arg.machineActor, selectionSelector)
|
||||
|
||||
const selectionsByType = useMemo(() => {
|
||||
return getSelectionCountByType(selection)
|
||||
}, [selection])
|
||||
|
||||
const canSubmitSelection = useMemo<boolean>(() => {
|
||||
if (!selection) return false
|
||||
const isNonZeroRange = selection.graphSelections.some((sel) => {
|
||||
const range = sel.codeRef.range
|
||||
return range[1] - range[0] !== 0 // Non-zero range is always valid
|
||||
})
|
||||
if (isNonZeroRange) return true
|
||||
return canSubmitSelectionArg(selectionsByType, arg)
|
||||
}, [selectionsByType, selection])
|
||||
|
||||
useEffect(() => {
|
||||
inputRef.current?.focus()
|
||||
}, [selection, inputRef])
|
||||
|
||||
// Only auto-skip on initial mount if we have a valid selection
|
||||
// different from the component CommandBarSelectionInput in the the dependency array
|
||||
// is empty
|
||||
useEffect(() => {
|
||||
if (!hasAutoSkipped && canSubmitSelection && arg.skip) {
|
||||
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
|
||||
if (argValue === undefined) {
|
||||
handleSubmit()
|
||||
setHasAutoSkipped(true)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
function handleChange() {
|
||||
inputRef.current?.focus()
|
||||
}
|
||||
|
||||
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
|
||||
e?.preventDefault()
|
||||
|
||||
if (!canSubmitSelection) {
|
||||
setHasSubmitted(true)
|
||||
return
|
||||
}
|
||||
|
||||
onSubmit(selection)
|
||||
}
|
||||
|
||||
const isMixedSelection = arg.inputType === 'selectionMixed'
|
||||
const allowNoSelection = isMixedSelection && arg.allowNoSelection
|
||||
const showSceneSelection =
|
||||
isMixedSelection && arg.selectionSource?.allowSceneSelection
|
||||
|
||||
return (
|
||||
<form id="arg-form" onSubmit={handleSubmit}>
|
||||
<label
|
||||
className={
|
||||
'relative flex flex-col mx-4 my-4 ' +
|
||||
(!hasSubmitted || canSubmitSelection || 'text-destroy-50')
|
||||
}
|
||||
>
|
||||
{canSubmitSelection
|
||||
? 'Select objects in the scene'
|
||||
: 'Select code or objects in the scene'}
|
||||
|
||||
{showSceneSelection && (
|
||||
<div className="scene-selection mt-2">
|
||||
<p className="text-sm text-chalkboard-60">
|
||||
Select objects in the scene
|
||||
</p>
|
||||
{/* Scene selection UI will be handled by the parent component */}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{allowNoSelection && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onSubmit(null)}
|
||||
className="mt-2 px-4 py-2 rounded border border-chalkboard-30 text-chalkboard-90 dark:text-chalkboard-10 hover:bg-chalkboard-10 dark:hover:bg-chalkboard-90 transition-colors"
|
||||
>
|
||||
Continue without selection
|
||||
</button>
|
||||
)}
|
||||
|
||||
<span data-testid="cmd-bar-arg-name" className="sr-only">
|
||||
{arg.name}
|
||||
</span>
|
||||
<input
|
||||
id="selection"
|
||||
name="selection"
|
||||
ref={inputRef}
|
||||
required
|
||||
data-testid="cmd-bar-arg-value"
|
||||
placeholder="Select an entity with your mouse"
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-default"
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Backspace') {
|
||||
stepBack()
|
||||
} else if (event.key === 'Escape') {
|
||||
commandBarActor.send({ type: 'Close' })
|
||||
}
|
||||
}}
|
||||
onChange={handleChange}
|
||||
value={JSON.stringify(selection || {})}
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
)
|
||||
}
|
@ -130,6 +130,8 @@ export const FileMachineProvider = ({
|
||||
navigateToFile: ({ context, event }) => {
|
||||
if (event.type !== 'xstate.done.actor.create-and-open-file') return
|
||||
if (event.output && 'name' in event.output) {
|
||||
// TODO: Technically this is not the same as the FileTree Onclick even if they are in the same page
|
||||
// What is "Open file?"
|
||||
commandBarActor.send({ type: 'Close' })
|
||||
navigate(
|
||||
`..${PATHS.FILE}/${encodeURIComponent(
|
||||
|
@ -23,6 +23,8 @@ import { FileEntry } from 'lib/project'
|
||||
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
||||
import { normalizeLineEndings } from 'lib/codeEditor'
|
||||
import { reportRejection } from 'lib/trap'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { kclErrorsByFilename, KCLError } from 'lang/errors'
|
||||
|
||||
function getIndentationCSS(level: number) {
|
||||
return `calc(1rem * ${level + 1})`
|
||||
@ -158,6 +160,7 @@ const FileTreeItem = ({
|
||||
level = 0,
|
||||
treeSelection,
|
||||
setTreeSelection,
|
||||
runtimeErrors,
|
||||
}: {
|
||||
parentDir: FileEntry | undefined
|
||||
project?: IndexLoaderData['project']
|
||||
@ -177,6 +180,7 @@ const FileTreeItem = ({
|
||||
level?: number
|
||||
treeSelection: FileEntry | undefined
|
||||
setTreeSelection: Dispatch<React.SetStateAction<FileEntry | undefined>>
|
||||
runtimeErrors: Map<string, KCLError[]>
|
||||
}) => {
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { onFileOpen, onFileClose } = useLspContext()
|
||||
@ -186,6 +190,8 @@ const FileTreeItem = ({
|
||||
const isFileOrDirHighlighted = treeSelection?.path === fileOrDir?.path
|
||||
const itemRef = useRef(null)
|
||||
|
||||
const hasRuntimeError = runtimeErrors.has(fileOrDir.path)
|
||||
|
||||
// Since every file or directory gets its own FileTreeItem, we can do this.
|
||||
// Because subtrees only render when they are opened, that means this
|
||||
// only listens when they open. Because this acts like a useEffect, when
|
||||
@ -292,7 +298,7 @@ const FileTreeItem = ({
|
||||
>
|
||||
{!isRenaming ? (
|
||||
<button
|
||||
className="flex gap-1 items-center py-0.5 rounded-none border-none p-0 m-0 text-sm w-full hover:!bg-transparent text-left !text-inherit"
|
||||
className="relative flex gap-1 items-center py-0.5 rounded-none border-none p-0 m-0 text-sm w-full hover:!bg-transparent text-left !text-inherit"
|
||||
style={{ paddingInlineStart: getIndentationCSS(level) }}
|
||||
onClick={(e) => {
|
||||
e.currentTarget.focus()
|
||||
@ -300,11 +306,21 @@ const FileTreeItem = ({
|
||||
}}
|
||||
onKeyUp={handleKeyUp}
|
||||
>
|
||||
{hasRuntimeError && (
|
||||
<p
|
||||
className={
|
||||
'absolute m-0 p-0 bottom-3 left-6 w-3 h-3 flex items-center justify-center text-[9px] font-semibold text-white bg-primary hue-rotate-90 rounded-full border border-chalkboard-10 dark:border-chalkboard-80 z-50 hover:cursor-pointer hover:scale-[2] transition-transform duration-200'
|
||||
}
|
||||
title={`Click to view notifications`}
|
||||
>
|
||||
<span>x</span>
|
||||
</p>
|
||||
)}
|
||||
<CustomIcon
|
||||
name={fileOrDir.name?.endsWith(FILE_EXT) ? 'kcl' : 'file'}
|
||||
className="inline-block w-3 text-current"
|
||||
/>
|
||||
{fileOrDir.name}
|
||||
<span className="pl-1">{fileOrDir.name}</span>
|
||||
</button>
|
||||
) : (
|
||||
<RenameForm
|
||||
@ -414,6 +430,7 @@ const FileTreeItem = ({
|
||||
key={level + '-' + child.path}
|
||||
treeSelection={treeSelection}
|
||||
setTreeSelection={setTreeSelection}
|
||||
runtimeErrors={runtimeErrors}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
@ -660,6 +677,8 @@ export const FileTreeInner = ({
|
||||
const loaderData = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
|
||||
const { send: fileSend, context: fileContext } = useFileContext()
|
||||
const { send: modelingSend } = useModelingContext()
|
||||
const { errors } = useKclContext()
|
||||
const runtimeErrors = kclErrorsByFilename(errors)
|
||||
|
||||
const [lastDirectoryClicked, setLastDirectoryClicked] = useState<
|
||||
FileEntry | undefined
|
||||
@ -769,6 +788,7 @@ export const FileTreeInner = ({
|
||||
key={fileOrDir.path}
|
||||
treeSelection={treeSelection}
|
||||
setTreeSelection={setTreeSelection}
|
||||
runtimeErrors={runtimeErrors}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
|
@ -18,6 +18,7 @@ import { editorManager } from 'lib/singletons'
|
||||
import { ContextFrom } from 'xstate'
|
||||
import { settingsMachine } from 'machines/settingsMachine'
|
||||
import { FeatureTreePane } from './FeatureTreePane'
|
||||
import { kclErrorsByFilename } from 'lang/errors'
|
||||
|
||||
export type SidebarType =
|
||||
| 'code'
|
||||
@ -30,8 +31,10 @@ export type SidebarType =
|
||||
| 'variables'
|
||||
|
||||
export interface BadgeInfo {
|
||||
value: (props: PaneCallbackProps) => boolean | number
|
||||
value: (props: PaneCallbackProps) => boolean | number | string
|
||||
onClick?: MouseEventHandler<any>
|
||||
className?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,6 +155,25 @@ export const sidebarPanes: SidebarPane[] = [
|
||||
},
|
||||
keybinding: 'Shift + F',
|
||||
hide: ({ platform }) => platform === 'web',
|
||||
showBadge: {
|
||||
value: (context) => {
|
||||
// Only compute runtime errors! Compilation errors are not tracked here.
|
||||
const errors = kclErrorsByFilename(context.kclContext.errors)
|
||||
return errors.size > 0 ? 'x' : ''
|
||||
},
|
||||
onClick: (e) => {
|
||||
e.preventDefault()
|
||||
// TODO: When we have generic file open
|
||||
// If badge is pressed
|
||||
// Open the first error in the array of errors
|
||||
// Then scroll to error
|
||||
// Do you automatically open the project files
|
||||
// editorManager.scrollToFirstErrorDiagnosticIfExists()
|
||||
},
|
||||
className:
|
||||
'absolute m-0 p-0 bottom-4 left-4 w-3 h-3 flex items-center justify-center text-[9px] font-semibold text-white bg-primary hue-rotate-90 rounded-full border border-chalkboard-10 dark:border-chalkboard-80 z-50 hover:cursor-pointer hover:scale-[2] transition-transform duration-200',
|
||||
title: 'Project files have runtime errors',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'variables',
|
||||
|
@ -27,8 +27,10 @@ interface ModelingSidebarProps {
|
||||
}
|
||||
|
||||
interface BadgeInfoComputed {
|
||||
value: number | boolean
|
||||
value: number | boolean | string
|
||||
onClick?: MouseEventHandler<any>
|
||||
className?: string
|
||||
title?: string
|
||||
}
|
||||
|
||||
function getPlatformString(): 'web' | 'desktop' {
|
||||
@ -116,6 +118,8 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
acc[pane.id] = {
|
||||
value: pane.showBadge.value(paneCallbackProps),
|
||||
onClick: pane.showBadge.onClick,
|
||||
className: pane.showBadge.className,
|
||||
title: pane.showBadge.title,
|
||||
}
|
||||
}
|
||||
return acc
|
||||
@ -125,6 +129,7 @@ export function ModelingSidebar({ paneOpacity }: ModelingSidebarProps) {
|
||||
// Clear any hidden panes from the `openPanes` array
|
||||
useEffect(() => {
|
||||
const panesToReset: SidebarType[] = []
|
||||
|
||||
sidebarPanes.forEach((pane) => {
|
||||
if (
|
||||
pane.hide === true ||
|
||||
@ -339,22 +344,31 @@ function ModelingPaneButton({
|
||||
<p
|
||||
id={`${paneConfig.id}-badge`}
|
||||
className={
|
||||
'absolute m-0 p-0 bottom-4 left-4 w-3 h-3 flex items-center justify-center text-[10px] font-semibold text-white bg-primary hue-rotate-90 rounded-full border border-chalkboard-10 dark:border-chalkboard-80 z-50 hover:cursor-pointer hover:scale-[2] transition-transform duration-200'
|
||||
showBadge.className
|
||||
? showBadge.className
|
||||
: 'absolute m-0 p-0 bottom-4 left-4 w-3 h-3 flex items-center justify-center text-[10px] font-semibold text-white bg-primary hue-rotate-90 rounded-full border border-chalkboard-10 dark:border-chalkboard-80 z-50 hover:cursor-pointer hover:scale-[2] transition-transform duration-200'
|
||||
}
|
||||
onClick={showBadge.onClick}
|
||||
title={`Click to view ${showBadge.value} notification${
|
||||
Number(showBadge.value) > 1 ? 's' : ''
|
||||
}`}
|
||||
title={
|
||||
showBadge.title
|
||||
? showBadge.title
|
||||
: `Click to view ${showBadge.value} notification${
|
||||
Number(showBadge.value) > 1 ? 's' : ''
|
||||
}`
|
||||
}
|
||||
>
|
||||
<span className="sr-only"> has </span>
|
||||
{typeof showBadge.value === 'number' ? (
|
||||
{typeof showBadge.value === 'number' ||
|
||||
typeof showBadge.value === 'string' ? (
|
||||
<span>{showBadge.value}</span>
|
||||
) : (
|
||||
<span className="sr-only">a</span>
|
||||
)}
|
||||
<span className="sr-only">
|
||||
notification{Number(showBadge.value) > 1 ? 's' : ''}
|
||||
</span>
|
||||
{typeof showBadge.value === 'number' && (
|
||||
<span className="sr-only">
|
||||
notification{Number(showBadge.value) > 1 ? 's' : ''}
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
@ -4,11 +4,12 @@ import {
|
||||
useLocation,
|
||||
useNavigate,
|
||||
useRouteLoaderData,
|
||||
redirect,
|
||||
} from 'react-router-dom'
|
||||
import { PATHS } from 'lib/paths'
|
||||
import { markOnce } from 'lib/performance'
|
||||
import { useAuthNavigation } from 'hooks/useAuthNavigation'
|
||||
import { useAuthState } from 'machines/appMachine'
|
||||
import { useAuthState, useSettings } from 'machines/appMachine'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { getAppSettingsFilePath } from 'lib/desktop'
|
||||
import { isDesktop } from 'lib/isDesktop'
|
||||
@ -16,6 +17,9 @@ import { trap } from 'lib/trap'
|
||||
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
||||
import { loadAndValidateSettings } from 'lib/settings/settingsUtils'
|
||||
import { settingsActor } from 'machines/appMachine'
|
||||
import makeUrlPathRelative from 'lib/makeUrlPathRelative'
|
||||
import { OnboardingStatus } from 'wasm-lib/kcl/bindings/OnboardingStatus'
|
||||
import { SnapshotFrom } from 'xstate'
|
||||
|
||||
export const RouteProviderContext = createContext({})
|
||||
|
||||
@ -29,6 +33,7 @@ export function RouteProvider({ children }: { children: ReactNode }) {
|
||||
const navigation = useNavigation()
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const settings = useSettings()
|
||||
|
||||
const authState = useAuthState()
|
||||
useEffect(() => {
|
||||
@ -43,6 +48,32 @@ export function RouteProvider({ children }: { children: ReactNode }) {
|
||||
markOnce('code/willLoadHome')
|
||||
} else if (isFile) {
|
||||
markOnce('code/willLoadFile')
|
||||
|
||||
/**
|
||||
* TODO: Move to XState. This block has been moved from routerLoaders
|
||||
* and is borrowing the `isFile` logic from the rest of this
|
||||
* telemetry-focused `useEffect`. Once `appMachine` knows about
|
||||
* the current route and navigation, this can be moved into settingsMachine
|
||||
* to fire as soon as the user settings have been read.
|
||||
*/
|
||||
const onboardingStatus: OnboardingStatus =
|
||||
settings.app.onboardingStatus.current || ''
|
||||
// '' is the initial state, 'completed' and 'dismissed' are the final states
|
||||
const needsToOnboard =
|
||||
onboardingStatus.length === 0 ||
|
||||
!(onboardingStatus === 'completed' || onboardingStatus === 'dismissed')
|
||||
const shouldRedirectToOnboarding = isFile && needsToOnboard
|
||||
|
||||
if (
|
||||
shouldRedirectToOnboarding &&
|
||||
settingsActor.getSnapshot().matches('idle')
|
||||
) {
|
||||
navigate(
|
||||
(first ? location.pathname : navigation.location?.pathname) +
|
||||
PATHS.ONBOARDING.INDEX +
|
||||
onboardingStatus.slice(1)
|
||||
)
|
||||
}
|
||||
}
|
||||
setFirstState(false)
|
||||
}, [navigation])
|
||||
|
@ -47,6 +47,8 @@ export const Stream = () => {
|
||||
overallState === NetworkHealthState.Ok ||
|
||||
overallState === NetworkHealthState.Weak
|
||||
|
||||
engineCommandManager.elVideo = videoRef.current
|
||||
|
||||
/**
|
||||
* Execute code and show a "building scene message"
|
||||
* in Stream.tsx in the meantime.
|
||||
@ -272,7 +274,7 @@ export const Stream = () => {
|
||||
|
||||
if (btnName(e.nativeEvent).left) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
sendSelectEventToEngine(e, videoRef.current)
|
||||
sendSelectEventToEngine(e)
|
||||
}
|
||||
}
|
||||
|
||||
@ -294,7 +296,7 @@ export const Stream = () => {
|
||||
return
|
||||
}
|
||||
|
||||
sendSelectEventToEngine(e, videoRef.current)
|
||||
sendSelectEventToEngine(e)
|
||||
.then(({ entity_id }) => {
|
||||
if (!entity_id) {
|
||||
// No entity selected. This is benign
|
||||
|
@ -101,10 +101,7 @@ export function useSetupEngineManager(
|
||||
streamRef?.current?.offsetWidth ?? 0,
|
||||
streamRef?.current?.offsetHeight ?? 0
|
||||
)
|
||||
engineCommandManager.handleResize({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
engineCommandManager.handleResize(engineCommandManager.streamDimensions)
|
||||
}, 500)
|
||||
|
||||
const onOnline = () => {
|
||||
|
@ -293,6 +293,13 @@ export class KclManager {
|
||||
return null
|
||||
}
|
||||
|
||||
// GOTCHA:
|
||||
// When we safeParse this is tied to execution because they clicked a new file to load
|
||||
// Clear all previous errors and logs because they are old since they executed a new file
|
||||
// If we decouple safeParse from execution we need to move this application logic.
|
||||
this._kclErrorsCallBack([])
|
||||
this._logsCallBack([])
|
||||
|
||||
this.addDiagnostics(complilationErrorsToDiagnostics(result.errors))
|
||||
this.addDiagnostics(complilationErrorsToDiagnostics(result.warnings))
|
||||
if (result.errors.length > 0) {
|
||||
|
@ -13,6 +13,7 @@ describe('test kclErrToDiagnostic', () => {
|
||||
operations: [],
|
||||
artifactCommands: [],
|
||||
artifactGraph: defaultArtifactGraph(),
|
||||
filenames: {},
|
||||
},
|
||||
{
|
||||
name: '',
|
||||
@ -23,6 +24,7 @@ describe('test kclErrToDiagnostic', () => {
|
||||
operations: [],
|
||||
artifactCommands: [],
|
||||
artifactGraph: defaultArtifactGraph(),
|
||||
filenames: {},
|
||||
},
|
||||
]
|
||||
const diagnostics = kclErrorsToDiagnostics(errors)
|
||||
|
@ -1,4 +1,7 @@
|
||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||
import {
|
||||
KclError,
|
||||
KclError as RustKclError,
|
||||
} from '../wasm-lib/kcl/bindings/KclError'
|
||||
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
||||
import { Diagnostic as CodeMirrorDiagnostic } from '@codemirror/lint'
|
||||
import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
||||
@ -13,6 +16,7 @@ import {
|
||||
SourceRange,
|
||||
} from 'lang/wasm'
|
||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||
import { ModulePath } from 'wasm-lib/kcl/bindings/ModulePath'
|
||||
|
||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||
export class KCLError extends Error {
|
||||
@ -22,6 +26,7 @@ export class KCLError extends Error {
|
||||
operations: Operation[]
|
||||
artifactCommands: ArtifactCommand[]
|
||||
artifactGraph: ArtifactGraph
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
|
||||
constructor(
|
||||
kind: ExtractKind<RustKclError> | 'name',
|
||||
@ -29,7 +34,8 @@ export class KCLError extends Error {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super()
|
||||
this.kind = kind
|
||||
@ -38,6 +44,7 @@ export class KCLError extends Error {
|
||||
this.operations = operations
|
||||
this.artifactCommands = artifactCommands
|
||||
this.artifactGraph = artifactGraph
|
||||
this.filenames = filenames
|
||||
Object.setPrototypeOf(this, KCLError.prototype)
|
||||
}
|
||||
}
|
||||
@ -48,7 +55,8 @@ export class KCLLexicalError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'lexical',
|
||||
@ -56,7 +64,8 @@ export class KCLLexicalError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
@ -68,7 +77,8 @@ export class KCLInternalError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'internal',
|
||||
@ -76,7 +86,8 @@ export class KCLInternalError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
@ -88,7 +99,8 @@ export class KCLSyntaxError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'syntax',
|
||||
@ -96,7 +108,8 @@ export class KCLSyntaxError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||
}
|
||||
@ -108,7 +121,8 @@ export class KCLSemanticError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'semantic',
|
||||
@ -116,7 +130,8 @@ export class KCLSemanticError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||
}
|
||||
@ -128,9 +143,18 @@ export class KCLTypeError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super('type', msg, sourceRange, operations, artifactCommands, artifactGraph)
|
||||
super(
|
||||
'type',
|
||||
msg,
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||
}
|
||||
}
|
||||
@ -141,7 +165,8 @@ export class KCLUnimplementedError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'unimplemented',
|
||||
@ -149,7 +174,8 @@ export class KCLUnimplementedError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||
}
|
||||
@ -161,7 +187,8 @@ export class KCLUnexpectedError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'unexpected',
|
||||
@ -169,7 +196,8 @@ export class KCLUnexpectedError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||
}
|
||||
@ -181,7 +209,8 @@ export class KCLValueAlreadyDefined extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'name',
|
||||
@ -189,7 +218,8 @@ export class KCLValueAlreadyDefined extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||
}
|
||||
@ -201,7 +231,8 @@ export class KCLUndefinedValueError extends KCLError {
|
||||
sourceRange: SourceRange,
|
||||
operations: Operation[],
|
||||
artifactCommands: ArtifactCommand[],
|
||||
artifactGraph: ArtifactGraph
|
||||
artifactGraph: ArtifactGraph,
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
) {
|
||||
super(
|
||||
'name',
|
||||
@ -209,7 +240,8 @@ export class KCLUndefinedValueError extends KCLError {
|
||||
sourceRange,
|
||||
operations,
|
||||
artifactCommands,
|
||||
artifactGraph
|
||||
artifactGraph,
|
||||
filenames
|
||||
)
|
||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||
}
|
||||
@ -232,7 +264,8 @@ export function lspDiagnosticsToKclErrors(
|
||||
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, 0],
|
||||
[],
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
defaultArtifactGraph(),
|
||||
{}
|
||||
)
|
||||
)
|
||||
.sort((a, b) => {
|
||||
@ -304,3 +337,34 @@ export function complilationErrorsToDiagnostics(
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Create an array of KCL Errors with a new formatting to
|
||||
// easily map SourceRange of an error to the filename to display in the
|
||||
// side bar UI. This is to indicate an error in an imported file, it isn't
|
||||
// the specific code mirror error interface.
|
||||
export function kclErrorsByFilename(
|
||||
errors: KCLError[]
|
||||
): Map<string, KCLError[]> {
|
||||
const fileNameToError: Map<string, KCLError[]> = new Map()
|
||||
errors.forEach((error: KCLError) => {
|
||||
const filenames = error.filenames
|
||||
const sourceRange: SourceRange = error.sourceRange
|
||||
const fileIndex = sourceRange[2]
|
||||
const modulePath: ModulePath | undefined = filenames[fileIndex]
|
||||
if (modulePath) {
|
||||
let stdOrLocalPath = modulePath.value
|
||||
if (stdOrLocalPath) {
|
||||
// Build up an array of errors per file name
|
||||
const value = fileNameToError.get(stdOrLocalPath)
|
||||
if (!value) {
|
||||
fileNameToError.set(stdOrLocalPath, [error])
|
||||
} else {
|
||||
value.push(error)
|
||||
fileNameToError.set(stdOrLocalPath, [error])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return fileNameToError
|
||||
}
|
||||
|
@ -511,7 +511,8 @@ const theExtrude = startSketchOn('XY')
|
||||
topLevelRange(129, 135),
|
||||
[],
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
defaultArtifactGraph(),
|
||||
{}
|
||||
)
|
||||
)
|
||||
})
|
||||
|
@ -116,7 +116,11 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
||||
}
|
||||
if (!extrudeInSketchPipe) {
|
||||
const init = expectedExtrudeNode.init
|
||||
if (init.type !== 'CallExpression' && init.type !== 'PipeExpression') {
|
||||
if (
|
||||
init.type !== 'CallExpression' &&
|
||||
init.type !== 'CallExpressionKw' &&
|
||||
init.type !== 'PipeExpression'
|
||||
) {
|
||||
return new Error(
|
||||
'Expected extrude expression is not a CallExpression or PipeExpression'
|
||||
)
|
||||
@ -129,25 +133,33 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
||||
// ast
|
||||
const ast = assertParse(code)
|
||||
|
||||
// selection
|
||||
// range
|
||||
const segmentRange = topLevelRange(
|
||||
code.indexOf(selectedSegmentSnippet),
|
||||
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length
|
||||
)
|
||||
const selection: Selection = {
|
||||
codeRef: codeRefFromRange(segmentRange, ast),
|
||||
}
|
||||
|
||||
// executeAst and artifactGraph
|
||||
await kclManager.executeAst({ ast })
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
|
||||
// find artifact
|
||||
const maybeArtifact = [...artifactGraph].find(([, artifact]) => {
|
||||
if (!('codeRef' in artifact && artifact.codeRef)) return false
|
||||
return isOverlap(artifact.codeRef.range, segmentRange)
|
||||
})
|
||||
|
||||
// build selection
|
||||
const selection: Selection = {
|
||||
codeRef: codeRefFromRange(segmentRange, ast),
|
||||
artifact: maybeArtifact ? maybeArtifact[1] : undefined,
|
||||
}
|
||||
|
||||
// get extrude expression
|
||||
const pathResult = getPathToExtrudeForSegmentSelection(
|
||||
ast,
|
||||
selection,
|
||||
artifactGraph,
|
||||
dependencies
|
||||
artifactGraph
|
||||
)
|
||||
if (err(pathResult)) return pathResult
|
||||
const { pathToExtrudeNode } = pathResult
|
||||
@ -234,6 +246,56 @@ extrude003 = extrude(sketch003, length = -15)`
|
||||
expectedExtrudeSnippet
|
||||
)
|
||||
})
|
||||
it('should return the correct paths for a (piped) extrude based on the other body (face)', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-25, -25], %)
|
||||
|> yLine(50, %)
|
||||
|> xLine(50, %)
|
||||
|> yLine(-50, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> extrude(length = 50)
|
||||
sketch002 = startSketchOn(sketch001, 'END')
|
||||
|> startProfileAt([-15, -15], %)
|
||||
|> yLine(30, %)
|
||||
|> xLine(30, %)
|
||||
|> yLine(-30, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> extrude(length = 30)`
|
||||
const selectedSegmentSnippet = `xLine(30, %)`
|
||||
const expectedExtrudeSnippet = `extrude(length = 30)`
|
||||
await runGetPathToExtrudeForSegmentSelectionTest(
|
||||
code,
|
||||
selectedSegmentSnippet,
|
||||
expectedExtrudeSnippet
|
||||
)
|
||||
})
|
||||
it('should return the correct paths for a (non-piped) extrude based on the other body (face)', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-25, -25], %)
|
||||
|> yLine(50, %)
|
||||
|> xLine(50, %)
|
||||
|> yLine(-50, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = 50)
|
||||
sketch002 = startSketchOn(extrude001, 'END')
|
||||
|> startProfileAt([-15, -15], %)
|
||||
|> yLine(30, %)
|
||||
|> xLine(30, %)
|
||||
|> yLine(-30, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude002 = extrude(sketch002, length = 30)`
|
||||
const selectedSegmentSnippet = `xLine(30, %)`
|
||||
const expectedExtrudeSnippet = `extrude002 = extrude(sketch002, length = 30)`
|
||||
await runGetPathToExtrudeForSegmentSelectionTest(
|
||||
code,
|
||||
selectedSegmentSnippet,
|
||||
expectedExtrudeSnippet
|
||||
)
|
||||
})
|
||||
it('should not return any path for missing extrusion', async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-30, 30], %)
|
||||
|
@ -10,7 +10,6 @@ import {
|
||||
Program,
|
||||
VariableDeclaration,
|
||||
VariableDeclarator,
|
||||
sketchFromKclValue,
|
||||
} from '../wasm'
|
||||
import {
|
||||
createCallExpressionStdLib,
|
||||
@ -35,11 +34,11 @@ import {
|
||||
sketchLineHelperMap,
|
||||
sketchLineHelperMapKw,
|
||||
} from '../std/sketch'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { err } from 'lib/trap'
|
||||
import { Selection, Selections } from 'lib/selections'
|
||||
import { KclCommandValue } from 'lib/commandTypes'
|
||||
import { isArray } from 'lib/utils'
|
||||
import { Artifact, getSweepFromSuspectedPath } from 'lang/std/artifactGraph'
|
||||
import { Artifact, getSweepArtifactFromSelection } from 'lang/std/artifactGraph'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { findKwArg } from 'lang/util'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
@ -121,8 +120,7 @@ export function modifyAstWithEdgeTreatmentAndTag(
|
||||
const result = getPathToExtrudeForSegmentSelection(
|
||||
clonedAstForGetExtrude,
|
||||
selection,
|
||||
artifactGraph,
|
||||
dependencies
|
||||
artifactGraph
|
||||
)
|
||||
if (err(result)) return result
|
||||
const { pathToSegmentNode, pathToExtrudeNode } = result
|
||||
@ -279,39 +277,19 @@ function insertParametersIntoAst(
|
||||
export function getPathToExtrudeForSegmentSelection(
|
||||
ast: Program,
|
||||
selection: Selection,
|
||||
artifactGraph: ArtifactGraph,
|
||||
dependencies: {
|
||||
kclManager: KclManager
|
||||
engineCommandManager: EngineCommandManager
|
||||
editorManager: EditorManager
|
||||
codeManager: CodeManager
|
||||
}
|
||||
artifactGraph: ArtifactGraph
|
||||
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
|
||||
const pathToSegmentNode = getNodePathFromSourceRange(
|
||||
ast,
|
||||
selection.codeRef?.range
|
||||
)
|
||||
|
||||
const varDecNode = getNodeFromPath<VariableDeclaration>(
|
||||
ast,
|
||||
pathToSegmentNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(varDecNode)) return varDecNode
|
||||
const sketchVar = varDecNode.node.declaration.id.name
|
||||
|
||||
const sketch = sketchFromKclValue(
|
||||
dependencies.kclManager.variables[sketchVar],
|
||||
sketchVar
|
||||
)
|
||||
if (trap(sketch)) return sketch
|
||||
|
||||
const extrusion = getSweepFromSuspectedPath(sketch.id, artifactGraph)
|
||||
if (err(extrusion)) return extrusion
|
||||
const sweepArtifact = getSweepArtifactFromSelection(selection, artifactGraph)
|
||||
if (err(sweepArtifact)) return sweepArtifact
|
||||
|
||||
const pathToExtrudeNode = getNodePathFromSourceRange(
|
||||
ast,
|
||||
extrusion.codeRef.range
|
||||
sweepArtifact.codeRef.range
|
||||
)
|
||||
if (err(pathToExtrudeNode)) return pathToExtrudeNode
|
||||
|
||||
|
@ -13,36 +13,23 @@ import {
|
||||
createLiteral,
|
||||
createIdentifier,
|
||||
findUniqueName,
|
||||
createCallExpressionStdLib,
|
||||
createObjectExpression,
|
||||
createArrayExpression,
|
||||
createVariableDeclaration,
|
||||
createCallExpressionStdLibKw,
|
||||
createLabeledArg,
|
||||
} from 'lang/modifyAst'
|
||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||
import { KclManager } from 'lang/KclSingleton'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import EditorManager from 'editor/manager'
|
||||
import CodeManager from 'lang/codeManager'
|
||||
|
||||
export function addShell({
|
||||
node,
|
||||
selection,
|
||||
artifactGraph,
|
||||
thickness,
|
||||
dependencies,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
selection: Selections
|
||||
artifactGraph: ArtifactGraph
|
||||
thickness: Expr
|
||||
dependencies: {
|
||||
kclManager: KclManager
|
||||
engineCommandManager: EngineCommandManager
|
||||
editorManager: EditorManager
|
||||
codeManager: CodeManager
|
||||
}
|
||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||
const modifiedAst = structuredClone(node)
|
||||
|
||||
@ -55,8 +42,7 @@ export function addShell({
|
||||
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
|
||||
clonedAstForGetExtrude,
|
||||
graphSelection,
|
||||
artifactGraph,
|
||||
dependencies
|
||||
artifactGraph
|
||||
)
|
||||
if (err(extrudeLookupResult)) {
|
||||
return new Error("Couldn't find extrude")
|
||||
|
@ -18,6 +18,7 @@ import {
|
||||
} from 'lang/wasm'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
import { Selection } from 'lib/selections'
|
||||
import { err } from 'lib/trap'
|
||||
import { Cap, Plane, Wall } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
import { CapSubType } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
@ -79,7 +80,7 @@ interface SegmentArtifactRich extends BaseArtifact {
|
||||
|
||||
interface SweepArtifactRich extends BaseArtifact {
|
||||
type: 'sweep'
|
||||
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep'
|
||||
subType: 'extrusion' | 'revolve' | 'revolveAboutEdge' | 'loft' | 'sweep'
|
||||
path: PathArtifact
|
||||
surfaces: Array<WallArtifact | CapArtifact>
|
||||
edges: Array<SweepEdge>
|
||||
@ -455,6 +456,47 @@ export function getSweepFromSuspectedPath(
|
||||
)
|
||||
}
|
||||
|
||||
export function getSweepArtifactFromSelection(
|
||||
selection: Selection,
|
||||
artifactGraph: ArtifactGraph
|
||||
): SweepArtifact | Error {
|
||||
let sweepArtifact: Artifact | null = null
|
||||
if (selection.artifact?.type === 'sweepEdge') {
|
||||
const _artifact = getArtifactOfTypes(
|
||||
{ key: selection.artifact.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (err(_artifact)) return _artifact
|
||||
sweepArtifact = _artifact
|
||||
} else if (selection.artifact?.type === 'segment') {
|
||||
const _pathArtifact = getArtifactOfTypes(
|
||||
{ key: selection.artifact.pathId, types: ['path'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (err(_pathArtifact)) return _pathArtifact
|
||||
if (!_pathArtifact.sweepId) return new Error('Path does not have a sweepId')
|
||||
const _artifact = getArtifactOfTypes(
|
||||
{ key: _pathArtifact.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (err(_artifact)) return _artifact
|
||||
sweepArtifact = _artifact
|
||||
} else if (
|
||||
selection.artifact?.type === 'cap' ||
|
||||
selection.artifact?.type === 'wall'
|
||||
) {
|
||||
const _artifact = getArtifactOfTypes(
|
||||
{ key: selection.artifact.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (err(_artifact)) return _artifact
|
||||
sweepArtifact = _artifact
|
||||
}
|
||||
if (!sweepArtifact) return new Error('No sweep artifact found')
|
||||
|
||||
return sweepArtifact
|
||||
}
|
||||
|
||||
export function getCodeRefsByArtifactId(
|
||||
id: string,
|
||||
artifactGraph: ArtifactGraph
|
||||
|
@ -1447,11 +1447,17 @@ export class EngineCommandManager extends EventTarget {
|
||||
commandId: string
|
||||
}
|
||||
settings: SettingsViaQueryString
|
||||
width: number = 1337
|
||||
height: number = 1337
|
||||
|
||||
streamDimensions = {
|
||||
// Random defaults that are overwritten pretty much immediately
|
||||
width: 1337,
|
||||
height: 1337,
|
||||
}
|
||||
|
||||
elVideo: HTMLVideoElement | null = null
|
||||
|
||||
/**
|
||||
* Export intent traxcks the intent of the export. If it is null there is no
|
||||
* Export intent tracks the intent of the export. If it is null there is no
|
||||
* export in progress. Otherwise it is an enum value of the intent.
|
||||
* Another export cannot be started if one is already in progress.
|
||||
*/
|
||||
@ -1554,15 +1560,14 @@ export class EngineCommandManager extends EventTarget {
|
||||
return
|
||||
}
|
||||
|
||||
this.width = width
|
||||
this.height = height
|
||||
this.streamDimensions = {
|
||||
width,
|
||||
height,
|
||||
}
|
||||
|
||||
// If we already have an engine connection, just need to resize the stream.
|
||||
if (this.engineConnection) {
|
||||
this.handleResize({
|
||||
streamWidth: width,
|
||||
streamHeight: height,
|
||||
})
|
||||
this.handleResize(this.streamDimensions)
|
||||
return
|
||||
}
|
||||
|
||||
@ -1858,27 +1863,22 @@ export class EngineCommandManager extends EventTarget {
|
||||
return
|
||||
}
|
||||
|
||||
handleResize({
|
||||
streamWidth,
|
||||
streamHeight,
|
||||
}: {
|
||||
streamWidth: number
|
||||
streamHeight: number
|
||||
}) {
|
||||
handleResize({ width, height }: { width: number; height: number }) {
|
||||
if (!this.engineConnection?.isReady()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.width = streamWidth
|
||||
this.height = streamHeight
|
||||
this.streamDimensions = {
|
||||
width,
|
||||
height,
|
||||
}
|
||||
|
||||
const resizeCmd: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'reconfigure_stream',
|
||||
width: streamWidth,
|
||||
height: streamHeight,
|
||||
...this.streamDimensions,
|
||||
fps: 60,
|
||||
},
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ import { MetaSettings } from 'wasm-lib/kcl/bindings/MetaSettings'
|
||||
import { UnitAngle, UnitLength } from 'wasm-lib/kcl/bindings/ModelingCmd'
|
||||
import { UnitLen } from 'wasm-lib/kcl/bindings/UnitLen'
|
||||
import { UnitAngle as UnitAng } from 'wasm-lib/kcl/bindings/UnitAngle'
|
||||
import { ModulePath } from 'wasm-lib/kcl/bindings/ModulePath'
|
||||
|
||||
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
@ -266,7 +267,8 @@ export const parse = (code: string | Error): ParseResult | Error => {
|
||||
firstSourceRange(parsed),
|
||||
[],
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
defaultArtifactGraph(),
|
||||
{}
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -296,6 +298,7 @@ export interface ExecState {
|
||||
artifactCommands: ArtifactCommand[]
|
||||
artifactGraph: ArtifactGraph
|
||||
errors: CompilationError[]
|
||||
filenames: { [x: number]: ModulePath | undefined }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,6 +313,7 @@ export function emptyExecState(): ExecState {
|
||||
artifactCommands: [],
|
||||
artifactGraph: defaultArtifactGraph(),
|
||||
errors: [],
|
||||
filenames: [],
|
||||
}
|
||||
}
|
||||
|
||||
@ -336,6 +340,7 @@ function execStateFromRust(
|
||||
artifactCommands: execOutcome.artifactCommands,
|
||||
artifactGraph,
|
||||
errors: execOutcome.errors,
|
||||
filenames: execOutcome.filenames,
|
||||
}
|
||||
}
|
||||
|
||||
@ -347,6 +352,7 @@ function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
||||
artifactCommands: execOutcome.artifactCommands,
|
||||
artifactGraph: new Map<ArtifactId, Artifact>(),
|
||||
errors: execOutcome.errors,
|
||||
filenames: execOutcome.filenames,
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,7 +480,7 @@ const jsAppSettings = async () => {
|
||||
}
|
||||
|
||||
const errFromErrWithOutputs = (e: any): KCLError => {
|
||||
console.log('execute error', e)
|
||||
console.log(e)
|
||||
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
||||
return new KCLError(
|
||||
parsed.error.kind,
|
||||
@ -482,7 +488,8 @@ const errFromErrWithOutputs = (e: any): KCLError => {
|
||||
firstSourceRange(parsed.error),
|
||||
parsed.operations,
|
||||
parsed.artifactCommands,
|
||||
rustArtifactGraphToMap(parsed.artifactGraph)
|
||||
rustArtifactGraphToMap(parsed.artifactGraph),
|
||||
parsed.filenames
|
||||
)
|
||||
}
|
||||
|
||||
@ -548,7 +555,8 @@ export const modifyAstForSketch = async (
|
||||
firstSourceRange(parsed),
|
||||
[],
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
defaultArtifactGraph(),
|
||||
{}
|
||||
)
|
||||
|
||||
return Promise.reject(kclError)
|
||||
|
@ -666,7 +666,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
icon: 'chat',
|
||||
args: {
|
||||
selection: {
|
||||
inputType: 'selection',
|
||||
inputType: 'selectionMixed',
|
||||
selectionTypes: [
|
||||
'solid2d',
|
||||
'segment',
|
||||
@ -678,6 +678,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
],
|
||||
multiple: true,
|
||||
required: true,
|
||||
selectionSource: {
|
||||
allowSceneSelection: true,
|
||||
allowCodeSelection: true,
|
||||
},
|
||||
skip: true,
|
||||
},
|
||||
prompt: {
|
||||
|
@ -16,6 +16,7 @@ const INPUT_TYPES = [
|
||||
'text',
|
||||
'kcl',
|
||||
'selection',
|
||||
'selectionMixed',
|
||||
'boolean',
|
||||
] as const
|
||||
export interface KclExpression {
|
||||
@ -156,6 +157,23 @@ export type CommandArgumentConfig<
|
||||
context: CommandBarContext
|
||||
}) => Promise<boolean | string>
|
||||
}
|
||||
| {
|
||||
inputType: 'selectionMixed'
|
||||
selectionTypes: Artifact['type'][]
|
||||
multiple: boolean
|
||||
allowNoSelection?: boolean
|
||||
validation?: ({
|
||||
data,
|
||||
context,
|
||||
}: {
|
||||
data: any
|
||||
context: CommandBarContext
|
||||
}) => Promise<boolean | string>
|
||||
selectionSource?: {
|
||||
allowSceneSelection?: boolean
|
||||
allowCodeSelection?: boolean
|
||||
}
|
||||
}
|
||||
| {
|
||||
inputType: 'kcl'
|
||||
createVariableByDefault?: boolean
|
||||
@ -252,6 +270,23 @@ export type CommandArgument<
|
||||
context: CommandBarContext
|
||||
}) => Promise<boolean | string>
|
||||
}
|
||||
| {
|
||||
inputType: 'selectionMixed'
|
||||
selectionTypes: Artifact['type'][]
|
||||
multiple: boolean
|
||||
allowNoSelection?: boolean
|
||||
validation?: ({
|
||||
data,
|
||||
context,
|
||||
}: {
|
||||
data: any
|
||||
context: CommandBarContext
|
||||
}) => Promise<boolean | string>
|
||||
selectionSource?: {
|
||||
allowSceneSelection?: boolean
|
||||
allowCodeSelection?: boolean
|
||||
}
|
||||
}
|
||||
| {
|
||||
inputType: 'kcl'
|
||||
createVariableByDefault?: boolean
|
||||
|
@ -187,6 +187,16 @@ export function buildCommandArgument<
|
||||
selectionTypes: arg.selectionTypes,
|
||||
validation: arg.validation,
|
||||
} satisfies CommandArgument<O, T> & { inputType: 'selection' }
|
||||
} else if (arg.inputType === 'selectionMixed') {
|
||||
return {
|
||||
inputType: arg.inputType,
|
||||
...baseCommandArgument,
|
||||
multiple: arg.multiple,
|
||||
selectionTypes: arg.selectionTypes,
|
||||
validation: arg.validation,
|
||||
allowNoSelection: arg.allowNoSelection,
|
||||
selectionSource: arg.selectionSource,
|
||||
} satisfies CommandArgument<O, T> & { inputType: 'selectionMixed' }
|
||||
} else if (arg.inputType === 'kcl') {
|
||||
return {
|
||||
inputType: arg.inputType,
|
||||
|
@ -43,15 +43,33 @@ export async function submitPromptToEditToQueue({
|
||||
projectName,
|
||||
}: {
|
||||
prompt: string
|
||||
selections: Selections
|
||||
selections: Selections | null
|
||||
code: string
|
||||
projectName: string
|
||||
token?: string
|
||||
artifactGraph: ArtifactGraph
|
||||
}): Promise<Models['TextToCadIteration_type'] | Error> {
|
||||
// If no selection, use whole file
|
||||
if (selections === null) {
|
||||
const body: Models['TextToCadIterationBody_type'] = {
|
||||
original_source_code: code,
|
||||
prompt,
|
||||
source_ranges: [], // Empty ranges indicates whole file
|
||||
project_name:
|
||||
projectName !== '' && projectName !== 'browser'
|
||||
? projectName
|
||||
: undefined,
|
||||
kcl_version: kclManager.kclVersion,
|
||||
}
|
||||
return submitToApi(body, token)
|
||||
}
|
||||
|
||||
// Handle manual code selections and artifact selections differently
|
||||
const ranges: Models['TextToCadIterationBody_type']['source_ranges'] =
|
||||
selections.graphSelections.flatMap((selection) => {
|
||||
const artifact = selection.artifact
|
||||
|
||||
// For artifact selections, add context
|
||||
const prompts: Models['TextToCadIterationBody_type']['source_ranges'] = []
|
||||
|
||||
if (artifact?.type === 'cap') {
|
||||
@ -153,8 +171,17 @@ See later source ranges for more context. about the sweep`,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!artifact) {
|
||||
// manually selected code is more likely to not have an artifact
|
||||
// an example might be highlighting the variable name only in a variable declaration
|
||||
prompts.push({
|
||||
prompt: '',
|
||||
range: convertAppRangeToApiRange(selection.codeRef.range, code),
|
||||
})
|
||||
}
|
||||
return prompts
|
||||
})
|
||||
|
||||
const body: Models['TextToCadIterationBody_type'] = {
|
||||
original_source_code: code,
|
||||
prompt,
|
||||
@ -163,6 +190,15 @@ See later source ranges for more context. about the sweep`,
|
||||
projectName !== '' && projectName !== 'browser' ? projectName : undefined,
|
||||
kcl_version: kclManager.kclVersion,
|
||||
}
|
||||
|
||||
return submitToApi(body, token)
|
||||
}
|
||||
|
||||
// Helper function to handle API submission
|
||||
async function submitToApi(
|
||||
body: Models['TextToCadIterationBody_type'],
|
||||
token?: string
|
||||
): Promise<Models['TextToCadIteration_type'] | Error> {
|
||||
const url = VITE_KC_API_BASE_URL + '/ml/text-to-cad/iteration'
|
||||
const data: Models['TextToCadIteration_type'] | Error =
|
||||
await crossPlatformFetch(
|
||||
|
@ -23,30 +23,6 @@ export const telemetryLoader: LoaderFunction = async ({
|
||||
return null
|
||||
}
|
||||
|
||||
// Redirect users to the appropriate onboarding page if they haven't completed it
|
||||
export const onboardingRedirectLoader: ActionFunction = async (args) => {
|
||||
const settings = getSettings()
|
||||
const onboardingStatus: OnboardingStatus =
|
||||
settings.app.onboardingStatus.current || ''
|
||||
const notEnRouteToOnboarding = !args.request.url.includes(
|
||||
PATHS.ONBOARDING.INDEX
|
||||
)
|
||||
// '' is the initial state, 'completed' and 'dismissed' are the final states
|
||||
const hasValidOnboardingStatus =
|
||||
onboardingStatus.length === 0 ||
|
||||
!(onboardingStatus === 'completed' || onboardingStatus === 'dismissed')
|
||||
const shouldRedirectToOnboarding =
|
||||
notEnRouteToOnboarding && hasValidOnboardingStatus
|
||||
|
||||
if (shouldRedirectToOnboarding) {
|
||||
return redirect(
|
||||
makeUrlPathRelative(PATHS.ONBOARDING.INDEX) + onboardingStatus.slice(1)
|
||||
)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export const fileLoader: LoaderFunction = async (
|
||||
routerData
|
||||
): Promise<FileLoaderData | Response> => {
|
||||
|
@ -481,7 +481,9 @@ export function getSelectionTypeDisplayText(
|
||||
|
||||
export function canSubmitSelectionArg(
|
||||
selectionsByType: 'none' | Map<ResolvedSelectionType, number>,
|
||||
argument: CommandArgument<unknown> & { inputType: 'selection' }
|
||||
argument: CommandArgument<unknown> & {
|
||||
inputType: 'selection' | 'selectionMixed'
|
||||
}
|
||||
) {
|
||||
return (
|
||||
selectionsByType !== 'none' &&
|
||||
@ -644,16 +646,17 @@ export function codeToIdSelections(
|
||||
}
|
||||
|
||||
export async function sendSelectEventToEngine(
|
||||
e: MouseEvent | React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
el: HTMLVideoElement
|
||||
e: React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||
) {
|
||||
const { x, y } = getNormalisedCoordinates({
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
el,
|
||||
streamWidth: engineCommandManager.width,
|
||||
streamHeight: engineCommandManager.height,
|
||||
})
|
||||
// No video stream to normalise against, return immediately
|
||||
if (!engineCommandManager.elVideo)
|
||||
return Promise.reject('video element not ready')
|
||||
|
||||
const { x, y } = getNormalisedCoordinates(
|
||||
e,
|
||||
engineCommandManager.elVideo,
|
||||
engineCommandManager.streamDimensions
|
||||
)
|
||||
const res = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
|
@ -161,25 +161,20 @@ export function toSync<F extends AsyncFn<F>>(
|
||||
}
|
||||
}
|
||||
|
||||
export function getNormalisedCoordinates({
|
||||
clientX,
|
||||
clientY,
|
||||
streamWidth,
|
||||
streamHeight,
|
||||
el,
|
||||
}: {
|
||||
clientX: number
|
||||
clientY: number
|
||||
streamWidth: number
|
||||
streamHeight: number
|
||||
el: HTMLElement
|
||||
}) {
|
||||
const { left, top, width, height } = el?.getBoundingClientRect()
|
||||
const browserX = clientX - left
|
||||
const browserY = clientY - top
|
||||
export function getNormalisedCoordinates(
|
||||
e: PointerEvent | React.MouseEvent<HTMLDivElement, MouseEvent>,
|
||||
elVideo: HTMLVideoElement,
|
||||
streamDimensions: {
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
) {
|
||||
const { left, top, width, height } = elVideo?.getBoundingClientRect()
|
||||
const browserX = e.clientX - left
|
||||
const browserY = e.clientY - top
|
||||
return {
|
||||
x: Math.round((browserX / width) * streamWidth),
|
||||
y: Math.round((browserY / height) * streamHeight),
|
||||
x: Math.round((browserX / width) * streamDimensions.width),
|
||||
y: Math.round((browserY / height) * streamDimensions.height),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +295,8 @@ export const commandBarMachine = setup({
|
||||
if (
|
||||
context.currentArgument &&
|
||||
context.selectedCommand &&
|
||||
argConfig?.inputType === 'selection' &&
|
||||
(argConfig?.inputType === 'selection' ||
|
||||
argConfig?.inputType === 'selectionMixed') &&
|
||||
argConfig?.validation
|
||||
) {
|
||||
argConfig
|
||||
|
@ -1995,12 +1995,6 @@ export const modelingMachine = setup({
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const { selection, thickness } = input
|
||||
const dependencies = {
|
||||
kclManager,
|
||||
engineCommandManager,
|
||||
editorManager,
|
||||
codeManager,
|
||||
}
|
||||
|
||||
// Insert the thickness variable if it exists
|
||||
if (
|
||||
@ -2026,7 +2020,6 @@ export const modelingMachine = setup({
|
||||
'variableName' in thickness
|
||||
? thickness.variableIdentifierAst
|
||||
: thickness.valueAst,
|
||||
dependencies,
|
||||
})
|
||||
if (err(shellResult)) {
|
||||
return err(shellResult)
|
||||
|
6
src/wasm-lib/Cargo.lock
generated
@ -730,7 +730,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "derive-docs"
|
||||
version = "0.1.38"
|
||||
version = "0.1.39"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"anyhow",
|
||||
@ -1724,7 +1724,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.38"
|
||||
version = "0.2.39"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1791,7 +1791,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.38"
|
||||
version = "0.1.39"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
|
@ -103,3 +103,7 @@ path = "tests/modify/main.rs"
|
||||
#[patch.crates-io]
|
||||
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
|
||||
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||
|
||||
# Local development only. Placeholder to speed up development cycle
|
||||
#[package.metadata.wasm-pack.profile.release]
|
||||
#wasm-opt = false
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.38"
|
||||
version = "0.1.39"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-test-server"
|
||||
description = "A test server for KCL"
|
||||
version = "0.1.38"
|
||||
version = "0.1.39"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.38"
|
||||
version = "0.2.39"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -2,9 +2,12 @@ use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
|
||||
|
||||
use indexmap::IndexMap;
|
||||
|
||||
use crate::{
|
||||
execution::{ArtifactCommand, ArtifactGraph, Operation},
|
||||
lsp::IntoDiagnostic,
|
||||
modules::ModulePath,
|
||||
source_range::SourceRange,
|
||||
ModuleId,
|
||||
};
|
||||
@ -116,6 +119,7 @@ pub struct KclErrorWithOutputs {
|
||||
pub operations: Vec<Operation>,
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||
}
|
||||
|
||||
impl KclErrorWithOutputs {
|
||||
@ -124,12 +128,14 @@ impl KclErrorWithOutputs {
|
||||
operations: Vec<Operation>,
|
||||
artifact_commands: Vec<ArtifactCommand>,
|
||||
artifact_graph: ArtifactGraph,
|
||||
filenames: IndexMap<ModuleId, ModulePath>,
|
||||
) -> Self {
|
||||
Self {
|
||||
error,
|
||||
operations,
|
||||
artifact_commands,
|
||||
artifact_graph,
|
||||
filenames,
|
||||
}
|
||||
}
|
||||
pub fn no_outputs(error: KclError) -> Self {
|
||||
@ -138,6 +144,7 @@ impl KclErrorWithOutputs {
|
||||
operations: Default::default(),
|
||||
artifact_commands: Default::default(),
|
||||
artifact_graph: Default::default(),
|
||||
filenames: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -165,6 +165,7 @@ pub struct Sweep {
|
||||
pub enum SweepSubType {
|
||||
Extrusion,
|
||||
Revolve,
|
||||
RevolveAboutEdge,
|
||||
Loft,
|
||||
Sweep,
|
||||
}
|
||||
@ -751,10 +752,12 @@ fn artifacts_to_update(
|
||||
}
|
||||
ModelingCmd::Extrude(kcmc::Extrude { target, .. })
|
||||
| ModelingCmd::Revolve(kcmc::Revolve { target, .. })
|
||||
| ModelingCmd::RevolveAboutEdge(kcmc::RevolveAboutEdge { target, .. })
|
||||
| ModelingCmd::Sweep(kcmc::Sweep { target, .. }) => {
|
||||
let sub_type = match cmd {
|
||||
ModelingCmd::Extrude(_) => SweepSubType::Extrusion,
|
||||
ModelingCmd::Revolve(_) => SweepSubType::Revolve,
|
||||
ModelingCmd::RevolveAboutEdge(_) => SweepSubType::RevolveAboutEdge,
|
||||
ModelingCmd::Sweep(_) => SweepSubType::Sweep,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
@ -885,7 +888,7 @@ fn artifacts_to_update(
|
||||
let path_sweep_id = path.sweep_id.ok_or_else(|| {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
message:format!(
|
||||
"Expected a sweep ID on the path when processing Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
|
||||
"Expected a sweep ID on the path when processing last path's Solid3dGetExtrusionFaceInfo command, but we have none: {id:?}, {path:?}"
|
||||
),
|
||||
source_ranges: vec![range],
|
||||
})
|
||||
|
@ -329,6 +329,8 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
let id = exec_state.next_module_id();
|
||||
// Add file path string to global state even if it fails to import
|
||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
// TODO handle parsing errors properly
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?;
|
||||
@ -343,6 +345,8 @@ impl ExecutorContext {
|
||||
|
||||
let id = exec_state.next_module_id();
|
||||
let path = resolved_path.expect_path();
|
||||
// Add file path string to global state even if it fails to import
|
||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||
let format = super::import::format_from_annotations(attrs, path, source_range)?;
|
||||
let geom = super::import::import_foreign(path, format, exec_state, self, source_range).await?;
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Foreign(geom));
|
||||
@ -354,6 +358,8 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
let id = exec_state.next_module_id();
|
||||
// Add file path string to global state even if it fails to import
|
||||
exec_state.add_path_to_source_id(resolved_path.clone(), id);
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err().unwrap();
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||
|
@ -23,6 +23,7 @@ use crate::{
|
||||
cache::{CacheInformation, CacheResult},
|
||||
},
|
||||
fs::FileManager,
|
||||
modules::{ModuleId, ModulePath},
|
||||
parsing::ast::types::{Expr, ImportPath, Node, NodeRef, Program},
|
||||
settings::types::UnitLength,
|
||||
source_range::SourceRange,
|
||||
@ -70,6 +71,8 @@ pub struct ExecOutcome {
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
/// Non-fatal errors and warnings.
|
||||
pub errors: Vec<CompilationError>,
|
||||
/// File Names in module Id array index order
|
||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
@ -712,11 +715,19 @@ impl ExecutorContext {
|
||||
.execute_and_build_graph(program, exec_state, preserve_mem)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
let module_id_to_module_path: IndexMap<ModuleId, ModulePath> = exec_state
|
||||
.global
|
||||
.path_to_source_id
|
||||
.iter()
|
||||
.map(|(k, v)| ((*v), k.clone()))
|
||||
.collect();
|
||||
|
||||
KclErrorWithOutputs::new(
|
||||
e,
|
||||
exec_state.mod_local.operations.clone(),
|
||||
exec_state.global.artifact_commands.clone(),
|
||||
exec_state.global.artifact_graph.clone(),
|
||||
module_id_to_module_path,
|
||||
)
|
||||
})?;
|
||||
|
||||
|
@ -124,6 +124,12 @@ impl ExecState {
|
||||
artifact_commands: self.global.artifact_commands,
|
||||
artifact_graph: self.global.artifact_graph,
|
||||
errors: self.global.errors,
|
||||
filenames: self
|
||||
.global
|
||||
.path_to_source_id
|
||||
.iter()
|
||||
.map(|(k, v)| ((*v), k.clone()))
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +147,7 @@ impl ExecState {
|
||||
artifact_commands: Default::default(),
|
||||
artifact_graph: Default::default(),
|
||||
errors: self.global.errors,
|
||||
filenames: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,11 +176,13 @@ impl ExecState {
|
||||
self.global.path_to_source_id.get(path).cloned()
|
||||
}
|
||||
|
||||
pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
|
||||
pub(super) fn add_path_to_source_id(&mut self, path: ModulePath, id: ModuleId) {
|
||||
debug_assert!(!self.global.path_to_source_id.contains_key(&path));
|
||||
|
||||
self.global.path_to_source_id.insert(path.clone(), id);
|
||||
}
|
||||
|
||||
pub(super) fn add_module(&mut self, id: ModuleId, path: ModulePath, repr: ModuleRepr) {
|
||||
debug_assert!(self.global.path_to_source_id.contains_key(&path));
|
||||
let module_info = ModuleInfo { id, repr, path };
|
||||
self.global.module_infos.insert(id, module_info);
|
||||
}
|
||||
@ -225,11 +234,15 @@ impl GlobalState {
|
||||
root_id,
|
||||
ModuleInfo {
|
||||
id: root_id,
|
||||
path: ModulePath::Local(root_path.clone()),
|
||||
path: ModulePath::Local {
|
||||
value: root_path.clone(),
|
||||
},
|
||||
repr: ModuleRepr::Root,
|
||||
},
|
||||
);
|
||||
global.path_to_source_id.insert(ModulePath::Local(root_path), root_id);
|
||||
global
|
||||
.path_to_source_id
|
||||
.insert(ModulePath::Local { value: root_path }, root_id);
|
||||
global
|
||||
}
|
||||
}
|
||||
|
@ -64,13 +64,13 @@ impl ModuleLoader {
|
||||
}
|
||||
|
||||
pub(crate) fn enter_module(&mut self, path: &ModulePath) {
|
||||
if let ModulePath::Local(ref path) = path {
|
||||
if let ModulePath::Local { value: ref path } = path {
|
||||
self.import_stack.push(path.clone());
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn leave_module(&mut self, path: &ModulePath) {
|
||||
if let ModulePath::Local(ref path) = path {
|
||||
if let ModulePath::Local { value: ref path } = path {
|
||||
let popped = self.import_stack.pop().unwrap();
|
||||
assert_eq!(path, &popped);
|
||||
}
|
||||
@ -119,31 +119,32 @@ pub enum ModuleRepr {
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize, Hash, ts_rs::TS)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum ModulePath {
|
||||
Local(PathBuf),
|
||||
Std(String),
|
||||
Local { value: PathBuf },
|
||||
Std { value: String },
|
||||
}
|
||||
|
||||
impl ModulePath {
|
||||
pub(crate) fn expect_path(&self) -> &PathBuf {
|
||||
match self {
|
||||
ModulePath::Local(p) => p,
|
||||
ModulePath::Local { value: p } => p,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn std_path(&self) -> Option<String> {
|
||||
match self {
|
||||
ModulePath::Local(_) => None,
|
||||
ModulePath::Std(p) => Some(p.clone()),
|
||||
ModulePath::Local { value: _ } => None,
|
||||
ModulePath::Std { value: p } => Some(p.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn source(&self, fs: &FileManager, source_range: SourceRange) -> Result<String, KclError> {
|
||||
match self {
|
||||
ModulePath::Local(p) => fs.read_to_string(p, source_range).await,
|
||||
ModulePath::Std(name) => read_std(name)
|
||||
ModulePath::Local { value: p } => fs.read_to_string(p, source_range).await,
|
||||
ModulePath::Std { value: name } => read_std(name)
|
||||
.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Cannot find standard library module to import: std::{name}."),
|
||||
@ -162,14 +163,14 @@ impl ModulePath {
|
||||
} else {
|
||||
std::path::PathBuf::from(path)
|
||||
};
|
||||
ModulePath::Local(resolved_path)
|
||||
ModulePath::Local { value: resolved_path }
|
||||
}
|
||||
ImportPath::Std { path } => {
|
||||
// For now we only support importing from singly-nested modules inside std.
|
||||
assert_eq!(path.len(), 2);
|
||||
assert_eq!(&path[0], "std");
|
||||
|
||||
ModulePath::Std(path[1].clone())
|
||||
ModulePath::Std { value: path[1].clone() }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -178,8 +179,8 @@ impl ModulePath {
|
||||
impl fmt::Display for ModulePath {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
ModulePath::Local(path) => path.display().fmt(f),
|
||||
ModulePath::Std(s) => write!(f, "std::{s}"),
|
||||
ModulePath::Local { value: path } => path.display().fmt(f),
|
||||
ModulePath::Std { value: s } => write!(f, "std::{s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1018,6 +1018,27 @@ mod sketch_on_face {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod revolve_about_edge {
|
||||
const TEST_NAME: &str = "revolve_about_edge";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod poop_chute {
|
||||
const TEST_NAME: &str = "poop_chute";
|
||||
|
||||
@ -1987,6 +2008,49 @@ mod array_elem_pop_fail {
|
||||
mod helix_simple {
|
||||
const TEST_NAME: &str = "helix_simple";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME);
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
|
||||
mod import_file_parse_error {
|
||||
const TEST_NAME: &str = "import_file_parse_error";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME);
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, true).await
|
||||
}
|
||||
}
|
||||
mod assembly_simplified_walkie {
|
||||
const TEST_NAME: &str = "assembly_simplified_walkie";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart assembly_simplified_walkie.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,219 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path3 [Path]
|
||||
3["Path<br>[1358, 1517, 3]"]
|
||||
4["Segment<br>[1523, 1618, 3]"]
|
||||
5["Segment<br>[1624, 1785, 3]"]
|
||||
6["Segment<br>[1791, 1886, 3]"]
|
||||
7["Segment<br>[1892, 2056, 3]"]
|
||||
8["Segment<br>[2062, 2158, 3]"]
|
||||
9["Segment<br>[2164, 2327, 3]"]
|
||||
10["Segment<br>[2333, 2428, 3]"]
|
||||
11["Segment<br>[2434, 2441, 3]"]
|
||||
12[Solid2d]
|
||||
end
|
||||
subgraph path41 [Path]
|
||||
41["Path<br>[1119, 1160, 5]"]
|
||||
42["Segment<br>[1168, 1263, 5]"]
|
||||
43["Segment<br>[1271, 1367, 5]"]
|
||||
44["Segment<br>[1375, 1461, 5]"]
|
||||
45["Segment<br>[1469, 1476, 5]"]
|
||||
46[Solid2d]
|
||||
end
|
||||
subgraph path63 [Path]
|
||||
63["Path<br>[503, 596, 4]"]
|
||||
64["Segment<br>[602, 639, 4]"]
|
||||
65["Segment<br>[645, 683, 4]"]
|
||||
66["Segment<br>[689, 727, 4]"]
|
||||
67["Segment<br>[733, 751, 4]"]
|
||||
68[Solid2d]
|
||||
end
|
||||
1["Plane<br>[358, 387, 3]"]
|
||||
2["Plane<br>[1322, 1351, 3]"]
|
||||
13["Sweep Extrusion<br>[2790, 2826, 3]"]
|
||||
14[Wall]
|
||||
15[Wall]
|
||||
16[Wall]
|
||||
17[Wall]
|
||||
18[Wall]
|
||||
19[Wall]
|
||||
20[Wall]
|
||||
21[Wall]
|
||||
22["Cap Start"]
|
||||
23["Cap End"]
|
||||
24["SweepEdge Opposite"]
|
||||
25["SweepEdge Adjacent"]
|
||||
26["SweepEdge Opposite"]
|
||||
27["SweepEdge Adjacent"]
|
||||
28["SweepEdge Opposite"]
|
||||
29["SweepEdge Adjacent"]
|
||||
30["SweepEdge Opposite"]
|
||||
31["SweepEdge Adjacent"]
|
||||
32["SweepEdge Opposite"]
|
||||
33["SweepEdge Adjacent"]
|
||||
34["SweepEdge Opposite"]
|
||||
35["SweepEdge Adjacent"]
|
||||
36["SweepEdge Opposite"]
|
||||
37["SweepEdge Adjacent"]
|
||||
38["SweepEdge Opposite"]
|
||||
39["SweepEdge Adjacent"]
|
||||
40["Plane<br>[405, 442, 0]"]
|
||||
47["Sweep Extrusion<br>[1495, 1542, 5]"]
|
||||
48[Wall]
|
||||
49[Wall]
|
||||
50[Wall]
|
||||
51[Wall]
|
||||
52["Cap Start"]
|
||||
53["Cap End"]
|
||||
54["SweepEdge Opposite"]
|
||||
55["SweepEdge Adjacent"]
|
||||
56["SweepEdge Opposite"]
|
||||
57["SweepEdge Adjacent"]
|
||||
58["SweepEdge Opposite"]
|
||||
59["SweepEdge Adjacent"]
|
||||
60["SweepEdge Opposite"]
|
||||
61["SweepEdge Adjacent"]
|
||||
62["Plane<br>[467, 497, 4]"]
|
||||
69["Sweep Extrusion<br>[797, 849, 4]"]
|
||||
70[Wall]
|
||||
71[Wall]
|
||||
72[Wall]
|
||||
73[Wall]
|
||||
74["Cap Start"]
|
||||
75["Cap End"]
|
||||
76["SweepEdge Opposite"]
|
||||
77["SweepEdge Adjacent"]
|
||||
78["SweepEdge Opposite"]
|
||||
79["SweepEdge Adjacent"]
|
||||
80["SweepEdge Opposite"]
|
||||
81["SweepEdge Adjacent"]
|
||||
82["SweepEdge Opposite"]
|
||||
83["SweepEdge Adjacent"]
|
||||
2 --- 3
|
||||
3 --- 4
|
||||
3 --- 5
|
||||
3 --- 6
|
||||
3 --- 7
|
||||
3 --- 8
|
||||
3 --- 9
|
||||
3 --- 10
|
||||
3 --- 11
|
||||
3 ---- 13
|
||||
3 --- 12
|
||||
4 --- 21
|
||||
4 --- 38
|
||||
4 --- 39
|
||||
5 --- 20
|
||||
5 --- 36
|
||||
5 --- 37
|
||||
6 --- 19
|
||||
6 --- 34
|
||||
6 --- 35
|
||||
7 --- 18
|
||||
7 --- 32
|
||||
7 --- 33
|
||||
8 --- 17
|
||||
8 --- 30
|
||||
8 --- 31
|
||||
9 --- 16
|
||||
9 --- 28
|
||||
9 --- 29
|
||||
10 --- 15
|
||||
10 --- 26
|
||||
10 --- 27
|
||||
11 --- 14
|
||||
11 --- 24
|
||||
11 --- 25
|
||||
13 --- 14
|
||||
13 --- 15
|
||||
13 --- 16
|
||||
13 --- 17
|
||||
13 --- 18
|
||||
13 --- 19
|
||||
13 --- 20
|
||||
13 --- 21
|
||||
13 --- 22
|
||||
13 --- 23
|
||||
13 --- 24
|
||||
13 --- 25
|
||||
13 --- 26
|
||||
13 --- 27
|
||||
13 --- 28
|
||||
13 --- 29
|
||||
13 --- 30
|
||||
13 --- 31
|
||||
13 --- 32
|
||||
13 --- 33
|
||||
13 --- 34
|
||||
13 --- 35
|
||||
13 --- 36
|
||||
13 --- 37
|
||||
13 --- 38
|
||||
13 --- 39
|
||||
40 --- 41
|
||||
41 --- 42
|
||||
41 --- 43
|
||||
41 --- 44
|
||||
41 --- 45
|
||||
41 ---- 47
|
||||
41 --- 46
|
||||
42 --- 48
|
||||
42 --- 54
|
||||
42 --- 55
|
||||
43 --- 49
|
||||
43 --- 56
|
||||
43 --- 57
|
||||
44 --- 50
|
||||
44 --- 58
|
||||
44 --- 59
|
||||
45 --- 51
|
||||
45 --- 60
|
||||
45 --- 61
|
||||
47 --- 48
|
||||
47 --- 49
|
||||
47 --- 50
|
||||
47 --- 51
|
||||
47 --- 52
|
||||
47 --- 53
|
||||
47 --- 54
|
||||
47 --- 55
|
||||
47 --- 56
|
||||
47 --- 57
|
||||
47 --- 58
|
||||
47 --- 59
|
||||
47 --- 60
|
||||
47 --- 61
|
||||
62 --- 63
|
||||
63 --- 64
|
||||
63 --- 65
|
||||
63 --- 66
|
||||
63 --- 67
|
||||
63 ---- 69
|
||||
63 --- 68
|
||||
64 --- 73
|
||||
64 --- 82
|
||||
64 --- 83
|
||||
65 --- 72
|
||||
65 --- 80
|
||||
65 --- 81
|
||||
66 --- 71
|
||||
66 --- 78
|
||||
66 --- 79
|
||||
67 --- 70
|
||||
67 --- 76
|
||||
67 --- 77
|
||||
69 --- 70
|
||||
69 --- 71
|
||||
69 --- 72
|
||||
69 --- 73
|
||||
69 --- 74
|
||||
69 --- 75
|
||||
69 --- 76
|
||||
69 --- 77
|
||||
69 --- 78
|
||||
69 --- 79
|
||||
69 --- 80
|
||||
69 --- 81
|
||||
69 --- 82
|
||||
69 --- 83
|
||||
```
|
428
src/wasm-lib/kcl/tests/assembly_simplified_walkie/ast.snap
Normal file
@ -0,0 +1,428 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing assembly_simplified_walkie.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"end": 112,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "case.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "None",
|
||||
"alias": null
|
||||
},
|
||||
"start": 95,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 151,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "talk-button.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "None",
|
||||
"alias": {
|
||||
"end": 151,
|
||||
"name": "talkButton",
|
||||
"start": 141,
|
||||
"type": "Identifier"
|
||||
}
|
||||
},
|
||||
"start": 113,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 183,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "button.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "List",
|
||||
"items": [
|
||||
{
|
||||
"alias": null,
|
||||
"end": 165,
|
||||
"name": {
|
||||
"end": 165,
|
||||
"name": "button",
|
||||
"start": 159,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 159,
|
||||
"type": "ImportItem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 152,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 289,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "globals.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "List",
|
||||
"items": [
|
||||
{
|
||||
"alias": null,
|
||||
"end": 196,
|
||||
"name": {
|
||||
"end": 196,
|
||||
"name": "width",
|
||||
"start": 191,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 191,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 204,
|
||||
"name": {
|
||||
"end": 204,
|
||||
"name": "height",
|
||||
"start": 198,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 198,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 215,
|
||||
"name": {
|
||||
"end": 215,
|
||||
"name": "thickness",
|
||||
"start": 206,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 206,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 228,
|
||||
"name": {
|
||||
"end": 228,
|
||||
"name": "screenWidth",
|
||||
"start": 217,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 217,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 242,
|
||||
"name": {
|
||||
"end": 242,
|
||||
"name": "screenHeight",
|
||||
"start": 230,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 230,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 259,
|
||||
"name": {
|
||||
"end": 259,
|
||||
"name": "screenYPosition",
|
||||
"start": 244,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 244,
|
||||
"type": "ImportItem"
|
||||
},
|
||||
{
|
||||
"alias": null,
|
||||
"end": 270,
|
||||
"name": {
|
||||
"end": 270,
|
||||
"name": "tolerance",
|
||||
"start": 261,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 261,
|
||||
"type": "ImportItem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 184,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
},
|
||||
{
|
||||
"end": 314,
|
||||
"expression": {
|
||||
"end": 314,
|
||||
"name": "case",
|
||||
"start": 310,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 310,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"end": 443,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"argument": {
|
||||
"end": 378,
|
||||
"left": {
|
||||
"end": 366,
|
||||
"left": {
|
||||
"end": 362,
|
||||
"name": "screenWidth",
|
||||
"start": 351,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "/",
|
||||
"right": {
|
||||
"end": 366,
|
||||
"raw": "2",
|
||||
"start": 365,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 2.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"start": 351,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"operator": "+",
|
||||
"right": {
|
||||
"end": 378,
|
||||
"name": "tolerance",
|
||||
"start": 369,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 351,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"end": 378,
|
||||
"operator": "-",
|
||||
"start": 349,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 398,
|
||||
"name": "screenYPosition",
|
||||
"start": 383,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
],
|
||||
"end": 400,
|
||||
"start": 345,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 403,
|
||||
"raw": "0",
|
||||
"start": 402,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"type": "LabeledArg",
|
||||
"label": {
|
||||
"type": "Identifier",
|
||||
"name": "offset"
|
||||
},
|
||||
"arg": {
|
||||
"end": 441,
|
||||
"name": "thickness",
|
||||
"start": 432,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 416,
|
||||
"name": "offsetPlane",
|
||||
"start": 405,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 442,
|
||||
"start": 405,
|
||||
"type": "CallExpressionKw",
|
||||
"type": "CallExpressionKw",
|
||||
"unlabeled": {
|
||||
"end": 421,
|
||||
"raw": "\"XZ\"",
|
||||
"start": 417,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "XZ"
|
||||
}
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 344,
|
||||
"name": "button",
|
||||
"start": 338,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 443,
|
||||
"start": 338,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 338,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"end": 481,
|
||||
"expression": {
|
||||
"end": 481,
|
||||
"name": "talkButton",
|
||||
"start": 471,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 471,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 482,
|
||||
"innerAttrs": [
|
||||
{
|
||||
"end": 33,
|
||||
"name": {
|
||||
"end": 9,
|
||||
"name": "settings",
|
||||
"start": 1,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"end": 32,
|
||||
"key": {
|
||||
"end": 27,
|
||||
"name": "defaultLengthUnit",
|
||||
"start": 10,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 10,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 32,
|
||||
"name": "in",
|
||||
"start": 30,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"start": 0,
|
||||
"type": "Annotation"
|
||||
}
|
||||
],
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"3": [
|
||||
{
|
||||
"end": 309,
|
||||
"start": 289,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLineBlockComment",
|
||||
"value": "Import the case",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
],
|
||||
"4": [
|
||||
{
|
||||
"end": 337,
|
||||
"start": 314,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLineBlockComment",
|
||||
"value": "Import the buttons",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
],
|
||||
"5": [
|
||||
{
|
||||
"end": 470,
|
||||
"start": 443,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLineBlockComment",
|
||||
"value": "Import the talk button",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": [
|
||||
{
|
||||
"end": 36,
|
||||
"start": 33,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 64,
|
||||
"start": 36,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "blockComment",
|
||||
"value": "Part of the Walkie Talkie",
|
||||
"style": "line"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 94,
|
||||
"start": 65,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "blockComment",
|
||||
"value": "Import parts and constants",
|
||||
"style": "line"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
74
src/wasm-lib/kcl/tests/assembly_simplified_walkie/button.kcl
Normal file
@ -0,0 +1,74 @@
|
||||
// Walkie Talkie button
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
// Import constants
|
||||
//import screenHeight, buttonWidth, tolerance, buttonHeight, buttonThickness from 'globals.kcl'
|
||||
export height = 4
|
||||
export width = 2.5
|
||||
export thickness = 1
|
||||
export chamferLength = .325
|
||||
export offset = .125
|
||||
export screenWidth = 1.75
|
||||
export screenHeight = 1
|
||||
export screenYPosition = height / 2 - 0.75
|
||||
export screenDepth = -.0625
|
||||
export speakerBoxWidth = 1.25
|
||||
export speakerBoxHeight = 1.25
|
||||
|
||||
// antenna
|
||||
export antennaBaseWidth = .5
|
||||
export antennaBaseHeight = .25
|
||||
export antennaTopWidth = .30
|
||||
export antennaTopHeight = .05
|
||||
|
||||
// button
|
||||
export buttonWidth = .15
|
||||
export tolerance = 0.020
|
||||
export buttonHeight = screenHeight / 2 - tolerance
|
||||
export buttonThickness = .040
|
||||
|
||||
// case
|
||||
export squareHoleSideLength = 0.0625
|
||||
export caseTolerance = 0.010
|
||||
|
||||
// knob
|
||||
export knobDiameter = .5
|
||||
export knobHeight = .25
|
||||
export knobRadius = 0.050
|
||||
|
||||
// talk-button
|
||||
export talkButtonSideLength = 0.5
|
||||
export talkButtonHeight = 0.050
|
||||
|
||||
|
||||
// Create a function for the button
|
||||
export fn button(origin, rotation, plane) {
|
||||
buttonSketch = startSketchOn(plane)
|
||||
|> startProfileAt([origin[0], origin[1]], %)
|
||||
|> angledLine({
|
||||
angle = 180 + rotation,
|
||||
length = buttonWidth
|
||||
}, %, $tag1)
|
||||
|> angledLine({
|
||||
angle = 270 + rotation,
|
||||
length = buttonHeight
|
||||
}, %, $tag2)
|
||||
|> angledLine({
|
||||
angle = 0 + rotation,
|
||||
length = buttonWidth
|
||||
}, %)
|
||||
|> close()
|
||||
buttonExtrude = extrude(buttonSketch, length = buttonThickness)
|
||||
|> chamfer(
|
||||
length = .050,
|
||||
tags = [
|
||||
getNextAdjacentEdge(tag1),
|
||||
getNextAdjacentEdge(tag2)
|
||||
]
|
||||
)
|
||||
|> appearance(color = "#ff0000")
|
||||
|
||||
return buttonExtrude
|
||||
}
|
85
src/wasm-lib/kcl/tests/assembly_simplified_walkie/case.kcl
Normal file
@ -0,0 +1,85 @@
|
||||
// Walkie talkie case
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import constants and Zoo logo
|
||||
import width, height, chamferLength, offset, screenWidth, screenHeight, screenYPosition, screenDepth, speakerBoxWidth, speakerBoxHeight, squareHoleSideLength, caseTolerance from "globals.kcl"
|
||||
// import zLogo, oLogo, oLogo2 from "zoo-logo.kcl"
|
||||
|
||||
plane = offsetPlane("XZ", offset = 1)
|
||||
|
||||
fn screenHole(sketchStart) {
|
||||
sketch006 = startSketchOn(sketchStart)
|
||||
|> startProfileAt([-screenWidth / 2, screenYPosition], %)
|
||||
|> xLine(screenWidth, %)
|
||||
|> yLine(-screenHeight, %)
|
||||
|> xLine(-screenWidth, %)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
return sketch006
|
||||
}
|
||||
|
||||
fn squareHolePattern(plane, x, y) {
|
||||
fn transformX(i) {
|
||||
return { translate = [.125 * i, 0] }
|
||||
}
|
||||
fn transformY(i) {
|
||||
return { translate = [0, -.125 * i] }
|
||||
}
|
||||
squareHolePatternSketch = startSketchOn(plane)
|
||||
|> startProfileAt([-x, -y], %)
|
||||
|> line(end = [squareHoleSideLength / 2, 0])
|
||||
|> line(end = [0, -squareHoleSideLength / 2])
|
||||
|> line(end = [-squareHoleSideLength / 2, 0])
|
||||
|> close()
|
||||
|> patternTransform2d(instances = 13, transform = transformX)
|
||||
|> patternTransform2d(instances = 11, transform = transformY)
|
||||
return squareHolePatternSketch
|
||||
}
|
||||
sketch005 = startSketchOn(offsetPlane("XZ", offset = 1))
|
||||
|> startProfileAt([
|
||||
-width / 2 + offset + caseTolerance,
|
||||
height / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45)))
|
||||
], %)
|
||||
|> angledLineToY({
|
||||
angle = 45,
|
||||
to = height / 2 - (offset + caseTolerance)
|
||||
}, %)
|
||||
|> line(endAbsolute = [
|
||||
width / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45))),
|
||||
height / 2 - (offset + caseTolerance)
|
||||
])
|
||||
|> angledLineToX({
|
||||
angle = -45,
|
||||
to = width / 2 - (offset + caseTolerance)
|
||||
}, %)
|
||||
|> line(endAbsolute = [
|
||||
width / 2 - (offset + caseTolerance),
|
||||
-(height / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45))))
|
||||
])
|
||||
|> angledLineToY({
|
||||
angle = -135,
|
||||
to = -height / 2 + offset + caseTolerance
|
||||
}, %)
|
||||
|> line(endAbsolute = [
|
||||
-(width / 2 - (chamferLength + (offset + caseTolerance) / 2 * cos(toRadians(45)))),
|
||||
-height / 2 + offset + caseTolerance
|
||||
])
|
||||
|> angledLineToX({
|
||||
angle = -225,
|
||||
to = -width / 2 + offset + caseTolerance
|
||||
}, %)
|
||||
|> close()
|
||||
// |> hole(screenHole(plane), %)
|
||||
// |> hole(squareHolePattern(plane, .75, .125), %)
|
||||
// |> hole(zLogo(plane, [-.30, -1.825], .20), %)
|
||||
// |> hole(oLogo(plane, [-.075, -1.825], .20), %)
|
||||
// |> hole(oLogo2(plane, [-.075, -1.825], .20), %)
|
||||
// |> hole(oLogo(plane, [.175, -1.825], .20), %)
|
||||
// |> hole(oLogo2(plane, [.175, -1.825], .20), %)
|
||||
|
||||
extrude(sketch005, length = -0.0625)
|
||||
|> appearance(color = '#D0FF01', metalness = 0, roughness = 50)
|
@ -0,0 +1,14 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Error from executing assembly_simplified_walkie.kcl
|
||||
---
|
||||
KCL Semantic error
|
||||
|
||||
× semantic: Error loading imported file. Open it to view more details.
|
||||
│ tests/assembly_simplified_walkie/talk-button.kcl: Modeling command failed:
|
||||
│ [ApiError { error_code: BadRequest, message: "Chamfer failed" }]
|
||||
╭─[21:1]
|
||||
20 │ // Import the talk button
|
||||
21 │ talkButton
|
||||
· ──────────
|
||||
╰────
|
@ -0,0 +1,42 @@
|
||||
// Global constants for the walkie talkie
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
// body
|
||||
export height = 4
|
||||
export width = 2.5
|
||||
export thickness = 1
|
||||
export chamferLength = .325
|
||||
export offset = .125
|
||||
export screenWidth = 1.75
|
||||
export screenHeight = 1
|
||||
export screenYPosition = height / 2 - 0.75
|
||||
export screenDepth = -.0625
|
||||
export speakerBoxWidth = 1.25
|
||||
export speakerBoxHeight = 1.25
|
||||
|
||||
// antenna
|
||||
export antennaBaseWidth = .5
|
||||
export antennaBaseHeight = .25
|
||||
export antennaTopWidth = .30
|
||||
export antennaTopHeight = .05
|
||||
|
||||
// button
|
||||
export buttonWidth = .15
|
||||
export tolerance = 0.020
|
||||
export buttonHeight = screenHeight / 2 - tolerance
|
||||
export buttonThickness = .040
|
||||
|
||||
// case
|
||||
export squareHoleSideLength = 0.0625
|
||||
export caseTolerance = 0.010
|
||||
|
||||
// knob
|
||||
export knobDiameter = .5
|
||||
export knobHeight = .25
|
||||
export knobRadius = 0.050
|
||||
|
||||
// talk-button
|
||||
export talkButtonSideLength = 0.5
|
||||
export talkButtonHeight = 0.050
|
21
src/wasm-lib/kcl/tests/assembly_simplified_walkie/input.kcl
Normal file
@ -0,0 +1,21 @@
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Part of the Walkie Talkie
|
||||
// Import parts and constants
|
||||
import "case.kcl"
|
||||
import "talk-button.kcl" as talkButton
|
||||
import button from "button.kcl"
|
||||
import width, height, thickness, screenWidth, screenHeight, screenYPosition, tolerance from "globals.kcl"
|
||||
|
||||
// Import the case
|
||||
case
|
||||
|
||||
// Import the buttons
|
||||
button([
|
||||
-(screenWidth / 2 + tolerance),
|
||||
screenYPosition
|
||||
], 0, offsetPlane("XZ", offset = thickness))
|
||||
|
||||
// Import the talk button
|
||||
talkButton
|
198
src/wasm-lib/kcl/tests/assembly_simplified_walkie/ops.snap
Normal file
@ -0,0 +1,198 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed assembly_simplified_walkie.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"labeledArgs": {
|
||||
"offset": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Inches"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
432,
|
||||
441,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "offsetPlane",
|
||||
"sourceRange": [
|
||||
405,
|
||||
442,
|
||||
0
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "String",
|
||||
"value": "XZ"
|
||||
},
|
||||
"sourceRange": [
|
||||
417,
|
||||
421,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UserDefinedFunctionCall",
|
||||
"name": "button",
|
||||
"functionSourceRange": [
|
||||
1046,
|
||||
1759,
|
||||
5
|
||||
],
|
||||
"unlabeledArg": null,
|
||||
"labeledArgs": {},
|
||||
"sourceRange": [
|
||||
338,
|
||||
443,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "Plane",
|
||||
"artifact_id": "[uuid]"
|
||||
},
|
||||
"sourceRange": [
|
||||
1105,
|
||||
1110,
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [
|
||||
1091,
|
||||
1111,
|
||||
5
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 0.04,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Inches"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
1526,
|
||||
1541,
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "extrude",
|
||||
"sourceRange": [
|
||||
1495,
|
||||
1542,
|
||||
5
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
1503,
|
||||
1515,
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"length": {
|
||||
"value": {
|
||||
"type": "Number",
|
||||
"value": 0.05,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Inches"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
1577,
|
||||
1581,
|
||||
5
|
||||
]
|
||||
},
|
||||
"tags": {
|
||||
"value": {
|
||||
"type": "Array",
|
||||
"value": [
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
},
|
||||
{
|
||||
"type": "Uuid",
|
||||
"value": "[uuid]"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sourceRange": [
|
||||
1599,
|
||||
1686,
|
||||
5
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "chamfer",
|
||||
"sourceRange": [
|
||||
1550,
|
||||
1695,
|
||||
5
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": {
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "UserDefinedFunctionReturn"
|
||||
}
|
||||
]
|
@ -0,0 +1,46 @@
|
||||
// Walkie talkie talk button
|
||||
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in)
|
||||
|
||||
|
||||
// Import constants
|
||||
import width, thickness, talkButtonSideLength, talkButtonHeight from "globals.kcl"
|
||||
|
||||
talkButtonPlane = {
|
||||
plane = {
|
||||
origin = {
|
||||
x = width / 2,
|
||||
y = -thickness / 2,
|
||||
z = .5
|
||||
},
|
||||
xAxis = { x = 0, y = 1, z = 0 },
|
||||
yAxis = { x = 0, y = 0, z = 1 },
|
||||
zAxis = { x = 1, y = 0, z = 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// Create the talk button sketch
|
||||
talkButtonSketch = startSketchOn(talkButtonPlane)
|
||||
|> startProfileAt([
|
||||
-talkButtonSideLength / 2,
|
||||
talkButtonSideLength / 2
|
||||
], %)
|
||||
|> xLine(talkButtonSideLength, %, $tag1)
|
||||
|> yLine(-talkButtonSideLength, %, $tag2)
|
||||
|> xLine(-talkButtonSideLength, %, $tag3)
|
||||
|> close(tag = $tag4)
|
||||
|
||||
// Create the talk button and apply fillets
|
||||
extrude(talkButtonSketch, length = talkButtonHeight)
|
||||
|> fillet(
|
||||
radius = 0.050,
|
||||
tags = [
|
||||
getNextAdjacentEdge(tag1),
|
||||
getNextAdjacentEdge(tag2),
|
||||
getNextAdjacentEdge(tag3),
|
||||
getNextAdjacentEdge(tag4)
|
||||
]
|
||||
)
|
||||
|> appearance(color = '#D0FF01', metalness = 90, roughness = 90)
|
@ -0,0 +1,284 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact commands import_file_parse_error.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.7,
|
||||
"g": 0.28,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.7,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.28,
|
||||
"b": 0.7,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart import_file_parse_error.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,3 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
```
|
39
src/wasm-lib/kcl/tests/import_file_parse_error/ast.snap
Normal file
@ -0,0 +1,39 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing import_file_parse_error.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"end": 38,
|
||||
"path": {
|
||||
"type": "Kcl",
|
||||
"filename": "parse-failure.kcl"
|
||||
},
|
||||
"selector": {
|
||||
"type": "List",
|
||||
"items": [
|
||||
{
|
||||
"alias": null,
|
||||
"end": 13,
|
||||
"name": {
|
||||
"end": 13,
|
||||
"name": "hotdog",
|
||||
"start": 7,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 7,
|
||||
"type": "ImportItem"
|
||||
}
|
||||
]
|
||||
},
|
||||
"start": 0,
|
||||
"type": "ImportStatement",
|
||||
"type": "ImportStatement"
|
||||
}
|
||||
],
|
||||
"end": 39,
|
||||
"start": 0
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Error from executing import_file_parse_error.kcl
|
||||
---
|
||||
KCL Syntax error
|
||||
|
||||
× syntax: Unexpected token: }
|
||||
╭────
|
||||
1 │ import hotdog from "parse-failure.kcl"
|
||||
· ─
|
||||
╰────
|
1
src/wasm-lib/kcl/tests/import_file_parse_error/input.kcl
Normal file
@ -0,0 +1 @@
|
||||
import hotdog from "parse-failure.kcl"
|
5
src/wasm-lib/kcl/tests/import_file_parse_error/ops.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed import_file_parse_error.kcl
|
||||
---
|
||||
[]
|
@ -0,0 +1,3 @@
|
||||
export fn hotdog () {
|
||||
return
|
||||
}
|
571
src/wasm-lib/kcl/tests/revolve_about_edge/artifact_commands.snap
Normal file
@ -0,0 +1,571 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact commands revolve_about_edge.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.7,
|
||||
"g": 0.28,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.7,
|
||||
"b": 0.28,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "plane_set_color",
|
||||
"plane_id": "[uuid]",
|
||||
"color": {
|
||||
"r": 0.28,
|
||||
"g": 0.28,
|
||||
"b": 0.7,
|
||||
"a": 0.4
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "make_plane",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"x_axis": {
|
||||
"x": 0.0,
|
||||
"y": -1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"y_axis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"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": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"size": 100.0,
|
||||
"clobber": false,
|
||||
"hide": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "edge_lines_visible",
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "set_scene_units",
|
||||
"unit": "mm"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_visible",
|
||||
"object_id": "[uuid]",
|
||||
"hidden": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
12,
|
||||
31,
|
||||
0
|
||||
],
|
||||
"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": [
|
||||
37,
|
||||
65,
|
||||
0
|
||||
],
|
||||
"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": [
|
||||
37,
|
||||
65,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
37,
|
||||
65,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -25.0,
|
||||
"y": 25.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
71,
|
||||
107,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "line",
|
||||
"end": {
|
||||
"x": 0.0,
|
||||
"y": -50.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"relative": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
120,
|
||||
139,
|
||||
0
|
||||
],
|
||||
"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": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"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": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "start_path"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "move_path_pen",
|
||||
"path": "[uuid]",
|
||||
"to": {
|
||||
"x": -40.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "extend_path",
|
||||
"path": "[uuid]",
|
||||
"segment": {
|
||||
"type": "arc",
|
||||
"center": {
|
||||
"x": -50.0,
|
||||
"y": 0.0
|
||||
},
|
||||
"radius": 10.0,
|
||||
"start": {
|
||||
"unit": "degrees",
|
||||
"value": 0.0
|
||||
},
|
||||
"end": {
|
||||
"unit": "degrees",
|
||||
"value": 360.0
|
||||
},
|
||||
"relative": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "close_path",
|
||||
"path_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "revolve_about_edge",
|
||||
"target": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"angle": {
|
||||
"unit": "degrees",
|
||||
"value": 90.0
|
||||
},
|
||||
"tolerance": 0.0000001
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "object_bring_to_front",
|
||||
"object_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "solid3d_get_extrusion_face_info",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "solid3d_get_opposite_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmdId": "[uuid]",
|
||||
"range": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"command": {
|
||||
"type": "solid3d_get_next_adjacent_edge",
|
||||
"object_id": "[uuid]",
|
||||
"edge_id": "[uuid]",
|
||||
"face_id": "[uuid]"
|
||||
}
|
||||
}
|
||||
]
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Artifact graph flowchart revolve_about_edge.kcl
|
||||
extension: md
|
||||
snapshot_kind: binary
|
||||
---
|
@ -0,0 +1,34 @@
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph path2 [Path]
|
||||
2["Path<br>[37, 65, 0]"]
|
||||
3["Segment<br>[71, 107, 0]"]
|
||||
end
|
||||
subgraph path5 [Path]
|
||||
5["Path<br>[145, 190, 0]"]
|
||||
6["Segment<br>[145, 190, 0]"]
|
||||
7[Solid2d]
|
||||
end
|
||||
1["Plane<br>[12, 31, 0]"]
|
||||
4["Plane<br>[120, 139, 0]"]
|
||||
8["Sweep RevolveAboutEdge<br>[196, 270, 0]"]
|
||||
9[Wall]
|
||||
10["Cap Start"]
|
||||
11["Cap End"]
|
||||
12["SweepEdge Opposite"]
|
||||
13["SweepEdge Adjacent"]
|
||||
1 --- 2
|
||||
2 --- 3
|
||||
4 --- 5
|
||||
5 --- 6
|
||||
5 ---- 8
|
||||
5 --- 7
|
||||
6 --- 9
|
||||
6 --- 12
|
||||
6 --- 13
|
||||
8 --- 9
|
||||
8 --- 10
|
||||
8 --- 11
|
||||
8 --- 12
|
||||
8 --- 13
|
||||
```
|
376
src/wasm-lib/kcl/tests/revolve_about_edge/ast.snap
Normal file
@ -0,0 +1,376 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing revolve_about_edge.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 107,
|
||||
"id": {
|
||||
"end": 9,
|
||||
"name": "sketch001",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"body": [
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 30,
|
||||
"raw": "'XY'",
|
||||
"start": 26,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "XY"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 25,
|
||||
"name": "startSketchOn",
|
||||
"start": 12,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 31,
|
||||
"start": 12,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"elements": [
|
||||
{
|
||||
"argument": {
|
||||
"end": 56,
|
||||
"raw": "25",
|
||||
"start": 54,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 25.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"end": 56,
|
||||
"operator": "-",
|
||||
"start": 53,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 60,
|
||||
"raw": "25",
|
||||
"start": 58,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 25.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"end": 61,
|
||||
"start": 52,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
},
|
||||
{
|
||||
"end": 64,
|
||||
"start": 63,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 51,
|
||||
"name": "startProfileAt",
|
||||
"start": 37,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 65,
|
||||
"start": 37,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"argument": {
|
||||
"end": 80,
|
||||
"raw": "50",
|
||||
"start": 78,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 50.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"end": 80,
|
||||
"operator": "-",
|
||||
"start": 77,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 83,
|
||||
"start": 82,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
},
|
||||
{
|
||||
"end": 106,
|
||||
"start": 85,
|
||||
"type": "TagDeclarator",
|
||||
"type": "TagDeclarator",
|
||||
"value": "rectangleSegmentB001"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 76,
|
||||
"name": "yLine",
|
||||
"start": 71,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 107,
|
||||
"start": 71,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
}
|
||||
],
|
||||
"end": 107,
|
||||
"start": 12,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 107,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 270,
|
||||
"id": {
|
||||
"end": 117,
|
||||
"name": "sketch002",
|
||||
"start": 108,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"body": [
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 138,
|
||||
"raw": "'XY'",
|
||||
"start": 134,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "XY"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 133,
|
||||
"name": "startSketchOn",
|
||||
"start": 120,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 139,
|
||||
"start": 120,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 186,
|
||||
"properties": [
|
||||
{
|
||||
"end": 171,
|
||||
"key": {
|
||||
"end": 160,
|
||||
"name": "center",
|
||||
"start": 154,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 154,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"elements": [
|
||||
{
|
||||
"argument": {
|
||||
"end": 167,
|
||||
"raw": "50",
|
||||
"start": 165,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 50.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
},
|
||||
"end": 167,
|
||||
"operator": "-",
|
||||
"start": 164,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 170,
|
||||
"raw": "0",
|
||||
"start": 169,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 0.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
],
|
||||
"end": 171,
|
||||
"start": 163,
|
||||
"type": "ArrayExpression",
|
||||
"type": "ArrayExpression"
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 184,
|
||||
"key": {
|
||||
"end": 179,
|
||||
"name": "radius",
|
||||
"start": 173,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 173,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 184,
|
||||
"raw": "10",
|
||||
"start": 182,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 10.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"start": 152,
|
||||
"type": "ObjectExpression",
|
||||
"type": "ObjectExpression"
|
||||
},
|
||||
{
|
||||
"end": 189,
|
||||
"start": 188,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 151,
|
||||
"name": "circle",
|
||||
"start": 145,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 190,
|
||||
"start": 145,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
{
|
||||
"arguments": [
|
||||
{
|
||||
"end": 266,
|
||||
"properties": [
|
||||
{
|
||||
"end": 223,
|
||||
"key": {
|
||||
"end": 218,
|
||||
"name": "angle",
|
||||
"start": 213,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 213,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 223,
|
||||
"raw": "90",
|
||||
"start": 221,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": {
|
||||
"value": 90.0,
|
||||
"suffix": "None"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"end": 259,
|
||||
"key": {
|
||||
"end": 236,
|
||||
"name": "axis",
|
||||
"start": 232,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 232,
|
||||
"type": "ObjectProperty",
|
||||
"value": {
|
||||
"end": 259,
|
||||
"name": "rectangleSegmentB001",
|
||||
"start": 239,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
}
|
||||
}
|
||||
],
|
||||
"start": 204,
|
||||
"type": "ObjectExpression",
|
||||
"type": "ObjectExpression"
|
||||
},
|
||||
{
|
||||
"end": 269,
|
||||
"start": 268,
|
||||
"type": "PipeSubstitution",
|
||||
"type": "PipeSubstitution"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 203,
|
||||
"name": "revolve",
|
||||
"start": 196,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 270,
|
||||
"start": 196,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
}
|
||||
],
|
||||
"end": 270,
|
||||
"start": 120,
|
||||
"type": "PipeExpression",
|
||||
"type": "PipeExpression"
|
||||
},
|
||||
"start": 108,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 270,
|
||||
"kind": "const",
|
||||
"start": 108,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"end": 271,
|
||||
"start": 0
|
||||
}
|
||||
}
|
9
src/wasm-lib/kcl/tests/revolve_about_edge/input.kcl
Normal file
@ -0,0 +1,9 @@
|
||||
sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-25, 25], %)
|
||||
|> yLine(-50, %, $rectangleSegmentB001)
|
||||
sketch002 = startSketchOn('XY')
|
||||
|> circle({ center = [-50, 0], radius = 10 }, %)
|
||||
|> revolve({
|
||||
angle = 90,
|
||||
axis = rectangleSegmentB001
|
||||
}, %)
|
107
src/wasm-lib/kcl/tests/revolve_about_edge/ops.snap
Normal file
@ -0,0 +1,107 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed revolve_about_edge.kcl
|
||||
---
|
||||
[
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "String",
|
||||
"value": "XY"
|
||||
},
|
||||
"sourceRange": [
|
||||
26,
|
||||
30,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [
|
||||
12,
|
||||
31,
|
||||
0
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "String",
|
||||
"value": "XY"
|
||||
},
|
||||
"sourceRange": [
|
||||
134,
|
||||
138,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "startSketchOn",
|
||||
"sourceRange": [
|
||||
120,
|
||||
139,
|
||||
0
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
},
|
||||
{
|
||||
"labeledArgs": {
|
||||
"data": {
|
||||
"value": {
|
||||
"type": "Object",
|
||||
"value": {
|
||||
"angle": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"ty": {
|
||||
"type": "Default",
|
||||
"len": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"angle": {
|
||||
"type": "Degrees"
|
||||
}
|
||||
}
|
||||
},
|
||||
"axis": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "rectangleSegmentB001",
|
||||
"artifact_id": "[uuid]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
204,
|
||||
266,
|
||||
0
|
||||
]
|
||||
},
|
||||
"sketch": {
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"artifactId": "[uuid]"
|
||||
}
|
||||
},
|
||||
"sourceRange": [
|
||||
268,
|
||||
269,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"name": "revolve",
|
||||
"sourceRange": [
|
||||
196,
|
||||
270,
|
||||
0
|
||||
],
|
||||
"type": "StdLibCall",
|
||||
"unlabeledArg": null
|
||||
}
|
||||
]
|
343
src/wasm-lib/kcl/tests/revolve_about_edge/program_memory.snap
Normal file
@ -0,0 +1,343 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Variables in memory after executing revolve_about_edge.kcl
|
||||
---
|
||||
{
|
||||
"rectangleSegmentB001": {
|
||||
"type": "TagIdentifier",
|
||||
"type": "TagIdentifier",
|
||||
"value": "rectangleSegmentB001",
|
||||
"info": {
|
||||
"type": "TagEngineInfo",
|
||||
"id": "[uuid]",
|
||||
"sketch": "[uuid]",
|
||||
"path": {
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
71,
|
||||
107,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
-25.0,
|
||||
25.0
|
||||
],
|
||||
"tag": {
|
||||
"end": 106,
|
||||
"start": 85,
|
||||
"type": "TagDeclarator",
|
||||
"value": "rectangleSegmentB001"
|
||||
},
|
||||
"to": [
|
||||
-25.0,
|
||||
-25.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"surface": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
85,
|
||||
106,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"sketch001": {
|
||||
"type": "Sketch",
|
||||
"value": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
71,
|
||||
107,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
-25.0,
|
||||
25.0
|
||||
],
|
||||
"tag": {
|
||||
"end": 106,
|
||||
"start": 85,
|
||||
"type": "TagDeclarator",
|
||||
"value": "rectangleSegmentB001"
|
||||
},
|
||||
"to": [
|
||||
-25.0,
|
||||
-25.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-25.0,
|
||||
25.0
|
||||
],
|
||||
"to": [
|
||||
-25.0,
|
||||
25.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
37,
|
||||
65,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"tags": {
|
||||
"rectangleSegmentB001": {
|
||||
"type": "TagIdentifier",
|
||||
"value": "rectangleSegmentB001",
|
||||
"info": {
|
||||
"type": "TagEngineInfo",
|
||||
"id": "[uuid]",
|
||||
"sketch": "[uuid]",
|
||||
"path": {
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
71,
|
||||
107,
|
||||
0
|
||||
]
|
||||
},
|
||||
"from": [
|
||||
-25.0,
|
||||
25.0
|
||||
],
|
||||
"tag": {
|
||||
"end": 106,
|
||||
"start": 85,
|
||||
"type": "TagDeclarator",
|
||||
"value": "rectangleSegmentB001"
|
||||
},
|
||||
"to": [
|
||||
-25.0,
|
||||
-25.0
|
||||
],
|
||||
"type": "ToPoint",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
},
|
||||
"surface": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
85,
|
||||
106,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
37,
|
||||
65,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"sketch002": {
|
||||
"type": "Solid",
|
||||
"value": {
|
||||
"type": "Solid",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": [
|
||||
{
|
||||
"faceId": "[uuid]",
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
],
|
||||
"tag": null,
|
||||
"type": "extrudeArc"
|
||||
}
|
||||
],
|
||||
"sketch": {
|
||||
"type": "Sketch",
|
||||
"id": "[uuid]",
|
||||
"paths": [
|
||||
{
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
]
|
||||
},
|
||||
"ccw": true,
|
||||
"center": [
|
||||
-50.0,
|
||||
0.0
|
||||
],
|
||||
"from": [
|
||||
-40.0,
|
||||
0.0
|
||||
],
|
||||
"radius": 10.0,
|
||||
"tag": null,
|
||||
"to": [
|
||||
-40.0,
|
||||
0.0
|
||||
],
|
||||
"type": "Circle",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
}
|
||||
}
|
||||
],
|
||||
"on": {
|
||||
"type": "plane",
|
||||
"id": "[uuid]",
|
||||
"artifactId": "[uuid]",
|
||||
"value": "XY",
|
||||
"origin": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"xAxis": {
|
||||
"x": 1.0,
|
||||
"y": 0.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"yAxis": {
|
||||
"x": 0.0,
|
||||
"y": 1.0,
|
||||
"z": 0.0
|
||||
},
|
||||
"zAxis": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 1.0
|
||||
},
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"__meta": []
|
||||
},
|
||||
"start": {
|
||||
"from": [
|
||||
-40.0,
|
||||
0.0
|
||||
],
|
||||
"to": [
|
||||
-40.0,
|
||||
0.0
|
||||
],
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"tag": null,
|
||||
"__geoMeta": {
|
||||
"id": "[uuid]",
|
||||
"sourceRange": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
]
|
||||
}
|
||||
},
|
||||
"artifactId": "[uuid]",
|
||||
"originalId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"height": 0.0,
|
||||
"startCapId": "[uuid]",
|
||||
"endCapId": "[uuid]",
|
||||
"units": {
|
||||
"type": "Mm"
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
145,
|
||||
190,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
BIN
src/wasm-lib/kcl/tests/revolve_about_edge/rendered_model.png
Normal file
After Width: | Height: | Size: 93 KiB |