Compare commits
6 Commits
delete-net
...
cut-releas
Author | SHA1 | Date | |
---|---|---|---|
04a77efae3 | |||
379cd1e067 | |||
8c3d438f6d | |||
ac15049e2c | |||
466da6be55 | |||
38d5be001b |
31
.github/workflows/build-test-publish-apps.yml
vendored
@ -5,6 +5,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- cut-release-v0.25.1-updater-test-build-1
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
schedule:
|
schedule:
|
||||||
@ -13,8 +14,8 @@ on:
|
|||||||
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
||||||
|
|
||||||
env:
|
env:
|
||||||
CUT_RELEASE_PR: ${{ github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
CUT_RELEASE_PR: true
|
||||||
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
|
BUILD_RELEASE: true
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@ -44,7 +45,7 @@ jobs:
|
|||||||
|
|
||||||
# TODO: see if we can fetch from main instead if no diff at src/wasm-lib
|
# TODO: see if we can fetch from main instead if no diff at src/wasm-lib
|
||||||
- name: Run build:wasm
|
- name: Run build:wasm
|
||||||
run: "yarn build:wasm${{ env.BUILD_RELEASE == 'true' && '-dev' || ''}}"
|
run: "yarn build:wasm"
|
||||||
|
|
||||||
- name: Set nightly version
|
- name: Set nightly version
|
||||||
if: github.event_name == 'schedule'
|
if: github.event_name == 'schedule'
|
||||||
@ -156,15 +157,15 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
# if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
|
||||||
needs: [prepare-files, build-apps]
|
needs: [prepare-files, build-apps]
|
||||||
env:
|
env:
|
||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||||
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
|
||||||
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
|
||||||
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
|
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
|
||||||
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app' }}
|
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app/test/cut-release-v0.25.1-updater-test' }}
|
||||||
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app/test/cut-release-v0.25.1-updater-test' }}
|
||||||
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
@ -245,6 +246,15 @@ jobs:
|
|||||||
parent: false
|
parent: false
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
|
# TODO: remove workaround introduced in https://github.com/KittyCAD/modeling-app/issues/3817
|
||||||
|
- name: Upload release files to public bucket (test/electron-builder workaround)
|
||||||
|
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||||
|
with:
|
||||||
|
path: out
|
||||||
|
glob: 'Zoo*'
|
||||||
|
parent: false
|
||||||
|
destination: '${{ env.BUCKET_DIR }}/test/electron-builder'
|
||||||
|
|
||||||
- name: Upload update endpoint to public bucket
|
- name: Upload update endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||||
with:
|
with:
|
||||||
@ -253,6 +263,15 @@ jobs:
|
|||||||
parent: false
|
parent: false
|
||||||
destination: ${{ env.BUCKET_DIR }}
|
destination: ${{ env.BUCKET_DIR }}
|
||||||
|
|
||||||
|
# TODO: remove workaround introduced in https://github.com/KittyCAD/modeling-app/issues/3817
|
||||||
|
- name: Upload update endpoint to public bucket (test/electron-builder workaround)
|
||||||
|
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||||
|
with:
|
||||||
|
path: out
|
||||||
|
glob: 'latest*'
|
||||||
|
parent: false
|
||||||
|
destination: '${{ env.BUCKET_DIR }}/test/electron-builder'
|
||||||
|
|
||||||
- name: Upload download endpoint to public bucket
|
- name: Upload download endpoint to public bucket
|
||||||
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
uses: google-github-actions/upload-cloud-storage@v2.2.0
|
||||||
with:
|
with:
|
||||||
|
3
.github/workflows/cargo-clippy.yml
vendored
@ -28,6 +28,7 @@ jobs:
|
|||||||
dir: ['src/wasm-lib']
|
dir: ['src/wasm-lib']
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
- uses: taiki-e/install-action@just
|
||||||
- name: Install latest rust
|
- name: Install latest rust
|
||||||
uses: actions-rs/toolchain@v1
|
uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
@ -41,7 +42,7 @@ jobs:
|
|||||||
- name: Run clippy
|
- name: Run clippy
|
||||||
run: |
|
run: |
|
||||||
cd "${{ matrix.dir }}"
|
cd "${{ matrix.dir }}"
|
||||||
cargo clippy --all --tests --benches -- -D warnings
|
just lint
|
||||||
# If this fails, run "cargo check" to update Cargo.lock,
|
# If this fails, run "cargo check" to update Cargo.lock,
|
||||||
# then add Cargo.lock to the PR.
|
# then add Cargo.lock to the PR.
|
||||||
- name: Check Cargo.lock doesn't need updating
|
- name: Check Cargo.lock doesn't need updating
|
||||||
|
@ -112,7 +112,8 @@ test.describe('when using the file tree to', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
openKclCodePanel,
|
||||||
|
openFilePanel,
|
||||||
createAndSelectProject,
|
createAndSelectProject,
|
||||||
pasteCodeInEditor,
|
pasteCodeInEditor,
|
||||||
createNewFileAndSelect,
|
createNewFileAndSelect,
|
||||||
@ -124,9 +125,9 @@ test.describe('when using the file tree to', () => {
|
|||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen(['files', 'code'])
|
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
await createAndSelectProject('project-000')
|
||||||
|
await openKclCodePanel()
|
||||||
|
await openFilePanel()
|
||||||
// File the main.kcl with contents
|
// File the main.kcl with contents
|
||||||
const kclCube = await fsp.readFile(
|
const kclCube = await fsp.readFile(
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
||||||
|
@ -548,13 +548,16 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
|
|
||||||
createNewFileAndSelect: async (name: string) => {
|
createNewFileAndSelect: async (name: string) => {
|
||||||
return test?.step(`Create a file named ${name}, select it`, async () => {
|
return test?.step(`Create a file named ${name}, select it`, async () => {
|
||||||
|
await openFilePanel(page)
|
||||||
await page.getByTestId('create-file-button').click()
|
await page.getByTestId('create-file-button').click()
|
||||||
await page.getByTestId('file-rename-field').fill(name)
|
await page.getByTestId('file-rename-field').fill(name)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page
|
const newFile = page
|
||||||
.locator('[data-testid="file-pane-scroll-container"] button')
|
.locator('[data-testid="file-pane-scroll-container"] button')
|
||||||
.filter({ hasText: name })
|
.filter({ hasText: name })
|
||||||
.click()
|
|
||||||
|
await expect(newFile).toBeVisible()
|
||||||
|
await newFile.click()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -585,6 +588,15 @@ export async function getUtils(page: Page, test_?: typeof test) {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Sorry I don't have time to fix this right now, but runs like
|
||||||
|
* the one linked below show me that setting the open panes in this manner is not reliable.
|
||||||
|
* You can either set `openPanes` as a part of the same initScript we run in setupElectron/setup,
|
||||||
|
* or you can imperatively open the panes with functions like {openKclCodePanel}
|
||||||
|
* (or we can make a general openPane function that takes a paneId).,
|
||||||
|
* but having a separate initScript does not seem to work reliably.
|
||||||
|
* @link https://github.com/KittyCAD/modeling-app/actions/runs/10731890169/job/29762700806?pr=3807#step:20:19553
|
||||||
|
*/
|
||||||
panesOpen: async (paneIds: PaneId[]) => {
|
panesOpen: async (paneIds: PaneId[]) => {
|
||||||
return test?.step(`Setting ${paneIds} panes to be open`, async () => {
|
return test?.step(`Setting ${paneIds} panes to be open`, async () => {
|
||||||
await page.addInitScript(
|
await page.addInitScript(
|
||||||
|
@ -288,7 +288,7 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Refresh the application and see project setting applied', async () => {
|
await test.step('Refresh the application and see project setting applied', async () => {
|
||||||
await page.reload()
|
await page.reload({ waitUntil: 'domcontentloaded' })
|
||||||
|
|
||||||
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
|
await expect(logoLink).toHaveCSS('--primary-hue', projectThemeColor)
|
||||||
await settingsCloseButton.click()
|
await settingsCloseButton.click()
|
||||||
@ -364,47 +364,48 @@ test.describe('Testing settings', () => {
|
|||||||
async ({ browser: _ }, testInfo) => {
|
async ({ browser: _ }, testInfo) => {
|
||||||
const { electronApp, page } = await setupElectron({
|
const { electronApp, page } = await setupElectron({
|
||||||
testInfo,
|
testInfo,
|
||||||
folderSetupFn: async () => {},
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'project-000')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('cube.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('cylinder.kcl'),
|
||||||
|
join(bracketDir, '2.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
const kclCube = await fsp.readFile(executorInputPath('cube.kcl'), 'utf-8')
|
||||||
|
const kclCylinder = await fsp.readFile(
|
||||||
|
executorInputPath('cylinder.kcl'),
|
||||||
|
'utf8'
|
||||||
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
panesOpen,
|
openKclCodePanel,
|
||||||
createAndSelectProject,
|
openFilePanel,
|
||||||
pasteCodeInEditor,
|
waitForPageLoad,
|
||||||
clickPane,
|
selectFile,
|
||||||
createNewFileAndSelect,
|
|
||||||
editorTextMatches,
|
editorTextMatches,
|
||||||
} = await getUtils(page, test)
|
} = await getUtils(page, test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
await panesOpen([])
|
await test.step('Precondition: Open to second project file', async () => {
|
||||||
|
|
||||||
await test.step('Precondition: No projects exist', async () => {
|
|
||||||
await expect(page.getByTestId('home-section')).toBeVisible()
|
await expect(page.getByTestId('home-section')).toBeVisible()
|
||||||
const projectLinksPre = page.getByTestId('project-link')
|
await page.getByText('project-000').click()
|
||||||
await expect(projectLinksPre).toHaveCount(0)
|
await waitForPageLoad()
|
||||||
|
await openKclCodePanel()
|
||||||
|
await openFilePanel()
|
||||||
|
await editorTextMatches(kclCube)
|
||||||
|
|
||||||
|
await selectFile('2.kcl')
|
||||||
|
await editorTextMatches(kclCylinder)
|
||||||
})
|
})
|
||||||
|
|
||||||
await createAndSelectProject('project-000')
|
|
||||||
|
|
||||||
await clickPane('code')
|
|
||||||
const kclCube = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cube.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCube)
|
|
||||||
|
|
||||||
await clickPane('files')
|
|
||||||
await createNewFileAndSelect('2.kcl')
|
|
||||||
|
|
||||||
const kclCylinder = await fsp.readFile(
|
|
||||||
'src/wasm-lib/tests/executor/inputs/cylinder.kcl',
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
await pasteCodeInEditor(kclCylinder)
|
|
||||||
|
|
||||||
const settingsOpenButton = page.getByRole('link', {
|
const settingsOpenButton = page.getByRole('link', {
|
||||||
name: 'settings Settings',
|
name: 'settings Settings',
|
||||||
})
|
})
|
||||||
@ -412,6 +413,9 @@ test.describe('Testing settings', () => {
|
|||||||
|
|
||||||
await test.step('Open and close settings', async () => {
|
await test.step('Open and close settings', async () => {
|
||||||
await settingsOpenButton.click()
|
await settingsOpenButton.click()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('heading', { name: 'Settings', exact: true })
|
||||||
|
).toBeVisible()
|
||||||
await settingsCloseButton.click()
|
await settingsCloseButton.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -534,7 +534,7 @@ test.describe('Text-to-CAD tests', () => {
|
|||||||
|
|
||||||
// Ensure the final toast remains.
|
// Ensure the final toast remains.
|
||||||
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
await expect(page.getByText(`a 2x10 lego`)).not.toBeVisible()
|
||||||
await expect(page.getByText(`a 2x8 lego`)).not.toBeVisible()
|
await expect(page.getByText(`Prompt: "a 2x8 lego`)).not.toBeVisible()
|
||||||
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
await expect(page.getByText(`a 2x4 lego`)).toBeVisible()
|
||||||
|
|
||||||
// Ensure you can copy the code for the final model.
|
// Ensure you can copy the code for the final model.
|
||||||
@ -690,40 +690,53 @@ test(
|
|||||||
'Text-to-CAD functionality',
|
'Text-to-CAD functionality',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ browserName }, testInfo) => {
|
async ({ browserName }, testInfo) => {
|
||||||
|
const projectName = 'project-000'
|
||||||
|
const prompt = 'lego 2x4'
|
||||||
|
const textToCadFileName = 'lego-2x4.kcl'
|
||||||
|
|
||||||
const { electronApp, page, dir } = await setupElectron({ testInfo })
|
const { electronApp, page, dir } = await setupElectron({ testInfo })
|
||||||
const fileExists = () =>
|
const fileExists = () =>
|
||||||
fs.existsSync(join(dir, 'project-000', 'lego-2x4.kcl'))
|
fs.existsSync(join(dir, projectName, textToCadFileName))
|
||||||
|
|
||||||
const { createAndSelectProject, panesOpen } = await getUtils(page, test)
|
const {
|
||||||
|
createAndSelectProject,
|
||||||
|
openFilePanel,
|
||||||
|
openKclCodePanel,
|
||||||
|
waitForPageLoad,
|
||||||
|
} = await getUtils(page, test)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await panesOpen(['code', 'files'])
|
// Locators
|
||||||
|
const projectMenuButton = page.getByRole('button', { name: projectName })
|
||||||
|
const textToCadFileButton = page.getByRole('listitem').filter({
|
||||||
|
has: page.getByRole('button', { name: textToCadFileName }),
|
||||||
|
})
|
||||||
|
const textToCadComment = page.getByText(
|
||||||
|
`// Generated by Text-to-CAD: ${prompt}`
|
||||||
|
)
|
||||||
|
|
||||||
// Create and navigate to the project
|
// Create and navigate to the project
|
||||||
await createAndSelectProject('project-000')
|
await createAndSelectProject('project-000')
|
||||||
|
|
||||||
// Wait for Start Sketch otherwise you will not have access Text-to-CAD command
|
// Wait for Start Sketch otherwise you will not have access Text-to-CAD command
|
||||||
await expect(
|
await waitForPageLoad()
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
await openFilePanel()
|
||||||
).toBeEnabled({
|
await openKclCodePanel()
|
||||||
timeout: 20_000,
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Test file creation`, async () => {
|
await test.step(`Test file creation`, async () => {
|
||||||
await sendPromptFromCommandBar(page, 'lego 2x4')
|
await sendPromptFromCommandBar(page, prompt)
|
||||||
// File is considered created if it shows up in the Project Files pane
|
// File is considered created if it shows up in the Project Files pane
|
||||||
const file = page.getByRole('button', { name: 'lego-2x4.kcl' })
|
await expect(textToCadFileButton).toBeVisible({ timeout: 20_000 })
|
||||||
await expect(file).toBeVisible({ timeout: 20_000 })
|
|
||||||
expect(fileExists()).toBeTruthy()
|
expect(fileExists()).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Test file navigation`, async () => {
|
await test.step(`Test file navigation`, async () => {
|
||||||
const file = page.getByRole('button', { name: 'lego-2x4.kcl' })
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
await file.click()
|
await textToCadFileButton.click()
|
||||||
const kclComment = page.getByText('Lego 2x4 Brick')
|
|
||||||
// File can be navigated and loaded assuming a specific KCL comment is loaded into the KCL code pane
|
// File can be navigated and loaded assuming a specific KCL comment is loaded into the KCL code pane
|
||||||
await expect(kclComment).toBeVisible({ timeout: 20_000 })
|
await expect(textToCadComment).toBeVisible({ timeout: 20_000 })
|
||||||
|
await expect(projectMenuButton).toContainText(textToCadFileName)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Test file deletion on rejection`, async () => {
|
await test.step(`Test file deletion on rejection`, async () => {
|
||||||
@ -737,6 +750,8 @@ test(
|
|||||||
)
|
)
|
||||||
await expect(submittingToastMessage).toBeVisible()
|
await expect(submittingToastMessage).toBeVisible()
|
||||||
expect(fileExists()).toBeFalsy()
|
expect(fileExists()).toBeFalsy()
|
||||||
|
// Confirm we've navigated back to the main.kcl file after deletion
|
||||||
|
await expect(projectMenuButton).toContainText('main.kcl')
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
|
@ -79,5 +79,5 @@ linux:
|
|||||||
|
|
||||||
publish:
|
publish:
|
||||||
- provider: generic
|
- provider: generic
|
||||||
url: https://dl.zoo.dev/releases/modeling-app/test/electron-builder
|
url: https://dl.zoo.dev/releases/modeling-app/test/cut-release-v0.25.1-updater-test
|
||||||
channel: latest
|
channel: latest
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "zoo-modeling-app",
|
"name": "zoo-modeling-app",
|
||||||
"version": "0.25.0",
|
"version": "0.25.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"author": {
|
"author": {
|
||||||
|
@ -8,7 +8,7 @@ import { moveValueIntoNewVariable } from 'lang/modifyAst'
|
|||||||
import { isNodeSafeToReplace } from 'lang/queryAst'
|
import { isNodeSafeToReplace } from 'lang/queryAst'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { PathToNode, SourceRange, parse, recast } from 'lang/wasm'
|
import { PathToNode, SourceRange } from 'lang/wasm'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
|
|
||||||
export const getVarNameModal = createSetVarNameModal(SetVarNameModal)
|
export const getVarNameModal = createSetVarNameModal(SetVarNameModal)
|
||||||
@ -23,8 +23,7 @@ export function useConvertToVariable(range?: SourceRange) {
|
|||||||
}, [enable])
|
}, [enable])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parsed = parse(recast(ast))
|
const parsed = ast
|
||||||
if (trap(parsed)) return
|
|
||||||
|
|
||||||
const meta = isNodeSafeToReplace(
|
const meta = isNodeSafeToReplace(
|
||||||
parsed,
|
parsed,
|
||||||
|
@ -56,11 +56,6 @@ body.dark {
|
|||||||
.dark .body-bg {
|
.dark .body-bg {
|
||||||
@apply bg-chalkboard-100;
|
@apply bg-chalkboard-100;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
|
||||||
scrollbar-color: var(--color-chalkboard-70) var(--color-chalkboard-90);
|
|
||||||
@apply text-chalkboard-10;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
@ -300,32 +295,11 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
/* Modified from the very helpful https://www.transition.style/#in:circle:hesitate */
|
/*
|
||||||
@keyframes circle-in-hesitate {
|
This is where your own custom Tailwind utility classes can go,
|
||||||
0% {
|
which lets you use them with @apply in your CSS, and get
|
||||||
clip-path: circle(
|
autocomplete in classNames in your JSX.
|
||||||
var(--circle-size-start, 0%) at var(--circle-x, 50%)
|
*/
|
||||||
var(--circle-y, 50%)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
40% {
|
|
||||||
clip-path: circle(
|
|
||||||
var(--circle-size-mid, 40%) at var(--circle-x, 50%) var(--circle-y, 50%)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
clip-path: circle(
|
|
||||||
var(--circle-size-end, 125%) at var(--circle-x, 50%)
|
|
||||||
var(--circle-y, 50%)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.in-circle-hesitate {
|
|
||||||
animation: var(--circle-duration, 2.5s)
|
|
||||||
var(--circle-timing, cubic-bezier(0.25, 1, 0.3, 1)) circle-in-hesitate
|
|
||||||
both;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#code-mirror-override .cm-scroller,
|
#code-mirror-override .cm-scroller,
|
||||||
|
@ -5,7 +5,7 @@ import {
|
|||||||
kclManager,
|
kclManager,
|
||||||
sceneEntitiesManager,
|
sceneEntitiesManager,
|
||||||
} from 'lib/singletons'
|
} from 'lib/singletons'
|
||||||
import { CallExpression, SourceRange, Expr, parse, recast } from 'lang/wasm'
|
import { CallExpression, SourceRange, Expr, parse } from 'lang/wasm'
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
import { EditorSelection, SelectionRange } from '@codemirror/state'
|
||||||
@ -300,8 +300,7 @@ export function processCodeMirrorRanges({
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
function updateSceneObjectColors(codeBasedSelections: Selection[]) {
|
||||||
const updated = parse(recast(kclManager.ast))
|
const updated = kclManager.ast
|
||||||
if (err(updated)) return
|
|
||||||
|
|
||||||
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
Object.values(sceneEntitiesManager.activeSegments).forEach((segmentGroup) => {
|
||||||
if (
|
if (
|
||||||
|
@ -70,17 +70,11 @@ const SignIn = () => {
|
|||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
style={
|
style={
|
||||||
{
|
isDesktop()
|
||||||
height: 'calc(100vh - 16px)',
|
? ({ '-webkit-app-region': 'no-drag' } as CSSProperties)
|
||||||
'--circle-x': '14%',
|
: {}
|
||||||
'--circle-y': '12%',
|
|
||||||
'--circle-size-mid': '15%',
|
|
||||||
'--circle-size-end': '200%',
|
|
||||||
'--circle-timing': 'cubic-bezier(0.25, 1, 0.4, 0.9)',
|
|
||||||
...(isDesktop() ? { '-webkit-app-region': 'no-drag' } : {}),
|
|
||||||
} as CSSProperties
|
|
||||||
}
|
}
|
||||||
className="in-circle-hesitate body-bg py-5 px-12 rounded-lg grid place-items-center overflow-y-auto"
|
className="body-bg py-5 px-12 rounded-lg grid place-items-center overflow-y-auto"
|
||||||
>
|
>
|
||||||
<div className="max-w-7xl grid gap-5 grid-cols-3 xl:grid-cols-4 xl:grid-rows-5">
|
<div className="max-w-7xl grid gap-5 grid-cols-3 xl:grid-cols-4 xl:grid-rows-5">
|
||||||
<div className="col-span-2 xl:col-span-3 xl:row-span-3 max-w-3xl mr-8 mb-8">
|
<div className="col-span-2 xl:col-span-3 xl:row-span-3 max-w-3xl mr-8 mb-8">
|
||||||
@ -204,7 +198,7 @@ const SignIn = () => {
|
|||||||
<div className="flex gap-4 flex-wrap items-center">
|
<div className="flex gap-4 flex-wrap items-center">
|
||||||
<ActionButton
|
<ActionButton
|
||||||
Element="externalLink"
|
Element="externalLink"
|
||||||
to="https://zoo.dev/docs/kcl-samples/ball-bearing"
|
to="https://zoo.dev/docs/kcl-samples/a-parametric-bearing-pillow-block"
|
||||||
iconStart={{ icon: 'settings' }}
|
iconStart={{ icon: 'settings' }}
|
||||||
className="border-chalkboard-30 dark:border-chalkboard-80"
|
className="border-chalkboard-30 dark:border-chalkboard-80"
|
||||||
>
|
>
|
||||||
|
19
src/wasm-lib/Cargo.lock
generated
@ -620,9 +620,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dashmap"
|
name = "dashmap"
|
||||||
version = "6.0.1"
|
version = "6.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "804c8821570c3f8b70230c2ba75ffa5c0f9a4189b9a432b6656c536712acae28"
|
checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
@ -1357,7 +1357,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"criterion",
|
"criterion",
|
||||||
"dashmap 6.0.1",
|
"dashmap 6.1.0",
|
||||||
"databake",
|
"databake",
|
||||||
"derive-docs",
|
"derive-docs",
|
||||||
"expectorate",
|
"expectorate",
|
||||||
@ -1399,7 +1399,7 @@ dependencies = [
|
|||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winnow 0.5.40",
|
"winnow",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3117,7 +3117,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
"winnow 0.6.18",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3800,15 +3800,6 @@ version = "0.52.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winnow"
|
|
||||||
version = "0.5.40"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.6.18"
|
version = "0.6.18"
|
||||||
|
@ -2,3 +2,6 @@
|
|||||||
new-test name:
|
new-test name:
|
||||||
echo "kcl_test!(\"{{name}}\", {{name}});" >> tests/executor/visuals.rs
|
echo "kcl_test!(\"{{name}}\", {{name}});" >> tests/executor/visuals.rs
|
||||||
TWENTY_TWENTY=overwrite cargo nextest run --test executor -E 'test(=visuals::{{name}})'
|
TWENTY_TWENTY=overwrite cargo nextest run --test executor -E 'test(=visuals::{{name}})'
|
||||||
|
|
||||||
|
lint:
|
||||||
|
cargo clippy --all --tests --benches -- -D warnings
|
||||||
|
@ -18,7 +18,7 @@ base64 = "0.22.1"
|
|||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.5.17", default-features = false, optional = true, features = ["std", "derive"] }
|
clap = { version = "4.5.17", default-features = false, optional = true, features = ["std", "derive"] }
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
dashmap = "6.0.1"
|
dashmap = "6.1.0"
|
||||||
databake = { version = "0.1.8", features = ["derive"] }
|
databake = { version = "0.1.8", features = ["derive"] }
|
||||||
derive-docs = { version = "0.1.26", path = "../derive-docs" }
|
derive-docs = { version = "0.1.26", path = "../derive-docs" }
|
||||||
form_urlencoded = "1.2.1"
|
form_urlencoded = "1.2.1"
|
||||||
@ -47,7 +47,7 @@ url = { version = "2.5.2", features = ["serde"] }
|
|||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.10.0", features = ["v4", "js", "serde"] }
|
||||||
validator = { version = "0.18.1", features = ["derive"] }
|
validator = { version = "0.18.1", features = ["derive"] }
|
||||||
winnow = "0.5.40"
|
winnow = "0.6.18"
|
||||||
zip = { version = "2.0.0", default-features = false }
|
zip = { version = "2.0.0", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use kcl_lib::test_server;
|
use kcl_lib::{settings::types::UnitLength::Mm, test_server};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
pub fn bench_execute(c: &mut Criterion) {
|
pub fn bench_execute(c: &mut Criterion) {
|
||||||
@ -13,26 +13,42 @@ pub fn bench_execute(c: &mut Criterion) {
|
|||||||
// Configure Criterion.rs to detect smaller differences and increase sample size to improve
|
// Configure Criterion.rs to detect smaller differences and increase sample size to improve
|
||||||
// precision and counteract the resulting noise.
|
// precision and counteract the resulting noise.
|
||||||
group.sample_size(10);
|
group.sample_size(10);
|
||||||
group.bench_with_input(BenchmarkId::new("execute_", name), &code, |b, &s| {
|
group.bench_with_input(BenchmarkId::new("execute", name), &code, |b, &s| {
|
||||||
let rt = Runtime::new().unwrap();
|
let rt = Runtime::new().unwrap();
|
||||||
|
|
||||||
// Spawn a future onto the runtime
|
// Spawn a future onto the runtime
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
rt.block_on(test_server::execute_and_snapshot(
|
rt.block_on(test_server::execute_and_snapshot(s, Mm)).unwrap();
|
||||||
s,
|
|
||||||
kcl_lib::settings::types::UnitLength::Mm,
|
|
||||||
))
|
|
||||||
.unwrap();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
group.finish();
|
group.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
criterion_group!(benches, bench_execute);
|
pub fn bench_lego(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("executor_lego_pattern");
|
||||||
|
// Configure Criterion.rs to detect smaller differences and increase sample size to improve
|
||||||
|
// precision and counteract the resulting noise.
|
||||||
|
group.sample_size(10);
|
||||||
|
// Create lego bricks with N x 10 bumps, where N is each element of `sizes`.
|
||||||
|
let sizes = vec![1, 2, 4];
|
||||||
|
for size in sizes {
|
||||||
|
group.bench_with_input(BenchmarkId::from_parameter(size), &size, |b, &size| {
|
||||||
|
let rt = Runtime::new().unwrap();
|
||||||
|
let code = LEGO_PROGRAM.replace("{{N}}", &size.to_string());
|
||||||
|
// Spawn a future onto the runtime
|
||||||
|
b.iter(|| {
|
||||||
|
rt.block_on(test_server::execute_and_snapshot(&code, Mm)).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_lego, bench_execute);
|
||||||
criterion_main!(benches);
|
criterion_main!(benches);
|
||||||
|
|
||||||
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
||||||
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
||||||
const SERVER_RACK_HEAVY_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-heavy.kcl");
|
const SERVER_RACK_HEAVY_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-heavy.kcl");
|
||||||
const SERVER_RACK_LITE_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-lite.kcl");
|
const SERVER_RACK_LITE_PROGRAM: &str = include_str!("../../tests/executor/inputs/server-rack-lite.kcl");
|
||||||
|
const LEGO_PROGRAM: &str = include_str!("../../tests/executor/inputs/slow_lego.kcl.tmpl");
|
||||||
|
@ -927,7 +927,7 @@ pub fn function_body(i: TokenSlice) -> PResult<Program> {
|
|||||||
|
|
||||||
match body_items_within_function.parse_next(i) {
|
match body_items_within_function.parse_next(i) {
|
||||||
Err(ErrMode::Backtrack(_)) => {
|
Err(ErrMode::Backtrack(_)) => {
|
||||||
i.reset(start);
|
i.reset(&start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
@ -937,7 +937,7 @@ pub fn function_body(i: TokenSlice) -> PResult<Program> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Err(ErrMode::Backtrack(_)), _) => {
|
(Err(ErrMode::Backtrack(_)), _) => {
|
||||||
i.reset(start);
|
i.reset(&start);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
(Err(e), _) => return Err(e),
|
(Err(e), _) => return Err(e),
|
||||||
@ -1276,7 +1276,7 @@ fn unary_expression(i: TokenSlice) -> PResult<UnaryExpression> {
|
|||||||
|
|
||||||
/// Consume tokens that make up a binary expression, but don't actually return them.
|
/// Consume tokens that make up a binary expression, but don't actually return them.
|
||||||
/// Why not?
|
/// Why not?
|
||||||
/// Because this is designed to be used with .recognize() within the `binary_expression` parser.
|
/// Because this is designed to be used with .take() within the `binary_expression` parser.
|
||||||
fn binary_expression_tokens(i: TokenSlice) -> PResult<Vec<BinaryExpressionToken>> {
|
fn binary_expression_tokens(i: TokenSlice) -> PResult<Vec<BinaryExpressionToken>> {
|
||||||
let first = operand.parse_next(i).map(BinaryExpressionToken::from)?;
|
let first = operand.parse_next(i).map(BinaryExpressionToken::from)?;
|
||||||
let remaining: Vec<_> = repeat(
|
let remaining: Vec<_> = repeat(
|
||||||
@ -1308,7 +1308,7 @@ fn binary_expression(i: TokenSlice) -> PResult<BinaryExpression> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn binary_expr_in_parens(i: TokenSlice) -> PResult<BinaryExpression> {
|
fn binary_expr_in_parens(i: TokenSlice) -> PResult<BinaryExpression> {
|
||||||
let span_with_brackets = bracketed_section.recognize().parse_next(i)?;
|
let span_with_brackets = bracketed_section.take().parse_next(i)?;
|
||||||
let n = span_with_brackets.len();
|
let n = span_with_brackets.len();
|
||||||
let mut span_no_brackets = &span_with_brackets[1..n - 1];
|
let mut span_no_brackets = &span_with_brackets[1..n - 1];
|
||||||
let expr = binary_expression.parse_next(&mut span_no_brackets)?;
|
let expr = binary_expression.parse_next(&mut span_no_brackets)?;
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use winnow::{
|
use winnow::{
|
||||||
error::{ErrorKind, ParseError, StrContext},
|
error::{ErrorKind, ParseError, StrContext},
|
||||||
|
stream::Stream,
|
||||||
Located,
|
Located,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -102,14 +103,17 @@ impl<C> std::default::Default for ContextError<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, C> winnow::error::ParserError<I> for ContextError<C> {
|
impl<I, C> winnow::error::ParserError<I> for ContextError<C>
|
||||||
|
where
|
||||||
|
I: Stream,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
|
fn from_error_kind(_input: &I, _kind: ErrorKind) -> Self {
|
||||||
Self::default()
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn append(self, _input: &I, _kind: ErrorKind) -> Self {
|
fn append(self, _input: &I, _input_checkpoint: &<I as Stream>::Checkpoint, _kind: ErrorKind) -> Self {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,9 +123,12 @@ impl<I, C> winnow::error::ParserError<I> for ContextError<C> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C, I> winnow::error::AddContext<I, C> for ContextError<C> {
|
impl<C, I> winnow::error::AddContext<I, C> for ContextError<C>
|
||||||
|
where
|
||||||
|
I: Stream,
|
||||||
|
{
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_context(mut self, _input: &I, ctx: C) -> Self {
|
fn add_context(mut self, _input: &I, _input_checkpoint: &<I as Stream>::Checkpoint, ctx: C) -> Self {
|
||||||
self.context.push(ctx);
|
self.context.push(ctx);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
//! Functions related to extruding.
|
//! Functions related to extruding.
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_docs::stdlib;
|
use derive_docs::stdlib;
|
||||||
use kittycad::types::ExtrusionFaceCapType;
|
use kittycad::types::{ExtrusionFaceCapType, ExtrusionFaceInfo};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@ -99,7 +101,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
args.send_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
id,
|
id,
|
||||||
kittycad::types::ModelingCmd::Extrude {
|
kittycad::types::ModelingCmd::Extrude {
|
||||||
target: sketch_group.id,
|
target: sketch_group.id,
|
||||||
@ -112,7 +114,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
|
|||||||
// Disable the sketch mode.
|
// Disable the sketch mode.
|
||||||
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
|
args.batch_modeling_cmd(uuid::Uuid::new_v4(), kittycad::types::ModelingCmd::SketchModeDisable {})
|
||||||
.await?;
|
.await?;
|
||||||
extrude_groups.push(do_post_extrude(sketch_group.clone(), length, id, args.clone()).await?);
|
extrude_groups.push(do_post_extrude(sketch_group.clone(), length, args.clone()).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(extrude_groups.into())
|
Ok(extrude_groups.into())
|
||||||
@ -121,7 +123,6 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
|
|||||||
pub(crate) async fn do_post_extrude(
|
pub(crate) async fn do_post_extrude(
|
||||||
sketch_group: SketchGroup,
|
sketch_group: SketchGroup,
|
||||||
length: f64,
|
length: f64,
|
||||||
id: Uuid,
|
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<ExtrudeGroup>, KclError> {
|
) -> Result<Box<ExtrudeGroup>, KclError> {
|
||||||
// Bring the object to the front of the scene.
|
// Bring the object to the front of the scene.
|
||||||
@ -165,7 +166,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
|
|
||||||
let solid3d_info = args
|
let solid3d_info = args
|
||||||
.send_modeling_cmd(
|
.send_modeling_cmd(
|
||||||
id,
|
uuid::Uuid::new_v4(),
|
||||||
kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo {
|
kittycad::types::ModelingCmd::Solid3DGetExtrusionFaceInfo {
|
||||||
edge_id,
|
edge_id,
|
||||||
object_id: sketch_group.id,
|
object_id: sketch_group.id,
|
||||||
@ -218,26 +219,11 @@ pub(crate) async fn do_post_extrude(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a hashmap for quick id lookup
|
let Faces {
|
||||||
let mut face_id_map = std::collections::HashMap::new();
|
sides: face_id_map,
|
||||||
// creating fake ids for start and end caps is to make extrudes mock-execute safe
|
start_cap_id,
|
||||||
let (mut start_cap_id, mut end_cap_id) = if args.ctx.is_mock {
|
end_cap_id,
|
||||||
(Some(Uuid::new_v4()), Some(Uuid::new_v4()))
|
} = analyze_faces(&args, face_infos);
|
||||||
} else {
|
|
||||||
(None, None)
|
|
||||||
};
|
|
||||||
for face_info in face_infos {
|
|
||||||
match face_info.cap {
|
|
||||||
ExtrusionFaceCapType::Bottom => start_cap_id = face_info.face_id,
|
|
||||||
ExtrusionFaceCapType::Top => end_cap_id = face_info.face_id,
|
|
||||||
ExtrusionFaceCapType::None => {
|
|
||||||
if let Some(curve_id) = face_info.curve_id {
|
|
||||||
face_id_map.insert(curve_id, face_info.face_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate over the sketch_group.value array and add face_id to GeoMeta
|
// Iterate over the sketch_group.value array and add face_id to GeoMeta
|
||||||
let new_value = sketch_group
|
let new_value = sketch_group
|
||||||
.value
|
.value
|
||||||
@ -301,3 +287,37 @@ pub(crate) async fn do_post_extrude(
|
|||||||
edge_cuts: vec![],
|
edge_cuts: vec![],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Faces {
|
||||||
|
/// Maps curve ID to face ID for each side.
|
||||||
|
sides: HashMap<Uuid, Option<Uuid>>,
|
||||||
|
/// Top face ID.
|
||||||
|
end_cap_id: Option<Uuid>,
|
||||||
|
/// Bottom face ID.
|
||||||
|
start_cap_id: Option<Uuid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn analyze_faces(args: &Args, face_infos: Vec<ExtrusionFaceInfo>) -> Faces {
|
||||||
|
let mut faces = Faces {
|
||||||
|
sides: HashMap::with_capacity(face_infos.len()),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
if args.ctx.is_mock {
|
||||||
|
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
||||||
|
faces.start_cap_id = Some(Uuid::new_v4());
|
||||||
|
faces.end_cap_id = Some(Uuid::new_v4());
|
||||||
|
}
|
||||||
|
for face_info in face_infos {
|
||||||
|
match face_info.cap {
|
||||||
|
ExtrusionFaceCapType::Bottom => faces.start_cap_id = face_info.face_id,
|
||||||
|
ExtrusionFaceCapType::Top => faces.end_cap_id = face_info.face_id,
|
||||||
|
ExtrusionFaceCapType::None => {
|
||||||
|
if let Some(curve_id) = face_info.curve_id {
|
||||||
|
faces.sides.insert(curve_id, face_info.face_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
faces
|
||||||
|
}
|
||||||
|
@ -170,5 +170,5 @@ async fn inner_loft(
|
|||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Using the first sketch as the base curve, idk we might want to change this later.
|
// Using the first sketch as the base curve, idk we might want to change this later.
|
||||||
do_post_extrude(sketch_groups[0].clone(), 0.0, id, args).await
|
do_post_extrude(sketch_groups[0].clone(), 0.0, args).await
|
||||||
}
|
}
|
||||||
|
@ -299,7 +299,7 @@ async fn inner_revolve(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do_post_extrude(sketch_group, 0.0, id, args).await
|
do_post_extrude(sketch_group, 0.0, args).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -50,13 +50,13 @@ pub fn token(i: &mut Located<&str>) -> PResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn block_comment(i: &mut Located<&str>) -> PResult<Token> {
|
fn block_comment(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
let inner = ("/*", take_until(0.., "*/"), "*/").recognize();
|
let inner = ("/*", take_until(0.., "*/"), "*/").take();
|
||||||
let (value, range) = inner.with_span().parse_next(i)?;
|
let (value, range) = inner.with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::BlockComment, value.to_string()))
|
Ok(Token::from_range(range, TokenType::BlockComment, value.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
|
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).recognize();
|
let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).take();
|
||||||
let (value, range) = inner.with_span().parse_next(i)?;
|
let (value, range) = inner.with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
|
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ fn number(i: &mut Located<&str>) -> PResult<Token> {
|
|||||||
// No digits before the decimal point.
|
// No digits before the decimal point.
|
||||||
('.', digit1).map(|_| ()),
|
('.', digit1).map(|_| ()),
|
||||||
));
|
));
|
||||||
let (value, range) = number_parser.recognize().with_span().parse_next(i)?;
|
let (value, range) = number_parser.take().with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::Number, value.to_string()))
|
Ok(Token::from_range(range, TokenType::Number, value.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ fn inner_word(i: &mut Located<&str>) -> PResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn word(i: &mut Located<&str>) -> PResult<Token> {
|
fn word(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
let (value, range) = inner_word.recognize().with_span().parse_next(i)?;
|
let (value, range) = inner_word.take().with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::Word, value.to_string()))
|
Ok(Token::from_range(range, TokenType::Word, value.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,9 +162,9 @@ fn inner_single_quote(i: &mut Located<&str>) -> PResult<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn string(i: &mut Located<&str>) -> PResult<Token> {
|
fn string(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
let single_quoted_string = ('\'', inner_single_quote.recognize(), '\'');
|
let single_quoted_string = ('\'', inner_single_quote.take(), '\'');
|
||||||
let double_quoted_string = ('"', inner_double_quote.recognize(), '"');
|
let double_quoted_string = ('"', inner_double_quote.take(), '"');
|
||||||
let either_quoted_string = alt((single_quoted_string.recognize(), double_quoted_string.recognize()));
|
let either_quoted_string = alt((single_quoted_string.take(), double_quoted_string.take()));
|
||||||
let (value, range): (&str, _) = either_quoted_string.with_span().parse_next(i)?;
|
let (value, range): (&str, _) = either_quoted_string.with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::String, value.to_string()))
|
Ok(Token::from_range(range, TokenType::String, value.to_string()))
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 99 KiB After Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 135 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 101 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 91 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 65 KiB |
81
src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
// 2x8 Lego Brick
|
||||||
|
// A standard Lego brick with 2 bumps wide and 8 bumps long.
|
||||||
|
// Define constants
|
||||||
|
const lbumps = 10 // number of bumps long
|
||||||
|
const wbumps = {{N}} // number of bumps wide
|
||||||
|
const pitch = 8.0
|
||||||
|
const clearance = 0.1
|
||||||
|
const bumpDiam = 4.8
|
||||||
|
const bumpHeight = 1.8
|
||||||
|
const height = 9.6
|
||||||
|
const t = (pitch - (2 * clearance) - bumpDiam) / 2.0
|
||||||
|
const totalLength = lbumps * pitch - (2.0 * clearance)
|
||||||
|
const totalWidth = wbumps * pitch - (2.0 * clearance)
|
||||||
|
// Create the plane for the pegs. This is a hack so that the pegs can be patterned along the face of the lego base.
|
||||||
|
const pegFace = {
|
||||||
|
plane: {
|
||||||
|
origin: { x: 0, y: 0, z: height },
|
||||||
|
xAxis: { x: 1, y: 0, z: 0 },
|
||||||
|
yAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
zAxis: { x: 0, y: 0, z: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the plane for the tubes underneath the lego. This is a hack so that the tubes can be patterned underneath the lego.
|
||||||
|
const tubeFace = {
|
||||||
|
plane: {
|
||||||
|
origin: { x: 0, y: 0, z: height - t },
|
||||||
|
xAxis: { x: 1, y: 0, z: 0 },
|
||||||
|
yAxis: { x: 0, y: 1, z: 0 },
|
||||||
|
zAxis: { x: 0, y: 0, z: 1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make the base
|
||||||
|
const s = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-totalWidth / 2, -totalLength / 2], %)
|
||||||
|
|> line([totalWidth, 0], %)
|
||||||
|
|> line([0, totalLength], %)
|
||||||
|
|> line([-totalWidth, 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(height, %)
|
||||||
|
|
||||||
|
// Sketch and extrude a rectangular shape to create the shell underneath the lego. This is a hack until we have a shell function.
|
||||||
|
const shellExtrude = startSketchOn(s, "start")
|
||||||
|
|> startProfileAt([
|
||||||
|
-(totalWidth / 2 - t),
|
||||||
|
-(totalLength / 2 - t)
|
||||||
|
], %)
|
||||||
|
|> line([totalWidth - (2 * t), 0], %)
|
||||||
|
|> line([0, totalLength - (2 * t)], %)
|
||||||
|
|> line([-(totalWidth - (2 * t)), 0], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(-(height - t), %)
|
||||||
|
|
||||||
|
fn tr = (i) => {
|
||||||
|
let j = i + 1
|
||||||
|
let x = (j/wbumps) * pitch
|
||||||
|
let y = (j % wbumps) * pitch
|
||||||
|
return {
|
||||||
|
translate: [x, y, 0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the pegs on the top of the base
|
||||||
|
const totalBumps = (wbumps * lbumps)-1
|
||||||
|
const peg = startSketchOn(s, 'end')
|
||||||
|
|> circle([
|
||||||
|
-(pitch*(wbumps-1)/2),
|
||||||
|
-(pitch*(lbumps-1)/2)
|
||||||
|
], bumpDiam / 2, %)
|
||||||
|
|> patternLinear2d({
|
||||||
|
axis: [1, 0],
|
||||||
|
repetitions: wbumps-1,
|
||||||
|
distance: pitch
|
||||||
|
}, %)
|
||||||
|
|> patternLinear2d({
|
||||||
|
axis: [0, 1],
|
||||||
|
repetitions: lbumps-1,
|
||||||
|
distance: pitch
|
||||||
|
}, %)
|
||||||
|
|> extrude(bumpHeight, %)
|
||||||
|
// |> patternTransform(int(totalBumps-1), tr, %)
|
||||||
|
|