Compare commits
1 Commits
v0.24.2
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
0604da9c37 |
13
README.md
@ -135,18 +135,9 @@ 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.
|
||||
|
||||
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)
|
||||
|
||||
2. Merge the PR
|
||||
|
||||
3. Profit (A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions if the PR was correctly named)
|
||||
|
||||
## Fuzzing the parser
|
||||
|
||||
|
@ -3099,49 +3099,6 @@ const sketch002 = startSketchOn(extrude001, $seg01)
|
||||
).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 index = inputString.indexOf('(')
|
||||
if (index !== -1) {
|
||||
@ -3543,62 +3500,11 @@ test.describe('Command bar tests', () => {
|
||||
`const extrude001 = extrude(${KCL_DEFAULT_LENGTH}, sketch001)`
|
||||
)
|
||||
})
|
||||
|
||||
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,
|
||||
}) => {
|
||||
test('Command bar works and can change a setting', async ({ page }) => {
|
||||
const u = await getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
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(
|
||||
page.getByRole('button', { name: 'Start Sketch' })
|
||||
).not.toBeDisabled()
|
||||
@ -3609,17 +3515,23 @@ const extrude001 = extrude(-10, sketch001)`
|
||||
.or(page.getByRole('button', { name: '⌘K' }))
|
||||
.click()
|
||||
|
||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
await expect(cmdSearchBar).toBeVisible()
|
||||
await page.keyboard.press('Escape')
|
||||
cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
await expect(cmdSearchBar).not.toBeVisible()
|
||||
|
||||
// Now try the same, but with the keyboard shortcut, check focus
|
||||
await page.keyboard.press('Meta+K')
|
||||
cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
await expect(cmdSearchBar).toBeVisible()
|
||||
await expect(cmdSearchBar).toBeFocused()
|
||||
|
||||
// Try typing in the command bar
|
||||
await cmdSearchBar.fill('theme')
|
||||
await page.keyboard.type('theme')
|
||||
const themeOption = page.getByRole('option', {
|
||||
name: 'Settings · app · theme',
|
||||
})
|
||||
await expect(themeOption).toBeVisible()
|
||||
await themeOption.click()
|
||||
const themeInput = page.getByPlaceholder('Select an option')
|
||||
@ -3641,24 +3553,6 @@ const extrude001 = extrude(-10, sketch001)`
|
||||
).toBeVisible()
|
||||
// Check that the theme changed
|
||||
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 ({
|
||||
@ -3683,7 +3577,7 @@ const extrude001 = extrude(-10, sketch001)`
|
||||
await expect(cmdSearchBar).toBeFocused()
|
||||
|
||||
// Try typing in the command bar
|
||||
await cmdSearchBar.fill('theme')
|
||||
await page.keyboard.type('theme')
|
||||
const themeOption = page.getByRole('option', {
|
||||
name: 'Settings · app · theme',
|
||||
})
|
||||
@ -3754,9 +3648,7 @@ const extrude001 = extrude(-10, sketch001)`
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
// Assert that we're on the distance step
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'distance', exact: false })
|
||||
).toBeDisabled()
|
||||
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
||||
|
||||
// Assert that the an alternative variable name is chosen,
|
||||
// since the default variable name is already in use (distance)
|
||||
@ -3771,12 +3663,11 @@ const extrude001 = extrude(-10, sketch001)`
|
||||
|
||||
// Review step and argument hotkeys
|
||||
await expect(submitButton).toBeEnabled()
|
||||
await expect(submitButton).toBeFocused()
|
||||
await submitButton.press('Backspace')
|
||||
await page.keyboard.press('Backspace')
|
||||
|
||||
// Assert we're back on the distance step
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'distance', exact: false })
|
||||
page.getByRole('button', { name: 'Distance 5', exact: false })
|
||||
).toBeDisabled()
|
||||
|
||||
await continueButton.click()
|
||||
@ -3833,7 +3724,6 @@ const extrude001 = extrude(distance001, sketch001)`.replace(
|
||||
// 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
|
||||
@ -4792,10 +4682,10 @@ test.describe('Sketch tests', () => {
|
||||
// click extrude
|
||||
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.
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'selection : 1 face', exact: false })
|
||||
page.getByRole('button', { name: 'Selection 1 face' })
|
||||
).toBeVisible()
|
||||
})
|
||||
test("Existing sketch with bad code delete user's code", async ({ page }) => {
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 32 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",
|
||||
"version": "0.24.2",
|
||||
"version": "0.24.1",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.17.0",
|
||||
|
@ -30,7 +30,7 @@ import { URI } from 'vscode-uri'
|
||||
import { LanguageServerClient } from '../client'
|
||||
import { CompletionItemKindMap } from './autocomplete'
|
||||
import { addToken, SemanticToken } from './semantic-tokens'
|
||||
import { posToOffset, formatMarkdownContents } from './util'
|
||||
import { deferExecution, posToOffset, formatMarkdownContents } from './util'
|
||||
import lspAutocompleteExt from './autocomplete'
|
||||
import lspHoverExt from './hover'
|
||||
import lspFormatExt from './format'
|
||||
|
@ -80,5 +80,5 @@
|
||||
}
|
||||
},
|
||||
"productName": "Zoo Modeling App",
|
||||
"version": "0.24.2"
|
||||
"version": "0.24.1"
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import {
|
||||
canRectangleTool,
|
||||
isEditingExistingSketch,
|
||||
} from 'machines/modelingMachine'
|
||||
import { DEV } from 'env'
|
||||
|
||||
export function Toolbar({
|
||||
className = '',
|
||||
@ -119,16 +118,6 @@ export function Toolbar({
|
||||
}),
|
||||
{ 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>) {
|
||||
const span = toolbarButtonsRef.current
|
||||
@ -415,36 +404,6 @@ export function Toolbar({
|
||||
</ActionButton>
|
||||
</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>
|
||||
</menu>
|
||||
)
|
||||
|
@ -41,7 +41,6 @@ function CommandArgOptionInput({
|
||||
)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const formRef = useRef<HTMLFormElement>(null)
|
||||
const [shouldSubmitOnChange, setShouldSubmitOnChange] = useState(false)
|
||||
const [selectedOption, setSelectedOption] = useState<
|
||||
CommandArgumentOption<unknown>
|
||||
>(currentOption || resolvedOptions[0])
|
||||
@ -83,10 +82,8 @@ function CommandArgOptionInput({
|
||||
// We deal with the whole option object internally
|
||||
setSelectedOption(option)
|
||||
|
||||
// But we only submit the value itself
|
||||
if (shouldSubmitOnChange) {
|
||||
onSubmit(option.value)
|
||||
}
|
||||
// But we only submit the value
|
||||
onSubmit(option.value)
|
||||
}
|
||||
|
||||
function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
|
||||
@ -97,18 +94,7 @@ function CommandArgOptionInput({
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
id="arg-form"
|
||||
onSubmit={handleSubmit}
|
||||
ref={formRef}
|
||||
onKeyDownCapture={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
setShouldSubmitOnChange(true)
|
||||
} else {
|
||||
setShouldSubmitOnChange(false)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<form id="arg-form" onSubmit={handleSubmit} ref={formRef}>
|
||||
<Combobox
|
||||
value={selectedOption}
|
||||
onChange={handleSelectOption}
|
||||
@ -132,12 +118,6 @@ function CommandArgOptionInput({
|
||||
if (event.key === 'Backspace' && !event.currentTarget.value) {
|
||||
stepBack()
|
||||
}
|
||||
|
||||
if (event.key === 'Enter') {
|
||||
setShouldSubmitOnChange(true)
|
||||
} else {
|
||||
setShouldSubmitOnChange(false)
|
||||
}
|
||||
}}
|
||||
value={query}
|
||||
placeholder={
|
||||
@ -156,9 +136,6 @@ function CommandArgOptionInput({
|
||||
<Combobox.Options
|
||||
static
|
||||
className="overflow-y-auto max-h-96 cursor-pointer"
|
||||
onMouseDown={() => {
|
||||
setShouldSubmitOnChange(true)
|
||||
}}
|
||||
>
|
||||
{filteredOptions?.map((option) => (
|
||||
<Combobox.Option
|
||||
|
@ -114,7 +114,6 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
||||
>
|
||||
{argName}
|
||||
</span>
|
||||
<span className="sr-only">: </span>
|
||||
{argValue ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(argValue as Selections)
|
||||
|
@ -3,7 +3,6 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { CommandArgument } from 'lib/commandTypes'
|
||||
import {
|
||||
Selection,
|
||||
canSubmitSelectionArg,
|
||||
getSelectionType,
|
||||
getSelectionTypeDisplayText,
|
||||
@ -12,25 +11,6 @@ import { modelingMachine } from 'machines/modelingMachine'
|
||||
import { useCallback, useEffect, useRef, useState } from 'react'
|
||||
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>) =>
|
||||
snapshot.context.selectionRanges
|
||||
|
||||
@ -105,9 +85,7 @@ function CommandBarSelectionInput({
|
||||
>
|
||||
{canSubmitSelection
|
||||
? getSelectionTypeDisplayText(selection) + ' selected'
|
||||
: `Please select ${
|
||||
arg.multiple ? 'one or more ' : 'one '
|
||||
}${getSemanticSelectionType(arg.selectionTypes).join(' or ')}`}
|
||||
: `Please select ${arg.multiple ? 'one or more faces' : 'one face'}`}
|
||||
<input
|
||||
id="selection"
|
||||
name="selection"
|
||||
|
@ -187,22 +187,6 @@ const CustomIconMap = {
|
||||
/>
|
||||
</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: (
|
||||
<svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
|
@ -72,7 +72,6 @@ import { uuidv4 } from 'lib/utils'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -165,7 +164,7 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
store.videoElement?.pause()
|
||||
kclManager.executeCode(true).then(() => {
|
||||
if (engineCommandManager.engineConnection?.idleMode) return
|
||||
if (engineCommandManager.engineConnection?.freezeFrame) return
|
||||
|
||||
store.videoElement?.play()
|
||||
})
|
||||
@ -445,12 +444,6 @@ export const ModelingMachineProvider = ({
|
||||
if (selectionRanges.codeBasedSelections.length <= 0) return false
|
||||
return true
|
||||
},
|
||||
'has valid fillet selection': ({ selectionRanges }) =>
|
||||
hasValidFilletSelection({
|
||||
selectionRanges,
|
||||
ast: kclManager.ast,
|
||||
code: codeManager.code,
|
||||
}),
|
||||
'Selection is on face': ({ selectionRanges }, { data }) => {
|
||||
if (data?.forceNewSketch) return false
|
||||
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||
@ -501,6 +494,7 @@ export const ModelingMachineProvider = ({
|
||||
kclManager.ast,
|
||||
data.sketchPathToNode,
|
||||
data.extrudePathToNode,
|
||||
kclManager.programMemory,
|
||||
data.cap
|
||||
)
|
||||
if (trap(sketched)) return Promise.reject(sketched)
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { DEV } from 'env'
|
||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
||||
import { getNormalisedCoordinates } from '../lib/utils'
|
||||
import Loading from './Loading'
|
||||
@ -10,10 +11,6 @@ import { btnName } from 'lib/cameraControls'
|
||||
import { sendSelectEventToEngine } from 'lib/selections'
|
||||
import { kclManager, engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||
import { useAppStream } from 'AppState'
|
||||
import {
|
||||
EngineConnectionStateType,
|
||||
DisconnectingType,
|
||||
} from 'lang/std/engineConnection'
|
||||
|
||||
export const Stream = () => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
@ -23,28 +20,15 @@ export const Stream = () => {
|
||||
const { settings } = useSettingsAuthContext()
|
||||
const { state, send, context } = useModelingContext()
|
||||
const { mediaStream } = useAppStream()
|
||||
const { overallState, immediateState } = useNetworkContext()
|
||||
const { overallState } = useNetworkContext()
|
||||
const [isFreezeFrame, setIsFreezeFrame] = useState(false)
|
||||
const [isPaused, setIsPaused] = useState(false)
|
||||
|
||||
const IDLE = settings.context.app.streamIdleMode.current
|
||||
const IDLE = true
|
||||
|
||||
const isNetworkOkay =
|
||||
overallState === NetworkHealthState.Ok ||
|
||||
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
|
||||
// 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
|
||||
@ -81,26 +65,23 @@ export const Stream = () => {
|
||||
sceneInfra.modelingSend({ type: 'Cancel' })
|
||||
// Give video time to pause
|
||||
window.requestAnimationFrame(() => {
|
||||
engineCommandManager.tearDown({ idleMode: true })
|
||||
engineCommandManager.tearDown()
|
||||
})
|
||||
}
|
||||
|
||||
const onVisibilityChange = () => {
|
||||
if (globalThis.window.document.visibilityState === 'hidden') {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
||||
} else if (!engineCommandManager.engineConnection?.isReady()) {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
engineCommandManager.engineConnection?.connect(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Teardown everything if we go hidden or reconnect
|
||||
if (IDLE) {
|
||||
globalThis?.window?.document?.addEventListener(
|
||||
'visibilitychange',
|
||||
onVisibilityChange
|
||||
)
|
||||
if (IDLE && DEV) {
|
||||
if (globalThis?.window?.document) {
|
||||
globalThis.window.document.onvisibilitychange = () => {
|
||||
if (globalThis.window.document.visibilityState === 'hidden') {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
timeoutIdIdleA = setTimeout(teardown, IDLE_TIME_MS)
|
||||
} else if (!engineCommandManager.engineConnection?.isReady()) {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
engineCommandManager.engineConnection?.connect(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let timeoutIdIdleB: ReturnType<typeof setTimeout> | undefined = undefined
|
||||
@ -112,7 +93,7 @@ export const Stream = () => {
|
||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||
}
|
||||
|
||||
if (IDLE) {
|
||||
if (IDLE && DEV) {
|
||||
globalThis?.window?.document?.addEventListener('keydown', onAnyInput)
|
||||
globalThis?.window?.document?.addEventListener('mousemove', onAnyInput)
|
||||
globalThis?.window?.document?.addEventListener('mousedown', onAnyInput)
|
||||
@ -120,7 +101,7 @@ export const Stream = () => {
|
||||
globalThis?.window?.document?.addEventListener('touchstart', onAnyInput)
|
||||
}
|
||||
|
||||
if (IDLE) {
|
||||
if (IDLE && DEV) {
|
||||
timeoutIdIdleB = setTimeout(teardown, IDLE_TIME_MS)
|
||||
}
|
||||
|
||||
@ -128,14 +109,7 @@ export const Stream = () => {
|
||||
globalThis?.window?.document?.removeEventListener('paste', handlePaste, {
|
||||
capture: true,
|
||||
})
|
||||
if (IDLE) {
|
||||
clearTimeout(timeoutIdIdleA)
|
||||
clearTimeout(timeoutIdIdleB)
|
||||
|
||||
globalThis?.window?.document?.removeEventListener(
|
||||
'visibilitychange',
|
||||
onVisibilityChange
|
||||
)
|
||||
if (IDLE && DEV) {
|
||||
globalThis?.window?.document?.removeEventListener('keydown', onAnyInput)
|
||||
globalThis?.window?.document?.removeEventListener(
|
||||
'mousemove',
|
||||
@ -152,7 +126,7 @@ export const Stream = () => {
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [IDLE])
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
setIsFirstRender(kclManager.isFirstRender)
|
||||
@ -275,32 +249,6 @@ export const Stream = () => {
|
||||
<ClientSideScene
|
||||
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 && (
|
||||
<div className="text-center absolute inset-0">
|
||||
<Loading>
|
||||
|
@ -1,16 +1,11 @@
|
||||
import { createContext, useContext } from 'react'
|
||||
import {
|
||||
ConnectingTypeGroup,
|
||||
EngineConnectionStateType,
|
||||
EngineConnectionState,
|
||||
initialConnectingTypeGroupState,
|
||||
} from '../lang/std/engineConnection'
|
||||
import { NetworkStatus, NetworkHealthState } from './useNetworkStatus'
|
||||
|
||||
export const NetworkContext = createContext<NetworkStatus>({
|
||||
immediateState: {
|
||||
type: EngineConnectionStateType.Disconnected,
|
||||
} as EngineConnectionState,
|
||||
hasIssues: undefined,
|
||||
overallState: NetworkHealthState.Disconnected,
|
||||
internetConnected: true,
|
||||
|
@ -6,7 +6,6 @@ import {
|
||||
EngineCommandManagerEvents,
|
||||
EngineConnectionEvents,
|
||||
EngineConnectionStateType,
|
||||
EngineConnectionState,
|
||||
ErrorType,
|
||||
initialConnectingTypeGroupState,
|
||||
} from '../lang/std/engineConnection'
|
||||
@ -20,7 +19,6 @@ export enum NetworkHealthState {
|
||||
}
|
||||
|
||||
export interface NetworkStatus {
|
||||
immediateState: EngineConnectionState
|
||||
hasIssues: boolean | undefined
|
||||
overallState: NetworkHealthState
|
||||
internetConnected: boolean
|
||||
@ -35,9 +33,6 @@ export interface NetworkStatus {
|
||||
// Must be called from one place in the application.
|
||||
// We've chosen the <Router /> component for this.
|
||||
export function useNetworkStatus() {
|
||||
const [immediateState, setImmediateState] = useState<EngineConnectionState>({
|
||||
type: EngineConnectionStateType.Disconnected,
|
||||
})
|
||||
const [steps, setSteps] = useState(
|
||||
structuredClone(initialConnectingTypeGroupState)
|
||||
)
|
||||
@ -131,7 +126,6 @@ export function useNetworkStatus() {
|
||||
const onConnectionStateChange = ({
|
||||
detail: engineConnectionState,
|
||||
}: CustomEvent) => {
|
||||
setImmediateState(engineConnectionState)
|
||||
setSteps((steps) => {
|
||||
let nextSteps = structuredClone(steps)
|
||||
|
||||
@ -221,7 +215,6 @@ export function useNetworkStatus() {
|
||||
}, [])
|
||||
|
||||
return {
|
||||
immediateState,
|
||||
hasIssues,
|
||||
overallState,
|
||||
internetConnected,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useLayoutEffect, useEffect, useRef } from 'react'
|
||||
import { useLayoutEffect, useEffect, useRef, useState } from 'react'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { Themes } from 'lib/theme'
|
||||
|
@ -304,6 +304,7 @@ describe('testing sketchOnExtrudedFace', () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const segmentSnippet = `line([9.7, 9.19], %)`
|
||||
const segmentRange: [number, number] = [
|
||||
code.indexOf(segmentSnippet),
|
||||
@ -320,7 +321,8 @@ describe('testing sketchOnExtrudedFace', () => {
|
||||
const extruded = sketchOnExtrudedFace(
|
||||
ast,
|
||||
segmentPathToNode,
|
||||
extrudePathToNode
|
||||
extrudePathToNode,
|
||||
programMemory
|
||||
)
|
||||
if (err(extruded)) throw extruded
|
||||
const { modifiedAst } = extruded
|
||||
@ -343,6 +345,7 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
||||
|> extrude(5 + 7, %)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const segmentSnippet = `close(%)`
|
||||
const segmentRange: [number, number] = [
|
||||
code.indexOf(segmentSnippet),
|
||||
@ -359,7 +362,8 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
||||
const extruded = sketchOnExtrudedFace(
|
||||
ast,
|
||||
segmentPathToNode,
|
||||
extrudePathToNode
|
||||
extrudePathToNode,
|
||||
programMemory
|
||||
)
|
||||
if (err(extruded)) throw extruded
|
||||
const { modifiedAst } = extruded
|
||||
@ -382,6 +386,7 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
||||
|> extrude(5 + 7, %)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const sketchSnippet = `startProfileAt([3.58, 2.06], %)`
|
||||
const sketchRange: [number, number] = [
|
||||
code.indexOf(sketchSnippet),
|
||||
@ -399,6 +404,7 @@ const sketch001 = startSketchOn(part001, seg01)`)
|
||||
ast,
|
||||
sketchPathToNode,
|
||||
extrudePathToNode,
|
||||
programMemory,
|
||||
'end'
|
||||
)
|
||||
if (err(extruded)) throw extruded
|
||||
@ -430,6 +436,7 @@ const sketch001 = startSketchOn(part001, 'END')`)
|
||||
const part001 = extrude(5 + 7, sketch001)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const segmentSnippet = `line([4.99, -0.46], %)`
|
||||
const segmentRange: [number, number] = [
|
||||
code.indexOf(segmentSnippet),
|
||||
@ -446,7 +453,8 @@ const sketch001 = startSketchOn(part001, 'END')`)
|
||||
const updatedAst = sketchOnExtrudedFace(
|
||||
ast,
|
||||
segmentPathToNode,
|
||||
extrudePathToNode
|
||||
extrudePathToNode,
|
||||
programMemory
|
||||
)
|
||||
if (err(updatedAst)) throw updatedAst
|
||||
const newCode = recast(updatedAst.modifiedAst)
|
||||
|
@ -349,6 +349,7 @@ export function sketchOnExtrudedFace(
|
||||
node: Program,
|
||||
sketchPathToNode: PathToNode,
|
||||
extrudePathToNode: PathToNode,
|
||||
programMemory: ProgramMemory,
|
||||
cap: 'none' | 'start' | 'end' = 'none'
|
||||
): { modifiedAst: Program; pathToNode: PathToNode } | Error {
|
||||
let _node = { ...node }
|
||||
@ -387,6 +388,7 @@ export function sketchOnExtrudedFace(
|
||||
if (cap === 'none') {
|
||||
const __tag = addTagForSketchOnFace(
|
||||
{
|
||||
previousProgramMemory: programMemory,
|
||||
pathToNode: sketchPathToNode,
|
||||
node: _node,
|
||||
},
|
||||
|
@ -1,315 +0,0 @@
|
||||
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([])
|
||||
})
|
||||
})
|
@ -1,404 +0,0 @@
|
||||
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,7 +143,6 @@ export enum DisconnectingType {
|
||||
Error = 'error',
|
||||
Timeout = 'timeout',
|
||||
Quit = 'quit',
|
||||
Pause = 'pause',
|
||||
}
|
||||
|
||||
// Sorted by severity
|
||||
@ -201,7 +200,6 @@ export type DisconnectingValue =
|
||||
| State<DisconnectingType.Error, ErrorType>
|
||||
| State<DisconnectingType.Timeout, void>
|
||||
| State<DisconnectingType.Quit, void>
|
||||
| State<DisconnectingType.Pause, void>
|
||||
|
||||
// These are ordered by the expected sequence.
|
||||
export enum ConnectingType {
|
||||
@ -302,7 +300,7 @@ class EngineConnection extends EventTarget {
|
||||
pc?: RTCPeerConnection
|
||||
unreliableDataChannel?: RTCDataChannel
|
||||
mediaStream?: MediaStream
|
||||
idleMode: boolean = false
|
||||
freezeFrame: boolean = false
|
||||
|
||||
onIceCandidate = function (
|
||||
this: RTCPeerConnection,
|
||||
@ -393,10 +391,10 @@ class EngineConnection extends EventTarget {
|
||||
this.pingPongSpan = { ping: undefined, pong: undefined }
|
||||
|
||||
// Without an interval ping, our connection will timeout.
|
||||
// If this.idleMode is true we skip this logic so only reconnect
|
||||
// If this.freezeFrame is true we skip this logic so only reconnect
|
||||
// happens on mouse move
|
||||
this.pingIntervalId = setInterval(() => {
|
||||
if (this.idleMode) return
|
||||
if (this.freezeFrame) return
|
||||
|
||||
switch (this.state.type as EngineConnectionStateType) {
|
||||
case EngineConnectionStateType.ConnectionEstablished:
|
||||
@ -458,8 +456,8 @@ class EngineConnection extends EventTarget {
|
||||
return this.state.type === EngineConnectionStateType.ConnectionEstablished
|
||||
}
|
||||
|
||||
tearDown(opts?: { idleMode: boolean }) {
|
||||
this.idleMode = opts?.idleMode ?? false
|
||||
tearDown(opts?: { freeze: boolean }) {
|
||||
this.freezeFrame = opts?.freeze ?? false
|
||||
this.disconnectAll()
|
||||
clearInterval(this.pingIntervalId)
|
||||
|
||||
@ -499,19 +497,10 @@ class EngineConnection extends EventTarget {
|
||||
this.onNetworkStatusReady
|
||||
)
|
||||
|
||||
this.state = opts?.idleMode
|
||||
? {
|
||||
type: EngineConnectionStateType.Disconnecting,
|
||||
value: {
|
||||
type: DisconnectingType.Pause,
|
||||
},
|
||||
}
|
||||
: {
|
||||
type: EngineConnectionStateType.Disconnecting,
|
||||
value: {
|
||||
type: DisconnectingType.Quit,
|
||||
},
|
||||
}
|
||||
this.state = {
|
||||
type: EngineConnectionStateType.Disconnecting,
|
||||
value: { type: DisconnectingType.Quit },
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1110,6 +1099,8 @@ class EngineConnection extends EventTarget {
|
||||
this.unreliableDataChannel?.readyState === 'closed'
|
||||
if (allClosed) {
|
||||
// Do not notify the rest of the program that we have cut off anything.
|
||||
if (this.freezeFrame) return
|
||||
|
||||
this.state = { type: EngineConnectionStateType.Disconnected }
|
||||
}
|
||||
}
|
||||
@ -1747,7 +1738,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
}
|
||||
}
|
||||
}
|
||||
tearDown(opts?: { idleMode: boolean }) {
|
||||
tearDown() {
|
||||
if (this.engineConnection) {
|
||||
this.engineConnection.removeEventListener(
|
||||
EngineConnectionEvents.Opened,
|
||||
@ -1766,7 +1757,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
this.onEngineConnectionNewTrack as EventListener
|
||||
)
|
||||
|
||||
this.engineConnection?.tearDown(opts)
|
||||
this.engineConnection?.tearDown()
|
||||
this.engineConnection = undefined
|
||||
|
||||
// Our window.tearDown assignment causes this case to happen which is
|
||||
@ -1774,7 +1765,7 @@ export class EngineCommandManager extends EventTarget {
|
||||
// @ts-ignore
|
||||
} else if (this.engineCommandManager?.engineConnection) {
|
||||
// @ts-ignore
|
||||
this.engineCommandManager?.engineConnection?.tearDown(opts)
|
||||
this.engineCommandManager?.engineConnection?.tearDown()
|
||||
}
|
||||
}
|
||||
async startNewSession() {
|
||||
|
@ -221,7 +221,7 @@ describe('testing addTagForSketchOnFace', () => {
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const sketchOnFaceRetVal = addTagForSketchOnFace(
|
||||
{
|
||||
// previousProgramMemory: programMemory, // redundant?
|
||||
previousProgramMemory: programMemory,
|
||||
pathToNode,
|
||||
node: ast,
|
||||
},
|
||||
|
@ -28,6 +28,7 @@ import { createPipeExpression, splitPathAtPipeExpression } from '../modifyAst'
|
||||
|
||||
import {
|
||||
SketchLineHelper,
|
||||
ModifyAstBase,
|
||||
TransformCallback,
|
||||
ConstrainInfo,
|
||||
RawValues,
|
||||
@ -36,7 +37,6 @@ import {
|
||||
SingleValueInput,
|
||||
VarValueKeys,
|
||||
ArrayOrObjItemInput,
|
||||
AddTagInfo,
|
||||
} from 'lang/std/stdTypes'
|
||||
|
||||
import {
|
||||
@ -308,18 +308,6 @@ 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 = {
|
||||
add: ({
|
||||
node,
|
||||
@ -389,7 +377,6 @@ export const lineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -516,7 +503,6 @@ export const line: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -577,7 +563,6 @@ export const xLineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
horzVertConstraintInfoHelper(
|
||||
@ -638,7 +623,6 @@ export const yLineTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
horzVertConstraintInfoHelper(
|
||||
@ -698,7 +682,6 @@ export const xLine: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
horzVertConstraintInfoHelper(
|
||||
@ -755,7 +738,6 @@ export const yLine: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
horzVertConstraintInfoHelper(
|
||||
@ -848,7 +830,6 @@ export const tangentialArcTo: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
||||
if (callExp.type !== 'CallExpression') return []
|
||||
@ -967,7 +948,6 @@ export const angledLine: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -1064,7 +1044,6 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -1161,7 +1140,6 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -1249,7 +1227,6 @@ export const angledLineToX: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -1339,7 +1316,6 @@ export const angledLineToY: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp, ...args) =>
|
||||
commonConstraintInfoHelper(
|
||||
@ -1464,7 +1440,6 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
pathToNode,
|
||||
}
|
||||
},
|
||||
getTag: getTag(),
|
||||
addTag: addTag(),
|
||||
getConstraintInfo: (callExp: CallExpression, code, pathToNode) => {
|
||||
if (callExp.type !== 'CallExpression') return []
|
||||
@ -1817,7 +1792,10 @@ export function replaceSketchLine({
|
||||
return { modifiedAst, valueUsedInTransform, pathToNode }
|
||||
}
|
||||
|
||||
export function addTagForSketchOnFace(a: AddTagInfo, expressionName: string) {
|
||||
export function addTagForSketchOnFace(
|
||||
a: ModifyAstBase,
|
||||
expressionName: string
|
||||
) {
|
||||
if (expressionName === 'close') {
|
||||
return addTag(1)(a)
|
||||
}
|
||||
@ -1828,17 +1806,6 @@ export function addTagForSketchOnFace(a: AddTagInfo, expressionName: string) {
|
||||
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 {
|
||||
return lineArugement?.type === 'ArrayExpression'
|
||||
? isLiteralArrayOrStatic(lineArugement.elements[0])
|
||||
@ -1849,7 +1816,9 @@ function isAngleLiteral(lineArugement: Value): boolean {
|
||||
: false
|
||||
}
|
||||
|
||||
type addTagFn = (a: AddTagInfo) => { modifiedAst: Program; tag: string } | Error
|
||||
type addTagFn = (
|
||||
a: ModifyAstBase
|
||||
) => { modifiedAst: Program; tag: string } | Error
|
||||
|
||||
function addTag(tagIndex = 2): addTagFn {
|
||||
return ({ node, pathToNode }) => {
|
||||
|
@ -32,11 +32,6 @@ export interface ModifyAstBase {
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
|
||||
export interface AddTagInfo {
|
||||
node: Program
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
|
||||
interface addCall extends ModifyAstBase {
|
||||
to: [number, number]
|
||||
from: [number, number]
|
||||
@ -132,8 +127,7 @@ export interface SketchLineHelper {
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
| Error
|
||||
getTag: (a: CallExpression) => string | Error
|
||||
addTag: (a: AddTagInfo) =>
|
||||
addTag: (a: ModifyAstBase) =>
|
||||
| {
|
||||
modifiedAst: Program
|
||||
tag: string
|
||||
|
@ -27,11 +27,6 @@ export type ModelingCommandSchema = {
|
||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||
distance: KclCommandValue
|
||||
}
|
||||
Fillet: {
|
||||
// todo
|
||||
selection: Selections
|
||||
radius: KclCommandValue
|
||||
}
|
||||
'change tool': {
|
||||
tool: SketchTool
|
||||
}
|
||||
@ -190,36 +185,4 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
},
|
||||
},
|
||||
},
|
||||
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,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -51,7 +51,6 @@ export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||
SKETCH: 'sketch',
|
||||
EXTRUDE: 'extrude',
|
||||
SEGMENT: 'seg',
|
||||
} as const
|
||||
/** The default KCL length expression */
|
||||
export const KCL_DEFAULT_LENGTH = `5`
|
||||
|
@ -406,17 +406,6 @@ 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) {
|
||||
const commonNode = buildCommonNodeFromSelection(selection, i)
|
||||
|
||||
|
@ -163,17 +163,6 @@ export function createSettings() {
|
||||
validate: (v) => typeof v === 'boolean',
|
||||
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>({
|
||||
defaultValue: '',
|
||||
validate: (v) => typeof v === 'string',
|
||||
|
@ -38,7 +38,6 @@ function configurationToSettingsPayload(
|
||||
: undefined,
|
||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
||||
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
||||
projectDirectory: configuration?.settings?.project?.directory,
|
||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
||||
},
|
||||
@ -76,7 +75,6 @@ function projectConfigurationToSettingsPayload(
|
||||
: undefined,
|
||||
onboardingStatus: configuration?.settings?.app?.onboarding_status,
|
||||
dismissWebBanner: configuration?.settings?.app?.dismiss_web_banner,
|
||||
streamIdleMode: configuration?.settings?.app?.stream_idle_mode,
|
||||
enableSSAO: configuration?.settings?.modeling?.enable_ssao,
|
||||
},
|
||||
modeling: {
|
||||
|
@ -37,7 +37,4 @@ if (typeof window !== 'undefined') {
|
||||
document.addEventListener('mousemove', (e) =>
|
||||
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 { useEffect, useRef, useState } from 'react'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { err, trap } from 'lib/trap'
|
||||
import { trap } from 'lib/trap'
|
||||
|
||||
const isValidVariableName = (name: string) =>
|
||||
/^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name)
|
||||
@ -86,7 +86,6 @@ export function useCalculateKclExpression({
|
||||
const execAstAndSetResult = async () => {
|
||||
const _code = `const __result__ = ${value}`
|
||||
const ast = parse(_code)
|
||||
if (err(ast)) return
|
||||
if (trap(ast, { suppress: true })) return
|
||||
|
||||
const _programMem: any = { root: {}, return: null }
|
||||
|
@ -203,7 +203,7 @@ export const commandBarMachine = createMachine(
|
||||
'Change current argument': {
|
||||
target: 'Gathering arguments',
|
||||
internal: true,
|
||||
actions: ['Set current argument'],
|
||||
actions: ['Remove current argument and set a new one'],
|
||||
},
|
||||
|
||||
'Deselect command': {
|
||||
@ -359,13 +359,29 @@ export const commandBarMachine = createMachine(
|
||||
switch (event.type) {
|
||||
case 'Edit argument':
|
||||
return event.data.arg
|
||||
case 'Change current argument':
|
||||
return Object.values(event.data)[0]
|
||||
default:
|
||||
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({
|
||||
selectedCommand: undefined,
|
||||
currentArgument: undefined,
|
||||
|
@ -63,12 +63,6 @@ export const settingsMachine = createMachine(
|
||||
],
|
||||
},
|
||||
|
||||
'set.app.streamIdleMode': {
|
||||
target: 'persisting settings',
|
||||
|
||||
actions: ['setSettingAtLevel', 'toastSuccess'],
|
||||
},
|
||||
|
||||
'set.modeling.highlightEdges': {
|
||||
target: 'persisting settings',
|
||||
|
||||
|
72
src/wasm-lib/Cargo.lock
generated
@ -169,7 +169,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -180,7 +180,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -191,7 +191,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -431,7 +431,7 @@ dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -631,7 +631,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.10.0",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -642,7 +642,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -697,7 +697,7 @@ checksum = "4078275de501a61ceb9e759d37bdd3d7210e654dbc167ac1a3678ef4435ed57b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
@ -726,7 +726,7 @@ dependencies = [
|
||||
"rustfmt-wrapper",
|
||||
"serde",
|
||||
"serde_tokenstream",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -737,7 +737,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -764,7 +764,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -936,7 +936,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1026,7 +1026,7 @@ dependencies = [
|
||||
"inflections",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1448,7 +1448,7 @@ dependencies = [
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1824,7 +1824,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.8.3",
|
||||
"structmeta",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1877,7 +1877,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2041,7 +2041,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-macros-backend",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2054,7 +2054,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"pyo3-build-config",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2516,7 +2516,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde_derive_internals",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2590,7 +2590,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2601,7 +2601,7 @@ checksum = "330f01ce65a3a5fe59a60c82f3c9a024b573b8a6e875bd233fe5f934e71d54e3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2624,7 +2624,7 @@ checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2645,7 +2645,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2782,7 +2782,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2793,7 +2793,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2837,9 +2837,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.71"
|
||||
version = "2.0.70"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462"
|
||||
checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2860,7 +2860,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2943,7 +2943,7 @@ checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3039,7 +3039,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3192,7 +3192,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3220,7 +3220,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3297,7 +3297,7 @@ checksum = "c88cc88fd23b5a04528f3a8436024f20010a16ec18eb23c164b1242f65860130"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
@ -3455,7 +3455,7 @@ dependencies = [
|
||||
"proc-macro-error",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3516,7 +3516,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -3551,7 +3551,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
@ -3876,7 +3876,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.71",
|
||||
"syn 2.0.70",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -20,7 +20,7 @@ quote = "1"
|
||||
regex = "1.10"
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
serde_tokenstream = "0.2"
|
||||
syn = { version = "2.0.71", features = ["full"] }
|
||||
syn = { version = "2.0.70", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
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.
|
||||
let actual = image::io::Reader::open(output_file).unwrap().decode().unwrap();
|
||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &actual, 0.99);
|
||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #test_name_str), &actual, 1.0);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_someFn {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_someFn {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ mod test_examples_show {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show1"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ mod test_examples_my_func {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ mod test_examples_my_func {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func1"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ mod test_examples_line_to {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
|
||||
@ -178,7 +178,7 @@ mod test_examples_line_to {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to1"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_min {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ mod test_examples_min {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min1"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_import {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -86,7 +86,7 @@ mod test_examples_show {
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&actual,
|
||||
0.99,
|
||||
1.0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ databake = "0.1.8"
|
||||
kcl-lib = { path = "../kcl" }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
syn = { version = "2.0.71", features = ["full"] }
|
||||
syn = { version = "2.0.70", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.4.0"
|
||||
|
@ -98,10 +98,6 @@ harness = false
|
||||
name = "compiler_benchmark_iai"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "digest_benchmark"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "lsp_semantic_tokens_benchmark_criterion"
|
||||
harness = false
|
||||
|
@ -1,31 +0,0 @@
|
||||
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,6 +100,18 @@ impl ProgramMemory {
|
||||
})
|
||||
.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 {
|
||||
|
@ -232,7 +232,7 @@ impl crate::lsp::backend::Backend for Backend {
|
||||
// Lets update the ast.
|
||||
let parser = crate::parser::Parser::new(tokens.clone());
|
||||
let result = parser.ast();
|
||||
let mut ast = match result {
|
||||
let ast = match result {
|
||||
Ok(ast) => ast,
|
||||
Err(err) => {
|
||||
self.add_to_diagnostics(¶ms, &[err], true).await;
|
||||
@ -243,11 +243,6 @@ 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.
|
||||
let ast_changed = match self.ast_map.get(&filename) {
|
||||
Some(old_ast) => {
|
||||
|
@ -2371,12 +2371,9 @@ async fn serial_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
||||
})
|
||||
.await;
|
||||
|
||||
let mut default_hashed = crate::ast::types::Program::default();
|
||||
default_hashed.compute_digest();
|
||||
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(ast, default_hashed);
|
||||
assert_eq!(ast, crate::ast::types::Program::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
@ -2838,12 +2835,9 @@ async fn serial_test_kcl_lsp_cant_execute_set() {
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||
|
||||
let mut default_hashed = crate::ast::types::Program::default();
|
||||
default_hashed.compute_digest();
|
||||
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != default_hashed);
|
||||
assert!(ast != crate::ast::types::Program::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
// Now it should be the default memory.
|
||||
|
@ -234,9 +234,6 @@ pub struct AppSettings {
|
||||
/// 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")]
|
||||
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.
|
||||
@ -654,7 +651,6 @@ textWrapping = true
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::In,
|
||||
@ -714,7 +710,6 @@ includeSettings = false
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
@ -779,7 +774,6 @@ defaultProjectName = "projects-$nnn"
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
@ -856,7 +850,6 @@ projectDirectory = "/Users/macinatormax/Documents/kittycad-modeling-projects""#;
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Mm,
|
||||
|
@ -123,7 +123,6 @@ includeSettings = false
|
||||
theme_color: None,
|
||||
dismiss_web_banner: false,
|
||||
enable_ssao: None,
|
||||
stream_idle_mode: false,
|
||||
},
|
||||
modeling: ModelingSettings {
|
||||
base_unit: UnitLength::Yd,
|
||||
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 134 KiB After Width: | Height: | Size: 134 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 123 KiB |
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 146 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 KiB |
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 139 KiB After Width: | Height: | Size: 139 KiB |
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 100 KiB After Width: | Height: | Size: 100 KiB |
Before Width: | Height: | Size: 125 KiB After Width: | Height: | Size: 125 KiB |
Before Width: | Height: | Size: 131 KiB After Width: | Height: | Size: 131 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 122 KiB |