Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
60e187bd3e | |||
c64175425b | |||
36464e6984 | |||
2f0002e53c | |||
482833c88f | |||
d9d0a72306 | |||
65cd9fab64 | |||
5e41e382ce | |||
1e3cb00092 | |||
d1a2bd01ca | |||
aca13d087b | |||
fcdde3e482 | |||
a1df3d0ffc | |||
1852e6167b | |||
29bf77bb82 | |||
e81b614523 | |||
5a5fe3bb95 | |||
0710f6e5f2 | |||
c9d5633647 |
13
README.md
@ -135,9 +135,18 @@ After it runs you should just need to push the push the branch and open a PR (it
|
|||||||
|
|
||||||
The PR may serve as a place to discuss the human-readable changelog and extra QA.
|
The PR may serve as a place to discuss the human-readable changelog and extra QA.
|
||||||
|
|
||||||
2. Merge the PR
|
2. Smoke test the artifact from the above PR
|
||||||
|
We don't have a strict process, but click around and check for anything obvious
|
||||||
|
One of the artifacts is called updater-test, because we don't have a way to test this fully automated, we have a semi-automated process.
|
||||||
|
|
||||||
|
Download updater-test zip file, install the app, run it, expect an updater prompt to v0.99.99, install it and check that the app comes back at that version (on both macOS and Windows).
|
||||||
|
|
||||||
|
3. Merge the PR
|
||||||
|
|
||||||
|
|
||||||
|
4. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named)
|
||||||
|
|
||||||
|
|
||||||
3. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named)
|
|
||||||
|
|
||||||
## Fuzzing the parser
|
## Fuzzing the parser
|
||||||
|
|
||||||
|
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 249 KiB After Width: | Height: | Size: 249 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 171 KiB |
@ -3099,6 +3099,49 @@ const sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Fillet button states test', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([-5, -5], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const selectSegment = () => page.getByText(`line([10, 0], %)`).click()
|
||||||
|
const selectClose = () => page.getByText(`close(%)`).click()
|
||||||
|
const clickEmpty = () => page.mouse.click(950, 100)
|
||||||
|
|
||||||
|
// expect fillet button without any bodies in the scene
|
||||||
|
await selectSegment()
|
||||||
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
||||||
|
await clickEmpty()
|
||||||
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
||||||
|
|
||||||
|
// test fillet button with the body in the scene
|
||||||
|
const codeToAdd = `${await u.codeLocator.allInnerTexts()}
|
||||||
|
const extrude001 = extrude(10, sketch001)`
|
||||||
|
await u.codeLocator.fill(codeToAdd)
|
||||||
|
await selectSegment()
|
||||||
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
|
await selectClose()
|
||||||
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeDisabled()
|
||||||
|
await clickEmpty()
|
||||||
|
await expect(page.getByRole('button', { name: 'Fillet' })).toBeEnabled()
|
||||||
|
})
|
||||||
|
|
||||||
const removeAfterFirstParenthesis = (inputString: string) => {
|
const removeAfterFirstParenthesis = (inputString: string) => {
|
||||||
const index = inputString.indexOf('(')
|
const index = inputString.indexOf('(')
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
@ -3500,11 +3543,62 @@ test.describe('Command bar tests', () => {
|
|||||||
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
test('Command bar works and can change a setting', async ({ page }) => {
|
|
||||||
|
test('Fillet from command bar', async ({ page }) => {
|
||||||
|
await page.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-5, -5], %)
|
||||||
|
|> line([0, 10], %)
|
||||||
|
|> line([10, 0], %)
|
||||||
|
|> line([0, -10], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(-10, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const selectSegment = () => page.getByText(`line([0, -10], %)`).click()
|
||||||
|
|
||||||
|
await selectSegment()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByRole('button', { name: 'Fillet' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-activeLine')).toContainText(
|
||||||
|
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Command bar can change a setting, and switch back and forth between arguments', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const commandBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
const themeOption = page.getByRole('option', {
|
||||||
|
name: 'theme',
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
const commandLevelArgButton = page.getByRole('button', { name: 'level' })
|
||||||
|
const commandThemeArgButton = page.getByRole('button', { name: 'value' })
|
||||||
|
// This selector changes after we set the setting
|
||||||
|
let commandOptionInput = page.getByPlaceholder('Select an option')
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
@ -3515,23 +3609,17 @@ test.describe('Command bar tests', () => {
|
|||||||
.or(page.getByRole('button', { name: '⌘K' }))
|
.or(page.getByRole('button', { name: '⌘K' }))
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
||||||
await expect(cmdSearchBar).not.toBeVisible()
|
await expect(cmdSearchBar).not.toBeVisible()
|
||||||
|
|
||||||
// Now try the same, but with the keyboard shortcut, check focus
|
// Now try the same, but with the keyboard shortcut, check focus
|
||||||
await page.keyboard.press('Meta+K')
|
await page.keyboard.press('Meta+K')
|
||||||
cmdSearchBar = page.getByPlaceholder('Search commands')
|
|
||||||
await expect(cmdSearchBar).toBeVisible()
|
await expect(cmdSearchBar).toBeVisible()
|
||||||
await expect(cmdSearchBar).toBeFocused()
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
// Try typing in the command bar
|
// Try typing in the command bar
|
||||||
await page.keyboard.type('theme')
|
await cmdSearchBar.fill('theme')
|
||||||
const themeOption = page.getByRole('option', {
|
|
||||||
name: 'Settings · app · theme',
|
|
||||||
})
|
|
||||||
await expect(themeOption).toBeVisible()
|
await expect(themeOption).toBeVisible()
|
||||||
await themeOption.click()
|
await themeOption.click()
|
||||||
const themeInput = page.getByPlaceholder('Select an option')
|
const themeInput = page.getByPlaceholder('Select an option')
|
||||||
@ -3553,6 +3641,24 @@ test.describe('Command bar tests', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
// Check that the theme changed
|
// Check that the theme changed
|
||||||
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
await expect(page.locator('body')).not.toHaveClass(`body-bg dark`)
|
||||||
|
|
||||||
|
commandOptionInput = page.getByPlaceholder('system')
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2882
|
||||||
|
await commandBarButton.click()
|
||||||
|
await cmdSearchBar.focus()
|
||||||
|
await cmdSearchBar.fill('theme')
|
||||||
|
await themeOption.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await commandOptionInput.focus()
|
||||||
|
await commandOptionInput.fill('lig')
|
||||||
|
await commandLevelArgButton.click()
|
||||||
|
await expect(commandLevelArgButton).toBeDisabled()
|
||||||
|
|
||||||
|
// Test case for https://github.com/KittyCAD/modeling-app/issues/2881
|
||||||
|
await commandThemeArgButton.click()
|
||||||
|
await expect(commandThemeArgButton).toBeDisabled()
|
||||||
|
await expect(commandLevelArgButton).toHaveText('level: project')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Command bar keybinding works from code editor and can change a setting', async ({
|
test('Command bar keybinding works from code editor and can change a setting', async ({
|
||||||
@ -3577,7 +3683,7 @@ test.describe('Command bar tests', () => {
|
|||||||
await expect(cmdSearchBar).toBeFocused()
|
await expect(cmdSearchBar).toBeFocused()
|
||||||
|
|
||||||
// Try typing in the command bar
|
// Try typing in the command bar
|
||||||
await page.keyboard.type('theme')
|
await cmdSearchBar.fill('theme')
|
||||||
const themeOption = page.getByRole('option', {
|
const themeOption = page.getByRole('option', {
|
||||||
name: 'Settings · app · theme',
|
name: 'Settings · app · theme',
|
||||||
})
|
})
|
||||||
@ -3648,7 +3754,9 @@ test.describe('Command bar tests', () => {
|
|||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
// Assert that we're on the distance step
|
// Assert that we're on the distance step
|
||||||
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
|
).toBeDisabled()
|
||||||
|
|
||||||
// Assert that the an alternative variable name is chosen,
|
// Assert that the an alternative variable name is chosen,
|
||||||
// since the default variable name is already in use (distance)
|
// since the default variable name is already in use (distance)
|
||||||
@ -3663,11 +3771,12 @@ test.describe('Command bar tests', () => {
|
|||||||
|
|
||||||
// Review step and argument hotkeys
|
// Review step and argument hotkeys
|
||||||
await expect(submitButton).toBeEnabled()
|
await expect(submitButton).toBeEnabled()
|
||||||
await page.keyboard.press('Backspace')
|
await expect(submitButton).toBeFocused()
|
||||||
|
await submitButton.press('Backspace')
|
||||||
|
|
||||||
// Assert we're back on the distance step
|
// Assert we're back on the distance step
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Distance 5', exact: false })
|
page.getByRole('button', { name: 'distance', exact: false })
|
||||||
).toBeDisabled()
|
).toBeDisabled()
|
||||||
|
|
||||||
await continueButton.click()
|
await continueButton.click()
|
||||||
@ -3691,6 +3800,47 @@ const extrude001 = extrude(distance001, sketch001)`.replace(
|
|||||||
) // remove newlines
|
) // remove newlines
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Can switch between sketch tools via command bar', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
const sketchButton = page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
const cmdBarButton = page.getByRole('button', { name: 'Commands' })
|
||||||
|
const rectangleToolCommand = page.getByRole('option', {
|
||||||
|
name: 'Rectangle',
|
||||||
|
})
|
||||||
|
const rectangleToolButton = page.getByRole('button', { name: 'Rectangle' })
|
||||||
|
const lineToolCommand = page.getByRole('option', { name: 'Line' })
|
||||||
|
const lineToolButton = page.getByRole('button', { name: 'Line' })
|
||||||
|
const arcToolCommand = page.getByRole('option', { name: 'Tangential Arc' })
|
||||||
|
const arcToolButton = page.getByRole('button', { name: 'Tangential Arc' })
|
||||||
|
|
||||||
|
// Start a sketch
|
||||||
|
await sketchButton.click()
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
// Switch between sketch tools via the command bar
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await rectangleToolCommand.click()
|
||||||
|
await expect(rectangleToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await lineToolCommand.click()
|
||||||
|
await expect(lineToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// Click in the scene a couple times to draw a line
|
||||||
|
// so tangential arc is valid
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.mouse.move(700, 300, { steps: 5 })
|
||||||
|
await page.mouse.click(700, 300)
|
||||||
|
|
||||||
|
// switch to tangential arc via command bar
|
||||||
|
await cmdBarButton.click()
|
||||||
|
await arcToolCommand.click()
|
||||||
|
await expect(arcToolButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test.describe('Regression tests', () => {
|
test.describe('Regression tests', () => {
|
||||||
@ -4642,10 +4792,10 @@ test.describe('Sketch tests', () => {
|
|||||||
// click extrude
|
// click extrude
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
// sketch selection should already have been made. "Selection 1 face" only show up when the selection has been made already
|
// sketch selection should already have been made. "Selection: 1 face" only show up when the selection has been made already
|
||||||
// otherwise the cmdbar would be waiting for a selection.
|
// otherwise the cmdbar would be waiting for a selection.
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Selection 1 face' })
|
page.getByRole('button', { name: 'selection : 1 face', exact: false })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
})
|
})
|
||||||
test("Existing sketch with bad code delete user's code", async ({ page }) => {
|
test("Existing sketch with bad code delete user's code", async ({ page }) => {
|
||||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 62 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.24.1",
|
"version": "0.24.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.17.0",
|
"@codemirror/autocomplete": "^6.17.0",
|
||||||
|
@ -30,7 +30,7 @@ import { URI } from 'vscode-uri'
|
|||||||
import { LanguageServerClient } from '../client'
|
import { LanguageServerClient } from '../client'
|
||||||
import { CompletionItemKindMap } from './autocomplete'
|
import { CompletionItemKindMap } from './autocomplete'
|
||||||
import { addToken, SemanticToken } from './semantic-tokens'
|
import { addToken, SemanticToken } from './semantic-tokens'
|
||||||
import { deferExecution, posToOffset, formatMarkdownContents } from './util'
|
import { posToOffset, formatMarkdownContents } from './util'
|
||||||
import lspAutocompleteExt from './autocomplete'
|
import lspAutocompleteExt from './autocomplete'
|
||||||
import lspHoverExt from './hover'
|
import lspHoverExt from './hover'
|
||||||
import lspFormatExt from './format'
|
import lspFormatExt from './format'
|
||||||
|
@ -80,5 +80,5 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"productName": "Zoo Modeling App",
|
"productName": "Zoo Modeling App",
|
||||||
"version": "0.24.1"
|
"version": "0.24.2"
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import {
|
|||||||
canRectangleTool,
|
canRectangleTool,
|
||||||
isEditingExistingSketch,
|
isEditingExistingSketch,
|
||||||
} from 'machines/modelingMachine'
|
} from 'machines/modelingMachine'
|
||||||
|
import { DEV } from 'env'
|
||||||
|
|
||||||
export function Toolbar({
|
export function Toolbar({
|
||||||
className = '',
|
className = '',
|
||||||
@ -60,7 +61,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'line',
|
data: { tool: 'line' },
|
||||||
}),
|
}),
|
||||||
{ enabled: !disableLineButton, scopes: ['sketch'] }
|
{ enabled: !disableLineButton, scopes: ['sketch'] }
|
||||||
)
|
)
|
||||||
@ -75,7 +76,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'tangentialArc',
|
data: { tool: 'tangentialArc' },
|
||||||
}),
|
}),
|
||||||
{ enabled: !disableTangentialArc, scopes: ['sketch'] }
|
{ enabled: !disableTangentialArc, scopes: ['sketch'] }
|
||||||
)
|
)
|
||||||
@ -89,7 +90,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'rectangle',
|
data: { tool: 'rectangle' },
|
||||||
}),
|
}),
|
||||||
{ enabled: !disableRectangle, scopes: ['sketch'] }
|
{ enabled: !disableRectangle, scopes: ['sketch'] }
|
||||||
)
|
)
|
||||||
@ -118,6 +119,16 @@ export function Toolbar({
|
|||||||
}),
|
}),
|
||||||
{ enabled: !disableAllButtons, scopes: ['modeling'] }
|
{ enabled: !disableAllButtons, scopes: ['modeling'] }
|
||||||
)
|
)
|
||||||
|
const disableFillet = !state.can('Fillet') || disableAllButtons
|
||||||
|
useHotkeys(
|
||||||
|
'f',
|
||||||
|
() =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Fillet', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
{ enabled: !disableFillet, scopes: ['modeling'] }
|
||||||
|
)
|
||||||
|
|
||||||
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
||||||
const span = toolbarButtonsRef.current
|
const span = toolbarButtonsRef.current
|
||||||
@ -263,7 +274,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'line',
|
data: { tool: 'line' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
aria-pressed={state?.matches('Sketch.Line tool')}
|
aria-pressed={state?.matches('Sketch.Line tool')}
|
||||||
@ -293,7 +304,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'tangentialArc',
|
data: { tool: 'tangentialArc' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
||||||
@ -323,7 +334,7 @@ export function Toolbar({
|
|||||||
? send('CancelSketch')
|
? send('CancelSketch')
|
||||||
: send({
|
: send({
|
||||||
type: 'change tool',
|
type: 'change tool',
|
||||||
data: 'rectangle',
|
data: { tool: 'rectangle' },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
aria-pressed={state.matches('Sketch.Rectangle tool')}
|
aria-pressed={state.matches('Sketch.Rectangle tool')}
|
||||||
@ -404,6 +415,36 @@ export function Toolbar({
|
|||||||
</ActionButton>
|
</ActionButton>
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
|
{state.matches('idle') && (DEV || (window as any)._enableFillet) && (
|
||||||
|
<li className="contents">
|
||||||
|
<ActionButton
|
||||||
|
className={buttonClassName}
|
||||||
|
Element="button"
|
||||||
|
onClick={() =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Fillet', groupId: 'modeling' },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
disabled={disableFillet}
|
||||||
|
title={disableFillet ? 'fillet' : "edge can't be filleted"}
|
||||||
|
iconStart={{
|
||||||
|
icon: 'fillet', // todo: add fillet icon
|
||||||
|
iconClassName,
|
||||||
|
bgClassName,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Fillet
|
||||||
|
<Tooltip
|
||||||
|
delay={1250}
|
||||||
|
position="bottom"
|
||||||
|
className="!px-2 !text-xs"
|
||||||
|
>
|
||||||
|
Shortcut: F
|
||||||
|
</Tooltip>
|
||||||
|
</ActionButton>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</menu>
|
</menu>
|
||||||
)
|
)
|
||||||
|
@ -41,6 +41,7 @@ function CommandArgOptionInput({
|
|||||||
)
|
)
|
||||||
const inputRef = useRef<HTMLInputElement>(null)
|
const inputRef = useRef<HTMLInputElement>(null)
|
||||||
const formRef = useRef<HTMLFormElement>(null)
|
const formRef = useRef<HTMLFormElement>(null)
|
||||||
|
const [shouldSubmitOnChange, setShouldSubmitOnChange] = useState(false)
|
||||||
const [selectedOption, setSelectedOption] = useState<
|
const [selectedOption, setSelectedOption] = useState<
|
||||||
CommandArgumentOption<unknown>
|
CommandArgumentOption<unknown>
|
||||||
>(currentOption || resolvedOptions[0])
|
>(currentOption || resolvedOptions[0])
|
||||||
@ -82,9 +83,11 @@ function CommandArgOptionInput({
|
|||||||
// We deal with the whole option object internally
|
// We deal with the whole option object internally
|
||||||
setSelectedOption(option)
|
setSelectedOption(option)
|
||||||
|
|
||||||
// But we only submit the value
|
// But we only submit the value itself
|
||||||
|
if (shouldSubmitOnChange) {
|
||||||
onSubmit(option.value)
|
onSubmit(option.value)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
@ -94,7 +97,18 @@ function CommandArgOptionInput({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form id="arg-form" onSubmit={handleSubmit} ref={formRef}>
|
<form
|
||||||
|
id="arg-form"
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
ref={formRef}
|
||||||
|
onKeyDownCapture={(e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
setShouldSubmitOnChange(true)
|
||||||
|
} else {
|
||||||
|
setShouldSubmitOnChange(false)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Combobox
|
<Combobox
|
||||||
value={selectedOption}
|
value={selectedOption}
|
||||||
onChange={handleSelectOption}
|
onChange={handleSelectOption}
|
||||||
@ -118,6 +132,12 @@ function CommandArgOptionInput({
|
|||||||
if (event.key === 'Backspace' && !event.currentTarget.value) {
|
if (event.key === 'Backspace' && !event.currentTarget.value) {
|
||||||
stepBack()
|
stepBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
setShouldSubmitOnChange(true)
|
||||||
|
} else {
|
||||||
|
setShouldSubmitOnChange(false)
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
value={query}
|
value={query}
|
||||||
placeholder={
|
placeholder={
|
||||||
@ -136,6 +156,9 @@ function CommandArgOptionInput({
|
|||||||
<Combobox.Options
|
<Combobox.Options
|
||||||
static
|
static
|
||||||
className="overflow-y-auto max-h-96 cursor-pointer"
|
className="overflow-y-auto max-h-96 cursor-pointer"
|
||||||
|
onMouseDown={() => {
|
||||||
|
setShouldSubmitOnChange(true)
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{filteredOptions?.map((option) => (
|
{filteredOptions?.map((option) => (
|
||||||
<Combobox.Option
|
<Combobox.Option
|
||||||
|
@ -114,6 +114,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
|||||||
>
|
>
|
||||||
{argName}
|
{argName}
|
||||||
</span>
|
</span>
|
||||||
|
<span className="sr-only">: </span>
|
||||||
{argValue ? (
|
{argValue ? (
|
||||||
arg.inputType === 'selection' ? (
|
arg.inputType === 'selection' ? (
|
||||||
getSelectionTypeDisplayText(argValue as Selections)
|
getSelectionTypeDisplayText(argValue as Selections)
|
||||||
|
@ -28,6 +28,11 @@ export const CommandBarProvider = ({
|
|||||||
Object.keys(context.selectedCommand?.args).length === 0
|
Object.keys(context.selectedCommand?.args).length === 0
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
'All arguments are skippable': (context, _event) => {
|
||||||
|
return Object.values(context.selectedCommand!.args!).every(
|
||||||
|
(argConfig) => argConfig.skip
|
||||||
|
)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
|
|||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { CommandArgument } from 'lib/commandTypes'
|
import { CommandArgument } from 'lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
|
Selection,
|
||||||
canSubmitSelectionArg,
|
canSubmitSelectionArg,
|
||||||
getSelectionType,
|
getSelectionType,
|
||||||
getSelectionTypeDisplayText,
|
getSelectionTypeDisplayText,
|
||||||
@ -11,6 +12,25 @@ import { modelingMachine } from 'machines/modelingMachine'
|
|||||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { StateFrom } from 'xstate'
|
import { StateFrom } from 'xstate'
|
||||||
|
|
||||||
|
const semanticEntityNames: { [key: string]: Array<Selection['type']> } = {
|
||||||
|
face: ['extrude-wall', 'start-cap', 'end-cap'],
|
||||||
|
edge: ['edge', 'line', 'arc'],
|
||||||
|
point: ['point', 'line-end', 'line-mid'],
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSemanticSelectionType(selectionType: Array<Selection['type']>) {
|
||||||
|
const semanticSelectionType = new Set()
|
||||||
|
selectionType.forEach((type) => {
|
||||||
|
Object.entries(semanticEntityNames).forEach(([entity, entityTypes]) => {
|
||||||
|
if (entityTypes.includes(type)) {
|
||||||
|
semanticSelectionType.add(entity)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.from(semanticSelectionType)
|
||||||
|
}
|
||||||
|
|
||||||
const selectionSelector = (snapshot: StateFrom<typeof modelingMachine>) =>
|
const selectionSelector = (snapshot: StateFrom<typeof modelingMachine>) =>
|
||||||
snapshot.context.selectionRanges
|
snapshot.context.selectionRanges
|
||||||
|
|
||||||
@ -85,7 +105,9 @@ function CommandBarSelectionInput({
|
|||||||
>
|
>
|
||||||
{canSubmitSelection
|
{canSubmitSelection
|
||||||
? getSelectionTypeDisplayText(selection) + ' selected'
|
? getSelectionTypeDisplayText(selection) + ' selected'
|
||||||
: `Please select ${arg.multiple ? 'one or more faces' : 'one face'}`}
|
: `Please select ${
|
||||||
|
arg.multiple ? 'one or more ' : 'one '
|
||||||
|
}${getSemanticSelectionType(arg.selectionTypes).join(' or ')}`}
|
||||||
<input
|
<input
|
||||||
id="selection"
|
id="selection"
|
||||||
name="selection"
|
name="selection"
|
||||||
|
@ -70,19 +70,23 @@ function CommandComboBox({
|
|||||||
>
|
>
|
||||||
{filteredOptions?.map((option) => (
|
{filteredOptions?.map((option) => (
|
||||||
<Combobox.Option
|
<Combobox.Option
|
||||||
key={option.name}
|
key={option.groupId + option.name + (option.displayName || '')}
|
||||||
value={option}
|
value={option}
|
||||||
className="flex items-center gap-2 px-4 py-1 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
|
className="flex items-center gap-4 px-4 py-1.5 first:mt-2 last:mb-2 ui-active:bg-primary/10 dark:ui-active:bg-chalkboard-90"
|
||||||
>
|
>
|
||||||
{'icon' in option && option.icon && (
|
{'icon' in option && option.icon && (
|
||||||
<CustomIcon name={option.icon} className="w-5 h-5" />
|
<CustomIcon name={option.icon} className="w-5 h-5" />
|
||||||
)}
|
)}
|
||||||
<p className="flex-grow">{option.displayName || option.name} </p>
|
<div className="flex-grow flex flex-col">
|
||||||
|
<p className="my-0 leading-tight">
|
||||||
|
{option.displayName || option.name}{' '}
|
||||||
|
</p>
|
||||||
{option.description && (
|
{option.description && (
|
||||||
<p className="text-xs text-chalkboard-60 dark:text-chalkboard-40">
|
<p className="my-0 text-xs text-chalkboard-60 dark:text-chalkboard-50">
|
||||||
{option.description}
|
{option.description}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
</div>
|
||||||
</Combobox.Option>
|
</Combobox.Option>
|
||||||
))}
|
))}
|
||||||
</Combobox.Options>
|
</Combobox.Options>
|
||||||
|
@ -131,6 +131,16 @@ const CustomIconMap = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
code: (
|
||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M10.7071 5L7.77734 14.7794L8.73527 15.0663L11.665 5.28698L10.7071 5ZM2.35356 9.64644L5.85362 6.14644L6.56072 6.85355L3.41423 10L6.56072 13.1464L5.85362 13.8536L2.35356 10.3536L2 10L2.35356 9.64644ZM17.0607 9.64644L13.5607 6.14644L12.8536 6.85355L16 10L12.8536 13.1464L13.5607 13.8535L17.0607 10.3536L17.4142 10L17.0607 9.64644Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
dimension: (
|
dimension: (
|
||||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
@ -177,6 +187,22 @@ const CustomIconMap = {
|
|||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
),
|
),
|
||||||
|
fillet: (
|
||||||
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M8 5H5V15H15V12C15 8.13401 11.866 5 8 5ZM5 4H4V5V15V16H5H15H16V15V12C16 7.58172 12.4183 4 8 4H5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M4.5 3.5H5.5H8.5C12.9183 3.5 16.5 7.08172 16.5 11.5V14.5V15.5H16V12C16 7.58172 12.4182 4 7.99996 4H4.5V3.5Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
file: (
|
file: (
|
||||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path
|
||||||
|
@ -42,7 +42,7 @@ import {
|
|||||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import {
|
import {
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
TANGENTIAL_ARC_TO_SEGMENT,
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
@ -72,6 +72,7 @@ import { uuidv4 } from 'lib/utils'
|
|||||||
import { err, trap } from 'lib/trap'
|
import { err, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
|
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -164,7 +165,7 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
store.videoElement?.pause()
|
store.videoElement?.pause()
|
||||||
kclManager.executeCode(true).then(() => {
|
kclManager.executeCode(true).then(() => {
|
||||||
if (engineCommandManager.engineConnection?.freezeFrame) return
|
if (engineCommandManager.engineConnection?.idleMode) return
|
||||||
|
|
||||||
store.videoElement?.play()
|
store.videoElement?.play()
|
||||||
})
|
})
|
||||||
@ -444,6 +445,12 @@ export const ModelingMachineProvider = ({
|
|||||||
if (selectionRanges.codeBasedSelections.length <= 0) return false
|
if (selectionRanges.codeBasedSelections.length <= 0) return false
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
'has valid fillet selection': ({ selectionRanges }) =>
|
||||||
|
hasValidFilletSelection({
|
||||||
|
selectionRanges,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
code: codeManager.code,
|
||||||
|
}),
|
||||||
'Selection is on face': ({ selectionRanges }, { data }) => {
|
'Selection is on face': ({ selectionRanges }, { data }) => {
|
||||||
if (data?.forceNewSketch) return false
|
if (data?.forceNewSketch) return false
|
||||||
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||||
@ -494,7 +501,6 @@ export const ModelingMachineProvider = ({
|
|||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
data.sketchPathToNode,
|
data.sketchPathToNode,
|
||||||
data.extrudePathToNode,
|
data.extrudePathToNode,
|
||||||
kclManager.programMemory,
|
|
||||||
data.cap
|
data.cap
|
||||||
)
|
)
|
||||||
if (trap(sketched)) return Promise.reject(sketched)
|
if (trap(sketched)) return Promise.reject(sketched)
|
||||||
@ -920,7 +926,7 @@ export const ModelingMachineProvider = ({
|
|||||||
state: modelingState,
|
state: modelingState,
|
||||||
send: modelingSend,
|
send: modelingSend,
|
||||||
actor: modelingActor,
|
actor: modelingActor,
|
||||||
commandBarConfig: modelingMachineConfig,
|
commandBarConfig: modelingMachineCommandConfig,
|
||||||
allCommandsRequireNetwork: true,
|
allCommandsRequireNetwork: true,
|
||||||
// TODO for when sketch tools are in the toolbar: This was added when we used one "Cancel" event,
|
// TODO for when sketch tools are in the toolbar: This was added when we used one "Cancel" event,
|
||||||
// but we need to support "SketchCancel" and basically
|
// but we need to support "SketchCancel" and basically
|
||||||
|
@ -10,6 +10,10 @@ import { btnName } from 'lib/cameraControls'
|
|||||||
import { sendSelectEventToEngine } from 'lib/selections'
|
import { sendSelectEventToEngine } from 'lib/selections'
|
||||||
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||||
import { useAppStream } from 'AppState'
|
import { useAppStream } from 'AppState'
|
||||||
|
import {
|
||||||
|
EngineConnectionStateType,
|
||||||
|
DisconnectingType,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
|
|
||||||
export const Stream = () => {
|
export const Stream = () => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
@ -19,13 +23,28 @@ export const Stream = () => {
|
|||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const { state, send, context } = useModelingContext()
|
const { state, send, context } = useModelingContext()
|
||||||
const { mediaStream } = useAppStream()
|
const { mediaStream } = useAppStream()
|
||||||
const { overallState } = useNetworkContext()
|
const { overallState, immediateState } = useNetworkContext()
|
||||||
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
||||||
|
const [isPaused, setIsPaused] = useState(false)
|
||||||
|
|
||||||
|
const IDLE = settings.context.app.streamIdleMode.current
|
||||||
|
|
||||||
const isNetworkOkay =
|
const isNetworkOkay =
|
||||||
overallState === NetworkHealthState.Ok ||
|
overallState === NetworkHealthState.Ok ||
|
||||||
overallState === NetworkHealthState.Weak
|
overallState === NetworkHealthState.Weak
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
immediateState.type === EngineConnectionStateType.Disconnecting &&
|
||||||
|
immediateState.value.type === DisconnectingType.Pause
|
||||||
|
) {
|
||||||
|
setIsPaused(true)
|
||||||
|
}
|
||||||
|
if (immediateState.type === EngineConnectionStateType.Connecting) {
|
||||||
|
setIsPaused(false)
|
||||||
|
}
|
||||||
|
}, [immediateState])
|
||||||
|
|
||||||
// Linux has a default behavior to paste text on middle mouse up
|
// Linux has a default behavior to paste text on middle mouse up
|
||||||
// This adds a listener to block that pasting if the click target
|
// This adds a listener to block that pasting if the click target
|
||||||
// is not a text input, so users can move in the 3D scene with
|
// is not a text input, so users can move in the 3D scene with
|
||||||
@ -53,7 +72,7 @@ export const Stream = () => {
|
|||||||
capture: true,
|
capture: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
const IDLE_TIME_MS = 1000 * 20
|
const IDLE_TIME_MS = 1000 * 60 * 2
|
||||||
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
|
let timeoutIdIdleA: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
|
||||||
const teardown = () => {
|
const teardown = () => {
|
||||||
@ -62,13 +81,11 @@ export const Stream = () => {
|
|||||||
sceneInfra.modelingSend({ type: 'Cancel' })
|
sceneInfra.modelingSend({ type: 'Cancel' })
|
||||||
// Give video time to pause
|
// Give video time to pause
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
engineCommandManager.engineConnection?.tearDown({ freeze: true })
|
engineCommandManager.tearDown({ idleMode: true })
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Teardown everything if we go hidden or reconnect
|
const onVisibilityChange = () => {
|
||||||
if (globalThis?.window?.document) {
|
|
||||||
globalThis.window.document.onvisibilitychange = () => {
|
|
||||||
if (globalThis.window.document.visibilityState === 'hidden') {
|
if (globalThis.window.document.visibilityState === 'hidden') {
|
||||||
clearTimeout(timeoutIdIdleA)
|
clearTimeout(timeoutIdIdleA)
|
||||||
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
@ -77,42 +94,65 @@ export const Stream = () => {
|
|||||||
engineCommandManager.engineConnection?.connect(true)
|
engineCommandManager.engineConnection?.connect(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Teardown everything if we go hidden or reconnect
|
||||||
|
if (IDLE) {
|
||||||
|
globalThis?.window?.document?.addEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
onVisibilityChange
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
||||||
|
|
||||||
const onAnyInput = () => {
|
const onAnyInput = () => {
|
||||||
if (!engineCommandManager.engineConnection?.isReady()) {
|
|
||||||
engineCommandManager.engineConnection?.connect(true)
|
|
||||||
}
|
|
||||||
// Clear both timers
|
// Clear both timers
|
||||||
clearTimeout(timeoutIdIdleA)
|
clearTimeout(timeoutIdIdleA)
|
||||||
clearTimeout(timeoutIdIdleB)
|
clearTimeout(timeoutIdIdleB)
|
||||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IDLE) {
|
||||||
globalThis?.window?.document?.addEventListener('keydown', onAnyInput)
|
globalThis?.window?.document?.addEventListener('keydown', onAnyInput)
|
||||||
globalThis?.window?.document?.addEventListener('mousemove', onAnyInput)
|
globalThis?.window?.document?.addEventListener('mousemove', onAnyInput)
|
||||||
globalThis?.window?.document?.addEventListener('mousedown', onAnyInput)
|
globalThis?.window?.document?.addEventListener('mousedown', onAnyInput)
|
||||||
globalThis?.window?.document?.addEventListener('scroll', onAnyInput)
|
globalThis?.window?.document?.addEventListener('scroll', onAnyInput)
|
||||||
globalThis?.window?.document?.addEventListener('touchstart', onAnyInput)
|
globalThis?.window?.document?.addEventListener('touchstart', onAnyInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IDLE) {
|
||||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||||
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
||||||
capture: true,
|
capture: true,
|
||||||
})
|
})
|
||||||
|
if (IDLE) {
|
||||||
|
clearTimeout(timeoutIdIdleA)
|
||||||
|
clearTimeout(timeoutIdIdleB)
|
||||||
|
|
||||||
|
globalThis?.window?.document?.removeEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
onVisibilityChange
|
||||||
|
)
|
||||||
globalThis?.window?.document?.removeEventListener('keydown', onAnyInput)
|
globalThis?.window?.document?.removeEventListener('keydown', onAnyInput)
|
||||||
globalThis?.window?.document?.removeEventListener('mousemove', onAnyInput)
|
globalThis?.window?.document?.removeEventListener(
|
||||||
globalThis?.window?.document?.removeEventListener('mousedown', onAnyInput)
|
'mousemove',
|
||||||
|
onAnyInput
|
||||||
|
)
|
||||||
|
globalThis?.window?.document?.removeEventListener(
|
||||||
|
'mousedown',
|
||||||
|
onAnyInput
|
||||||
|
)
|
||||||
globalThis?.window?.document?.removeEventListener('scroll', onAnyInput)
|
globalThis?.window?.document?.removeEventListener('scroll', onAnyInput)
|
||||||
globalThis?.window?.document?.removeEventListener(
|
globalThis?.window?.document?.removeEventListener(
|
||||||
'touchstart',
|
'touchstart',
|
||||||
onAnyInput
|
onAnyInput
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}, [])
|
}
|
||||||
|
}, [IDLE])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsFirstRender(kclManager.isFirstRender)
|
setIsFirstRender(kclManager.isFirstRender)
|
||||||
@ -235,6 +275,32 @@ export const Stream = () => {
|
|||||||
<ClientSideScene
|
<ClientSideScene
|
||||||
cameraControls={settings.context.modeling.mouseControls.current}
|
cameraControls={settings.context.modeling.mouseControls.current}
|
||||||
/>
|
/>
|
||||||
|
{isPaused && (
|
||||||
|
<div className="text-center absolute inset-0">
|
||||||
|
<div
|
||||||
|
className="flex flex-col items-center justify-center h-screen"
|
||||||
|
data-testid="paused"
|
||||||
|
>
|
||||||
|
<div className="border-primary border p-2 rounded-sm">
|
||||||
|
<svg
|
||||||
|
width="8"
|
||||||
|
height="12"
|
||||||
|
viewBox="0 0 8 12"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
clipRule="evenodd"
|
||||||
|
d="M2 12V0H0V12H2ZM8 12V0H6V12H8Z"
|
||||||
|
fill="var(--primary)"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-base mt-2 text-primary bold">Paused</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{(!isNetworkOkay || isLoading || isFirstRender) && !isFreezeFrame && (
|
{(!isNetworkOkay || isLoading || isFirstRender) && !isFreezeFrame && (
|
||||||
<div className="text-center absolute inset-0">
|
<div className="text-center absolute inset-0">
|
||||||
<Loading>
|
<Loading>
|
||||||
|
@ -1,11 +1,16 @@
|
|||||||
import { createContext, useContext } from 'react'
|
import { createContext, useContext } from 'react'
|
||||||
import {
|
import {
|
||||||
ConnectingTypeGroup,
|
ConnectingTypeGroup,
|
||||||
|
EngineConnectionStateType,
|
||||||
|
EngineConnectionState,
|
||||||
initialConnectingTypeGroupState,
|
initialConnectingTypeGroupState,
|
||||||
} from '../lang/std/engineConnection'
|
} from '../lang/std/engineConnection'
|
||||||
import { NetworkStatus, NetworkHealthState } from './useNetworkStatus'
|
import { NetworkStatus, NetworkHealthState } from './useNetworkStatus'
|
||||||
|
|
||||||
export const NetworkContext = createContext<NetworkStatus>({
|
export const NetworkContext = createContext<NetworkStatus>({
|
||||||
|
immediateState: {
|
||||||
|
type: EngineConnectionStateType.Disconnected,
|
||||||
|
} as EngineConnectionState,
|
||||||
hasIssues: undefined,
|
hasIssues: undefined,
|
||||||
overallState: NetworkHealthState.Disconnected,
|
overallState: NetworkHealthState.Disconnected,
|
||||||
internetConnected: true,
|
internetConnected: true,
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
EngineCommandManagerEvents,
|
EngineCommandManagerEvents,
|
||||||
EngineConnectionEvents,
|
EngineConnectionEvents,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
|
EngineConnectionState,
|
||||||
ErrorType,
|
ErrorType,
|
||||||
initialConnectingTypeGroupState,
|
initialConnectingTypeGroupState,
|
||||||
} from '../lang/std/engineConnection'
|
} from '../lang/std/engineConnection'
|
||||||
@ -19,6 +20,7 @@ export enum NetworkHealthState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NetworkStatus {
|
export interface NetworkStatus {
|
||||||
|
immediateState: EngineConnectionState
|
||||||
hasIssues: boolean | undefined
|
hasIssues: boolean | undefined
|
||||||
overallState: NetworkHealthState
|
overallState: NetworkHealthState
|
||||||
internetConnected: boolean
|
internetConnected: boolean
|
||||||
@ -33,6 +35,9 @@ export interface NetworkStatus {
|
|||||||
// Must be called from one place in the application.
|
// Must be called from one place in the application.
|
||||||
// We've chosen the <Router /> component for this.
|
// We've chosen the <Router /> component for this.
|
||||||
export function useNetworkStatus() {
|
export function useNetworkStatus() {
|
||||||
|
const [immediateState, setImmediateState] = useState<EngineConnectionState>({
|
||||||
|
type: EngineConnectionStateType.Disconnected,
|
||||||
|
})
|
||||||
const [steps, setSteps] = useState(
|
const [steps, setSteps] = useState(
|
||||||
structuredClone(initialConnectingTypeGroupState)
|
structuredClone(initialConnectingTypeGroupState)
|
||||||
)
|
)
|
||||||
@ -126,6 +131,7 @@ export function useNetworkStatus() {
|
|||||||
const onConnectionStateChange = ({
|
const onConnectionStateChange = ({
|
||||||
detail: engineConnectionState,
|
detail: engineConnectionState,
|
||||||
}: CustomEvent) => {
|
}: CustomEvent) => {
|
||||||
|
setImmediateState(engineConnectionState)
|
||||||
setSteps((steps) => {
|
setSteps((steps) => {
|
||||||
let nextSteps = structuredClone(steps)
|
let nextSteps = structuredClone(steps)
|
||||||
|
|
||||||
@ -215,6 +221,7 @@ export function useNetworkStatus() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
immediateState,
|
||||||
hasIssues,
|
hasIssues,
|
||||||
overallState,
|
overallState,
|
||||||
internetConnected,
|
internetConnected,
|
||||||
|
@ -30,9 +30,6 @@ export function useSetupEngineManager(
|
|||||||
const { setAppState } = useAppState()
|
const { setAppState } = useAppState()
|
||||||
const { setMediaStream } = useAppStream()
|
const { setMediaStream } = useAppStream()
|
||||||
|
|
||||||
const streamWidth = streamRef?.current?.offsetWidth
|
|
||||||
const streamHeight = streamRef?.current?.offsetHeight
|
|
||||||
|
|
||||||
const hasSetNonZeroDimensions = useRef<boolean>(false)
|
const hasSetNonZeroDimensions = useRef<boolean>(false)
|
||||||
|
|
||||||
if (settings.pool) {
|
if (settings.pool) {
|
||||||
@ -41,21 +38,19 @@ export function useSetupEngineManager(
|
|||||||
engineCommandManager.pool = settings.pool
|
engineCommandManager.pool = settings.pool
|
||||||
}
|
}
|
||||||
|
|
||||||
const startEngineInstance = () => {
|
const startEngineInstance = (restart: boolean = false) => {
|
||||||
// Load the engine command manager once with the initial width and height,
|
// Load the engine command manager once with the initial width and height,
|
||||||
// then we do not want to reload it.
|
// then we do not want to reload it.
|
||||||
const { width: quadWidth, height: quadHeight } = getDimensions(
|
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||||
streamWidth,
|
streamRef?.current?.offsetWidth ?? 0,
|
||||||
streamHeight
|
streamRef?.current?.offsetHeight ?? 0
|
||||||
)
|
)
|
||||||
if (
|
if (restart) {
|
||||||
!hasSetNonZeroDimensions.current &&
|
kclManager.isFirstRender = false
|
||||||
quadHeight &&
|
}
|
||||||
quadWidth &&
|
|
||||||
settings.modelingSend
|
|
||||||
) {
|
|
||||||
engineCommandManager.start({
|
engineCommandManager.start({
|
||||||
setMediaStream: setMediaStream,
|
restart,
|
||||||
|
setMediaStream: (mediaStream) => setMediaStream(mediaStream),
|
||||||
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
setIsStreamReady: (isStreamReady) => setAppState({ isStreamReady }),
|
||||||
width: quadWidth,
|
width: quadWidth,
|
||||||
height: quadHeight,
|
height: quadHeight,
|
||||||
@ -87,9 +82,16 @@ export function useSetupEngineManager(
|
|||||||
})
|
})
|
||||||
hasSetNonZeroDimensions.current = true
|
hasSetNonZeroDimensions.current = true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
useLayoutEffect(startEngineInstance, [
|
useLayoutEffect(() => {
|
||||||
|
const { width: quadWidth, height: quadHeight } = getDimensions(
|
||||||
|
streamRef?.current?.offsetWidth ?? 0,
|
||||||
|
streamRef?.current?.offsetHeight ?? 0
|
||||||
|
)
|
||||||
|
if (!hasSetNonZeroDimensions.current && quadHeight && quadWidth) {
|
||||||
|
startEngineInstance()
|
||||||
|
}
|
||||||
|
}, [
|
||||||
streamRef?.current?.offsetWidth,
|
streamRef?.current?.offsetWidth,
|
||||||
streamRef?.current?.offsetHeight,
|
streamRef?.current?.offsetHeight,
|
||||||
settings.modelingSend,
|
settings.modelingSend,
|
||||||
@ -98,8 +100,8 @@ export function useSetupEngineManager(
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = deferExecution(() => {
|
const handleResize = deferExecution(() => {
|
||||||
const { width, height } = getDimensions(
|
const { width, height } = getDimensions(
|
||||||
streamRef?.current?.offsetWidth,
|
streamRef?.current?.offsetWidth ?? 0,
|
||||||
streamRef?.current?.offsetHeight
|
streamRef?.current?.offsetHeight ?? 0
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
settings.modelingContext.store.streamDimensions.streamWidth !== width ||
|
settings.modelingContext.store.streamDimensions.streamWidth !== width ||
|
||||||
@ -122,10 +124,37 @@ export function useSetupEngineManager(
|
|||||||
}, 500)
|
}, 500)
|
||||||
|
|
||||||
const onOnline = () => {
|
const onOnline = () => {
|
||||||
startEngineInstance()
|
startEngineInstance(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const onVisibilityChange = () => {
|
||||||
|
if (window.document.visibilityState === 'visible') {
|
||||||
|
if (
|
||||||
|
!engineCommandManager.engineConnection?.isReady() &&
|
||||||
|
!engineCommandManager.engineConnection?.isConnecting()
|
||||||
|
) {
|
||||||
|
startEngineInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.document.addEventListener('visibilitychange', onVisibilityChange)
|
||||||
|
|
||||||
|
const onAnyInput = () => {
|
||||||
|
if (
|
||||||
|
!engineCommandManager.engineConnection?.isReady() &&
|
||||||
|
!engineCommandManager.engineConnection?.isConnecting()
|
||||||
|
) {
|
||||||
|
startEngineInstance()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.document.addEventListener('keydown', onAnyInput)
|
||||||
|
window.document.addEventListener('mousemove', onAnyInput)
|
||||||
|
window.document.addEventListener('mousedown', onAnyInput)
|
||||||
|
window.document.addEventListener('scroll', onAnyInput)
|
||||||
|
window.document.addEventListener('touchstart', onAnyInput)
|
||||||
|
|
||||||
const onOffline = () => {
|
const onOffline = () => {
|
||||||
|
kclManager.isFirstRender = true
|
||||||
engineCommandManager.tearDown()
|
engineCommandManager.tearDown()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,11 +162,30 @@ export function useSetupEngineManager(
|
|||||||
window.addEventListener('offline', onOffline)
|
window.addEventListener('offline', onOffline)
|
||||||
window.addEventListener('resize', handleResize)
|
window.addEventListener('resize', handleResize)
|
||||||
return () => {
|
return () => {
|
||||||
|
window.document.removeEventListener(
|
||||||
|
'visibilitychange',
|
||||||
|
onVisibilityChange
|
||||||
|
)
|
||||||
|
window.document.removeEventListener('keydown', onAnyInput)
|
||||||
|
window.document.removeEventListener('mousemove', onAnyInput)
|
||||||
|
window.document.removeEventListener('mousedown', onAnyInput)
|
||||||
|
window.document.removeEventListener('scroll', onAnyInput)
|
||||||
|
window.document.removeEventListener('touchstart', onAnyInput)
|
||||||
window.removeEventListener('online', onOnline)
|
window.removeEventListener('online', onOnline)
|
||||||
window.removeEventListener('offline', onOffline)
|
window.removeEventListener('offline', onOffline)
|
||||||
window.removeEventListener('resize', handleResize)
|
window.removeEventListener('resize', handleResize)
|
||||||
}
|
}
|
||||||
}, [])
|
|
||||||
|
// Engine relies on many settings so we should rebind events when it changes
|
||||||
|
// We have to list out the ones we care about because the settings object holds
|
||||||
|
// non-settings too...
|
||||||
|
}, [
|
||||||
|
settings.enableSSAO,
|
||||||
|
settings.highlightEdges,
|
||||||
|
settings.showScaleGrid,
|
||||||
|
settings.theme,
|
||||||
|
settings.pool,
|
||||||
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDimensions(streamWidth?: number, streamHeight?: number) {
|
function getDimensions(streamWidth?: number, streamHeight?: number) {
|
||||||
|
@ -6,7 +6,11 @@ import { modelingMachine } from 'machines/modelingMachine'
|
|||||||
import { authMachine } from 'machines/authMachine'
|
import { authMachine } from 'machines/authMachine'
|
||||||
import { settingsMachine } from 'machines/settingsMachine'
|
import { settingsMachine } from 'machines/settingsMachine'
|
||||||
import { homeMachine } from 'machines/homeMachine'
|
import { homeMachine } from 'machines/homeMachine'
|
||||||
import { Command, CommandSetConfig, CommandSetSchema } from 'lib/commandTypes'
|
import {
|
||||||
|
Command,
|
||||||
|
StateMachineCommandSetConfig,
|
||||||
|
StateMachineCommandSetSchema,
|
||||||
|
} from 'lib/commandTypes'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||||
@ -21,20 +25,20 @@ export type AllMachines =
|
|||||||
|
|
||||||
interface UseStateMachineCommandsArgs<
|
interface UseStateMachineCommandsArgs<
|
||||||
T extends AllMachines,
|
T extends AllMachines,
|
||||||
S extends CommandSetSchema<T>
|
S extends StateMachineCommandSetSchema<T>
|
||||||
> {
|
> {
|
||||||
machineId: T['id']
|
machineId: T['id']
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
send: Function
|
send: Function
|
||||||
actor: InterpreterFrom<T>
|
actor: InterpreterFrom<T>
|
||||||
commandBarConfig?: CommandSetConfig<T, S>
|
commandBarConfig?: StateMachineCommandSetConfig<T, S>
|
||||||
allCommandsRequireNetwork?: boolean
|
allCommandsRequireNetwork?: boolean
|
||||||
onCancel?: () => void
|
onCancel?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useStateMachineCommands<
|
export default function useStateMachineCommands<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
S extends CommandSetSchema<T>
|
S extends StateMachineCommandSetSchema<T>
|
||||||
>({
|
>({
|
||||||
machineId,
|
machineId,
|
||||||
state,
|
state,
|
||||||
@ -58,7 +62,7 @@ export default function useStateMachineCommands<
|
|||||||
const newCommands = state.nextEvents
|
const newCommands = state.nextEvents
|
||||||
.filter((_) => !allCommandsRequireNetwork || !disableAllButtons)
|
.filter((_) => !allCommandsRequireNetwork || !disableAllButtons)
|
||||||
.filter((e) => !['done.', 'error.'].some((n) => e.includes(n)))
|
.filter((e) => !['done.', 'error.'].some((n) => e.includes(n)))
|
||||||
.map((type) =>
|
.flatMap((type) =>
|
||||||
createMachineCommand<T, S>({
|
createMachineCommand<T, S>({
|
||||||
// The group is the owner machine's ID.
|
// The group is the owner machine's ID.
|
||||||
groupId: machineId,
|
groupId: machineId,
|
||||||
|
@ -3,6 +3,8 @@ import { createContext, useContext, useEffect, useState } from 'react'
|
|||||||
import { type IndexLoaderData } from 'lib/types'
|
import { type IndexLoaderData } from 'lib/types'
|
||||||
import { useLoaderData } from 'react-router-dom'
|
import { useLoaderData } from 'react-router-dom'
|
||||||
import { codeManager, kclManager } from 'lib/singletons'
|
import { codeManager, kclManager } from 'lib/singletons'
|
||||||
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
|
import { Command } from 'lib/commandTypes'
|
||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
@ -35,6 +37,7 @@ export function KclContextProvider({
|
|||||||
const [errors, setErrors] = useState<KCLError[]>([])
|
const [errors, setErrors] = useState<KCLError[]>([])
|
||||||
const [logs, setLogs] = useState<string[]>([])
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
const [wasmInitFailed, setWasmInitFailed] = useState(false)
|
||||||
|
const { commandBarSend } = useCommandsContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
codeManager.registerCallBacks({
|
codeManager.registerCallBacks({
|
||||||
@ -50,6 +53,28 @@ export function KclContextProvider({
|
|||||||
})
|
})
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// Add format code to command palette.
|
||||||
|
useEffect(() => {
|
||||||
|
const commands: Command[] = [
|
||||||
|
{
|
||||||
|
name: 'format-code',
|
||||||
|
displayName: 'Format Code',
|
||||||
|
description: 'Nicely formats the KCL code in the editor.',
|
||||||
|
needsReview: false,
|
||||||
|
groupId: 'code',
|
||||||
|
icon: 'code',
|
||||||
|
onSubmit: (data) => {
|
||||||
|
kclManager.format()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
commandBarSend({ type: 'Add commands', data: { commands } })
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
commandBarSend({ type: 'Remove commands', data: { commands } })
|
||||||
|
}
|
||||||
|
}, [kclManager, commandBarSend])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<KclContext.Provider
|
<KclContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
@ -304,7 +304,6 @@ describe('testing sketchOnExtrudedFace', () => {
|
|||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
|
|
||||||
const programMemory = await enginelessExecutor(ast)
|
|
||||||
const segmentSnippet = `line([9.7, 9.19], %)`
|
const segmentSnippet = `line([9.7, 9.19], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
@ -321,8 +320,7 @@ describe('testing sketchOnExtrudedFace', () => {
|
|||||||
const extruded = sketchOnExtrudedFace(
|
const extruded = sketchOnExtrudedFace(
|
||||||
ast,
|
ast,
|
||||||
segmentPathToNode,
|
segmentPathToNode,
|
||||||
extrudePathToNode,
|
extrudePathToNode
|
||||||
programMemory
|
|
||||||
)
|
)
|
||||||
if (err(extruded)) throw extruded
|
if (err(extruded)) throw extruded
|
||||||
const { modifiedAst } = extruded
|
const { modifiedAst } = extruded
|
||||||
@ -345,7 +343,6 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const programMemory = await enginelessExecutor(ast)
|
|
||||||
const segmentSnippet = `close(%)`
|
const segmentSnippet = `close(%)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
@ -362,8 +359,7 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
const extruded = sketchOnExtrudedFace(
|
const extruded = sketchOnExtrudedFace(
|
||||||
ast,
|
ast,
|
||||||
segmentPathToNode,
|
segmentPathToNode,
|
||||||
extrudePathToNode,
|
extrudePathToNode
|
||||||
programMemory
|
|
||||||
)
|
)
|
||||||
if (err(extruded)) throw extruded
|
if (err(extruded)) throw extruded
|
||||||
const { modifiedAst } = extruded
|
const { modifiedAst } = extruded
|
||||||
@ -386,7 +382,6 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
|> extrude(5 + 7, %)`
|
|> extrude(5 + 7, %)`
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const programMemory = await enginelessExecutor(ast)
|
|
||||||
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
||||||
const sketchRange: [number, number] = [
|
const sketchRange: [number, number] = [
|
||||||
code.indexOf(sketchSnippet),
|
code.indexOf(sketchSnippet),
|
||||||
@ -404,7 +399,6 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
|||||||
ast,
|
ast,
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
extrudePathToNode,
|
extrudePathToNode,
|
||||||
programMemory,
|
|
||||||
'end'
|
'end'
|
||||||
)
|
)
|
||||||
if (err(extruded)) throw extruded
|
if (err(extruded)) throw extruded
|
||||||
@ -436,7 +430,6 @@ const sketch001 = startSketchOn(part001, 'END')`)
|
|||||||
const part001 = extrude(5 + 7, sketch001)`
|
const part001 = extrude(5 + 7, sketch001)`
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const programMemory = await enginelessExecutor(ast)
|
|
||||||
const segmentSnippet = `line([4.99, -0.46], %)`
|
const segmentSnippet = `line([4.99, -0.46], %)`
|
||||||
const segmentRange: [number, number] = [
|
const segmentRange: [number, number] = [
|
||||||
code.indexOf(segmentSnippet),
|
code.indexOf(segmentSnippet),
|
||||||
@ -453,8 +446,7 @@ const sketch001 = startSketchOn(part001, 'END')`)
|
|||||||
const updatedAst = sketchOnExtrudedFace(
|
const updatedAst = sketchOnExtrudedFace(
|
||||||
ast,
|
ast,
|
||||||
segmentPathToNode,
|
segmentPathToNode,
|
||||||
extrudePathToNode,
|
extrudePathToNode
|
||||||
programMemory
|
|
||||||
)
|
)
|
||||||
if (err(updatedAst)) throw updatedAst
|
if (err(updatedAst)) throw updatedAst
|
||||||
const newCode = recast(updatedAst.modifiedAst)
|
const newCode = recast(updatedAst.modifiedAst)
|
||||||
|
@ -349,7 +349,6 @@ export function sketchOnExtrudedFace(
|
|||||||
node: Program,
|
node: Program,
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
extrudePathToNode: PathToNode,
|
extrudePathToNode: PathToNode,
|
||||||
programMemory: ProgramMemory,
|
|
||||||
cap: 'none' | 'start' | 'end' = 'none'
|
cap: 'none' | 'start' | 'end' = 'none'
|
||||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||||
let _node = { ...node }
|
let _node = { ...node }
|
||||||
@ -388,7 +387,6 @@ export function sketchOnExtrudedFace(
|
|||||||
if (cap === 'none') {
|
if (cap === 'none') {
|
||||||
const __tag = addTagForSketchOnFace(
|
const __tag = addTagForSketchOnFace(
|
||||||
{
|
{
|
||||||
previousProgramMemory: programMemory,
|
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
node: _node,
|
node: _node,
|
||||||
},
|
},
|
||||||
|
315
src/lang/modifyAst/addFillet.test.ts
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
import {
|
||||||
|
parse,
|
||||||
|
recast,
|
||||||
|
initPromise,
|
||||||
|
PathToNode,
|
||||||
|
Value,
|
||||||
|
Program,
|
||||||
|
CallExpression,
|
||||||
|
} from '../wasm'
|
||||||
|
import { addFillet, isTagUsedInFillet } from './addFillet'
|
||||||
|
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||||
|
import { createLiteral } from 'lang/modifyAst'
|
||||||
|
import { err } from 'lib/trap'
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await initPromise // Initialize the WASM environment before running tests
|
||||||
|
})
|
||||||
|
|
||||||
|
const runFilletTest = async (
|
||||||
|
code: string,
|
||||||
|
segmentSnippet: string,
|
||||||
|
extrudeSnippet: string,
|
||||||
|
radius = createLiteral(5) as Value,
|
||||||
|
expectedCode: string
|
||||||
|
) => {
|
||||||
|
const astOrError = parse(code)
|
||||||
|
if (astOrError instanceof Error) {
|
||||||
|
return new Error('AST not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const ast = astOrError as Program
|
||||||
|
|
||||||
|
const segmentRange: [number, number] = [
|
||||||
|
code.indexOf(segmentSnippet),
|
||||||
|
code.indexOf(segmentSnippet) + segmentSnippet.length,
|
||||||
|
]
|
||||||
|
const pathToSegmentNode: PathToNode = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
segmentRange
|
||||||
|
)
|
||||||
|
|
||||||
|
const extrudeRange: [number, number] = [
|
||||||
|
code.indexOf(extrudeSnippet),
|
||||||
|
code.indexOf(extrudeSnippet) + extrudeSnippet.length,
|
||||||
|
]
|
||||||
|
|
||||||
|
const pathToExtrudeNode: PathToNode = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
extrudeRange
|
||||||
|
)
|
||||||
|
if (pathToExtrudeNode instanceof Error) {
|
||||||
|
return new Error('Path to extrude node not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
// const radius = createLiteral(5) as Value
|
||||||
|
|
||||||
|
const result = addFillet(ast, pathToSegmentNode, pathToExtrudeNode, radius)
|
||||||
|
if (result instanceof Error) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
const { modifiedAst } = result
|
||||||
|
const newCode = recast(modifiedAst)
|
||||||
|
|
||||||
|
expect(newCode).toContain(expectedCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Testing addFillet', () => {
|
||||||
|
/**
|
||||||
|
* 1. Ideal Case
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('should add a fillet to a specific segment after extrusion, clean', async () => {
|
||||||
|
const code = `
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
`
|
||||||
|
const segmentSnippet = `line([60.04, -55.72], %)`
|
||||||
|
const extrudeSnippet = `const extrude001 = extrude(50, sketch001)`
|
||||||
|
const radius = createLiteral(5) as Value
|
||||||
|
const expectedCode = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %, $seg01)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
|> fillet({ radius: 5, tags: [seg01] }, %)`
|
||||||
|
|
||||||
|
await runFilletTest(
|
||||||
|
code,
|
||||||
|
segmentSnippet,
|
||||||
|
extrudeSnippet,
|
||||||
|
radius,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 2. Case of existing tag in the other line
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('should add a fillet to a specific segment after extrusion with existing tag in any other line', async () => {
|
||||||
|
const code = `
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg01)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
`
|
||||||
|
const segmentSnippet = `line([60.04, -55.72], %)`
|
||||||
|
const extrudeSnippet = `const extrude001 = extrude(50, sketch001)`
|
||||||
|
const radius = createLiteral(5) as Value
|
||||||
|
const expectedCode = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %, $seg02)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg01)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
|> fillet({ radius: 5, tags: [seg02] }, %)`
|
||||||
|
|
||||||
|
await runFilletTest(
|
||||||
|
code,
|
||||||
|
segmentSnippet,
|
||||||
|
extrudeSnippet,
|
||||||
|
radius,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 3. Case of existing tag in the fillet line
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('should add a fillet to a specific segment after extrusion with existing tag in that exact line', async () => {
|
||||||
|
const code = `
|
||||||
|
const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg03)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
`
|
||||||
|
const segmentSnippet = `line([-87.24, -47.08], %, $seg03)`
|
||||||
|
const extrudeSnippet = `const extrude001 = extrude(50, sketch001)`
|
||||||
|
const radius = createLiteral(5) as Value
|
||||||
|
const expectedCode = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg03)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
|> fillet({ radius: 5, tags: [seg03] }, %)`
|
||||||
|
|
||||||
|
await runFilletTest(
|
||||||
|
code,
|
||||||
|
segmentSnippet,
|
||||||
|
extrudeSnippet,
|
||||||
|
radius,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 4. Case of existing fillet on some other segment
|
||||||
|
*/
|
||||||
|
|
||||||
|
it('should add another fillet after the existing fillet', async () => {
|
||||||
|
const code = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg03)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
|> fillet({ radius: 10, tags: [seg03] }, %)`
|
||||||
|
const segmentSnippet = `line([60.04, -55.72], %)`
|
||||||
|
const extrudeSnippet = `const extrude001 = extrude(50, sketch001)`
|
||||||
|
const radius = createLiteral(5) as Value
|
||||||
|
const expectedCode = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([2.16, 49.67], %)
|
||||||
|
|> line([101.49, 139.93], %)
|
||||||
|
|> line([60.04, -55.72], %, $seg01)
|
||||||
|
|> line([1.29, -115.74], %)
|
||||||
|
|> line([-87.24, -47.08], %, $seg03)
|
||||||
|
|> tangentialArcTo([56.15, -94.58], %)
|
||||||
|
|> tangentialArcTo([14.68, -104.52], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(50, sketch001)
|
||||||
|
|> fillet({ radius: 10, tags: [seg03] }, %)
|
||||||
|
|> fillet({ radius: 5, tags: [seg01] }, %)`
|
||||||
|
|
||||||
|
await runFilletTest(
|
||||||
|
code,
|
||||||
|
segmentSnippet,
|
||||||
|
extrudeSnippet,
|
||||||
|
radius,
|
||||||
|
expectedCode
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Testing isTagUsedInFillet', () => {
|
||||||
|
const code = `const sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.72, 4.13], %)
|
||||||
|
|> line([7.11, 3.48], %, $seg01)
|
||||||
|
|> line([-3.29, -13.85], %)
|
||||||
|
|> line([-6.37, 3.88], %, $seg02)
|
||||||
|
|> close(%)
|
||||||
|
const extrude001 = extrude(-5, sketch001)
|
||||||
|
|> fillet({
|
||||||
|
radius: 1.11,
|
||||||
|
tags: [
|
||||||
|
getOppositeEdge(seg01, %),
|
||||||
|
seg01,
|
||||||
|
getPreviousAdjacentEdge(seg02, %)
|
||||||
|
]
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
it('should correctly identify getOppositeEdge and baseEdge edges', () => {
|
||||||
|
const ast = parse(code)
|
||||||
|
if (err(ast)) return
|
||||||
|
const lineOfInterest = `line([7.11, 3.48], %, $seg01)`
|
||||||
|
const range: [number, number] = [
|
||||||
|
code.indexOf(lineOfInterest),
|
||||||
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
]
|
||||||
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
|
if (err(pathToNode)) return
|
||||||
|
const callExp = getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
pathToNode,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(callExp)) return
|
||||||
|
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||||
|
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||||
|
})
|
||||||
|
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||||
|
const ast = parse(code)
|
||||||
|
if (err(ast)) return
|
||||||
|
const lineOfInterest = `line([-6.37, 3.88], %, $seg02)`
|
||||||
|
const range: [number, number] = [
|
||||||
|
code.indexOf(lineOfInterest),
|
||||||
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
]
|
||||||
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
|
if (err(pathToNode)) return
|
||||||
|
const callExp = getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
pathToNode,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(callExp)) return
|
||||||
|
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||||
|
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||||
|
})
|
||||||
|
it('should correctly identify no edges', () => {
|
||||||
|
const ast = parse(code)
|
||||||
|
if (err(ast)) return
|
||||||
|
const lineOfInterest = `line([-3.29, -13.85], %)`
|
||||||
|
const range: [number, number] = [
|
||||||
|
code.indexOf(lineOfInterest),
|
||||||
|
code.indexOf(lineOfInterest) + lineOfInterest.length,
|
||||||
|
]
|
||||||
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
|
if (err(pathToNode)) return
|
||||||
|
const callExp = getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
pathToNode,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(callExp)) return
|
||||||
|
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||||
|
expect(edges).toEqual([])
|
||||||
|
})
|
||||||
|
})
|
404
src/lang/modifyAst/addFillet.ts
Normal file
@ -0,0 +1,404 @@
|
|||||||
|
import {
|
||||||
|
ArrayExpression,
|
||||||
|
CallExpression,
|
||||||
|
ObjectExpression,
|
||||||
|
PathToNode,
|
||||||
|
Program,
|
||||||
|
Value,
|
||||||
|
VariableDeclaration,
|
||||||
|
VariableDeclarator,
|
||||||
|
} from '../wasm'
|
||||||
|
import {
|
||||||
|
createCallExpressionStdLib,
|
||||||
|
createLiteral,
|
||||||
|
createPipeSubstitution,
|
||||||
|
createObjectExpression,
|
||||||
|
createArrayExpression,
|
||||||
|
createIdentifier,
|
||||||
|
createPipeExpression,
|
||||||
|
} from '../modifyAst'
|
||||||
|
import {
|
||||||
|
getNodeFromPath,
|
||||||
|
getNodePathFromSourceRange,
|
||||||
|
hasSketchPipeBeenExtruded,
|
||||||
|
traverse,
|
||||||
|
} from '../queryAst'
|
||||||
|
import {
|
||||||
|
addTagForSketchOnFace,
|
||||||
|
getTagFromCallExpression,
|
||||||
|
sketchLineHelperMap,
|
||||||
|
} from '../std/sketch'
|
||||||
|
import { err } from 'lib/trap'
|
||||||
|
import { Selections, canFilletSelection } from 'lib/selections'
|
||||||
|
|
||||||
|
export function addFillet(
|
||||||
|
node: Program,
|
||||||
|
pathToSegmentNode: PathToNode,
|
||||||
|
pathToExtrudeNode: PathToNode,
|
||||||
|
radius = createLiteral(5) as Value
|
||||||
|
// shouldPipe = false, // TODO: Implement this feature
|
||||||
|
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error {
|
||||||
|
// close ast to make mutations safe
|
||||||
|
let _node: Program = JSON.parse(JSON.stringify(node))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Tag to the Segment Expression
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Find the specific sketch segment to tag with the new tag
|
||||||
|
const sketchSegmentChunk = getNodeFromPath(
|
||||||
|
_node,
|
||||||
|
pathToSegmentNode,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(sketchSegmentChunk)) return sketchSegmentChunk
|
||||||
|
const { node: sketchSegmentNode } = sketchSegmentChunk as {
|
||||||
|
node: CallExpression
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether selection is a valid segment from sketchLineHelpersMap
|
||||||
|
if (!(sketchSegmentNode.callee.name in sketchLineHelperMap)) {
|
||||||
|
return new Error('Selection is not a sketch segment')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add tag to the sketch segment or use existing tag
|
||||||
|
const taggedSegment = addTagForSketchOnFace(
|
||||||
|
{
|
||||||
|
// previousProgramMemory: programMemory,
|
||||||
|
pathToNode: pathToSegmentNode,
|
||||||
|
node: _node,
|
||||||
|
},
|
||||||
|
sketchSegmentNode.callee.name
|
||||||
|
)
|
||||||
|
if (err(taggedSegment)) return taggedSegment
|
||||||
|
const { tag } = taggedSegment
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find Extrude Expression automatically
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 1. Get the sketch name
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Fillet to the Extrude expression
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Create the fillet call expression in one line
|
||||||
|
const filletCall = createCallExpressionStdLib('fillet', [
|
||||||
|
createObjectExpression({
|
||||||
|
radius: radius,
|
||||||
|
tags: createArrayExpression([createIdentifier(tag)]),
|
||||||
|
}),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Locate the extrude call
|
||||||
|
const extrudeChunk = getNodeFromPath<VariableDeclaration>(
|
||||||
|
_node,
|
||||||
|
pathToExtrudeNode,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(extrudeChunk)) return extrudeChunk
|
||||||
|
const { node: extrudeVarDecl } = extrudeChunk
|
||||||
|
|
||||||
|
const extrudeDeclarator = extrudeVarDecl.declarations[0]
|
||||||
|
const extrudeInit = extrudeDeclarator.init
|
||||||
|
|
||||||
|
if (
|
||||||
|
!extrudeDeclarator ||
|
||||||
|
(extrudeInit.type !== 'CallExpression' &&
|
||||||
|
extrudeInit.type !== 'PipeExpression')
|
||||||
|
) {
|
||||||
|
return new Error('Extrude PipeExpression / CallExpression not found.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine if extrude is in a PipeExpression or CallExpression
|
||||||
|
|
||||||
|
// CallExpression - no fillet
|
||||||
|
// PipeExpression - fillet exists
|
||||||
|
|
||||||
|
const getPathToNodeOfFilletLiteral = (
|
||||||
|
pathToExtrudeNode: PathToNode,
|
||||||
|
extrudeDeclarator: VariableDeclarator,
|
||||||
|
tag: string
|
||||||
|
): PathToNode => {
|
||||||
|
let pathToFilletObj: any
|
||||||
|
let inFillet = false
|
||||||
|
traverse(extrudeDeclarator.init, {
|
||||||
|
enter(node, path) {
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||||
|
inFillet = true
|
||||||
|
}
|
||||||
|
if (inFillet && node.type === 'ObjectExpression') {
|
||||||
|
const hasTag = node.properties.some((prop) => {
|
||||||
|
const isTagProp = prop.key.name === 'tags'
|
||||||
|
if (isTagProp && prop.value.type === 'ArrayExpression') {
|
||||||
|
return prop.value.elements.some(
|
||||||
|
(element) =>
|
||||||
|
element.type === 'Identifier' && element.name === tag
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if (!hasTag) return false
|
||||||
|
pathToFilletObj = path
|
||||||
|
node.properties.forEach((prop, index) => {
|
||||||
|
if (prop.key.name === 'radius') {
|
||||||
|
pathToFilletObj.push(
|
||||||
|
['properties', 'ObjectExpression'],
|
||||||
|
[index, 'index'],
|
||||||
|
['value', 'Property']
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leave(node) {
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||||
|
inFillet = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
let indexOfPipeExpression = pathToExtrudeNode.findIndex(
|
||||||
|
(path) => path[1] === 'PipeExpression'
|
||||||
|
)
|
||||||
|
indexOfPipeExpression =
|
||||||
|
indexOfPipeExpression === -1
|
||||||
|
? pathToExtrudeNode.length
|
||||||
|
: indexOfPipeExpression
|
||||||
|
|
||||||
|
return [
|
||||||
|
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
||||||
|
...pathToFilletObj,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extrudeInit.type === 'CallExpression') {
|
||||||
|
// 1. no fillet case
|
||||||
|
extrudeDeclarator.init = createPipeExpression([extrudeInit, filletCall])
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToFilletNode: getPathToNodeOfFilletLiteral(
|
||||||
|
pathToExtrudeNode,
|
||||||
|
extrudeDeclarator,
|
||||||
|
tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else if (extrudeInit.type === 'PipeExpression') {
|
||||||
|
// 2. fillet case
|
||||||
|
|
||||||
|
// there are 2 options here:
|
||||||
|
|
||||||
|
const existingFilletCall = extrudeInit.body.find((node) => {
|
||||||
|
return node.type === 'CallExpression' && node.callee.name === 'fillet'
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!existingFilletCall || existingFilletCall.type !== 'CallExpression') {
|
||||||
|
return new Error('Fillet CallExpression not found.')
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the existing fillet has the same tag as the new fillet
|
||||||
|
let filletTag = null
|
||||||
|
if (existingFilletCall.arguments[0].type === 'ObjectExpression') {
|
||||||
|
const properties = (existingFilletCall.arguments[0] as ObjectExpression)
|
||||||
|
.properties
|
||||||
|
const tagsProperty = properties.find((prop) => prop.key.name === 'tags')
|
||||||
|
if (tagsProperty && tagsProperty.value.type === 'ArrayExpression') {
|
||||||
|
const elements = (tagsProperty.value as ArrayExpression).elements
|
||||||
|
if (elements.length > 0 && elements[0].type === 'Identifier') {
|
||||||
|
filletTag = elements[0].name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new Error('Expected an ObjectExpression node')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filletTag !== tag) {
|
||||||
|
extrudeInit.body.push(filletCall)
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToFilletNode: getPathToNodeOfFilletLiteral(
|
||||||
|
pathToExtrudeNode,
|
||||||
|
extrudeDeclarator,
|
||||||
|
tag
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return new Error('Unsupported extrude type.')
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Error('Unsupported extrude type.')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const hasValidFilletSelection = ({
|
||||||
|
selectionRanges,
|
||||||
|
ast,
|
||||||
|
code,
|
||||||
|
}: {
|
||||||
|
selectionRanges: Selections
|
||||||
|
ast: Program
|
||||||
|
code: string
|
||||||
|
}) => {
|
||||||
|
// case 0: check if there is anything filletable in the scene
|
||||||
|
let extrudeExists = false
|
||||||
|
traverse(ast, {
|
||||||
|
enter(node) {
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'extrude') {
|
||||||
|
extrudeExists = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!extrudeExists) return false
|
||||||
|
|
||||||
|
// case 1: nothing selected, test whether the extrusion exists
|
||||||
|
if (selectionRanges) {
|
||||||
|
if (selectionRanges.codeBasedSelections.length === 0) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
const range0 = selectionRanges.codeBasedSelections[0].range[0]
|
||||||
|
const codeLength = code.length
|
||||||
|
if (range0 === codeLength) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// case 2: sketch segment selected, test whether it is extruded
|
||||||
|
// TODO: add loft / sweep check
|
||||||
|
if (selectionRanges.codeBasedSelections.length > 0) {
|
||||||
|
const isExtruded = hasSketchPipeBeenExtruded(
|
||||||
|
selectionRanges.codeBasedSelections[0],
|
||||||
|
ast
|
||||||
|
)
|
||||||
|
if (isExtruded) {
|
||||||
|
const pathToSelectedNode = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
selectionRanges.codeBasedSelections[0].range
|
||||||
|
)
|
||||||
|
const segmentNode = getNodeFromPath<CallExpression>(
|
||||||
|
ast,
|
||||||
|
pathToSelectedNode,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(segmentNode)) return false
|
||||||
|
if (segmentNode.node.type === 'CallExpression') {
|
||||||
|
const segmentName = segmentNode.node.callee.name
|
||||||
|
if (segmentName in sketchLineHelperMap) {
|
||||||
|
const edges = isTagUsedInFillet({
|
||||||
|
ast,
|
||||||
|
callExp: segmentNode.node,
|
||||||
|
})
|
||||||
|
// edge has already been filleted
|
||||||
|
if (
|
||||||
|
['edge', 'default'].includes(
|
||||||
|
selectionRanges.codeBasedSelections[0].type
|
||||||
|
) &&
|
||||||
|
edges.includes('baseEdge')
|
||||||
|
)
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return canFilletSelection(selectionRanges)
|
||||||
|
}
|
||||||
|
|
||||||
|
type EdgeTypes =
|
||||||
|
| 'baseEdge'
|
||||||
|
| 'getNextAdjacentEdge'
|
||||||
|
| 'getPreviousAdjacentEdge'
|
||||||
|
| 'getOppositeEdge'
|
||||||
|
|
||||||
|
export const isTagUsedInFillet = ({
|
||||||
|
ast,
|
||||||
|
callExp,
|
||||||
|
}: {
|
||||||
|
ast: Program
|
||||||
|
callExp: CallExpression
|
||||||
|
}): Array<EdgeTypes> => {
|
||||||
|
const tag = getTagFromCallExpression(callExp)
|
||||||
|
if (err(tag)) return []
|
||||||
|
|
||||||
|
let inFillet = false
|
||||||
|
let inObj = false
|
||||||
|
let inTagHelper: EdgeTypes | '' = ''
|
||||||
|
const edges: Array<EdgeTypes> = []
|
||||||
|
traverse(ast, {
|
||||||
|
enter: (node) => {
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||||
|
inFillet = true
|
||||||
|
}
|
||||||
|
if (inFillet && node.type === 'ObjectExpression') {
|
||||||
|
node.properties.forEach((prop) => {
|
||||||
|
if (
|
||||||
|
prop.key.name === 'tags' &&
|
||||||
|
prop.value.type === 'ArrayExpression'
|
||||||
|
) {
|
||||||
|
inObj = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
inObj &&
|
||||||
|
inFillet &&
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
(node.callee.name === 'getOppositeEdge' ||
|
||||||
|
node.callee.name === 'getNextAdjacentEdge' ||
|
||||||
|
node.callee.name === 'getPreviousAdjacentEdge')
|
||||||
|
) {
|
||||||
|
inTagHelper = node.callee.name
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
inObj &&
|
||||||
|
inFillet &&
|
||||||
|
!inTagHelper &&
|
||||||
|
node.type === 'Identifier' &&
|
||||||
|
node.name === tag
|
||||||
|
) {
|
||||||
|
edges.push('baseEdge')
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
inObj &&
|
||||||
|
inFillet &&
|
||||||
|
inTagHelper &&
|
||||||
|
node.type === 'Identifier' &&
|
||||||
|
node.name === tag
|
||||||
|
) {
|
||||||
|
edges.push(inTagHelper)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
leave: (node) => {
|
||||||
|
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||||
|
inFillet = false
|
||||||
|
}
|
||||||
|
if (inFillet && node.type === 'ObjectExpression') {
|
||||||
|
node.properties.forEach((prop) => {
|
||||||
|
if (
|
||||||
|
prop.key.name === 'tags' &&
|
||||||
|
prop.value.type === 'ArrayExpression'
|
||||||
|
) {
|
||||||
|
inObj = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
inObj &&
|
||||||
|
inFillet &&
|
||||||
|
node.type === 'CallExpression' &&
|
||||||
|
(node.callee.name === 'getOppositeEdge' ||
|
||||||
|
node.callee.name === 'getNextAdjacentEdge' ||
|
||||||
|
node.callee.name === 'getPreviousAdjacentEdge')
|
||||||
|
) {
|
||||||
|
inTagHelper = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
return edges
|
||||||
|
}
|
@ -143,6 +143,7 @@ export enum DisconnectingType {
|
|||||||
Error = 'error',
|
Error = 'error',
|
||||||
Timeout = 'timeout',
|
Timeout = 'timeout',
|
||||||
Quit = 'quit',
|
Quit = 'quit',
|
||||||
|
Pause = 'pause',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sorted by severity
|
// Sorted by severity
|
||||||
@ -200,6 +201,7 @@ export type DisconnectingValue =
|
|||||||
| State<DisconnectingType.Error, ErrorType>
|
| State<DisconnectingType.Error, ErrorType>
|
||||||
| State<DisconnectingType.Timeout, void>
|
| State<DisconnectingType.Timeout, void>
|
||||||
| State<DisconnectingType.Quit, void>
|
| State<DisconnectingType.Quit, void>
|
||||||
|
| State<DisconnectingType.Pause, void>
|
||||||
|
|
||||||
// These are ordered by the expected sequence.
|
// These are ordered by the expected sequence.
|
||||||
export enum ConnectingType {
|
export enum ConnectingType {
|
||||||
@ -300,7 +302,31 @@ class EngineConnection extends EventTarget {
|
|||||||
pc?: RTCPeerConnection
|
pc?: RTCPeerConnection
|
||||||
unreliableDataChannel?: RTCDataChannel
|
unreliableDataChannel?: RTCDataChannel
|
||||||
mediaStream?: MediaStream
|
mediaStream?: MediaStream
|
||||||
freezeFrame: boolean = false
|
idleMode: boolean = false
|
||||||
|
|
||||||
|
onIceCandidate = function (
|
||||||
|
this: RTCPeerConnection,
|
||||||
|
event: RTCPeerConnectionIceEvent
|
||||||
|
) {}
|
||||||
|
onIceCandidateError = function (
|
||||||
|
this: RTCPeerConnection,
|
||||||
|
event: RTCPeerConnectionIceErrorEvent
|
||||||
|
) {}
|
||||||
|
onConnectionStateChange = function (this: RTCPeerConnection, event: Event) {}
|
||||||
|
onDataChannelOpen = function (this: RTCDataChannel, event: Event) {}
|
||||||
|
onDataChannelClose = function (this: RTCDataChannel, event: Event) {}
|
||||||
|
onDataChannelError = function (this: RTCDataChannel, event: Event) {}
|
||||||
|
onDataChannelMessage = function (this: RTCDataChannel, event: MessageEvent) {}
|
||||||
|
onDataChannel = function (
|
||||||
|
this: RTCPeerConnection,
|
||||||
|
event: RTCDataChannelEvent
|
||||||
|
) {}
|
||||||
|
onTrack = function (this: RTCPeerConnection, event: RTCTrackEvent) {}
|
||||||
|
onWebSocketOpen = function (event: Event) {}
|
||||||
|
onWebSocketClose = function (event: Event) {}
|
||||||
|
onWebSocketError = function (event: Event) {}
|
||||||
|
onWebSocketMessage = function (event: MessageEvent) {}
|
||||||
|
onNetworkStatusReady = () => {}
|
||||||
|
|
||||||
private _state: EngineConnectionState = {
|
private _state: EngineConnectionState = {
|
||||||
type: EngineConnectionStateType.Fresh,
|
type: EngineConnectionStateType.Fresh,
|
||||||
@ -346,6 +372,7 @@ class EngineConnection extends EventTarget {
|
|||||||
private engineCommandManager: EngineCommandManager
|
private engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
private pingPongSpan: { ping?: Date; pong?: Date }
|
private pingPongSpan: { ping?: Date; pong?: Date }
|
||||||
|
private pingIntervalId: ReturnType<typeof setInterval>
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
@ -366,10 +393,10 @@ class EngineConnection extends EventTarget {
|
|||||||
this.pingPongSpan = { ping: undefined, pong: undefined }
|
this.pingPongSpan = { ping: undefined, pong: undefined }
|
||||||
|
|
||||||
// Without an interval ping, our connection will timeout.
|
// Without an interval ping, our connection will timeout.
|
||||||
// If this.freezeFrame is true we skip this logic so only reconnect
|
// If this.idleMode is true we skip this logic so only reconnect
|
||||||
// happens on mouse move
|
// happens on mouse move
|
||||||
setInterval(() => {
|
this.pingIntervalId = setInterval(() => {
|
||||||
if (this.freezeFrame) return
|
if (this.idleMode) return
|
||||||
|
|
||||||
switch (this.state.type as EngineConnectionStateType) {
|
switch (this.state.type as EngineConnectionStateType) {
|
||||||
case EngineConnectionStateType.ConnectionEstablished:
|
case EngineConnectionStateType.ConnectionEstablished:
|
||||||
@ -431,12 +458,59 @@ class EngineConnection extends EventTarget {
|
|||||||
return this.state.type === EngineConnectionStateType.ConnectionEstablished
|
return this.state.type === EngineConnectionStateType.ConnectionEstablished
|
||||||
}
|
}
|
||||||
|
|
||||||
tearDown(opts?: { freeze: boolean }) {
|
tearDown(opts?: { idleMode: boolean }) {
|
||||||
this.freezeFrame = opts?.freeze ?? false
|
this.idleMode = opts?.idleMode ?? false
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
this.state = {
|
clearInterval(this.pingIntervalId)
|
||||||
|
|
||||||
|
this.pc?.removeEventListener('icecandidate', this.onIceCandidate)
|
||||||
|
this.pc?.removeEventListener('icecandidateerror', this.onIceCandidateError)
|
||||||
|
this.pc?.removeEventListener(
|
||||||
|
'connectionstatechange',
|
||||||
|
this.onConnectionStateChange
|
||||||
|
)
|
||||||
|
this.pc?.removeEventListener('track', this.onTrack)
|
||||||
|
|
||||||
|
this.unreliableDataChannel?.removeEventListener(
|
||||||
|
'open',
|
||||||
|
this.onDataChannelOpen
|
||||||
|
)
|
||||||
|
this.unreliableDataChannel?.removeEventListener(
|
||||||
|
'close',
|
||||||
|
this.onDataChannelClose
|
||||||
|
)
|
||||||
|
this.unreliableDataChannel?.removeEventListener(
|
||||||
|
'error',
|
||||||
|
this.onDataChannelError
|
||||||
|
)
|
||||||
|
this.unreliableDataChannel?.removeEventListener(
|
||||||
|
'message',
|
||||||
|
this.onDataChannelMessage
|
||||||
|
)
|
||||||
|
this.pc?.removeEventListener('datachannel', this.onDataChannel)
|
||||||
|
|
||||||
|
this.websocket?.removeEventListener('open', this.onWebSocketOpen)
|
||||||
|
this.websocket?.removeEventListener('close', this.onWebSocketClose)
|
||||||
|
this.websocket?.removeEventListener('error', this.onWebSocketError)
|
||||||
|
this.websocket?.removeEventListener('message', this.onWebSocketMessage)
|
||||||
|
|
||||||
|
window.removeEventListener(
|
||||||
|
'use-network-status-ready',
|
||||||
|
this.onNetworkStatusReady
|
||||||
|
)
|
||||||
|
|
||||||
|
this.state = opts?.idleMode
|
||||||
|
? {
|
||||||
type: EngineConnectionStateType.Disconnecting,
|
type: EngineConnectionStateType.Disconnecting,
|
||||||
value: { type: DisconnectingType.Quit },
|
value: {
|
||||||
|
type: DisconnectingType.Pause,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
type: EngineConnectionStateType.Disconnecting,
|
||||||
|
value: {
|
||||||
|
type: DisconnectingType.Quit,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -477,7 +551,7 @@ class EngineConnection extends EventTarget {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pc.addEventListener('icecandidate', (event) => {
|
this.onIceCandidate = (event: RTCPeerConnectionIceEvent) => {
|
||||||
if (event.candidate === null) {
|
if (event.candidate === null) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -499,18 +573,20 @@ class EngineConnection extends EventTarget {
|
|||||||
usernameFragment: event.candidate.usernameFragment || undefined,
|
usernameFragment: event.candidate.usernameFragment || undefined,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
this.pc.addEventListener('icecandidate', this.onIceCandidate)
|
||||||
|
|
||||||
this.pc.addEventListener('icecandidateerror', (_event: Event) => {
|
this.onIceCandidateError = (_event: Event) => {
|
||||||
const event = _event as RTCPeerConnectionIceErrorEvent
|
const event = _event as RTCPeerConnectionIceErrorEvent
|
||||||
console.warn(
|
console.warn(
|
||||||
`ICE candidate returned an error: ${event.errorCode}: ${event.errorText} for ${event.url}`
|
`ICE candidate returned an error: ${event.errorCode}: ${event.errorText} for ${event.url}`
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
this.pc.addEventListener('icecandidateerror', this.onIceCandidateError)
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
|
// https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/connectionstatechange_event
|
||||||
// Event type: generic Event type...
|
// Event type: generic Event type...
|
||||||
this.pc.addEventListener('connectionstatechange', (event: any) => {
|
this.onConnectionStateChange = (event: any) => {
|
||||||
console.log('connectionstatechange: ' + event.target?.connectionState)
|
console.log('connectionstatechange: ' + event.target?.connectionState)
|
||||||
switch (event.target?.connectionState) {
|
switch (event.target?.connectionState) {
|
||||||
// From what I understand, only after have we done the ICE song and
|
// From what I understand, only after have we done the ICE song and
|
||||||
@ -539,9 +615,13 @@ class EngineConnection extends EventTarget {
|
|||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
this.pc.addEventListener(
|
||||||
|
'connectionstatechange',
|
||||||
|
this.onConnectionStateChange
|
||||||
|
)
|
||||||
|
|
||||||
this.pc.addEventListener('track', (event) => {
|
this.onTrack = (event) => {
|
||||||
const mediaStream = event.streams[0]
|
const mediaStream = event.streams[0]
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -625,9 +705,10 @@ class EngineConnection extends EventTarget {
|
|||||||
// to pass it to the rest of the application.
|
// to pass it to the rest of the application.
|
||||||
|
|
||||||
this.mediaStream = mediaStream
|
this.mediaStream = mediaStream
|
||||||
})
|
}
|
||||||
|
this.pc.addEventListener('track', this.onTrack)
|
||||||
|
|
||||||
this.pc.addEventListener('datachannel', (event) => {
|
this.onDataChannel = (event) => {
|
||||||
this.unreliableDataChannel = event.channel
|
this.unreliableDataChannel = event.channel
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -638,7 +719,7 @@ class EngineConnection extends EventTarget {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
this.unreliableDataChannel.addEventListener('open', (event) => {
|
this.onDataChannelOpen = (event) => {
|
||||||
this.state = {
|
this.state = {
|
||||||
type: EngineConnectionStateType.Connecting,
|
type: EngineConnectionStateType.Connecting,
|
||||||
value: {
|
value: {
|
||||||
@ -654,14 +735,22 @@ class EngineConnection extends EventTarget {
|
|||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent(EngineConnectionEvents.Opened, { detail: this })
|
new CustomEvent(EngineConnectionEvents.Opened, { detail: this })
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
this.unreliableDataChannel?.addEventListener(
|
||||||
|
'open',
|
||||||
|
this.onDataChannelOpen
|
||||||
|
)
|
||||||
|
|
||||||
this.unreliableDataChannel.addEventListener('close', (event) => {
|
this.onDataChannelClose = (event) => {
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
this.finalizeIfAllConnectionsClosed()
|
this.finalizeIfAllConnectionsClosed()
|
||||||
})
|
}
|
||||||
|
this.unreliableDataChannel?.addEventListener(
|
||||||
|
'close',
|
||||||
|
this.onDataChannelClose
|
||||||
|
)
|
||||||
|
|
||||||
this.unreliableDataChannel.addEventListener('error', (event) => {
|
this.onDataChannelError = (event) => {
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -674,8 +763,13 @@ class EngineConnection extends EventTarget {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
this.unreliableDataChannel.addEventListener('message', (event) => {
|
this.unreliableDataChannel?.addEventListener(
|
||||||
|
'error',
|
||||||
|
this.onDataChannelError
|
||||||
|
)
|
||||||
|
|
||||||
|
this.onDataChannelMessage = (event) => {
|
||||||
const result: UnreliableResponses = JSON.parse(event.data)
|
const result: UnreliableResponses = JSON.parse(event.data)
|
||||||
Object.values(
|
Object.values(
|
||||||
this.engineCommandManager.unreliableSubscriptions[result.type] || {}
|
this.engineCommandManager.unreliableSubscriptions[result.type] || {}
|
||||||
@ -697,8 +791,13 @@ class EngineConnection extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
})
|
this.unreliableDataChannel.addEventListener(
|
||||||
|
'message',
|
||||||
|
this.onDataChannelMessage
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.pc.addEventListener('datachannel', this.onDataChannel)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createWebSocketConnection = () => {
|
const createWebSocketConnection = () => {
|
||||||
@ -712,7 +811,7 @@ class EngineConnection extends EventTarget {
|
|||||||
this.websocket = new WebSocket(this.url, [])
|
this.websocket = new WebSocket(this.url, [])
|
||||||
this.websocket.binaryType = 'arraybuffer'
|
this.websocket.binaryType = 'arraybuffer'
|
||||||
|
|
||||||
this.websocket.addEventListener('open', (event) => {
|
this.onWebSocketOpen = (event) => {
|
||||||
this.state = {
|
this.state = {
|
||||||
type: EngineConnectionStateType.Connecting,
|
type: EngineConnectionStateType.Connecting,
|
||||||
value: {
|
value: {
|
||||||
@ -733,14 +832,16 @@ class EngineConnection extends EventTarget {
|
|||||||
// Send an initial ping
|
// Send an initial ping
|
||||||
this.send({ type: 'ping' })
|
this.send({ type: 'ping' })
|
||||||
this.pingPongSpan.ping = new Date()
|
this.pingPongSpan.ping = new Date()
|
||||||
})
|
}
|
||||||
|
this.websocket.addEventListener('open', this.onWebSocketOpen)
|
||||||
|
|
||||||
this.websocket.addEventListener('close', (event) => {
|
this.onWebSocketClose = (event) => {
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
this.finalizeIfAllConnectionsClosed()
|
this.finalizeIfAllConnectionsClosed()
|
||||||
})
|
}
|
||||||
|
this.websocket.addEventListener('close', this.onWebSocketClose)
|
||||||
|
|
||||||
this.websocket.addEventListener('error', (event) => {
|
this.onWebSocketError = (event) => {
|
||||||
this.disconnectAll()
|
this.disconnectAll()
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@ -753,9 +854,10 @@ class EngineConnection extends EventTarget {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
this.websocket.addEventListener('error', this.onWebSocketError)
|
||||||
|
|
||||||
this.websocket.addEventListener('message', (event) => {
|
this.onWebSocketMessage = (event) => {
|
||||||
// In the EngineConnection, we're looking for messages to/from
|
// In the EngineConnection, we're looking for messages to/from
|
||||||
// the server that relate to the ICE handshake, or WebRTC
|
// the server that relate to the ICE handshake, or WebRTC
|
||||||
// negotiation. There may be other messages (including ArrayBuffer
|
// negotiation. There may be other messages (including ArrayBuffer
|
||||||
@ -960,15 +1062,20 @@ class EngineConnection extends EventTarget {
|
|||||||
})
|
})
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
this.websocket.addEventListener('message', this.onWebSocketMessage)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reconnecting) {
|
if (reconnecting) {
|
||||||
createWebSocketConnection()
|
createWebSocketConnection()
|
||||||
} else {
|
} else {
|
||||||
window.addEventListener('use-network-status-ready', () => {
|
this.onNetworkStatusReady = () => {
|
||||||
createWebSocketConnection()
|
createWebSocketConnection()
|
||||||
})
|
}
|
||||||
|
window.addEventListener(
|
||||||
|
'use-network-status-ready',
|
||||||
|
this.onNetworkStatusReady
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Do not change this back to an object or any, we should only be sending the
|
// Do not change this back to an object or any, we should only be sending the
|
||||||
@ -1003,8 +1110,6 @@ class EngineConnection extends EventTarget {
|
|||||||
this.unreliableDataChannel?.readyState === 'closed'
|
this.unreliableDataChannel?.readyState === 'closed'
|
||||||
if (allClosed) {
|
if (allClosed) {
|
||||||
// Do not notify the rest of the program that we have cut off anything.
|
// Do not notify the rest of the program that we have cut off anything.
|
||||||
if (this.freezeFrame) return
|
|
||||||
|
|
||||||
this.state = { type: EngineConnectionStateType.Disconnected }
|
this.state = { type: EngineConnectionStateType.Disconnected }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1154,7 +1259,15 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
private makeDefaultPlanes: () => Promise<DefaultPlanes> | null = () => null
|
private makeDefaultPlanes: () => Promise<DefaultPlanes> | null = () => null
|
||||||
private modifyGrid: (hidden: boolean) => Promise<void> | null = () => null
|
private modifyGrid: (hidden: boolean) => Promise<void> | null = () => null
|
||||||
|
|
||||||
|
private onEngineConnectionOpened = () => {}
|
||||||
|
private onEngineConnectionClosed = () => {}
|
||||||
|
private onEngineConnectionStarted = ({ detail: engineConnection }: any) => {}
|
||||||
|
private onEngineConnectionNewTrack = ({
|
||||||
|
detail,
|
||||||
|
}: CustomEvent<NewTrackArgs>) => {}
|
||||||
|
|
||||||
start({
|
start({
|
||||||
|
restart,
|
||||||
setMediaStream,
|
setMediaStream,
|
||||||
setIsStreamReady,
|
setIsStreamReady,
|
||||||
width,
|
width,
|
||||||
@ -1170,6 +1283,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
showScaleGrid: false,
|
showScaleGrid: false,
|
||||||
},
|
},
|
||||||
}: {
|
}: {
|
||||||
|
restart?: boolean
|
||||||
setMediaStream: (stream: MediaStream) => void
|
setMediaStream: (stream: MediaStream) => void
|
||||||
setIsStreamReady: (isStreamReady: boolean) => void
|
setIsStreamReady: (isStreamReady: boolean) => void
|
||||||
width: number
|
width: number
|
||||||
@ -1215,9 +1329,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
this.engineConnection.addEventListener(
|
this.onEngineConnectionOpened = () => {
|
||||||
EngineConnectionEvents.Opened,
|
|
||||||
() => {
|
|
||||||
// Set the stream background color
|
// Set the stream background color
|
||||||
// This takes RGBA values from 0-1
|
// This takes RGBA values from 0-1
|
||||||
// So we convert from the conventional 0-255 found in Figma
|
// So we convert from the conventional 0-255 found in Figma
|
||||||
@ -1272,18 +1384,20 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
await executeCode()
|
await executeCode()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
this.engineConnection.addEventListener(
|
||||||
|
EngineConnectionEvents.Opened,
|
||||||
|
this.onEngineConnectionOpened
|
||||||
)
|
)
|
||||||
|
|
||||||
this.engineConnection.addEventListener(
|
this.onEngineConnectionClosed = () => {
|
||||||
EngineConnectionEvents.Closed,
|
|
||||||
() => {
|
|
||||||
setIsStreamReady(false)
|
setIsStreamReady(false)
|
||||||
}
|
}
|
||||||
|
this.engineConnection.addEventListener(
|
||||||
|
EngineConnectionEvents.Closed,
|
||||||
|
this.onEngineConnectionClosed
|
||||||
)
|
)
|
||||||
|
|
||||||
this.engineConnection.addEventListener(
|
this.onEngineConnectionStarted = ({ detail: engineConnection }: any) => {
|
||||||
EngineConnectionEvents.ConnectionStarted,
|
|
||||||
({ detail: engineConnection }: any) => {
|
|
||||||
engineConnection?.pc?.addEventListener(
|
engineConnection?.pc?.addEventListener(
|
||||||
'datachannel',
|
'datachannel',
|
||||||
(event: RTCDataChannelEvent) => {
|
(event: RTCDataChannelEvent) => {
|
||||||
@ -1356,9 +1470,9 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
}) as EventListener)
|
}) as EventListener)
|
||||||
|
|
||||||
this.engineConnection?.addEventListener(
|
this.onEngineConnectionNewTrack = ({
|
||||||
EngineConnectionEvents.NewTrack,
|
detail: { mediaStream },
|
||||||
(({ detail: { mediaStream } }: CustomEvent<NewTrackArgs>) => {
|
}: CustomEvent<NewTrackArgs>) => {
|
||||||
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
|
mediaStream.getVideoTracks()[0].addEventListener('mute', () => {
|
||||||
console.error(
|
console.error(
|
||||||
'video track mute: check webrtc internals -> inbound rtp'
|
'video track mute: check webrtc internals -> inbound rtp'
|
||||||
@ -1366,11 +1480,17 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
})
|
})
|
||||||
|
|
||||||
setMediaStream(mediaStream)
|
setMediaStream(mediaStream)
|
||||||
}) as EventListener
|
}
|
||||||
|
this.engineConnection?.addEventListener(
|
||||||
|
EngineConnectionEvents.NewTrack,
|
||||||
|
this.onEngineConnectionNewTrack as EventListener
|
||||||
)
|
)
|
||||||
|
|
||||||
this.engineConnection?.connect()
|
this.engineConnection?.connect()
|
||||||
}
|
}
|
||||||
|
this.engineConnection.addEventListener(
|
||||||
|
EngineConnectionEvents.ConnectionStarted,
|
||||||
|
this.onEngineConnectionStarted
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1627,15 +1747,34 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tearDown() {
|
tearDown(opts?: { idleMode: boolean }) {
|
||||||
if (this.engineConnection) {
|
if (this.engineConnection) {
|
||||||
this.engineConnection?.tearDown()
|
this.engineConnection.removeEventListener(
|
||||||
|
EngineConnectionEvents.Opened,
|
||||||
|
this.onEngineConnectionOpened
|
||||||
|
)
|
||||||
|
this.engineConnection.removeEventListener(
|
||||||
|
EngineConnectionEvents.Closed,
|
||||||
|
this.onEngineConnectionClosed
|
||||||
|
)
|
||||||
|
this.engineConnection.removeEventListener(
|
||||||
|
EngineConnectionEvents.ConnectionStarted,
|
||||||
|
this.onEngineConnectionStarted
|
||||||
|
)
|
||||||
|
this.engineConnection.removeEventListener(
|
||||||
|
EngineConnectionEvents.NewTrack,
|
||||||
|
this.onEngineConnectionNewTrack as EventListener
|
||||||
|
)
|
||||||
|
|
||||||
|
this.engineConnection?.tearDown(opts)
|
||||||
|
this.engineConnection = undefined
|
||||||
|
|
||||||
// Our window.tearDown assignment causes this case to happen which is
|
// Our window.tearDown assignment causes this case to happen which is
|
||||||
// only really for tests.
|
// only really for tests.
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} else if (this.engineCommandManager?.engineConnection) {
|
} else if (this.engineCommandManager?.engineConnection) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
this.engineCommandManager?.engineConnection?.tearDown()
|
this.engineCommandManager?.engineConnection?.tearDown(opts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async startNewSession() {
|
async startNewSession() {
|
||||||
|
@ -221,7 +221,7 @@ describe('testing addTagForSketchOnFace', () => {
|
|||||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
const sketchOnFaceRetVal = addTagForSketchOnFace(
|
const sketchOnFaceRetVal = addTagForSketchOnFace(
|
||||||
{
|
{
|
||||||
previousProgramMemory: programMemory,
|
// previousProgramMemory: programMemory, // redundant?
|
||||||
pathToNode,
|
pathToNode,
|
||||||
node: ast,
|
node: ast,
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,6 @@ import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
SketchLineHelper,
|
SketchLineHelper,
|
||||||
ModifyAstBase,
|
|
||||||
TransformCallback,
|
TransformCallback,
|
||||||
ConstrainInfo,
|
ConstrainInfo,
|
||||||
RawValues,
|
RawValues,
|
||||||
@ -37,6 +36,7 @@ import {
|
|||||||
SingleValueInput,
|
SingleValueInput,
|
||||||
VarValueKeys,
|
VarValueKeys,
|
||||||
ArrayOrObjItemInput,
|
ArrayOrObjItemInput,
|
||||||
|
AddTagInfo,
|
||||||
} from 'lang/std/stdTypes'
|
} from 'lang/std/stdTypes'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -308,6 +308,18 @@ function singleRawValueHelper(
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getTag(index = 2): SketchLineHelper['getTag'] {
|
||||||
|
return (callExp: CallExpression) => {
|
||||||
|
if (callExp.type !== 'CallExpression')
|
||||||
|
return new Error('Not a CallExpression')
|
||||||
|
const arg = callExp.arguments?.[index]
|
||||||
|
if (!arg) return new Error('No argument')
|
||||||
|
if (arg.type !== 'TagDeclarator')
|
||||||
|
return new Error('Tag not a TagDeclarator')
|
||||||
|
return arg.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const lineTo: SketchLineHelper = {
|
export const lineTo: SketchLineHelper = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
@ -377,6 +389,7 @@ export const lineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -503,6 +516,7 @@ export const line: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -563,6 +577,7 @@ export const xLineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
horzVertConstraintInfoHelper(
|
horzVertConstraintInfoHelper(
|
||||||
@ -623,6 +638,7 @@ export const yLineTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
horzVertConstraintInfoHelper(
|
horzVertConstraintInfoHelper(
|
||||||
@ -682,6 +698,7 @@ export const xLine: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
horzVertConstraintInfoHelper(
|
horzVertConstraintInfoHelper(
|
||||||
@ -738,6 +755,7 @@ export const yLine: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
horzVertConstraintInfoHelper(
|
horzVertConstraintInfoHelper(
|
||||||
@ -830,6 +848,7 @@ export const tangentialArcTo: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
||||||
if (callExp.type !== 'CallExpression') return []
|
if (callExp.type !== 'CallExpression') return []
|
||||||
@ -948,6 +967,7 @@ export const angledLine: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -1044,6 +1064,7 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -1140,6 +1161,7 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -1227,6 +1249,7 @@ export const angledLineToX: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -1316,6 +1339,7 @@ export const angledLineToY: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp, ...args) =>
|
getConstraintInfo: (callExp, ...args) =>
|
||||||
commonConstraintInfoHelper(
|
commonConstraintInfoHelper(
|
||||||
@ -1440,6 +1464,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getTag: getTag(),
|
||||||
addTag: addTag(),
|
addTag: addTag(),
|
||||||
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
||||||
if (callExp.type !== 'CallExpression') return []
|
if (callExp.type !== 'CallExpression') return []
|
||||||
@ -1792,10 +1817,7 @@ export function replaceSketchLine({
|
|||||||
return { modifiedAst, valueUsedInTransform, pathToNode }
|
return { modifiedAst, valueUsedInTransform, pathToNode }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addTagForSketchOnFace(
|
export function addTagForSketchOnFace(a: AddTagInfo, expressionName: string) {
|
||||||
a: ModifyAstBase,
|
|
||||||
expressionName: string
|
|
||||||
) {
|
|
||||||
if (expressionName === 'close') {
|
if (expressionName === 'close') {
|
||||||
return addTag(1)(a)
|
return addTag(1)(a)
|
||||||
}
|
}
|
||||||
@ -1806,6 +1828,17 @@ export function addTagForSketchOnFace(
|
|||||||
return new Error(`"${expressionName}" is not a sketch line helper`)
|
return new Error(`"${expressionName}" is not a sketch line helper`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTagFromCallExpression(
|
||||||
|
callExp: CallExpression
|
||||||
|
): string | Error {
|
||||||
|
if (callExp.callee.name === 'close') return getTag(1)(callExp)
|
||||||
|
if (callExp.callee.name in sketchLineHelperMap) {
|
||||||
|
const { getTag } = sketchLineHelperMap[callExp.callee.name]
|
||||||
|
return getTag(callExp)
|
||||||
|
}
|
||||||
|
return new Error(`"${callExp.callee.name}" is not a sketch line helper`)
|
||||||
|
}
|
||||||
|
|
||||||
function isAngleLiteral(lineArugement: Value): boolean {
|
function isAngleLiteral(lineArugement: Value): boolean {
|
||||||
return lineArugement?.type === 'ArrayExpression'
|
return lineArugement?.type === 'ArrayExpression'
|
||||||
? isLiteralArrayOrStatic(lineArugement.elements[0])
|
? isLiteralArrayOrStatic(lineArugement.elements[0])
|
||||||
@ -1816,9 +1849,7 @@ function isAngleLiteral(lineArugement: Value): boolean {
|
|||||||
: false
|
: false
|
||||||
}
|
}
|
||||||
|
|
||||||
type addTagFn = (
|
type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error
|
||||||
a: ModifyAstBase
|
|
||||||
) => { modifiedAst: Program; tag: string } | Error
|
|
||||||
|
|
||||||
function addTag(tagIndex = 2): addTagFn {
|
function addTag(tagIndex = 2): addTagFn {
|
||||||
return ({ node, pathToNode }) => {
|
return ({ node, pathToNode }) => {
|
||||||
|
@ -32,6 +32,11 @@ export interface ModifyAstBase {
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AddTagInfo {
|
||||||
|
node: Program
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
|
||||||
interface addCall extends ModifyAstBase {
|
interface addCall extends ModifyAstBase {
|
||||||
to: [number, number]
|
to: [number, number]
|
||||||
from: [number, number]
|
from: [number, number]
|
||||||
@ -127,7 +132,8 @@ export interface SketchLineHelper {
|
|||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}
|
}
|
||||||
| Error
|
| Error
|
||||||
addTag: (a: ModifyAstBase) =>
|
getTag: (a: CallExpression) => string | Error
|
||||||
|
addTag: (a: AddTagInfo) =>
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
tag: string
|
tag: string
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { CommandSetConfig } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
||||||
import { authMachine } from 'machines/authMachine'
|
import { authMachine } from 'machines/authMachine'
|
||||||
|
|
||||||
type AuthCommandSchema = {}
|
type AuthCommandSchema = {}
|
||||||
|
|
||||||
export const authCommandBarConfig: CommandSetConfig<
|
export const authCommandBarConfig: StateMachineCommandSetConfig<
|
||||||
typeof authMachine,
|
typeof authMachine,
|
||||||
AuthCommandSchema
|
AuthCommandSchema
|
||||||
> = {
|
> = {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { CommandSetConfig } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig } from 'lib/commandTypes'
|
||||||
import { homeMachine } from 'machines/homeMachine'
|
import { homeMachine } from 'machines/homeMachine'
|
||||||
|
|
||||||
export type HomeCommandSchema = {
|
export type HomeCommandSchema = {
|
||||||
@ -17,7 +17,7 @@ export type HomeCommandSchema = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const homeCommandBarConfig: CommandSetConfig<
|
export const homeCommandBarConfig: StateMachineCommandSetConfig<
|
||||||
typeof homeMachine,
|
typeof homeMachine,
|
||||||
HomeCommandSchema
|
HomeCommandSchema
|
||||||
> = {
|
> = {
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { CommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
||||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { modelingMachine } from 'machines/modelingMachine'
|
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
|
||||||
|
|
||||||
type OutputFormat = Models['OutputFormat_type']
|
type OutputFormat = Models['OutputFormat_type']
|
||||||
type OutputTypeKey = OutputFormat['type']
|
type OutputTypeKey = OutputFormat['type']
|
||||||
@ -27,9 +27,17 @@ export type ModelingCommandSchema = {
|
|||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||||
distance: KclCommandValue
|
distance: KclCommandValue
|
||||||
}
|
}
|
||||||
|
Fillet: {
|
||||||
|
// todo
|
||||||
|
selection: Selections
|
||||||
|
radius: KclCommandValue
|
||||||
|
}
|
||||||
|
'change tool': {
|
||||||
|
tool: SketchTool
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modelingMachineConfig: CommandSetConfig<
|
export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||||
typeof modelingMachine,
|
typeof modelingMachine,
|
||||||
ModelingCommandSchema
|
ModelingCommandSchema
|
||||||
> = {
|
> = {
|
||||||
@ -37,22 +45,47 @@ export const modelingMachineConfig: CommandSetConfig<
|
|||||||
description: 'Enter sketch mode.',
|
description: 'Enter sketch mode.',
|
||||||
icon: 'sketch',
|
icon: 'sketch',
|
||||||
},
|
},
|
||||||
// TODO the event is no 'change tool' with data: 'line', 'rectangle' etc
|
'change tool': [
|
||||||
// 'Equip Line tool': {
|
{
|
||||||
// description: 'Start drawing straight lines.',
|
description: 'Start drawing straight lines.',
|
||||||
// icon: 'line',
|
icon: 'line',
|
||||||
// displayName: 'Line',
|
displayName: 'Line',
|
||||||
// },
|
args: {
|
||||||
// 'Equip tangential arc to': {
|
tool: {
|
||||||
// description: 'Start drawing an arc tangent to the current segment.',
|
defaultValue: 'line',
|
||||||
// icon: 'arc',
|
required: true,
|
||||||
// displayName: 'Tangential Arc',
|
skip: true,
|
||||||
// },
|
inputType: 'string',
|
||||||
// 'Equip rectangle tool': {
|
},
|
||||||
// description: 'Start drawing a rectangle.',
|
},
|
||||||
// icon: 'rectangle',
|
},
|
||||||
// displayName: 'Rectangle',
|
{
|
||||||
// },
|
description: 'Start drawing an arc tangent to the current segment.',
|
||||||
|
icon: 'arc',
|
||||||
|
displayName: 'Tangential Arc',
|
||||||
|
args: {
|
||||||
|
tool: {
|
||||||
|
defaultValue: 'tangentialArc',
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
inputType: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Start drawing a rectangle.',
|
||||||
|
icon: 'rectangle',
|
||||||
|
displayName: 'Rectangle',
|
||||||
|
args: {
|
||||||
|
tool: {
|
||||||
|
defaultValue: 'rectangle',
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
inputType: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
Export: {
|
Export: {
|
||||||
description: 'Export the current model.',
|
description: 'Export the current model.',
|
||||||
icon: 'exportFile',
|
icon: 'exportFile',
|
||||||
@ -157,4 +190,36 @@ export const modelingMachineConfig: CommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Fillet: {
|
||||||
|
// todo
|
||||||
|
description: 'Fillet edge',
|
||||||
|
icon: 'fillet',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
selection: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: [
|
||||||
|
'default',
|
||||||
|
'line-end',
|
||||||
|
'line-mid',
|
||||||
|
'extrude-wall', // to fix: accespts only this selection type
|
||||||
|
'start-cap',
|
||||||
|
'end-cap',
|
||||||
|
'point',
|
||||||
|
'edge',
|
||||||
|
'line',
|
||||||
|
'arc',
|
||||||
|
'all',
|
||||||
|
],
|
||||||
|
multiple: true, // TODO: multiple selection like in extrude command
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
radius: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -124,6 +124,7 @@ export function createSettingsCommand({
|
|||||||
displayName: `Settings · ${decamelize(type.replaceAll('.', ' · '), {
|
displayName: `Settings · ${decamelize(type.replaceAll('.', ' · '), {
|
||||||
separator: ' ',
|
separator: ' ',
|
||||||
})}`,
|
})}`,
|
||||||
|
description: settingConfig.description,
|
||||||
groupId: 'settings',
|
groupId: 'settings',
|
||||||
icon: 'settings',
|
icon: 'settings',
|
||||||
needsReview: false,
|
needsReview: false,
|
||||||
|
@ -33,13 +33,13 @@ export interface KclExpressionWithVariable extends KclExpression {
|
|||||||
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
export type KclCommandValue = KclExpression | KclExpressionWithVariable
|
||||||
export type CommandInputType = (typeof INPUT_TYPES)[number]
|
export type CommandInputType = (typeof INPUT_TYPES)[number]
|
||||||
|
|
||||||
export type CommandSetSchema<T extends AnyStateMachine> = Partial<{
|
export type StateMachineCommandSetSchema<T extends AnyStateMachine> = Partial<{
|
||||||
[EventType in EventFrom<T>['type']]: Record<string, any>
|
[EventType in EventFrom<T>['type']]: Record<string, any>
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type CommandSet<
|
export type StateMachineCommandSet<
|
||||||
T extends AllMachines,
|
T extends AllMachines,
|
||||||
Schema extends CommandSetSchema<T>
|
Schema extends StateMachineCommandSetSchema<T>
|
||||||
> = Partial<{
|
> = Partial<{
|
||||||
[EventType in EventFrom<T>['type']]: Command<
|
[EventType in EventFrom<T>['type']]: Command<
|
||||||
T,
|
T,
|
||||||
@ -48,21 +48,25 @@ export type CommandSet<
|
|||||||
>
|
>
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type CommandSetConfig<
|
/**
|
||||||
|
* A configuration object for a set of commands tied to a state machine.
|
||||||
|
* Each event type can have one or more commands associated with it.
|
||||||
|
* @param T The state machine type.
|
||||||
|
* @param Schema The schema for the command set, defined by the developer.
|
||||||
|
*/
|
||||||
|
export type StateMachineCommandSetConfig<
|
||||||
T extends AllMachines,
|
T extends AllMachines,
|
||||||
Schema extends CommandSetSchema<T>
|
Schema extends StateMachineCommandSetSchema<T>
|
||||||
> = Partial<{
|
> = Partial<{
|
||||||
[EventType in EventFrom<T>['type']]: CommandConfig<
|
[EventType in EventFrom<T>['type']]:
|
||||||
T,
|
| CommandConfig<T, EventFrom<T>['type'], Schema[EventType]>
|
||||||
EventFrom<T>['type'],
|
| CommandConfig<T, EventFrom<T>['type'], Schema[EventType]>[]
|
||||||
Schema[EventType]
|
|
||||||
>
|
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export type Command<
|
export type Command<
|
||||||
T extends AnyStateMachine = AnyStateMachine,
|
T extends AnyStateMachine = AnyStateMachine,
|
||||||
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
|
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
|
||||||
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
|
CommandSchema extends StateMachineCommandSetSchema<T>[CommandName] = StateMachineCommandSetSchema<T>[CommandName]
|
||||||
> = {
|
> = {
|
||||||
name: CommandName
|
name: CommandName
|
||||||
groupId: T['id']
|
groupId: T['id']
|
||||||
@ -81,7 +85,7 @@ export type Command<
|
|||||||
export type CommandConfig<
|
export type CommandConfig<
|
||||||
T extends AnyStateMachine = AnyStateMachine,
|
T extends AnyStateMachine = AnyStateMachine,
|
||||||
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
|
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type'],
|
||||||
CommandSchema extends CommandSetSchema<T>[CommandName] = CommandSetSchema<T>[CommandName]
|
CommandSchema extends StateMachineCommandSetSchema<T>[CommandName] = StateMachineCommandSetSchema<T>[CommandName]
|
||||||
> = Omit<
|
> = Omit<
|
||||||
Command<T, CommandName, CommandSchema>,
|
Command<T, CommandName, CommandSchema>,
|
||||||
'name' | 'groupId' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
|
'name' | 'groupId' | 'onSubmit' | 'onCancel' | 'args' | 'needsReview'
|
||||||
|
@ -51,6 +51,7 @@ export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
|||||||
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||||
SKETCH: 'sketch',
|
SKETCH: 'sketch',
|
||||||
EXTRUDE: 'extrude',
|
EXTRUDE: 'extrude',
|
||||||
|
SEGMENT: 'seg',
|
||||||
} as const
|
} as const
|
||||||
/** The default KCL length expression */
|
/** The default KCL length expression */
|
||||||
export const KCL_DEFAULT_LENGTH = `5`
|
export const KCL_DEFAULT_LENGTH = `5`
|
||||||
|
@ -11,20 +11,20 @@ import {
|
|||||||
CommandArgument,
|
CommandArgument,
|
||||||
CommandArgumentConfig,
|
CommandArgumentConfig,
|
||||||
CommandConfig,
|
CommandConfig,
|
||||||
CommandSetConfig,
|
StateMachineCommandSetConfig,
|
||||||
CommandSetSchema,
|
StateMachineCommandSetSchema,
|
||||||
} from './commandTypes'
|
} from './commandTypes'
|
||||||
|
|
||||||
interface CreateMachineCommandProps<
|
interface CreateMachineCommandProps<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
S extends CommandSetSchema<T>
|
S extends StateMachineCommandSetSchema<T>
|
||||||
> {
|
> {
|
||||||
type: EventFrom<T>['type']
|
type: EventFrom<T>['type']
|
||||||
groupId: T['id']
|
groupId: T['id']
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
send: Function
|
send: Function
|
||||||
actor: InterpreterFrom<T>
|
actor: InterpreterFrom<T>
|
||||||
commandBarConfig?: CommandSetConfig<T, S>
|
commandBarConfig?: StateMachineCommandSetConfig<T, S>
|
||||||
onCancel?: () => void
|
onCancel?: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ interface CreateMachineCommandProps<
|
|||||||
// from a more terse Command Bar Meta definition.
|
// from a more terse Command Bar Meta definition.
|
||||||
export function createMachineCommand<
|
export function createMachineCommand<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
S extends CommandSetSchema<T>
|
S extends StateMachineCommandSetSchema<T>
|
||||||
>({
|
>({
|
||||||
groupId,
|
groupId,
|
||||||
type,
|
type,
|
||||||
@ -41,13 +41,30 @@ export function createMachineCommand<
|
|||||||
actor,
|
actor,
|
||||||
commandBarConfig,
|
commandBarConfig,
|
||||||
onCancel,
|
onCancel,
|
||||||
}: CreateMachineCommandProps<T, S>): Command<
|
}: CreateMachineCommandProps<T, S>):
|
||||||
T,
|
| Command<T, typeof type, S[typeof type]>
|
||||||
typeof type,
|
| Command<T, typeof type, S[typeof type]>[]
|
||||||
S[typeof type]
|
| null {
|
||||||
> | null {
|
|
||||||
const commandConfig = commandBarConfig && commandBarConfig[type]
|
const commandConfig = commandBarConfig && commandBarConfig[type]
|
||||||
if (!commandConfig) return null
|
// There may be no command config for this event type,
|
||||||
|
// or there may be multiple commands to create.
|
||||||
|
if (!commandConfig) {
|
||||||
|
return null
|
||||||
|
} else if (commandConfig instanceof Array) {
|
||||||
|
return commandConfig
|
||||||
|
.map((config) =>
|
||||||
|
createMachineCommand({
|
||||||
|
groupId,
|
||||||
|
type,
|
||||||
|
state,
|
||||||
|
send,
|
||||||
|
actor,
|
||||||
|
commandBarConfig: { [type]: config },
|
||||||
|
onCancel,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.filter((c) => c !== null) as Command<T, typeof type, S[typeof type]>[]
|
||||||
|
}
|
||||||
|
|
||||||
// Hide commands based on platform by returning `null`
|
// Hide commands based on platform by returning `null`
|
||||||
// so the consumer can filter them out
|
// so the consumer can filter them out
|
||||||
@ -64,6 +81,7 @@ export function createMachineCommand<
|
|||||||
name: type,
|
name: type,
|
||||||
groupId,
|
groupId,
|
||||||
icon,
|
icon,
|
||||||
|
description: commandConfig.description,
|
||||||
needsReview: commandConfig.needsReview || false,
|
needsReview: commandConfig.needsReview || false,
|
||||||
onSubmit: (data?: S[typeof type]) => {
|
onSubmit: (data?: S[typeof type]) => {
|
||||||
if (data !== undefined && data !== null) {
|
if (data !== undefined && data !== null) {
|
||||||
@ -84,6 +102,10 @@ export function createMachineCommand<
|
|||||||
command.onCancel = onCancel
|
command.onCancel = onCancel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ('displayName' in commandConfig) {
|
||||||
|
command.displayName = commandConfig.displayName
|
||||||
|
}
|
||||||
|
|
||||||
return command
|
return command
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +114,7 @@ export function createMachineCommand<
|
|||||||
// bundled together into the args for a Command.
|
// bundled together into the args for a Command.
|
||||||
function buildCommandArguments<
|
function buildCommandArguments<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
S extends CommandSetSchema<T>,
|
S extends StateMachineCommandSetSchema<T>,
|
||||||
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type']
|
CommandName extends EventFrom<T>['type'] = EventFrom<T>['type']
|
||||||
>(
|
>(
|
||||||
state: StateFrom<T>,
|
state: StateFrom<T>,
|
||||||
@ -112,7 +134,7 @@ function buildCommandArguments<
|
|||||||
|
|
||||||
export function buildCommandArgument<
|
export function buildCommandArgument<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
O extends CommandSetSchema<T> = CommandSetSchema<T>
|
O extends StateMachineCommandSetSchema<T> = StateMachineCommandSetSchema<T>
|
||||||
>(
|
>(
|
||||||
arg: CommandArgumentConfig<O, T>,
|
arg: CommandArgumentConfig<O, T>,
|
||||||
context: ContextFrom<T>,
|
context: ContextFrom<T>,
|
||||||
|
@ -406,6 +406,17 @@ export function canExtrudeSelection(selection: Selections) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canFilletSelection(selection: Selections) {
|
||||||
|
const commonNodes = selection.codeBasedSelections.map((_, i) =>
|
||||||
|
buildCommonNodeFromSelection(selection, i)
|
||||||
|
) // TODO FILLET DUMMY PLACEHOLDER
|
||||||
|
return (
|
||||||
|
!!isSketchPipe(selection) &&
|
||||||
|
commonNodes.every((n) => nodeHasClose(n)) &&
|
||||||
|
commonNodes.every((n) => !nodeHasExtrude(n))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function canExtrudeSelectionItem(selection: Selections, i: number) {
|
function canExtrudeSelectionItem(selection: Selections, i: number) {
|
||||||
const commonNode = buildCommonNodeFromSelection(selection, i)
|
const commonNode = buildCommonNodeFromSelection(selection, i)
|
||||||
|
|
||||||
|
@ -163,6 +163,17 @@ export function createSettings() {
|
|||||||
validate: (v) => typeof v === 'boolean',
|
validate: (v) => typeof v === 'boolean',
|
||||||
hideOnPlatform: 'both', //for now
|
hideOnPlatform: 'both', //for now
|
||||||
}),
|
}),
|
||||||
|
/**
|
||||||
|
* Stream resource saving behavior toggle
|
||||||
|
*/
|
||||||
|
streamIdleMode: new Setting<boolean>({
|
||||||
|
defaultValue: false,
|
||||||
|
description: 'Toggle stream idling, saving bandwidth and battery',
|
||||||
|
validate: (v) => typeof v === 'boolean',
|
||||||
|
commandConfig: {
|
||||||
|
inputType: 'boolean',
|
||||||
|
},
|
||||||
|
}),
|
||||||
onboardingStatus: new Setting<string>({
|
onboardingStatus: new Setting<string>({
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
validate: (v) => typeof v === 'string',
|
validate: (v) => typeof v === 'string',
|
||||||
|
@ -38,6 +38,7 @@ function configurationToSettingsPayload(
|
|||||||
: undefined,
|
: undefined,
|
||||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
||||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
||||||
|
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
||||||
projectDirectory: configuration?.settings?.project?.directory,
|
projectDirectory: configuration?.settings?.project?.directory,
|
||||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
||||||
},
|
},
|
||||||
@ -75,6 +76,7 @@ function projectConfigurationToSettingsPayload(
|
|||||||
: undefined,
|
: undefined,
|
||||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
||||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
||||||
|
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
||||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
||||||
},
|
},
|
||||||
modeling: {
|
modeling: {
|
||||||
|
@ -37,4 +37,7 @@ if (typeof window !== 'undefined') {
|
|||||||
document.addEventListener('mousemove', (e) =>
|
document.addEventListener('mousemove', (e) =>
|
||||||
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
|
console.log(`await page.mouse.click(${e.clientX}, ${e.clientY})`)
|
||||||
)
|
)
|
||||||
|
;(window as any).enableFillet = () => {
|
||||||
|
;(window as any)._enableFillet = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
|
|||||||
import { Value, parse } from 'lang/wasm'
|
import { Value, parse } from 'lang/wasm'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { executeAst } from 'lang/langHelpers'
|
import { executeAst } from 'lang/langHelpers'
|
||||||
import { trap } from 'lib/trap'
|
import { err, trap } from 'lib/trap'
|
||||||
|
|
||||||
const isValidVariableName = (name: string) =>
|
const isValidVariableName = (name: string) =>
|
||||||
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
|
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
|
||||||
@ -86,6 +86,7 @@ export function useCalculateKclExpression({
|
|||||||
const execAstAndSetResult = async () => {
|
const execAstAndSetResult = async () => {
|
||||||
const _code = `const __result__ = ${value}`
|
const _code = `const __result__ = ${value}`
|
||||||
const ast = parse(_code)
|
const ast = parse(_code)
|
||||||
|
if (err(ast)) return
|
||||||
if (trap(ast, { suppress: true })) return
|
if (trap(ast, { suppress: true })) return
|
||||||
|
|
||||||
const _programMem: any = { root: {}, return: null }
|
const _programMem: any = { root: {}, return: null }
|
||||||
|
@ -66,7 +66,7 @@ export type CommandBarMachineEvent =
|
|||||||
|
|
||||||
export const commandBarMachine = createMachine(
|
export const commandBarMachine = createMachine(
|
||||||
{
|
{
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswZJbPltM0U3eXLZAsRFeQcrerUHRbTFvfkmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUilkskqdiUWjWCAcDC0kjUqnElkc6lkoK0JzOT0CnWo1zIAEEIARvn48LA-uwuIC4qAEqIHJjJPJcYtxFoYdJFFjVsZEG5pqCquJxJoGvJxOUCT82sSrj0AEpgdCoABuYC+yog9OYIyZsQmYmhWzMmTqLnU0kyikRGVRbj5lg2SmUam5StpFxIkgAymBXh8HlADQGyKHw58aecGZEzUDWYgchY7CisWoSlpQQ5EVDLJJxKkdIpyypUop-ed2kHCW0Xu9uD1kwDzcCEJCtlUNgWhZZ1OlnaKEBpZJItHVzDZ5xlpPWiZdDc8w22Ow5wozosyLUiVNMSg5ytVKmfrIipzO564z2otPVpI1vKd18SAOJYbgACzAEhI3wUgoAAV3QQZuFgCg-1wGAvjAkgSCgkCSHAyCcG4TtUxZYRED2TQZGSOw80qaQ7ARCc9mmZYSksextGkNJBRXFUOh-f9AOA0CIKgmCABE4E3D5oyTE1-lww8clKBjZF2Bh5SFZwXUUyRVDzJwNE2LQzzYwNJE4gCgJwKNeMw6DJHJAB3LAYlM-AHjYMDuFjMCACN0B4NCMKgnD927dMkWSUs8yY8RISfWQshvcsrDlapshULJPHfZsDKM7iHPM-jJAANSwShOAgX9IzICB+DASQHh1VAAGsqp1Qrit-MByXQvisP8sY8ISZwbCsDllBLSwz1qRFBQsRZ5WRIVkui-TG0M39jJ4jqLNgfLmpK3hTLIQCSFQIM2EoX8ADMjvQSQmqKna2vWvyJL3HrD1SEoyzSdJUkUm9dnUtQn10aK6hkxbiU1HVODAay3M87zE1+J6UwCtN8IQUauR0OZ0ksFwUUqRFPWmUdouPXR3TBjoIahmHKQIHKuqRrtUYSaVShhLF+TkeTzBFQosVKDRM2RVInEdSmg2p6GyE1bU9R8zrsKZqSexFqQHWlHNXHzCbFhnX1+UydFkVxiXJClmGAFEIG8hmld3ZGXp7TR5C5RSCxdjlQV1tR9bqaVdhsSFxDN5AALeOrgPa3ysJgiqcCqmr6sa7bWujxXjQd5nesQZYthsblIQYJx6hUic9AsdEFT2HQzByVLmgDJaw-eSOHPTjbysq6qcFqhrrtT9sO-4ugd1NFGc4QXEPo5eVLGsFxlnKREXGnZI9HcT1mOikO0s-S5w7bqNh9j-bkKOyQTvOy6B9utOHtj7qD1V8pSyrHJZ3STY4QmjQ0R2Ww0oSjuF3u+HAqAIBwEEOlRs48nZBVENkKQNprC42qBoJ0LothaXMJ9aElRXDLj3k3VUpJIBwOfgg4oMx8y41SPUOY8p5DFk2GiKoxsoRVmhGoM2cY2zAQRngChgU0a7HZtFH0ilRp1D5usVh6JXCKD2CUdI8lQ7rlbB8chkkJ6HkxFsBeulbQZAUCwrYCiqzIhyE+fqZtMomTMg-aCwiWYEV2NOBUCghQ6EFGzYsvpwQUT5MxawFECx2JWllRxMdLI2TsrtKMTkXIuMnmeCiZYwrePMFWCKv1XZKVGroWwmxNARK4g4hWG0tp3wSSk16lQpC41ULjV8uxUjSBvFCNEDTdCOkWCY44xCGzgzAJDaGdTnaZHBADYcqg5DpCovzeRno5izA0BeUOh8o5OPgDo+BaNvqV0xNFaE7gdYTnnvkmUChdKEMyF4LwQA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QGED2BbdBDAdhABAEJYBOAxMgDaqxgDaADALqKgAONAlgC6eo6sQAD0QBaAJwA6AGwAmAKwBmBoukAWafIAcDcSoA0IAJ6JZDaZIDs8hgzV6AjA61a1DWQF8PhtJlwFiciowUkYWJBAOWB4+AQiRBFF5CwdpcVkHS1lpVyU5QxNEh1lFGTUsrUUtOQd5SwZLLx8MbDwiUkkqGkgyAHk2MBwwwSiY-kEEswZJbPltM0U3eXLZAsRFeQcrerUHRbTFvfkmkF9WgI6u2ggyADFONv98WkowAGNufDeW-2GI0d443iiHESkktUUilkskqdiUWjWCAcDC0kjUqnElkc6lkoK0JzOT0CnWo1zIAEEIARvn48LA-uwuIC4qAEqIHJjJPJcYtxFoYdJFFjVsZEG5pqCquJxJoGvJxOUCT82sSrj0AEpgdCoABuYC+yog9OYIyZsQmYmhWzMmTqLnU0kyikRGVRbj5lg2SmUam5StpFxIkgAymBXh8HlADQGyKHw58aecGZEzUDWYgchY7CisWoSlpQQ5EVDLJJxKkdIpyypUop-ed2kHCW0Xu9uD1kwDzcCEJCtlUNgWhZZ1OlnaKEBpZJItHVzDZ5xlpPWiZdDc8w22O7JwozosyLUj3KiZZY85YtMUNgx5Ii81JuS5xBtPVCCyuVR0AOJYbgACzAEhI3wUgoAAV3QQZuFgCg-1wGAvjAkgSCgkCSHAyCcG4TtUxZYRED2TQZGSOw80qaQ7ARCc9mmZYSjPPFpDSQUP0DSQf3-QDgNAiCoJggAROBNw+aMkxNf5cMPHJSjPWRdhvZ9LGcF0b0kVQ8ycDRNkvNRWMbdjfwAoCcCjHjMOgyRyQAdywGITPwB42DA7hYzAgAjdAeDQjCoJw-du3TJE6mnWwoT0NIUTUAs71sMtdGsRYCyUtI9OJDijO49DeKw2BJAANSwShOAgX9IzICB+DASQHh1VAAGsqp1Qrit-MBySy8y-LGPCElSeoy2lZJcwcNRfXHQpBWmWQsRsPYdDPZJUu-QyuPssy+Py5qSt4EyyEAkhUCDNhKF-AAzQ70EkJqiu2tqOt88S926w9UhhLlykWXFHXcTJixsd60hHGxNj2Jag01HVODAKzXI8rzE1+R6U38tN8IQJSuR0OZ0gvHQXHGxBPWmUdppUWwFV0MHJAhqGYcpAh1qwrqDx7ZFajUmVpulEbqlqREsfBTQ0jcPM5P5KmaehshNW1PVvOy7Cka7VHeoYDkyjSPQtAvPMqkRQU1BnX1+UydFkQvCWwEhqWAFEIC8xnFd3ZHntZuTDZG4ob1nGxPTUfXrBnS8nDmEpT2ObxTnXVUALeOrgPanycvKyrqpwWqGqurbWsThXjWd5WesQZYtmBkplCceplIncK0SrXYqnccxxcj5s2OQWP4-s3PzJgiqcCqmr6sa7P2x7vi6AcAvJJ7XEy0dUFMWsFxlnKfn+S5CL3E9Jiuapjv3i7qNx+T-bDskY6zourObpz+6cuZgK0Y5Ua0TqEvXDkJR-YnR0s1xjkIMnDln3uuVsHwOxT1NCjIuR5nCSBUExJi1huRqyooUTYpYnzeyUFkKsy4Tg4FQBAOAgg26Nmga7QKohshSBtNYXGDonQumtPYaQfsSjLBHDefepJICUJZtQ4oMx8wXj6jebGt4JwbC2OiVwig9hh2hLpVu0cOhxjbMBBGeABFPwSA3ERRMlhKWCn9WRVQzZQirMo0BAYNzxn4RJGBh5MRbGXsHawGQFBmLrpUasOQorOAjs0OxaUVrGVMvfaCuiVYEV2KWTYg1yxyA0i6ZYaIPSL3lOkS8wSo6hOWpxCJ8te6WRsnZKMjlnIxNgSNIiiTF6vVSdI9JM1taXlxLzTwqiClBnSqtSJScLIFVvjtKANSXquENqoJwtgyZzRFIUQ4MhqgNACdNTQCpLbWyshMnsjpSwjXRJYHecgcmImsLIz0odNAaGqPiHpDYY6HwTlE+ATiqHPwohYewWQshmGhHrCcJz5Bck5toZwGhMheC8EAA */
|
||||||
predictableActionArguments: true,
|
predictableActionArguments: true,
|
||||||
tsTypes: {} as import('./commandBarMachine.typegen').Typegen0,
|
tsTypes: {} as import('./commandBarMachine.typegen').Typegen0,
|
||||||
context: {
|
context: {
|
||||||
@ -147,6 +147,10 @@ export const commandBarMachine = createMachine(
|
|||||||
cond: 'Command has no arguments',
|
cond: 'Command has no arguments',
|
||||||
actions: ['Execute command'],
|
actions: ['Execute command'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
target: 'Checking Arguments',
|
||||||
|
cond: 'All arguments are skippable',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
target: 'Gathering arguments',
|
target: 'Gathering arguments',
|
||||||
actions: ['Set current argument to first non-skippable'],
|
actions: ['Set current argument to first non-skippable'],
|
||||||
@ -199,7 +203,7 @@ export const commandBarMachine = createMachine(
|
|||||||
'Change current argument': {
|
'Change current argument': {
|
||||||
target: 'Gathering arguments',
|
target: 'Gathering arguments',
|
||||||
internal: true,
|
internal: true,
|
||||||
actions: ['Remove current argument and set a new one'],
|
actions: ['Set current argument'],
|
||||||
},
|
},
|
||||||
|
|
||||||
'Deselect command': {
|
'Deselect command': {
|
||||||
@ -355,29 +359,13 @@ export const commandBarMachine = createMachine(
|
|||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case 'Edit argument':
|
case 'Edit argument':
|
||||||
return event.data.arg
|
return event.data.arg
|
||||||
|
case 'Change current argument':
|
||||||
|
return Object.values(event.data)[0]
|
||||||
default:
|
default:
|
||||||
return context.currentArgument
|
return context.currentArgument
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
'Remove current argument and set a new one': assign({
|
|
||||||
argumentsToSubmit: (context, event) => {
|
|
||||||
if (
|
|
||||||
event.type !== 'Change current argument' ||
|
|
||||||
!context.currentArgument
|
|
||||||
)
|
|
||||||
return context.argumentsToSubmit
|
|
||||||
const { name } = context.currentArgument
|
|
||||||
|
|
||||||
const { [name]: _, ...rest } = context.argumentsToSubmit
|
|
||||||
return rest
|
|
||||||
},
|
|
||||||
currentArgument: (context, event) => {
|
|
||||||
if (event.type !== 'Change current argument')
|
|
||||||
return context.currentArgument
|
|
||||||
return Object.values(event.data)[0]
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
'Clear argument data': assign({
|
'Clear argument data': assign({
|
||||||
selectedCommand: undefined,
|
selectedCommand: undefined,
|
||||||
currentArgument: undefined,
|
currentArgument: undefined,
|
||||||
@ -510,7 +498,9 @@ export const commandBarMachine = createMachine(
|
|||||||
)
|
)
|
||||||
|
|
||||||
function sortCommands(a: Command, b: Command) {
|
function sortCommands(a: Command, b: Command) {
|
||||||
if (b.groupId === 'auth') return -1
|
if (b.groupId === 'auth' && !(a.groupId === 'auth')) return -2
|
||||||
if (a.groupId === 'auth') return 1
|
if (a.groupId === 'auth' && !(b.groupId === 'auth')) return 2
|
||||||
|
if (b.groupId === 'settings' && !(a.groupId === 'settings')) return -1
|
||||||
|
if (a.groupId === 'settings' && !(b.groupId === 'settings')) return 1
|
||||||
return a.name.localeCompare(b.name)
|
return a.name.localeCompare(b.name)
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,12 @@ export const settingsMachine = createMachine(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'set.app.streamIdleMode': {
|
||||||
|
target: 'persisting settings',
|
||||||
|
|
||||||
|
actions: ['setSettingAtLevel', 'toastSuccess'],
|
||||||
|
},
|
||||||
|
|
||||||
'set.modeling.highlightEdges': {
|
'set.modeling.highlightEdges': {
|
||||||
target: 'persisting settings',
|
target: 'persisting settings',
|
||||||
|
|
||||||
|
82
src/wasm-lib/Cargo.lock
generated
@ -169,7 +169,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -180,7 +180,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -191,7 +191,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -431,7 +431,7 @@ dependencies = [
|
|||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -631,7 +631,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim 0.10.0",
|
"strsim 0.10.0",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -642,7 +642,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -697,7 +697,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -726,7 +726,7 @@ dependencies = [
|
|||||||
"rustfmt-wrapper",
|
"rustfmt-wrapper",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_tokenstream",
|
"serde_tokenstream",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -737,7 +737,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -764,7 +764,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -936,7 +936,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1026,7 +1026,7 @@ dependencies = [
|
|||||||
"inflections",
|
"inflections",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1448,7 +1448,7 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1824,7 +1824,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.3",
|
"regex-syntax 0.8.3",
|
||||||
"structmeta",
|
"structmeta",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1842,7 +1842,7 @@ dependencies = [
|
|||||||
"bincode",
|
"bincode",
|
||||||
"either",
|
"either",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itertools 0.10.5",
|
"itertools 0.12.1",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"nom",
|
"nom",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
@ -1877,7 +1877,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2041,7 +2041,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-macros-backend",
|
"pyo3-macros-backend",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2054,7 +2054,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"pyo3-build-config",
|
"pyo3-build-config",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2516,7 +2516,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde_derive_internals",
|
"serde_derive_internals",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2590,7 +2590,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2601,7 +2601,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2624,7 +2624,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2645,7 +2645,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2782,7 +2782,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"structmeta-derive",
|
"structmeta-derive",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2793,7 +2793,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2837,9 +2837,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.70"
|
version = "2.0.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -2860,7 +2860,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2928,22 +2928,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.61"
|
version = "1.0.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709"
|
checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.61"
|
version = "1.0.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533"
|
checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3039,7 +3039,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3192,7 +3192,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3220,7 +3220,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3297,7 +3297,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3455,7 +3455,7 @@ dependencies = [
|
|||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3516,7 +3516,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3551,7 +3551,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@ -3876,7 +3876,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.70",
|
"syn 2.0.71",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -20,7 +20,7 @@ quote = "1"
|
|||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.70", features = ["full"] }
|
syn = { version = "2.0.71", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
|
@ -837,7 +837,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
|
|
||||||
// Read the output file.
|
// Read the output file.
|
||||||
let actual = image::io::Reader::open(output_file).unwrap().decode().unwrap();
|
let actual = image::io::Reader::open(output_file).unwrap().decode().unwrap();
|
||||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &actual, 1.0);
|
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &actual, 0.99);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_someFn {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_someFn {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ mod test_examples_show {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_show1"),
|
&format!("tests/outputs/{}.png", "serial_test_example_show1"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ mod test_examples_my_func {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_my_func0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ mod test_examples_my_func {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func1"),
|
&format!("tests/outputs/{}.png", "serial_test_example_my_func1"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,7 +88,7 @@ mod test_examples_line_to {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_line_to0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ mod test_examples_line_to {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to1"),
|
&format!("tests/outputs/{}.png", "serial_test_example_line_to1"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_min {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_min0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_min0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ mod test_examples_min {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_min1"),
|
&format!("tests/outputs/{}.png", "serial_test_example_min1"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||||
&actual,
|
&actual,
|
||||||
1.0,
|
0.99,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ databake = "0.1.8"
|
|||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.70", features = ["full"] }
|
syn = { version = "2.0.71", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
@ -35,7 +35,7 @@ schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1"]
|
|||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = "1.0.120"
|
serde_json = "1.0.120"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.62"
|
||||||
toml = "0.8.14"
|
toml = "0.8.14"
|
||||||
ts-rs = { version = "9.0.1", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
|
ts-rs = { version = "9.0.1", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
|
||||||
url = { version = "2.5.2", features = ["serde"] }
|
url = { version = "2.5.2", features = ["serde"] }
|
||||||
@ -98,6 +98,10 @@ harness = false
|
|||||||
name = "compiler_benchmark_iai"
|
name = "compiler_benchmark_iai"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[bench]]
|
||||||
|
name = "digest_benchmark"
|
||||||
|
harness = false
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "lsp_semantic_tokens_benchmark_criterion"
|
name = "lsp_semantic_tokens_benchmark_criterion"
|
||||||
harness = false
|
harness = false
|
||||||
|
31
src/wasm-lib/kcl/benches/digest_benchmark.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
use criterion::{criterion_group, criterion_main, Criterion};
|
||||||
|
|
||||||
|
pub fn bench_digest(c: &mut Criterion) {
|
||||||
|
for (name, file) in [
|
||||||
|
("pipes_on_pipes", PIPES_PROGRAM),
|
||||||
|
("big_kitt", KITT_PROGRAM),
|
||||||
|
("cube", CUBE_PROGRAM),
|
||||||
|
("math", MATH_PROGRAM),
|
||||||
|
("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
|
||||||
|
] {
|
||||||
|
let tokens = kcl_lib::token::lexer(file).unwrap();
|
||||||
|
let prog = kcl_lib::parser::Parser::new(tokens).ast().unwrap();
|
||||||
|
c.bench_function(&format!("digest_{name}"), move |b| {
|
||||||
|
let prog = prog.clone();
|
||||||
|
|
||||||
|
b.iter(move || {
|
||||||
|
let mut prog = prog.clone();
|
||||||
|
prog.compute_digest();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(benches, bench_digest);
|
||||||
|
criterion_main!(benches);
|
||||||
|
|
||||||
|
const KITT_PROGRAM: &str = include_str!("../../tests/executor/inputs/kittycad_svg.kcl");
|
||||||
|
const PIPES_PROGRAM: &str = include_str!("../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||||
|
const CUBE_PROGRAM: &str = include_str!("../../tests/executor/inputs/cube.kcl");
|
||||||
|
const MATH_PROGRAM: &str = include_str!("../../tests/executor/inputs/math.kcl");
|
||||||
|
const MIKE_STRESS_TEST_PROGRAM: &str = include_str!("../../tests/executor/inputs/mike_stress_test.kcl");
|
@ -100,18 +100,6 @@ impl ProgramMemory {
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all TagDeclarators and TagIdentifiers in the memory.
|
|
||||||
pub fn get_tags(&self) -> HashMap<String, MemoryItem> {
|
|
||||||
self.root
|
|
||||||
.values()
|
|
||||||
.filter_map(|item| match item {
|
|
||||||
MemoryItem::TagDeclarator(t) => Some((t.name.to_string(), item.clone())),
|
|
||||||
MemoryItem::TagIdentifier(t) => Some((t.value.to_string(), item.clone())),
|
|
||||||
_ => None,
|
|
||||||
})
|
|
||||||
.collect::<HashMap<String, MemoryItem>>()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ProgramMemory {
|
impl Default for ProgramMemory {
|
||||||
|
@ -232,7 +232,7 @@ impl crate::lsp::backend::Backend for Backend {
|
|||||||
// Lets update the ast.
|
// Lets update the ast.
|
||||||
let parser = crate::parser::Parser::new(tokens.clone());
|
let parser = crate::parser::Parser::new(tokens.clone());
|
||||||
let result = parser.ast();
|
let result = parser.ast();
|
||||||
let ast = match result {
|
let mut ast = match result {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.add_to_diagnostics(¶ms, &[err], true).await;
|
self.add_to_diagnostics(¶ms, &[err], true).await;
|
||||||
@ -243,6 +243,11 @@ impl crate::lsp::backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Here we will want to store the digest and compare, but for now
|
||||||
|
// we're doing this in a non-load-bearing capacity so we can remove
|
||||||
|
// this if it backfires and only hork the LSP.
|
||||||
|
ast.compute_digest();
|
||||||
|
|
||||||
// Check if the ast changed.
|
// Check if the ast changed.
|
||||||
let ast_changed = match self.ast_map.get(&filename) {
|
let ast_changed = match self.ast_map.get(&filename) {
|
||||||
Some(old_ast) => {
|
Some(old_ast) => {
|
||||||
|
@ -2371,9 +2371,12 @@ async fn serial_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let mut default_hashed = crate::ast::types::Program::default();
|
||||||
|
default_hashed.compute_digest();
|
||||||
|
|
||||||
// Get the ast.
|
// Get the ast.
|
||||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||||
assert_eq!(ast, crate::ast::types::Program::default());
|
assert_eq!(ast, default_hashed);
|
||||||
// Get the memory.
|
// Get the memory.
|
||||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||||
assert_eq!(memory, ProgramMemory::default());
|
assert_eq!(memory, ProgramMemory::default());
|
||||||
@ -2835,9 +2838,12 @@ async fn serial_test_kcl_lsp_cant_execute_set() {
|
|||||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||||
|
|
||||||
|
let mut default_hashed = crate::ast::types::Program::default();
|
||||||
|
default_hashed.compute_digest();
|
||||||
|
|
||||||
// Get the ast.
|
// Get the ast.
|
||||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||||
assert!(ast != crate::ast::types::Program::default());
|
assert!(ast != default_hashed);
|
||||||
// Get the memory.
|
// Get the memory.
|
||||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||||
// Now it should be the default memory.
|
// Now it should be the default memory.
|
||||||
|
@ -234,6 +234,9 @@ pub struct AppSettings {
|
|||||||
/// This setting only applies to the web app. And is temporary until we have Linux support.
|
/// This setting only applies to the web app. And is temporary until we have Linux support.
|
||||||
#[serde(default, alias = "dismissWebBanner", skip_serializing_if = "is_default")]
|
#[serde(default, alias = "dismissWebBanner", skip_serializing_if = "is_default")]
|
||||||
pub dismiss_web_banner: bool,
|
pub dismiss_web_banner: bool,
|
||||||
|
/// When the user is idle, and this is true, the stream will be torn down.
|
||||||
|
#[serde(default, alias = "streamIdleMode", skip_serializing_if = "is_default")]
|
||||||
|
stream_idle_mode: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
|
// TODO: When we remove backwards compatibility with the old settings file, we can remove this.
|
||||||
@ -651,6 +654,7 @@ textWrapping = true
|
|||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
|
stream_idle_mode: false,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::In,
|
base_unit: UnitLength::In,
|
||||||
@ -710,6 +714,7 @@ includeSettings = false
|
|||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
|
stream_idle_mode: false,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
@ -774,6 +779,7 @@ defaultProjectName = "projects-$nnn"
|
|||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
|
stream_idle_mode: false,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
@ -850,6 +856,7 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
|||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
|
stream_idle_mode: false,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Mm,
|
base_unit: UnitLength::Mm,
|
||||||
|
@ -123,6 +123,7 @@ includeSettings = false
|
|||||||
theme_color: None,
|
theme_color: None,
|
||||||
dismiss_web_banner: false,
|
dismiss_web_banner: false,
|
||||||
enable_ssao: None,
|
enable_ssao: None,
|
||||||
|
stream_idle_mode: false,
|
||||||
},
|
},
|
||||||
modeling: ModelingSettings {
|
modeling: ModelingSettings {
|
||||||
base_unit: UnitLength::Yd,
|
base_unit: UnitLength::Yd,
|
||||||
|
1013
src/wasm-lib/kcl/src/std/args.rs
Normal file
@ -4,6 +4,10 @@ use kcl_lib::{
|
|||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// The minimum permissible difference between asserted twenty-twenty images.
|
||||||
|
/// i.e. how different the current model snapshot can be from the previous saved one.
|
||||||
|
const MIN_DIFF: f64 = 0.99;
|
||||||
|
|
||||||
// mod server;
|
// mod server;
|
||||||
|
|
||||||
async fn new_context(units: UnitLength) -> Result<ExecutorContext> {
|
async fn new_context(units: UnitLength) -> Result<ExecutorContext> {
|
||||||
@ -83,35 +87,35 @@ const part002 = startSketchOn(part001, here)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_riddle_small() {
|
async fn serial_test_riddle_small() {
|
||||||
let code = include_str!("inputs/riddle_small.kcl");
|
let code = include_str!("inputs/riddle_small.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_lego() {
|
async fn serial_test_lego() {
|
||||||
let code = include_str!("inputs/lego.kcl");
|
let code = include_str!("inputs/lego.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/lego.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/lego.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_pipe_as_arg() {
|
async fn serial_test_pipe_as_arg() {
|
||||||
let code = include_str!("inputs/pipe_as_arg.kcl");
|
let code = include_str!("inputs/pipe_as_arg.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/pipe_as_arg.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/pipe_as_arg.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_pentagon_fillet_sugar() {
|
async fn serial_test_pentagon_fillet_sugar() {
|
||||||
let code = include_str!("inputs/pentagon_fillet_sugar.kcl");
|
let code = include_str!("inputs/pentagon_fillet_sugar.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Cm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Cm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/pentagon_fillet_sugar.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/pentagon_fillet_sugar.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -139,14 +143,14 @@ const part002 = startSketchOn(part001, "start")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_start.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_start.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_mike_stress_lines() {
|
async fn serial_test_mike_stress_lines() {
|
||||||
let code = include_str!("inputs/mike_stress_test.kcl");
|
let code = include_str!("inputs/mike_stress_test.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/mike_stress_test.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/mike_stress_test.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -174,7 +178,7 @@ const part002 = startSketchOn(part001, "END")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_end.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_end.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -205,7 +209,7 @@ const part002 = startSketchOn(part001, "END")
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/sketch_on_face_end_negative_extrude.png",
|
"tests/executor/outputs/sketch_on_face_end_negative_extrude.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -242,7 +246,7 @@ async fn serial_test_basic_fillet_cube_start() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_start.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_start.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -259,7 +263,7 @@ async fn serial_test_basic_fillet_cube_end() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_end.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/basic_fillet_cube_end.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -279,7 +283,7 @@ async fn serial_test_basic_fillet_cube_close_opposite() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/basic_fillet_cube_close_opposite.png",
|
"tests/executor/outputs/basic_fillet_cube_close_opposite.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +303,7 @@ async fn serial_test_basic_fillet_cube_next_adjacent() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/basic_fillet_cube_next_adjacent.png",
|
"tests/executor/outputs/basic_fillet_cube_next_adjacent.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,7 +323,7 @@ async fn serial_test_basic_fillet_cube_previous_adjacent() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/basic_fillet_cube_previous_adjacent.png",
|
"tests/executor/outputs/basic_fillet_cube_previous_adjacent.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -341,7 +345,7 @@ const fnBox = box(3, 6, 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/function_sketch.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/function_sketch.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -364,7 +368,7 @@ const thing = box([0,0], 3, 6, 10)"#;
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/function_sketch_with_position.png",
|
"tests/executor/outputs/function_sketch_with_position.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -382,7 +386,7 @@ async fn serial_test_execute_with_angled_line() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/angled_line.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/angled_line.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -408,7 +412,7 @@ const bracket = startSketchOn('XY')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/parametric.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/parametric.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -442,7 +446,7 @@ const bracket = startSketchAt([0, 0])
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/parametric_with_tan_arc.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/parametric_with_tan_arc.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -470,7 +474,7 @@ async fn serial_test_execute_i_shape() {
|
|||||||
let code = include_str!("inputs/i_shape.kcl");
|
let code = include_str!("inputs/i_shape.kcl");
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/i_shape.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/i_shape.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -479,7 +483,7 @@ async fn serial_test_execute_pipes_on_pipes() {
|
|||||||
let code = include_str!("inputs/pipes_on_pipes.kcl");
|
let code = include_str!("inputs/pipes_on_pipes.kcl");
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/pipes_on_pipes.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/pipes_on_pipes.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -487,7 +491,7 @@ async fn serial_test_execute_cylinder() {
|
|||||||
let code = include_str!("inputs/cylinder.kcl");
|
let code = include_str!("inputs/cylinder.kcl");
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cylinder.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/cylinder.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -495,7 +499,7 @@ async fn serial_test_execute_kittycad_svg() {
|
|||||||
let code = include_str!("inputs/kittycad_svg.kcl");
|
let code = include_str!("inputs/kittycad_svg.kcl");
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/kittycad_svg.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/kittycad_svg.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -523,7 +527,7 @@ const pt2 = b2.value[0]
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/member_expression_sketch_group.png",
|
"tests/executor/outputs/member_expression_sketch_group.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,7 +540,7 @@ async fn serial_test_helix_defaults() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/helix_defaults.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/helix_defaults.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -551,7 +555,7 @@ async fn serial_test_helix_defaults_negative_extrude() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/helix_defaults_negative_extrude.png",
|
"tests/executor/outputs/helix_defaults_negative_extrude.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -564,7 +568,7 @@ async fn serial_test_helix_ccw() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/helix_ccw.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/helix_ccw.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -576,7 +580,7 @@ async fn serial_test_helix_with_length() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/helix_with_length.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/helix_with_length.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -591,7 +595,7 @@ async fn serial_test_dimensions_match() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/dimensions_match.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/dimensions_match.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -608,7 +612,7 @@ const body = startSketchOn('XY')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/close_arc.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/close_arc.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -634,7 +638,7 @@ let thing = box(-12, -15, 10)
|
|||||||
box(-20, -5, 10)"#;
|
box(-20, -5, 10)"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/negative_args.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/negative_args.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -647,7 +651,7 @@ async fn serial_test_basic_tangential_arc() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -660,7 +664,11 @@ async fn serial_test_basic_tangential_arc_with_point() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_with_point.png", &result, 0.999);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/tangential_arc_with_point.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -673,7 +681,7 @@ async fn serial_test_basic_tangential_arc_to() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_to.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/tangential_arc_to.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -703,7 +711,7 @@ box(-20, -5, 10, 'xy')"#;
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/different_planes_same_drawing.png",
|
"tests/executor/outputs/different_planes_same_drawing.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -762,7 +770,7 @@ const part004 = startSketchOn('YZ')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -779,7 +787,7 @@ async fn serial_test_holes() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -798,7 +806,7 @@ async fn optional_params() {
|
|||||||
const thing = other_circle([2, 2], 20)
|
const thing = other_circle([2, 2], 20)
|
||||||
"#;
|
"#;
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/optional_params.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/optional_params.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -834,7 +842,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/rounded_with_holes.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -842,7 +850,7 @@ async fn serial_test_top_level_expression() {
|
|||||||
let code = r#"startSketchOn('XY') |> circle([0,0], 22, %) |> extrude(14, %)"#;
|
let code = r#"startSketchOn('XY') |> circle([0,0], 22, %) |> extrude(14, %)"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -859,7 +867,7 @@ const part = startSketchOn('XY')
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/patterns_linear_basic_with_math.png",
|
"tests/executor/outputs/patterns_linear_basic_with_math.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -872,7 +880,7 @@ async fn serial_test_patterns_linear_basic() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -888,7 +896,7 @@ async fn serial_test_patterns_linear_basic_3d() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_3d.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_3d.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -903,7 +911,7 @@ async fn serial_test_patterns_linear_basic_negative_distance() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/patterns_linear_basic_negative_distance.png",
|
"tests/executor/outputs/patterns_linear_basic_negative_distance.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -919,7 +927,7 @@ async fn serial_test_patterns_linear_basic_negative_axis() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/patterns_linear_basic_negative_axis.png",
|
"tests/executor/outputs/patterns_linear_basic_negative_axis.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,7 +949,11 @@ const rectangle = startSketchOn('XY')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_linear_basic_holes.png", &result, 0.999);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/patterns_linear_basic_holes.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -953,7 +965,11 @@ async fn serial_test_patterns_circular_basic_2d() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_2d.png", &result, 0.999);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/patterns_circular_basic_2d.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -969,7 +985,11 @@ async fn serial_test_patterns_circular_basic_3d() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/patterns_circular_basic_3d.png", &result, 0.999);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/patterns_circular_basic_3d.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -988,7 +1008,7 @@ async fn serial_test_patterns_circular_3d_tilted_axis() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/patterns_circular_3d_tilted_axis.png",
|
"tests/executor/outputs/patterns_circular_3d_tilted_axis.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1009,7 +1029,7 @@ async fn serial_test_import_obj_with_mtl() {
|
|||||||
let code = r#"const model = import("tests/executor/inputs/cube.obj")"#;
|
let code = r#"const model = import("tests/executor/inputs/cube.obj")"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1017,7 +1037,11 @@ async fn serial_test_import_obj_with_mtl_units() {
|
|||||||
let code = r#"const model = import("tests/executor/inputs/cube.obj", {type: "obj", units: "m"})"#;
|
let code = r#"const model = import("tests/executor/inputs/cube.obj", {type: "obj", units: "m"})"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_obj_with_mtl_units.png", &result, 0.999);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/import_obj_with_mtl_units.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1025,7 +1049,7 @@ async fn serial_test_import_gltf_with_bin() {
|
|||||||
let code = r#"const model = import("tests/executor/inputs/cube.gltf")"#;
|
let code = r#"const model = import("tests/executor/inputs/cube.gltf")"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_with_bin.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_with_bin.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1033,7 +1057,7 @@ async fn serial_test_import_gltf_embedded() {
|
|||||||
let code = r#"const model = import("tests/executor/inputs/cube-embedded.gltf")"#;
|
let code = r#"const model = import("tests/executor/inputs/cube-embedded.gltf")"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_embedded.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/import_gltf_embedded.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1041,7 +1065,7 @@ async fn serial_test_import_glb() {
|
|||||||
let code = r#"const model = import("tests/executor/inputs/cube.glb")"#;
|
let code = r#"const model = import("tests/executor/inputs/cube.glb")"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_glb.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/import_glb.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1049,7 +1073,7 @@ async fn serial_test_import_glb_no_assign() {
|
|||||||
let code = r#"import("tests/executor/inputs/cube.glb")"#;
|
let code = r#"import("tests/executor/inputs/cube.glb")"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/import_glb_no_assign.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/import_glb_no_assign.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1082,7 +1106,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_mm.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_mm.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1103,7 +1127,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Cm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Cm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_cm.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_cm.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1124,7 +1148,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::M).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::M).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_m.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_m.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1145,7 +1169,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::In).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::In).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_in.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_in.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1166,7 +1190,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Ft).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Ft).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_ft.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_ft.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1187,7 +1211,7 @@ const myCube = cube([0,0], 10)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Yd).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Yd).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/cube_yd.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/cube_yd.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1256,7 +1280,7 @@ const part003 = startSketchOn(part002, "end")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_of_face.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_of_face.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1301,7 +1325,7 @@ const part002 = startSketchOn(part001, "end")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1325,7 +1349,11 @@ const part002 = startSketchOn(part001, "end")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_circle_tagged.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/sketch_on_face_circle_tagged.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1391,7 +1419,7 @@ async fn serial_test_big_number_angle_to_match_length_x() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/big_number_angle_to_match_length_x.png",
|
"tests/executor/outputs/big_number_angle_to_match_length_x.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1412,7 +1440,7 @@ async fn serial_test_big_number_angle_to_match_length_y() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/big_number_angle_to_match_length_y.png",
|
"tests/executor/outputs/big_number_angle_to_match_length_y.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1433,7 +1461,7 @@ async fn serial_test_simple_revolve() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1453,7 +1481,7 @@ async fn serial_test_simple_revolve_uppercase() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_uppercase.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_uppercase.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1473,7 +1501,7 @@ async fn serial_test_simple_revolve_negative() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_negative.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_negative.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1543,7 +1571,11 @@ async fn serial_test_simple_revolve_custom_angle() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_angle.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/simple_revolve_custom_angle.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1563,7 +1595,11 @@ async fn serial_test_simple_revolve_custom_axis() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_custom_axis.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/simple_revolve_custom_axis.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1587,7 +1623,7 @@ const sketch001 = startSketchOn(box, "end")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_edge.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1638,7 +1674,11 @@ const sketch001 = startSketchOn(box, "END")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle_edge.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/revolve_on_face_circle_edge.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1660,7 +1700,7 @@ const sketch001 = startSketchOn(box, "END")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face_circle.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1686,7 +1726,7 @@ const sketch001 = startSketchOn(box, "end")
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/revolve_on_face.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1700,7 +1740,7 @@ async fn serial_test_basic_revolve_circle() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/basic_revolve_circle.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/basic_revolve_circle.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1727,7 +1767,11 @@ const part002 = startSketchOn(part001, 'end')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/simple_revolve_sketch_on_edge.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/simple_revolve_sketch_on_edge.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1788,7 +1832,7 @@ const plumbus1 = make_circle(p, b,$arc_b, [0, 0], 2.5)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/plumbus_fillets.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/plumbus_fillets.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1826,7 +1870,11 @@ capScrew([0, 0.5, 0], 50, 37.5, 50, 25)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/member_expression_in_params.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/member_expression_in_params.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1944,7 +1992,7 @@ async fn serial_test_xz_plane() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/xz_plane.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/xz_plane.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1958,7 +2006,7 @@ async fn serial_test_neg_xz_plane() {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/neg_xz_plane.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1985,7 +2033,11 @@ const pattn2 = patternLinear3d({
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/linear_pattern3d_a_pattern.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/linear_pattern3d_a_pattern.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2008,7 +2060,11 @@ const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repet
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/circular_pattern3d_a_pattern.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/circular_pattern3d_a_pattern.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2037,7 +2093,7 @@ extrude(10, sketch001)
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/array_of_sketches.png", &result, 1.0);
|
twenty_twenty::assert_image("tests/executor/outputs/array_of_sketches.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2103,7 +2159,7 @@ const sketch001 = startSketchOn(bracket, 'seg01')
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/sketch_on_face_after_fillets_referencing_face.png",
|
"tests/executor/outputs/sketch_on_face_after_fillets_referencing_face.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2140,7 +2196,11 @@ const pattn1 = patternLinear3d({
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/pattern3d_array_of_extrudes.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/pattern3d_array_of_extrudes.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2191,7 +2251,7 @@ const baseExtrusion = extrude(width, sketch001)
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/fillets_referencing_other_fillets.png",
|
"tests/executor/outputs/fillets_referencing_other_fillets.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2243,7 +2303,7 @@ const baseExtrusion = extrude(width, sketch001)
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/chamfers_referencing_other_chamfers.png",
|
"tests/executor/outputs/chamfers_referencing_other_chamfers.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2302,7 +2362,7 @@ const pattn1 = patternLinear3d({
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/linear_pattern3d_filleted_sketch.png",
|
"tests/executor/outputs/linear_pattern3d_filleted_sketch.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2333,7 +2393,7 @@ const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repet
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/circular_pattern3d_filleted_sketch.png",
|
"tests/executor/outputs/circular_pattern3d_filleted_sketch.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2364,7 +2424,7 @@ const pattn2 = patternCircular3d({axis: [0,0, 1], center: [-20, -20, -20], repet
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/circular_pattern3d_chamfered_sketch.png",
|
"tests/executor/outputs/circular_pattern3d_chamfered_sketch.png",
|
||||||
&result,
|
&result,
|
||||||
1.0,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2429,7 +2489,11 @@ const sketch001 = startSketchOn(part001, 'chamfer1')
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/sketch_on_face_of_chamfer.png", &result, 1.0);
|
twenty_twenty::assert_image(
|
||||||
|
"tests/executor/outputs/sketch_on_face_of_chamfer.png",
|
||||||
|
&result,
|
||||||
|
MIN_DIFF,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2463,21 +2527,21 @@ let p = triangle(200)
|
|||||||
async fn serial_test_global_tags() {
|
async fn serial_test_global_tags() {
|
||||||
let code = include_str!("inputs/global-tags.kcl");
|
let code = include_str!("inputs/global-tags.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/global_tags.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/global_tags.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_pattern_vase() {
|
async fn serial_test_pattern_vase() {
|
||||||
let code = include_str!("inputs/pattern_vase.kcl");
|
let code = include_str!("inputs/pattern_vase.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/pattern_vase.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/pattern_vase.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_scoped_tags() {
|
async fn serial_test_scoped_tags() {
|
||||||
let code = include_str!("inputs/scoped-tags.kcl");
|
let code = include_str!("inputs/scoped-tags.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/scoped_tags.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/scoped_tags.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2487,7 +2551,7 @@ async fn serial_test_order_sketch_extrude_in_order() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/order-sketch-extrude-in-order.png",
|
"tests/executor/outputs/order-sketch-extrude-in-order.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2498,7 +2562,7 @@ async fn serial_test_order_sketch_extrude_out_of_order() {
|
|||||||
twenty_twenty::assert_image(
|
twenty_twenty::assert_image(
|
||||||
"tests/executor/outputs/order-sketch-extrude-out-of-order.png",
|
"tests/executor/outputs/order-sketch-extrude-out-of-order.png",
|
||||||
&result,
|
&result,
|
||||||
0.999,
|
MIN_DIFF,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2506,5 +2570,5 @@ async fn serial_test_order_sketch_extrude_out_of_order() {
|
|||||||
async fn serial_test_extrude_custom_plane() {
|
async fn serial_test_extrude_custom_plane() {
|
||||||
let code = include_str!("inputs/extrude-custom-plane.kcl");
|
let code = include_str!("inputs/extrude-custom-plane.kcl");
|
||||||
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
|
||||||
twenty_twenty::assert_image("tests/executor/outputs/extrude-custom-plane.png", &result, 0.999);
|
twenty_twenty::assert_image("tests/executor/outputs/extrude-custom-plane.png", &result, MIN_DIFF);
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 171 KiB After Width: | Height: | Size: 165 KiB |