Multi-profile sweeps and more robust edit flows in point-and-click (#6437)
This commit is contained in:
@ -10,6 +10,8 @@ test.describe('Command bar tests', () => {
|
|||||||
test('Extrude from command bar selects extrude line after', async ({
|
test('Extrude from command bar selects extrude line after', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -35,20 +37,35 @@ test.describe('Command bar tests', () => {
|
|||||||
|
|
||||||
// Click the line of code for xLine.
|
// Click the line of code for xLine.
|
||||||
await page.getByText(`close()`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
await page.getByText(`close()`).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
await toolbar.extrudeButton.click()
|
||||||
await page.waitForTimeout(200)
|
await cmdBar.expectState({
|
||||||
await page.keyboard.press('Enter')
|
stage: 'arguments',
|
||||||
await page.waitForTimeout(200)
|
commandName: 'Extrude',
|
||||||
await page.keyboard.press('Enter')
|
currentArgKey: 'sketches',
|
||||||
await page.waitForTimeout(200)
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '',
|
||||||
|
Length: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '1 segment',
|
||||||
|
Length: '5',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
`extrude001 = extrude(sketch001, length = ${KCL_DEFAULT_LENGTH})`
|
`extrude001 = extrude(sketch001, length = ${KCL_DEFAULT_LENGTH})`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO: fix this test after the electron migration
|
|
||||||
test('Fillet from command bar', async ({ page, homePage }) => {
|
test('Fillet from command bar', async ({ page, homePage }) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -269,21 +286,22 @@ test.describe('Command bar tests', () => {
|
|||||||
await cmdBar.cmdOptions.getByText('Extrude').click()
|
await cmdBar.cmdOptions.getByText('Extrude').click()
|
||||||
|
|
||||||
// Assert that we're on the selection step
|
// Assert that we're on the selection step
|
||||||
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
await expect(page.getByRole('button', { name: 'sketches' })).toBeDisabled()
|
||||||
// Select a face
|
// Select a face
|
||||||
await page.mouse.move(700, 200)
|
await page.mouse.move(700, 200)
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
// Assert that we're on the distance step
|
// Assert that we're on the distance step
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'distance', exact: false })
|
page.getByRole('button', { name: 'length', exact: false })
|
||||||
).toBeDisabled()
|
).toBeDisabled()
|
||||||
|
|
||||||
// Assert that the an alternative variable name is chosen,
|
// Assert that the an alternative variable name is chosen,
|
||||||
// since the default variable name is already in use (distance)
|
// since the default variable name is already in use (distance)
|
||||||
await page.getByRole('button', { name: 'Create new variable' }).click()
|
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||||
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||||
'distance001'
|
'length001'
|
||||||
)
|
)
|
||||||
|
|
||||||
const continueButton = page.getByRole('button', { name: 'Continue' })
|
const continueButton = page.getByRole('button', { name: 'Continue' })
|
||||||
@ -297,7 +315,7 @@ test.describe('Command bar tests', () => {
|
|||||||
|
|
||||||
// Assert we're back on the distance step
|
// Assert we're back on the distance step
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'distance', exact: false })
|
page.getByRole('button', { name: 'length', exact: false })
|
||||||
).toBeDisabled()
|
).toBeDisabled()
|
||||||
|
|
||||||
await continueButton.click()
|
await continueButton.click()
|
||||||
@ -306,7 +324,7 @@ test.describe('Command bar tests', () => {
|
|||||||
await u.waitForCmdReceive('extrude')
|
await u.waitForCmdReceive('extrude')
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
'extrude001 = extrude(sketch001, length = distance001)'
|
'extrude001 = extrude(sketch001, length = length001)'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1085,6 +1085,9 @@ sketch001 = startSketchOn(XZ)
|
|||||||
page,
|
page,
|
||||||
context,
|
context,
|
||||||
homePage,
|
homePage,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
scene,
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await context.addInitScript(async () => {
|
await context.addInitScript(async () => {
|
||||||
@ -1128,17 +1131,30 @@ sketch001 = startSketchOn(XZ)
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.getByText('startProfile(at = [4.61, -14.01])').click()
|
await page.getByText('startProfile(at = [4.61, -14.01])').click()
|
||||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeVisible()
|
await toolbar.extrudeButton.click()
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
await expect(page.getByTestId('command-bar')).toBeVisible()
|
stage: 'arguments',
|
||||||
await page.waitForTimeout(100)
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: '5',
|
||||||
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
headerArguments: {
|
||||||
await page.waitForTimeout(100)
|
Sketches: '1 face',
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
Length: '',
|
||||||
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
|
},
|
||||||
await page.waitForTimeout(100)
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '1 face',
|
||||||
|
Length: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
// expect the code to have changed
|
// expect the code to have changed
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
@ -229,7 +229,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = 5)
|
|> circle(center = [0, 0], radius = 5)
|
||||||
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
renamedExtrude = extrude(sketch001, length = ${initialInput})`
|
||||||
const newConstantName = 'distance001'
|
const newConstantName = 'length001'
|
||||||
const expectedCode = `${newConstantName} = 23
|
const expectedCode = `${newConstantName} = 23
|
||||||
sketch001 = startSketchOn(XZ)
|
sketch001 = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = 5)
|
|> circle(center = [0, 0], radius = 5)
|
||||||
@ -270,12 +270,12 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Extrude',
|
commandName: 'Extrude',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'distance',
|
currentArgKey: 'length',
|
||||||
currentArgValue: initialInput,
|
currentArgValue: initialInput,
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Distance: initialInput,
|
Length: initialInput,
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'distance',
|
highlightedHeaderArg: 'length',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -290,7 +290,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
// The calculated value is shown in the argument summary
|
// The calculated value is shown in the argument summary
|
||||||
Distance: initialInput,
|
Length: initialInput,
|
||||||
},
|
},
|
||||||
commandName: 'Extrude',
|
commandName: 'Extrude',
|
||||||
})
|
})
|
||||||
|
@ -6,6 +6,7 @@ import type { EditorFixture } from '@e2e/playwright/fixtures/editorFixture'
|
|||||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||||
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
import type { ToolbarFixture } from '@e2e/playwright/fixtures/toolbarFixture'
|
||||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||||
|
import { bracket } from '@e2e/playwright/fixtures/bracket'
|
||||||
|
|
||||||
// test file is for testing point an click code gen functionality that's not sketch mode related
|
// test file is for testing point an click code gen functionality that's not sketch mode related
|
||||||
|
|
||||||
@ -75,10 +76,19 @@ test.describe('Point-and-click tests', () => {
|
|||||||
await toolbar.extrudeButton.click()
|
await toolbar.extrudeButton.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'distance',
|
currentArgKey: 'sketches',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Sketches: '', Length: '' },
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'length',
|
||||||
currentArgValue: '5',
|
currentArgValue: '5',
|
||||||
headerArguments: { Selection: '1 face', Distance: '' },
|
headerArguments: { Sketches: '1 face', Length: '' },
|
||||||
highlightedHeaderArg: 'distance',
|
highlightedHeaderArg: 'length',
|
||||||
commandName: 'Extrude',
|
commandName: 'Extrude',
|
||||||
})
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
@ -88,7 +98,7 @@ test.describe('Point-and-click tests', () => {
|
|||||||
|
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: { Selection: '1 face', Distance: '5' },
|
headerArguments: { Sketches: '1 face', Length: '5' },
|
||||||
commandName: 'Extrude',
|
commandName: 'Extrude',
|
||||||
})
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
@ -97,6 +107,102 @@ test.describe('Point-and-click tests', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Verify in-pipe extrudes in bracket can be edited', async ({
|
||||||
|
tronApp,
|
||||||
|
context,
|
||||||
|
editor,
|
||||||
|
homePage,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, bracket)
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await test.step(`Edit first extrude via feature tree`, async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Extrude', 0)).dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: 'width',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '5',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('width - 0.001')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '4.999',
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectEditor.toContain('extrude(length = width - 0.001)')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Edit second extrude via feature tree`, async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Extrude', 1)).dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: '-thickness - .01',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '-0.3949',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('-thickness - .01 - 0.001')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '-0.3959',
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
'extrude(length = -thickness - .01 - 0.001)'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Edit third extrude via feature tree`, async () => {
|
||||||
|
await (await toolbar.getFeatureTreeOperation('Extrude', 2)).dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: '-thickness - 0.1',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '-0.4849',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('-thickness - 0.1 - 0.001')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Length: '-0.4859',
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
'extrude(length = -thickness - 0.1 - 0.001)'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test.describe('verify sketch on chamfer works', () => {
|
test.describe('verify sketch on chamfer works', () => {
|
||||||
const _sketchOnAChamfer =
|
const _sketchOnAChamfer =
|
||||||
(
|
(
|
||||||
@ -1483,11 +1589,11 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const initialCode = `sketch001 = startSketchOn(XZ)
|
const initialCode = `sketch001 = startSketchOn(XZ)
|
||||||
|> circle(center = [0, 0], radius = 30)
|
|> circle(center = [0, 0], radius = 30)
|
||||||
plane001 = offsetPlane(XZ, offset = 50)
|
plane001 = offsetPlane(XZ, offset = 50)
|
||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> circle(center = [0, 0], radius = 20)
|
|> circle(center = [0, 0], radius = 20)
|
||||||
`
|
`
|
||||||
await context.addInitScript((initialCode) => {
|
await context.addInitScript((initialCode) => {
|
||||||
localStorage.setItem('persistCode', initialCode)
|
localStorage.setItem('persistCode', initialCode)
|
||||||
}, initialCode)
|
}, initialCode)
|
||||||
@ -1523,14 +1629,20 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
.toBe(1)
|
.toBe(1)
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'selection',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: { Selection: '' },
|
headerArguments: { Sketches: '' },
|
||||||
highlightedHeaderArg: 'selection',
|
highlightedHeaderArg: 'sketches',
|
||||||
commandName: 'Loft',
|
commandName: 'Loft',
|
||||||
})
|
})
|
||||||
await selectSketches()
|
await selectSketches()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Sketches: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.submit()
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
await test.step(`Preselect the two sketches`, async () => {
|
await test.step(`Preselect the two sketches`, async () => {
|
||||||
@ -1539,7 +1651,21 @@ extrude001 = extrude(profile001, length = 100)
|
|||||||
|
|
||||||
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||||
await toolbar.loftButton.click()
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'sketches',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Sketches: '' },
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Sketches: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.submit()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1681,8 +1807,7 @@ sketch002 = startSketchOn(XZ)
|
|||||||
testPoint.x - 50,
|
testPoint.x - 50,
|
||||||
testPoint.y
|
testPoint.y
|
||||||
)
|
)
|
||||||
const sweepDeclaration =
|
const sweepDeclaration = 'sweep001 = sweep(profile001, path = sketch002)'
|
||||||
'sweep001 = sweep(profile001, path = sketch002, sectional = false)'
|
|
||||||
const editedSweepDeclaration =
|
const editedSweepDeclaration =
|
||||||
'sweep001 = sweep(profile001, path = sketch002, sectional = true)'
|
'sweep001 = sweep(profile001, path = sketch002, sectional = true)'
|
||||||
|
|
||||||
@ -1698,37 +1823,49 @@ sketch002 = startSketchOn(XZ)
|
|||||||
.toBe(1)
|
.toBe(1)
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'target',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
Sectional: '',
|
||||||
Target: '',
|
Sketches: '',
|
||||||
Trajectory: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'target',
|
highlightedHeaderArg: 'sketches',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
})
|
})
|
||||||
await clickOnSketch1()
|
await clickOnSketch1()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'trajectory',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
Sectional: '',
|
||||||
Target: '1 face',
|
Sketches: '1 face',
|
||||||
Trajectory: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'trajectory',
|
highlightedHeaderArg: 'path',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
})
|
})
|
||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await page.waitForTimeout(500)
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
currentArgKey: 'path',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sectional: '',
|
||||||
|
Sketches: '1 face',
|
||||||
|
Path: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'path',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Target: '1 face',
|
Sketches: '1 face',
|
||||||
Trajectory: '1 segment',
|
Path: '1 segment',
|
||||||
Sectional: '',
|
Sectional: '',
|
||||||
},
|
},
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
@ -1837,31 +1974,31 @@ sketch002 = startSketchOn(XZ)
|
|||||||
.toBe(1)
|
.toBe(1)
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'target',
|
currentArgKey: 'sketches',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
Sectional: '',
|
||||||
Target: '',
|
Sketches: '',
|
||||||
Trajectory: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'target',
|
highlightedHeaderArg: 'sketches',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
})
|
})
|
||||||
await clickOnSketch1()
|
await clickOnSketch1()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Sweep',
|
commandName: 'Sweep',
|
||||||
currentArgKey: 'trajectory',
|
currentArgKey: 'path',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Sectional: '',
|
Sectional: '',
|
||||||
Target: '1 face',
|
Sketches: '1 face',
|
||||||
Trajectory: '',
|
Path: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'trajectory',
|
highlightedHeaderArg: 'path',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
})
|
})
|
||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await page.waitForTimeout(500)
|
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByText('Unable to sweep with the current selection. Reason:')
|
page.getByText('Unable to sweep with the current selection. Reason:')
|
||||||
@ -3513,6 +3650,7 @@ tag=$rectangleSegmentC002,
|
|||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
|
||||||
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)`
|
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = X)`
|
||||||
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
|
||||||
@ -3585,6 +3723,7 @@ sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001)
|
|||||||
await editor.scrollToText(codeToSelection)
|
await editor.scrollToText(codeToSelection)
|
||||||
await page.getByText(codeToSelection).click()
|
await page.getByText(codeToSelection).click()
|
||||||
await toolbar.revolveButton.click()
|
await toolbar.revolveButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await page.getByText('Edge', { exact: true }).click()
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
const lineCodeToSelection = `angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)`
|
const lineCodeToSelection = `angledLine(angle = 0, length = 202.6, tag = $rectangleSegmentA001)`
|
||||||
await page.getByText(lineCodeToSelection).click()
|
await page.getByText(lineCodeToSelection).click()
|
||||||
@ -3677,6 +3816,7 @@ sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001)
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await editor.scrollToText(codeToSelection)
|
await editor.scrollToText(codeToSelection)
|
||||||
await page.getByText(codeToSelection).click()
|
await page.getByText(codeToSelection).click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await expect.poll(() => page.getByText('AxisOrEdge').count()).toBe(2)
|
await expect.poll(() => page.getByText('AxisOrEdge').count()).toBe(2)
|
||||||
await page.getByText('Edge', { exact: true }).click()
|
await page.getByText('Edge', { exact: true }).click()
|
||||||
const lineCodeToSelection = `length = 2.6`
|
const lineCodeToSelection = `length = 2.6`
|
||||||
@ -4393,4 +4533,319 @@ extrude001 = extrude(profile001, length = 1)
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const multiProfileSweepsCode = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(sketch001, center = [3, 0], radius = 1)
|
||||||
|
profile002 = circle(sketch001, center = [6, 0], radius = 1)
|
||||||
|
path001 = startProfile(sketch001, at = [0, 0])
|
||||||
|
|> yLine(length = 2)
|
||||||
|
`
|
||||||
|
const profile001Point = { x: 470, y: 270 }
|
||||||
|
const profile002Point = { x: 670, y: 270 }
|
||||||
|
|
||||||
|
test('Point-and-click multi-profile sweeps: extrude', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, multiProfileSweepsCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await test.step('Select through scene', async () => {
|
||||||
|
// Unfortunately can't select thru code for multi profile yet
|
||||||
|
const [clickProfile001Point] = scene.makeMouseHelpers(
|
||||||
|
profile001Point.x,
|
||||||
|
profile001Point.y
|
||||||
|
)
|
||||||
|
const [clickProfile002Point] = scene.makeMouseHelpers(
|
||||||
|
profile002Point.x,
|
||||||
|
profile002Point.y
|
||||||
|
)
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await clickProfile001Point()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickProfile002Point()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Go through command bar flow', async () => {
|
||||||
|
await toolbar.extrudeButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'sketches',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '',
|
||||||
|
Length: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: '5',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
Length: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('1')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
Length: '1',
|
||||||
|
},
|
||||||
|
commandName: 'Extrude',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`extrude001 = extrude([profile001, profile002], length = 1)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
await editor.closePane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete extrude via feature tree selection', async () => {
|
||||||
|
const op = await toolbar.getFeatureTreeOperation('Extrude', 0)
|
||||||
|
await op.click({ button: 'right' })
|
||||||
|
await page.getByTestId('context-menu-delete').click()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.not.toContain(
|
||||||
|
`extrude001 = extrude([profile001, profile002], length = 1)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Point-and-click multi-profile sweeps: sweep', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, multiProfileSweepsCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await test.step('Select through scene', async () => {
|
||||||
|
// Unfortunately can't select thru code for multi profile yet
|
||||||
|
const [clickProfile001Point] = scene.makeMouseHelpers(
|
||||||
|
profile001Point.x,
|
||||||
|
profile001Point.y
|
||||||
|
)
|
||||||
|
const [clickProfile002Point] = scene.makeMouseHelpers(
|
||||||
|
profile002Point.x,
|
||||||
|
profile002Point.y
|
||||||
|
)
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await clickProfile001Point()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickProfile002Point()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Go through command bar flow', async () => {
|
||||||
|
await toolbar.sweepButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'sketches',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '',
|
||||||
|
Path: '',
|
||||||
|
Sectional: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
commandName: 'Sweep',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'path',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
Path: '',
|
||||||
|
Sectional: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'path',
|
||||||
|
commandName: 'Sweep',
|
||||||
|
})
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await page.getByText('yLine(length = 2)').click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
Path: '1 segment',
|
||||||
|
Sectional: '',
|
||||||
|
},
|
||||||
|
commandName: 'Sweep',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`sweep001 = sweep([profile001, profile002], path = path001)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete sweep via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const op = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
||||||
|
await op.click({ button: 'right' })
|
||||||
|
await page.getByTestId('context-menu-delete').click()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await editor.expectEditor.not.toContain(
|
||||||
|
`sweep001 = sweep([profile001, profile002], path = path001)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Point-and-click multi-profile sweeps: revolve', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, multiProfileSweepsCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await test.step('Select through scene', async () => {
|
||||||
|
// Unfortunately can't select thru code for multi profile yet
|
||||||
|
const [clickProfile001Point] = scene.makeMouseHelpers(
|
||||||
|
profile001Point.x,
|
||||||
|
profile001Point.y
|
||||||
|
)
|
||||||
|
const [clickProfile002Point] = scene.makeMouseHelpers(
|
||||||
|
profile002Point.x,
|
||||||
|
profile002Point.y
|
||||||
|
)
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await clickProfile001Point()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickProfile002Point()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Go through command bar flow', async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await toolbar.revolveButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'sketches',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '',
|
||||||
|
AxisOrEdge: '',
|
||||||
|
Angle: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'sketches',
|
||||||
|
commandName: 'Revolve',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'axisOrEdge',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
AxisOrEdge: '',
|
||||||
|
Angle: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'axisOrEdge',
|
||||||
|
commandName: 'Revolve',
|
||||||
|
})
|
||||||
|
await cmdBar.selectOption({ name: 'Edge' }).click()
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await page.getByText('yLine(length = 2)').click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'angle',
|
||||||
|
currentArgValue: '360',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
AxisOrEdge: 'Edge',
|
||||||
|
Edge: '1 segment',
|
||||||
|
Angle: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'angle',
|
||||||
|
commandName: 'Revolve',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('180')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Sketches: '2 faces',
|
||||||
|
AxisOrEdge: 'Edge',
|
||||||
|
Edge: '1 segment',
|
||||||
|
Angle: '180',
|
||||||
|
},
|
||||||
|
commandName: 'Revolve',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain(`yLine(length = 2, tag = $seg01)`, {
|
||||||
|
shouldNormalise: true,
|
||||||
|
})
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`revolve001 = revolve([profile001, profile002], angle=180, axis=seg01)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete revolve via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const op = await toolbar.getFeatureTreeOperation('Revolve', 0)
|
||||||
|
await op.click({ button: 'right' })
|
||||||
|
await page.getByTestId('context-menu-delete').click()
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await editor.expectEditor.not.toContain(
|
||||||
|
`revolve001 = revolve([profile001, profile002], axis = XY, angle = 180)`,
|
||||||
|
{ shouldNormalise: true }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -962,6 +962,8 @@ profile001 = startProfile(sketch001, at = [${roundOff(scale * 69.6)}, ${roundOff
|
|||||||
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
test('exiting a close extrude, has the extrude button enabled ready to go', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
|
cmdBar,
|
||||||
|
toolbar,
|
||||||
}) => {
|
}) => {
|
||||||
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
// this was a regression https://github.com/KittyCAD/modeling-app/issues/2832
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -1002,19 +1004,21 @@ profile001 = startProfile(sketch001, at = [${roundOff(scale * 69.6)}, ${roundOff
|
|||||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
// expect extrude button to be enabled
|
// expect extrude button to be enabled
|
||||||
await expect(
|
await expect(toolbar.extrudeButton).not.toBeDisabled()
|
||||||
page.getByRole('button', { name: 'Extrude' })
|
|
||||||
).not.toBeDisabled()
|
|
||||||
|
|
||||||
// click extrude
|
// click extrude
|
||||||
await page.getByRole('button', { name: 'Extrude' }).click()
|
await toolbar.extrudeButton.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. "Sketches: 1 face" only show up when the selection has been made already
|
||||||
// otherwise the cmdbar would be waiting for a selection.
|
// otherwise the cmdbar would be waiting for a selection.
|
||||||
await expect(
|
await cmdBar.progressCmdBar()
|
||||||
page.getByRole('button', { name: 'selection : 1 segment', exact: false })
|
await cmdBar.expectState({
|
||||||
).toBeVisible({
|
stage: 'arguments',
|
||||||
timeout: 10_000,
|
currentArgKey: 'length',
|
||||||
|
currentArgValue: '5',
|
||||||
|
headerArguments: { Sketches: '1 segment', Length: '' },
|
||||||
|
highlightedHeaderArg: 'length',
|
||||||
|
commandName: 'Extrude',
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
test("Existing sketch with bad code delete user's code", async ({
|
test("Existing sketch with bad code delete user's code", async ({
|
||||||
|
@ -573,6 +573,7 @@ profile001 = startProfile(sketch002, at = [-12.34, 12.34])
|
|||||||
await expect(page.getByTestId('command-bar')).toBeVisible()
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
|
1
package-lock.json
generated
1
package-lock.json
generated
@ -2492,7 +2492,6 @@
|
|||||||
},
|
},
|
||||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"extraneous": true,
|
|
||||||
"inBundle": true,
|
"inBundle": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
@ -31,8 +31,6 @@ import {
|
|||||||
UNLABELED_ARG,
|
UNLABELED_ARG,
|
||||||
} from '@src/lang/queryAstConstants'
|
} from '@src/lang/queryAstConstants'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
|
||||||
import { getPathsFromArtifact } from '@src/lang/std/artifactGraph'
|
|
||||||
import {
|
import {
|
||||||
addTagForSketchOnFace,
|
addTagForSketchOnFace,
|
||||||
getConstraintInfoKw,
|
getConstraintInfoKw,
|
||||||
@ -46,7 +44,6 @@ import {
|
|||||||
import type { SimplifiedArgDetails } from '@src/lang/std/stdTypes'
|
import type { SimplifiedArgDetails } from '@src/lang/std/stdTypes'
|
||||||
import type {
|
import type {
|
||||||
ArrayExpression,
|
ArrayExpression,
|
||||||
ArtifactGraph,
|
|
||||||
CallExpressionKw,
|
CallExpressionKw,
|
||||||
Expr,
|
Expr,
|
||||||
Literal,
|
Literal,
|
||||||
@ -340,122 +337,6 @@ export function mutateObjExpProp(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
export function extrudeSketch({
|
|
||||||
node,
|
|
||||||
pathToNode,
|
|
||||||
distance = createLiteral(4),
|
|
||||||
extrudeName,
|
|
||||||
artifact,
|
|
||||||
artifactGraph,
|
|
||||||
}: {
|
|
||||||
node: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
distance: Expr
|
|
||||||
extrudeName?: string
|
|
||||||
artifactGraph: ArtifactGraph
|
|
||||||
artifact?: Artifact
|
|
||||||
}):
|
|
||||||
| {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
pathToExtrudeArg: PathToNode
|
|
||||||
}
|
|
||||||
| Error {
|
|
||||||
const orderedSketchNodePaths = getPathsFromArtifact({
|
|
||||||
artifact: artifact,
|
|
||||||
sketchPathToNode: pathToNode,
|
|
||||||
artifactGraph,
|
|
||||||
ast: node,
|
|
||||||
})
|
|
||||||
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
|
||||||
const _node = structuredClone(node)
|
|
||||||
const _node1 = getNodeFromPath(_node, pathToNode)
|
|
||||||
if (err(_node1)) return _node1
|
|
||||||
|
|
||||||
// determine if sketchExpression is in a pipeExpression or not
|
|
||||||
const _node2 = getNodeFromPath<PipeExpression>(
|
|
||||||
_node,
|
|
||||||
pathToNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(_node2)) return _node2
|
|
||||||
|
|
||||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
|
||||||
_node,
|
|
||||||
pathToNode,
|
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
|
||||||
if (err(_node3)) return _node3
|
|
||||||
const { node: variableDeclarator } = _node3
|
|
||||||
|
|
||||||
const extrudeCall = createCallExpressionStdLibKw(
|
|
||||||
'extrude',
|
|
||||||
createLocalName(variableDeclarator.id.name),
|
|
||||||
[createLabeledArg('length', distance)]
|
|
||||||
)
|
|
||||||
// index of the 'length' arg above. If you reorder the labeled args above,
|
|
||||||
// make sure to update this too.
|
|
||||||
const argIndex = 0
|
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
|
||||||
// but rather a separate constant for the extrusion
|
|
||||||
const name =
|
|
||||||
extrudeName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
|
||||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
|
||||||
|
|
||||||
const lastSketchNodePath =
|
|
||||||
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
|
||||||
|
|
||||||
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
|
||||||
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
|
||||||
|
|
||||||
const pathToExtrudeArg: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[sketchIndexInBody + 1, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD],
|
|
||||||
]
|
|
||||||
return {
|
|
||||||
modifiedAst: _node,
|
|
||||||
pathToNode: [...pathToNode.slice(0, -1), [-1, 'index']],
|
|
||||||
pathToExtrudeArg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loftSketches(
|
|
||||||
node: Node<Program>,
|
|
||||||
declarators: VariableDeclarator[]
|
|
||||||
): {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
} {
|
|
||||||
const modifiedAst = structuredClone(node)
|
|
||||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
|
||||||
const elements = declarators.map((d) => createLocalName(d.id.name))
|
|
||||||
const loft = createCallExpressionStdLibKw(
|
|
||||||
'loft',
|
|
||||||
createArrayExpression(elements),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
const declaration = createVariableDeclaration(name, loft)
|
|
||||||
modifiedAst.body.push(declaration)
|
|
||||||
const pathToNode: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[modifiedAst.body.length - 1, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['unlabeled', UNLABELED_ARG],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addShell({
|
export function addShell({
|
||||||
node,
|
node,
|
||||||
sweepName,
|
sweepName,
|
||||||
@ -514,63 +395,6 @@ export function addShell({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addSweep({
|
|
||||||
node,
|
|
||||||
targetDeclarator,
|
|
||||||
trajectoryDeclarator,
|
|
||||||
sectional,
|
|
||||||
variableName,
|
|
||||||
insertIndex,
|
|
||||||
}: {
|
|
||||||
node: Node<Program>
|
|
||||||
targetDeclarator: VariableDeclarator
|
|
||||||
trajectoryDeclarator: VariableDeclarator
|
|
||||||
sectional: boolean
|
|
||||||
variableName?: string
|
|
||||||
insertIndex?: number
|
|
||||||
}): {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToNode: PathToNode
|
|
||||||
} {
|
|
||||||
const modifiedAst = structuredClone(node)
|
|
||||||
const name =
|
|
||||||
variableName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
|
|
||||||
const call = createCallExpressionStdLibKw(
|
|
||||||
'sweep',
|
|
||||||
createLocalName(targetDeclarator.id.name),
|
|
||||||
[
|
|
||||||
createLabeledArg('path', createLocalName(trajectoryDeclarator.id.name)),
|
|
||||||
createLabeledArg('sectional', createLiteral(sectional)),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
const variable = createVariableDeclaration(name, call)
|
|
||||||
const insertAt =
|
|
||||||
insertIndex !== undefined
|
|
||||||
? insertIndex
|
|
||||||
: modifiedAst.body.length
|
|
||||||
? modifiedAst.body.length
|
|
||||||
: 0
|
|
||||||
|
|
||||||
modifiedAst.body.length
|
|
||||||
? modifiedAst.body.splice(insertAt, 0, variable)
|
|
||||||
: modifiedAst.body.push(variable)
|
|
||||||
const argIndex = 0
|
|
||||||
const pathToNode: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[insertAt, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst,
|
|
||||||
pathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sketchOnExtrudedFace(
|
export function sketchOnExtrudedFace(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
@ -1345,7 +1169,7 @@ export function createNodeFromExprSnippet(
|
|||||||
export function insertVariableAndOffsetPathToNode(
|
export function insertVariableAndOffsetPathToNode(
|
||||||
variable: KclCommandValue,
|
variable: KclCommandValue,
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program>,
|
||||||
pathToNode: PathToNode
|
pathToNode?: PathToNode
|
||||||
) {
|
) {
|
||||||
if ('variableName' in variable && variable.variableName) {
|
if ('variableName' in variable && variable.variableName) {
|
||||||
modifiedAst.body.splice(
|
modifiedAst.body.splice(
|
||||||
@ -1353,7 +1177,7 @@ export function insertVariableAndOffsetPathToNode(
|
|||||||
0,
|
0,
|
||||||
variable.variableDeclarationAst
|
variable.variableDeclarationAst
|
||||||
)
|
)
|
||||||
if (typeof pathToNode[1][0] === 'number') {
|
if (pathToNode && pathToNode[1] && typeof pathToNode[1][0] === 'number') {
|
||||||
pathToNode[1][0]++
|
pathToNode[1][0]++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,142 +0,0 @@
|
|||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|
||||||
|
|
||||||
import {
|
|
||||||
createCallExpressionStdLibKw,
|
|
||||||
createLabeledArg,
|
|
||||||
createLocalName,
|
|
||||||
createVariableDeclaration,
|
|
||||||
findUniqueName,
|
|
||||||
} from '@src/lang/create'
|
|
||||||
import {
|
|
||||||
getEdgeTagCall,
|
|
||||||
mutateAstWithTagForSketchSegment,
|
|
||||||
} from '@src/lang/modifyAst/addEdgeTreatment'
|
|
||||||
import { getNodeFromPath } from '@src/lang/queryAst'
|
|
||||||
import { getSafeInsertIndex } from '@src/lang/queryAst/getSafeInsertIndex'
|
|
||||||
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
|
||||||
import type {
|
|
||||||
Expr,
|
|
||||||
PathToNode,
|
|
||||||
Program,
|
|
||||||
VariableDeclarator,
|
|
||||||
} from '@src/lang/wasm'
|
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
|
|
||||||
import type { Selections } from '@src/lib/selections'
|
|
||||||
import { err } from '@src/lib/trap'
|
|
||||||
|
|
||||||
export function getAxisExpressionAndIndex(
|
|
||||||
axisOrEdge: 'Axis' | 'Edge',
|
|
||||||
axis: string | undefined,
|
|
||||||
edge: Selections | undefined,
|
|
||||||
ast: Node<Program>
|
|
||||||
) {
|
|
||||||
let generatedAxis
|
|
||||||
let axisDeclaration: PathToNode | null = null
|
|
||||||
let axisIndexIfAxis: number | undefined = undefined
|
|
||||||
|
|
||||||
if (axisOrEdge === 'Edge' && edge) {
|
|
||||||
const pathToAxisSelection = getNodePathFromSourceRange(
|
|
||||||
ast,
|
|
||||||
edge.graphSelections[0]?.codeRef.range
|
|
||||||
)
|
|
||||||
const tagResult = mutateAstWithTagForSketchSegment(ast, pathToAxisSelection)
|
|
||||||
|
|
||||||
// Have the tag whether it is already created or a new one is generated
|
|
||||||
if (err(tagResult)) return tagResult
|
|
||||||
const { tag } = tagResult
|
|
||||||
const axisSelection = edge?.graphSelections[0]?.artifact
|
|
||||||
if (!axisSelection) return new Error('Generated axis selection is missing.')
|
|
||||||
generatedAxis = getEdgeTagCall(tag, axisSelection)
|
|
||||||
if (
|
|
||||||
axisSelection.type === 'segment' ||
|
|
||||||
axisSelection.type === 'path' ||
|
|
||||||
axisSelection.type === 'edgeCut'
|
|
||||||
) {
|
|
||||||
axisDeclaration = axisSelection.codeRef.pathToNode
|
|
||||||
if (!axisDeclaration)
|
|
||||||
return new Error('Expected to fine axis declaration')
|
|
||||||
const axisIndexInPathToNode =
|
|
||||||
axisDeclaration.findIndex((a) => a[0] === 'body') + 1
|
|
||||||
const value = axisDeclaration[axisIndexInPathToNode][0]
|
|
||||||
if (typeof value !== 'number')
|
|
||||||
return new Error('expected axis index value to be a number')
|
|
||||||
axisIndexIfAxis = value
|
|
||||||
}
|
|
||||||
} else if (axisOrEdge === 'Axis' && axis) {
|
|
||||||
generatedAxis = createLocalName(axis)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
generatedAxis,
|
|
||||||
axisIndexIfAxis,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function revolveSketch(
|
|
||||||
ast: Node<Program>,
|
|
||||||
pathToSketchNode: PathToNode,
|
|
||||||
angle: Expr,
|
|
||||||
axisOrEdge: 'Axis' | 'Edge',
|
|
||||||
axis: string | undefined,
|
|
||||||
edge: Selections | undefined,
|
|
||||||
variableName?: string,
|
|
||||||
insertIndex?: number
|
|
||||||
):
|
|
||||||
| {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToSketchNode: PathToNode
|
|
||||||
pathToRevolveArg: PathToNode
|
|
||||||
}
|
|
||||||
| Error {
|
|
||||||
const clonedAst = structuredClone(ast)
|
|
||||||
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
|
|
||||||
clonedAst,
|
|
||||||
pathToSketchNode,
|
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
|
||||||
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
|
|
||||||
const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode
|
|
||||||
|
|
||||||
const getAxisResult = getAxisExpressionAndIndex(
|
|
||||||
axisOrEdge,
|
|
||||||
axis,
|
|
||||||
edge,
|
|
||||||
clonedAst
|
|
||||||
)
|
|
||||||
if (err(getAxisResult)) return getAxisResult
|
|
||||||
const { generatedAxis } = getAxisResult
|
|
||||||
if (!generatedAxis) return new Error('Generated axis selection is missing.')
|
|
||||||
|
|
||||||
const revolveCall = createCallExpressionStdLibKw(
|
|
||||||
'revolve',
|
|
||||||
createLocalName(sketchVariableDeclarator.id.name),
|
|
||||||
[createLabeledArg('angle', angle), createLabeledArg('axis', generatedAxis)]
|
|
||||||
)
|
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
|
||||||
// but rather a separate constant for the extrusion
|
|
||||||
const name =
|
|
||||||
variableName ??
|
|
||||||
findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
|
||||||
const variableDeclaration = createVariableDeclaration(name, revolveCall)
|
|
||||||
const bodyInsertIndex =
|
|
||||||
insertIndex ?? getSafeInsertIndex(revolveCall, clonedAst)
|
|
||||||
clonedAst.body.splice(bodyInsertIndex, 0, variableDeclaration)
|
|
||||||
const argIndex = 0
|
|
||||||
const pathToRevolveArg: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[bodyInsertIndex, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst: clonedAst,
|
|
||||||
pathToSketchNode: [...pathToSketchNode.slice(0, -1), [-1, 'index']],
|
|
||||||
pathToRevolveArg,
|
|
||||||
}
|
|
||||||
}
|
|
406
src/lang/modifyAst/addSweep.ts
Normal file
406
src/lang/modifyAst/addSweep.ts
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
|
|
||||||
|
import {
|
||||||
|
createArrayExpression,
|
||||||
|
createCallExpressionStdLibKw,
|
||||||
|
createLabeledArg,
|
||||||
|
createLiteral,
|
||||||
|
createLocalName,
|
||||||
|
createVariableDeclaration,
|
||||||
|
findUniqueName,
|
||||||
|
} from '@src/lang/create'
|
||||||
|
import { insertVariableAndOffsetPathToNode } from '@src/lang/modifyAst'
|
||||||
|
import {
|
||||||
|
getEdgeTagCall,
|
||||||
|
mutateAstWithTagForSketchSegment,
|
||||||
|
} from '@src/lang/modifyAst/addEdgeTreatment'
|
||||||
|
import {
|
||||||
|
getNodeFromPath,
|
||||||
|
getSketchExprsFromSelection,
|
||||||
|
valueOrVariable,
|
||||||
|
} from '@src/lang/queryAst'
|
||||||
|
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
||||||
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
|
import type {
|
||||||
|
CallExpressionKw,
|
||||||
|
Expr,
|
||||||
|
PathToNode,
|
||||||
|
Program,
|
||||||
|
VariableDeclaration,
|
||||||
|
} from '@src/lang/wasm'
|
||||||
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
|
import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
|
||||||
|
import type { Selections } from '@src/lib/selections'
|
||||||
|
import { err } from '@src/lib/trap'
|
||||||
|
|
||||||
|
export function addExtrude({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
length,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
sketches: Selections
|
||||||
|
length: KclCommandValue
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}):
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const sketchesExprList = getSketchExprsFromSelection(
|
||||||
|
sketches,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit
|
||||||
|
)
|
||||||
|
if (err(sketchesExprList)) {
|
||||||
|
return sketchesExprList
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketchesExpr = createSketchExpression(sketchesExprList)
|
||||||
|
const call = createCallExpressionStdLibKw('extrude', sketchesExpr, [
|
||||||
|
createLabeledArg('length', valueOrVariable(length)),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Insert variables for labeled arguments if provided
|
||||||
|
if ('variableName' in length && length.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(length, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
let pathToNode: PathToNode | undefined
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<CallExpressionKw>(
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
'CallExpressionKw'
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(result.node, call)
|
||||||
|
pathToNode = nodeToEdit
|
||||||
|
} else {
|
||||||
|
const name = findUniqueName(
|
||||||
|
modifiedAst,
|
||||||
|
KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE
|
||||||
|
)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
pathToNode = createPathToNode(modifiedAst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addSweep({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
path,
|
||||||
|
sectional,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
sketches: Selections
|
||||||
|
path: Selections
|
||||||
|
sectional?: boolean
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}):
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const sketchesExprList = getSketchExprsFromSelection(
|
||||||
|
sketches,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit
|
||||||
|
)
|
||||||
|
if (err(sketchesExprList)) {
|
||||||
|
return sketchesExprList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra roundabout to find the trajectory (or path) declaration for the labeled argument
|
||||||
|
const pathNodePath = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
path.graphSelections[0].codeRef.range
|
||||||
|
)
|
||||||
|
const pathDeclaration = getNodeFromPath<VariableDeclaration>(
|
||||||
|
ast,
|
||||||
|
pathNodePath,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(pathDeclaration)) {
|
||||||
|
return pathDeclaration
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extra labeled args expressions
|
||||||
|
const pathExpr = createLocalName(pathDeclaration.node.declaration.id.name)
|
||||||
|
const sectionalExpr = sectional
|
||||||
|
? [createLabeledArg('sectional', createLiteral(sectional))]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const sketchesExpr = createSketchExpression(sketchesExprList)
|
||||||
|
const call = createCallExpressionStdLibKw('sweep', sketchesExpr, [
|
||||||
|
createLabeledArg('path', pathExpr),
|
||||||
|
...sectionalExpr,
|
||||||
|
])
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
let pathToNode: PathToNode | undefined
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<CallExpressionKw>(
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
'CallExpressionKw'
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(result.node, call)
|
||||||
|
pathToNode = nodeToEdit
|
||||||
|
} else {
|
||||||
|
const name = findUniqueName(
|
||||||
|
modifiedAst,
|
||||||
|
KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP
|
||||||
|
)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
pathToNode = createPathToNode(modifiedAst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLoft({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
sketches: Selections
|
||||||
|
}):
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const sketchesExprList = getSketchExprsFromSelection(sketches, modifiedAst)
|
||||||
|
if (err(sketchesExprList)) {
|
||||||
|
return sketchesExprList
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketchesExpr = createSketchExpression(sketchesExprList)
|
||||||
|
const call = createCallExpressionStdLibKw('loft', sketchesExpr, [])
|
||||||
|
|
||||||
|
// 3. Just push the declaration to the end
|
||||||
|
// Note that Loft doesn't support edit flows yet since it's selection only atm
|
||||||
|
const name = findUniqueName(modifiedAst, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const toFirstKwarg = false
|
||||||
|
const pathToNode = createPathToNode(modifiedAst, toFirstKwarg)
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addRevolve({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
angle,
|
||||||
|
axisOrEdge,
|
||||||
|
axis,
|
||||||
|
edge,
|
||||||
|
nodeToEdit,
|
||||||
|
}: {
|
||||||
|
ast: Node<Program>
|
||||||
|
sketches: Selections
|
||||||
|
angle: KclCommandValue
|
||||||
|
axisOrEdge: 'Axis' | 'Edge'
|
||||||
|
axis: string | undefined
|
||||||
|
edge: Selections | undefined
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
}):
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
// 1. Clone the ast so we can edit it
|
||||||
|
const modifiedAst = structuredClone(ast)
|
||||||
|
|
||||||
|
// 2. Prepare unlabeled and labeled arguments
|
||||||
|
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
|
||||||
|
const sketchesExprList = getSketchExprsFromSelection(
|
||||||
|
sketches,
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit
|
||||||
|
)
|
||||||
|
if (err(sketchesExprList)) {
|
||||||
|
return sketchesExprList
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve axis expression depending on mode
|
||||||
|
const getAxisResult = getAxisExpressionAndIndex(
|
||||||
|
axisOrEdge,
|
||||||
|
axis,
|
||||||
|
edge,
|
||||||
|
modifiedAst
|
||||||
|
)
|
||||||
|
if (err(getAxisResult) || !getAxisResult.generatedAxis) {
|
||||||
|
return new Error('Generated axis selection is missing.')
|
||||||
|
}
|
||||||
|
|
||||||
|
const sketchesExpr = createSketchExpression(sketchesExprList)
|
||||||
|
const call = createCallExpressionStdLibKw('revolve', sketchesExpr, [
|
||||||
|
createLabeledArg('angle', valueOrVariable(angle)),
|
||||||
|
createLabeledArg('axis', getAxisResult.generatedAxis),
|
||||||
|
])
|
||||||
|
|
||||||
|
// Insert variables for labeled arguments if provided
|
||||||
|
if ('variableName' in angle && angle.variableName) {
|
||||||
|
insertVariableAndOffsetPathToNode(angle, modifiedAst, nodeToEdit)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. If edit, we assign the new function call declaration to the existing node,
|
||||||
|
// otherwise just push to the end
|
||||||
|
let pathToNode: PathToNode | undefined
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<CallExpressionKw>(
|
||||||
|
modifiedAst,
|
||||||
|
nodeToEdit,
|
||||||
|
'CallExpressionKw'
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(result.node, call)
|
||||||
|
pathToNode = nodeToEdit
|
||||||
|
} else {
|
||||||
|
const name = findUniqueName(
|
||||||
|
modifiedAst,
|
||||||
|
KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE
|
||||||
|
)
|
||||||
|
const declaration = createVariableDeclaration(name, call)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
pathToNode = createPathToNode(modifiedAst)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
|
||||||
|
function createSketchExpression(sketches: Expr[]) {
|
||||||
|
let sketchesExpr: Expr | null = null
|
||||||
|
if (sketches.every((s) => s.type === 'PipeSubstitution')) {
|
||||||
|
// Keeping null so we don't even put it the % sign
|
||||||
|
} else if (sketches.length === 1) {
|
||||||
|
sketchesExpr = sketches[0]
|
||||||
|
} else {
|
||||||
|
sketchesExpr = createArrayExpression(sketches)
|
||||||
|
}
|
||||||
|
return sketchesExpr
|
||||||
|
}
|
||||||
|
|
||||||
|
function createPathToNode(
|
||||||
|
modifiedAst: Node<Program>,
|
||||||
|
toFirstKwarg = true
|
||||||
|
): PathToNode {
|
||||||
|
const argIndex = 0 // first kwarg for all sweeps here
|
||||||
|
const pathToCall: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
]
|
||||||
|
if (toFirstKwarg) {
|
||||||
|
pathToCall.push(
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[argIndex, ARG_INDEX_FIELD],
|
||||||
|
['arg', LABELED_ARG_FIELD]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathToCall
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getAxisExpressionAndIndex(
|
||||||
|
axisOrEdge: 'Axis' | 'Edge',
|
||||||
|
axis: string | undefined,
|
||||||
|
edge: Selections | undefined,
|
||||||
|
ast: Node<Program>
|
||||||
|
) {
|
||||||
|
let generatedAxis
|
||||||
|
let axisDeclaration: PathToNode | null = null
|
||||||
|
let axisIndexIfAxis: number | undefined = undefined
|
||||||
|
|
||||||
|
if (axisOrEdge === 'Edge' && edge) {
|
||||||
|
const pathToAxisSelection = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
edge.graphSelections[0]?.codeRef.range
|
||||||
|
)
|
||||||
|
const tagResult = mutateAstWithTagForSketchSegment(ast, pathToAxisSelection)
|
||||||
|
|
||||||
|
// Have the tag whether it is already created or a new one is generated
|
||||||
|
if (err(tagResult)) return tagResult
|
||||||
|
const { tag } = tagResult
|
||||||
|
const axisSelection = edge?.graphSelections[0]?.artifact
|
||||||
|
if (!axisSelection) return new Error('Generated axis selection is missing.')
|
||||||
|
generatedAxis = getEdgeTagCall(tag, axisSelection)
|
||||||
|
if (
|
||||||
|
axisSelection.type === 'segment' ||
|
||||||
|
axisSelection.type === 'path' ||
|
||||||
|
axisSelection.type === 'edgeCut'
|
||||||
|
) {
|
||||||
|
axisDeclaration = axisSelection.codeRef.pathToNode
|
||||||
|
if (!axisDeclaration)
|
||||||
|
return new Error('Expected to find axis declaration')
|
||||||
|
const axisIndexInPathToNode =
|
||||||
|
axisDeclaration.findIndex((a) => a[0] === 'body') + 1
|
||||||
|
const value = axisDeclaration[axisIndexInPathToNode][0]
|
||||||
|
if (typeof value !== 'number')
|
||||||
|
return new Error('expected axis index value to be a number')
|
||||||
|
axisIndexIfAxis = value
|
||||||
|
}
|
||||||
|
} else if (axisOrEdge === 'Axis' && axis) {
|
||||||
|
generatedAxis = createLocalName(axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
generatedAxis,
|
||||||
|
axisIndexIfAxis,
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,14 @@ import type { FunctionExpression } from '@rust/kcl-lib/bindings/FunctionExpressi
|
|||||||
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
||||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||||
import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration'
|
import type { TypeDeclaration } from '@rust/kcl-lib/bindings/TypeDeclaration'
|
||||||
|
import { createLocalName, createPipeSubstitution } from '@src/lang/create'
|
||||||
import { createLocalName } from '@src/lang/create'
|
|
||||||
import type { ToolTip } from '@src/lang/langHelpers'
|
import type { ToolTip } from '@src/lang/langHelpers'
|
||||||
import { splitPathAtLastIndex } from '@src/lang/modifyAst'
|
import { splitPathAtLastIndex } from '@src/lang/modifyAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import { codeRefFromRange } from '@src/lang/std/artifactGraph'
|
import {
|
||||||
|
codeRefFromRange,
|
||||||
|
getArtifactOfTypes,
|
||||||
|
} from '@src/lang/std/artifactGraph'
|
||||||
import { getArgForEnd } from '@src/lang/std/sketch'
|
import { getArgForEnd } from '@src/lang/std/sketch'
|
||||||
import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints'
|
import { getSketchSegmentFromSourceRange } from '@src/lang/std/sketchConstraints'
|
||||||
import {
|
import {
|
||||||
@ -53,6 +55,7 @@ import { getAngle, isArray } from '@src/lib/utils'
|
|||||||
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
import { ARG_INDEX_FIELD, LABELED_ARG_FIELD } from '@src/lang/queryAstConstants'
|
||||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||||
import type { UnaryExpression } from 'typescript'
|
import type { UnaryExpression } from 'typescript'
|
||||||
|
import type { Operation, OpKclValue } from '@rust/kcl-lib/bindings/Operation'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||||
@ -1053,6 +1056,125 @@ export const valueOrVariable = (variable: KclCommandValue) => {
|
|||||||
: variable.valueAst
|
: variable.valueAst
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Go from a selection of sketches to a list of KCL expressions that
|
||||||
|
// can be used to create KCL sweep call declarations.
|
||||||
|
export function getSketchExprsFromSelection(
|
||||||
|
selection: Selections,
|
||||||
|
ast: Node<Program>,
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
): Error | Expr[] {
|
||||||
|
const sketches: Expr[] = selection.graphSelections.flatMap((s) => {
|
||||||
|
const path = getNodePathFromSourceRange(ast, s?.codeRef.range)
|
||||||
|
const sketchVariable = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
path,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(sketchVariable)) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sketchVariable.node.id) {
|
||||||
|
const name = sketchVariable.node?.id.name
|
||||||
|
if (nodeToEdit) {
|
||||||
|
const result = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
nodeToEdit,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
!err(result) &&
|
||||||
|
result.node.type === 'VariableDeclarator' &&
|
||||||
|
name === result.node.id.name
|
||||||
|
) {
|
||||||
|
// Pointing to same variable case
|
||||||
|
return createPipeSubstitution()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Pointing to different variable case
|
||||||
|
return createLocalName(name)
|
||||||
|
} else {
|
||||||
|
// No variable case
|
||||||
|
return createPipeSubstitution()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (sketches.length === 0) {
|
||||||
|
return new Error("Couldn't map selections to program references")
|
||||||
|
}
|
||||||
|
|
||||||
|
return sketches
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go from the sketches argument in a KCL sweep call declaration
|
||||||
|
// to a list of graph selections, useful for edit flows.
|
||||||
|
// Somewhat of an inverse of getSketchExprsFromSelection.
|
||||||
|
export function getSketchSelectionsFromOperation(
|
||||||
|
operation: Operation,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): Error | Selections {
|
||||||
|
const error = new Error("Couldn't retrieve sketches from operation")
|
||||||
|
if (operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall') {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
let sketches: OpKclValue[] = []
|
||||||
|
if (operation.unlabeledArg?.value.type === 'Sketch') {
|
||||||
|
sketches = [operation.unlabeledArg.value]
|
||||||
|
} else if (operation.unlabeledArg?.value.type === 'Array') {
|
||||||
|
sketches = operation.unlabeledArg.value.value
|
||||||
|
} else {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphSelections: Selection[] = sketches.flatMap((sketch) => {
|
||||||
|
// We have to go a little roundabout to get from the original artifact
|
||||||
|
// to the solid2DId that we need to pass to the Extrude command.
|
||||||
|
if (sketch.type !== 'Sketch') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathArtifact = getArtifactOfTypes(
|
||||||
|
{
|
||||||
|
key: sketch.value.artifactId,
|
||||||
|
types: ['path'],
|
||||||
|
},
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
err(pathArtifact) ||
|
||||||
|
pathArtifact.type !== 'path' ||
|
||||||
|
!pathArtifact.solid2dId
|
||||||
|
) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const solid2DArtifact = getArtifactOfTypes(
|
||||||
|
{
|
||||||
|
key: pathArtifact.solid2dId,
|
||||||
|
types: ['solid2d'],
|
||||||
|
},
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(solid2DArtifact) || solid2DArtifact.type !== 'solid2d') {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
artifact: solid2DArtifact,
|
||||||
|
codeRef: pathArtifact.codeRef,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (graphSelections.length === 0) {
|
||||||
|
return error
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
graphSelections,
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function findImportNodeAndAlias(
|
export function findImportNodeAndAlias(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
|
@ -64,27 +64,20 @@ export type ModelingCommandSchema = {
|
|||||||
Extrude: {
|
Extrude: {
|
||||||
// Enables editing workflow
|
// Enables editing workflow
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
selection: Selections // & { type: 'face' } would be cool to lock that down
|
// KCL stdlib arguments
|
||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
sketches: Selections
|
||||||
distance: KclCommandValue
|
length: KclCommandValue
|
||||||
}
|
}
|
||||||
Sweep: {
|
Sweep: {
|
||||||
// Enables editing workflow
|
// Enables editing workflow
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
// Arguments
|
// KCL stdlib arguments
|
||||||
target: Selections
|
sketches: Selections
|
||||||
trajectory: Selections
|
path: Selections
|
||||||
sectional: boolean
|
sectional?: boolean
|
||||||
}
|
}
|
||||||
Loft: {
|
Loft: {
|
||||||
selection: Selections
|
sketches: Selections
|
||||||
}
|
|
||||||
Shell: {
|
|
||||||
// Enables editing workflow
|
|
||||||
nodeToEdit?: PathToNode
|
|
||||||
// KCL stdlib arguments
|
|
||||||
selection: Selections
|
|
||||||
thickness: KclCommandValue
|
|
||||||
}
|
}
|
||||||
Revolve: {
|
Revolve: {
|
||||||
// Enables editing workflow
|
// Enables editing workflow
|
||||||
@ -92,11 +85,18 @@ export type ModelingCommandSchema = {
|
|||||||
// Flow arg
|
// Flow arg
|
||||||
axisOrEdge: 'Axis' | 'Edge'
|
axisOrEdge: 'Axis' | 'Edge'
|
||||||
// KCL stdlib arguments
|
// KCL stdlib arguments
|
||||||
selection: Selections
|
sketches: Selections
|
||||||
angle: KclCommandValue
|
angle: KclCommandValue
|
||||||
axis: string | undefined
|
axis: string | undefined
|
||||||
edge: Selections | undefined
|
edge: Selections | undefined
|
||||||
}
|
}
|
||||||
|
Shell: {
|
||||||
|
// Enables editing workflow
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
// KCL stdlib arguments
|
||||||
|
selection: Selections
|
||||||
|
thickness: KclCommandValue
|
||||||
|
}
|
||||||
Fillet: {
|
Fillet: {
|
||||||
// Enables editing workflow
|
// Enables editing workflow
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
@ -388,26 +388,14 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: false,
|
required: false,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
},
|
},
|
||||||
selection: {
|
sketches: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['solid2d', 'segment'],
|
selectionTypes: ['solid2d', 'segment'],
|
||||||
multiple: false, // TODO: multiple selection
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
// result: {
|
length: {
|
||||||
// inputType: 'options',
|
|
||||||
// defaultValue: 'add',
|
|
||||||
// skip: true,
|
|
||||||
// required: true,
|
|
||||||
// options: EXTRUSION_RESULTS.map((r) => ({
|
|
||||||
// name: r,
|
|
||||||
// isCurrent: r === 'add',
|
|
||||||
// value: r,
|
|
||||||
// })),
|
|
||||||
// },
|
|
||||||
distance: {
|
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
required: true,
|
required: true,
|
||||||
@ -426,26 +414,28 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
skip: true,
|
skip: true,
|
||||||
inputType: 'text',
|
inputType: 'text',
|
||||||
required: false,
|
required: false,
|
||||||
|
hidden: true,
|
||||||
},
|
},
|
||||||
target: {
|
sketches: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['solid2d'],
|
selectionTypes: ['solid2d', 'segment'],
|
||||||
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
|
||||||
multiple: false,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
trajectory: {
|
path: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment'],
|
selectionTypes: ['segment'],
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
|
||||||
multiple: false,
|
multiple: false,
|
||||||
validation: sweepValidator,
|
validation: sweepValidator,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
sectional: {
|
sectional: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
|
skip: true,
|
||||||
|
defaultValue: false,
|
||||||
|
hidden: false,
|
||||||
required: true,
|
required: true,
|
||||||
options: [
|
options: [
|
||||||
{ name: 'False', value: false },
|
{ name: 'False', value: false },
|
||||||
@ -458,45 +448,17 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
Loft: {
|
Loft: {
|
||||||
description: 'Create a 3D body by blending between two or more sketches',
|
description: 'Create a 3D body by blending between two or more sketches',
|
||||||
icon: 'loft',
|
icon: 'loft',
|
||||||
needsReview: false,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
selection: {
|
sketches: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['solid2d'],
|
selectionTypes: ['solid2d'],
|
||||||
multiple: true,
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: false,
|
|
||||||
validation: loftValidator,
|
validation: loftValidator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Shell: {
|
|
||||||
description: 'Hollow out a 3D solid.',
|
|
||||||
icon: 'shell',
|
|
||||||
needsReview: true,
|
|
||||||
args: {
|
|
||||||
nodeToEdit: {
|
|
||||||
description:
|
|
||||||
'Path to the node in the AST to edit. Never shown to the user.',
|
|
||||||
skip: true,
|
|
||||||
inputType: 'text',
|
|
||||||
required: false,
|
|
||||||
},
|
|
||||||
selection: {
|
|
||||||
inputType: 'selection',
|
|
||||||
selectionTypes: ['cap', 'wall'],
|
|
||||||
multiple: true,
|
|
||||||
required: true,
|
|
||||||
validation: shellValidator,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
|
||||||
},
|
|
||||||
thickness: {
|
|
||||||
inputType: 'kcl',
|
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
@ -509,12 +471,11 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
inputType: 'text',
|
inputType: 'text',
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
selection: {
|
sketches: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['solid2d', 'segment'],
|
selectionTypes: ['solid2d', 'segment'],
|
||||||
multiple: false, // TODO: multiple selection
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
axisOrEdge: {
|
axisOrEdge: {
|
||||||
@ -557,6 +518,33 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Shell: {
|
||||||
|
description: 'Hollow out a 3D solid.',
|
||||||
|
icon: 'shell',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
nodeToEdit: {
|
||||||
|
description:
|
||||||
|
'Path to the node in the AST to edit. Never shown to the user.',
|
||||||
|
skip: true,
|
||||||
|
inputType: 'text',
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['cap', 'wall'],
|
||||||
|
multiple: true,
|
||||||
|
required: true,
|
||||||
|
validation: shellValidator,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
|
},
|
||||||
|
thickness: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
'Boolean Subtract': {
|
'Boolean Subtract': {
|
||||||
description: 'Subtract one solid from another.',
|
description: 'Subtract one solid from another.',
|
||||||
icon: 'booleanSubtract',
|
icon: 'booleanSubtract',
|
||||||
|
@ -94,11 +94,12 @@ export const revolveAxisValidator = async ({
|
|||||||
data: { [key: string]: Selections }
|
data: { [key: string]: Selections }
|
||||||
context: CommandBarContext
|
context: CommandBarContext
|
||||||
}): Promise<boolean | string> => {
|
}): Promise<boolean | string> => {
|
||||||
if (!isSelections(context.argumentsToSubmit.selection)) {
|
if (!isSelections(context.argumentsToSubmit.sketches)) {
|
||||||
return 'Unable to revolve, selections are missing'
|
return 'Unable to revolve, selections are missing'
|
||||||
}
|
}
|
||||||
|
// Gotcha: this validation only works for the first sketch
|
||||||
const artifact =
|
const artifact =
|
||||||
context.argumentsToSubmit.selection.graphSelections[0].artifact
|
context.argumentsToSubmit.sketches.graphSelections[0].artifact
|
||||||
|
|
||||||
if (!artifact) {
|
if (!artifact) {
|
||||||
return 'Unable to revolve, sketch not found'
|
return 'Unable to revolve, sketch not found'
|
||||||
@ -155,16 +156,16 @@ export const loftValidator = async ({
|
|||||||
data: { [key: string]: Selections }
|
data: { [key: string]: Selections }
|
||||||
context: CommandBarContext
|
context: CommandBarContext
|
||||||
}): Promise<boolean | string> => {
|
}): Promise<boolean | string> => {
|
||||||
if (!isSelections(data.selection)) {
|
if (!isSelections(data.sketches)) {
|
||||||
return 'Unable to loft, selections are missing'
|
return 'Unable to loft, selections are missing'
|
||||||
}
|
}
|
||||||
const { selection } = data
|
const { sketches } = data
|
||||||
|
|
||||||
if (selection.graphSelections.some((s) => s.artifact?.type !== 'solid2d')) {
|
if (sketches.graphSelections.some((s) => s.artifact?.type !== 'solid2d')) {
|
||||||
return 'Unable to loft, some selection are not solid2ds'
|
return 'Unable to loft, some selection are not solid2ds'
|
||||||
}
|
}
|
||||||
|
|
||||||
const sectionIds = data.selection.graphSelections.flatMap((s) =>
|
const sectionIds = sketches.graphSelections.flatMap((s) =>
|
||||||
s.artifact?.type === 'solid2d' ? s.artifact.pathId : []
|
s.artifact?.type === 'solid2d' ? s.artifact.pathId : []
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -258,15 +259,15 @@ export const sweepValidator = async ({
|
|||||||
data,
|
data,
|
||||||
}: {
|
}: {
|
||||||
context: CommandBarContext
|
context: CommandBarContext
|
||||||
data: { trajectory: Selections }
|
data: { path: Selections }
|
||||||
}): Promise<boolean | string> => {
|
}): Promise<boolean | string> => {
|
||||||
if (!isSelections(data.trajectory)) {
|
if (!isSelections(data.path)) {
|
||||||
console.log('Unable to sweep, selections are missing')
|
console.log('Unable to sweep, selections are missing')
|
||||||
return 'Unable to sweep, selections are missing'
|
return 'Unable to sweep, selections are missing'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the parent path from the segment selection directly
|
// Retrieve the parent path from the segment selection directly
|
||||||
const trajectoryArtifact = data.trajectory.graphSelections[0].artifact
|
const trajectoryArtifact = data.path.graphSelections[0].artifact
|
||||||
if (!trajectoryArtifact) {
|
if (!trajectoryArtifact) {
|
||||||
return "Unable to sweep, couldn't find the trajectory artifact"
|
return "Unable to sweep, couldn't find the trajectory artifact"
|
||||||
}
|
}
|
||||||
@ -276,7 +277,7 @@ export const sweepValidator = async ({
|
|||||||
const trajectory = trajectoryArtifact.pathId
|
const trajectory = trajectoryArtifact.pathId
|
||||||
|
|
||||||
// Get the former arg in the command bar flow, and retrieve the path from the solid2d directly
|
// Get the former arg in the command bar flow, and retrieve the path from the solid2d directly
|
||||||
const targetArg = context.argumentsToSubmit['target'] as Selections
|
const targetArg = context.argumentsToSubmit['sketches'] as Selections
|
||||||
const targetArtifact = targetArg.graphSelections[0].artifact
|
const targetArtifact = targetArg.graphSelections[0].artifact
|
||||||
if (!targetArtifact) {
|
if (!targetArtifact) {
|
||||||
return "Unable to sweep, couldn't find the profile artifact"
|
return "Unable to sweep, couldn't find the profile artifact"
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation'
|
import type { OpKclValue, Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||||
|
|
||||||
import type { CustomIconName } from '@src/components/CustomIcon'
|
import type { CustomIconName } from '@src/components/CustomIcon'
|
||||||
import { getNodeFromPath, findPipesWithImportAlias } from '@src/lang/queryAst'
|
import {
|
||||||
|
getNodeFromPath,
|
||||||
|
findPipesWithImportAlias,
|
||||||
|
getSketchSelectionsFromOperation,
|
||||||
|
} from '@src/lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||||
import {
|
import {
|
||||||
@ -57,81 +61,51 @@ interface StdLibCallInfo {
|
|||||||
* Gather up the argument values for the Extrude command
|
* Gather up the argument values for the Extrude command
|
||||||
* to be used in the command bar edit flow.
|
* to be used in the command bar edit flow.
|
||||||
*/
|
*/
|
||||||
const prepareToEditExtrude: PrepareToEditCallback =
|
const prepareToEditExtrude: PrepareToEditCallback = async ({ operation }) => {
|
||||||
async function prepareToEditExtrude({ operation, artifact }) {
|
const baseCommand = {
|
||||||
const baseCommand = {
|
name: 'Extrude',
|
||||||
name: 'Extrude',
|
groupId: 'modeling',
|
||||||
groupId: 'modeling',
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!artifact ||
|
|
||||||
!('pathId' in artifact) ||
|
|
||||||
(operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall')
|
|
||||||
) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
// We have to go a little roundabout to get from the original artifact
|
|
||||||
// to the solid2DId that we need to pass to the Extrude command.
|
|
||||||
const pathArtifact = getArtifactOfTypes(
|
|
||||||
{
|
|
||||||
key: artifact.pathId,
|
|
||||||
types: ['path'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
err(pathArtifact) ||
|
|
||||||
pathArtifact.type !== 'path' ||
|
|
||||||
!pathArtifact.solid2dId
|
|
||||||
)
|
|
||||||
return baseCommand
|
|
||||||
const solid2DArtifact = getArtifactOfTypes(
|
|
||||||
{
|
|
||||||
key: pathArtifact.solid2dId,
|
|
||||||
types: ['solid2d'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (err(solid2DArtifact) || solid2DArtifact.type !== 'solid2d') {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the length argument from a string to a KCL expression
|
|
||||||
const distanceResult = await stringToKclExpression(
|
|
||||||
codeManager.code.slice(
|
|
||||||
operation.labeledArgs?.['length']?.sourceRange[0],
|
|
||||||
operation.labeledArgs?.['length']?.sourceRange[1]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if (err(distanceResult) || 'errors' in distanceResult) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble the default argument values for the Extrude command,
|
|
||||||
// with `nodeToEdit` set, which will let the Extrude actor know
|
|
||||||
// to edit the node that corresponds to the StdLibCall.
|
|
||||||
const argDefaultValues: ModelingCommandSchema['Extrude'] = {
|
|
||||||
selection: {
|
|
||||||
graphSelections: [
|
|
||||||
{
|
|
||||||
artifact: solid2DArtifact,
|
|
||||||
codeRef: pathArtifact.codeRef,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
},
|
|
||||||
distance: distanceResult,
|
|
||||||
nodeToEdit: getNodePathFromSourceRange(
|
|
||||||
kclManager.ast,
|
|
||||||
sourceRangeFromRust(operation.sourceRange)
|
|
||||||
),
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...baseCommand,
|
|
||||||
argDefaultValues,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall') {
|
||||||
|
return { reason: 'Wrong operation type' }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. Map the unlabeled arguments to solid2d selections
|
||||||
|
const sketches = getSketchSelectionsFromOperation(
|
||||||
|
operation,
|
||||||
|
kclManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(sketches)) {
|
||||||
|
return { reason: "Couldn't retrieve sketches" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Convert the length argument from a string to a KCL expression
|
||||||
|
const length = await stringToKclExpression(
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs?.['length']?.sourceRange[0],
|
||||||
|
operation.labeledArgs?.['length']?.sourceRange[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if (err(length) || 'errors' in length) {
|
||||||
|
return { reason: "Couldn't retrieve length argument" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Assemble the default argument values for the command,
|
||||||
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
|
const argDefaultValues: ModelingCommandSchema['Extrude'] = {
|
||||||
|
sketches,
|
||||||
|
length,
|
||||||
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
sourceRangeFromRust(operation.sourceRange)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...baseCommand,
|
||||||
|
argDefaultValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather up the argument values for the Chamfer or Fillet command
|
* Gather up the argument values for the Chamfer or Fillet command
|
||||||
@ -446,78 +420,32 @@ const prepareToEditOffsetPlane: PrepareToEditCallback = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prepareToEditSweep: PrepareToEditCallback = async ({
|
/**
|
||||||
artifact,
|
* Gather up the argument values for the Revolve command
|
||||||
operation,
|
* to be used in the command bar edit flow.
|
||||||
}) => {
|
*/
|
||||||
|
const prepareToEditSweep: PrepareToEditCallback = async ({ operation }) => {
|
||||||
const baseCommand = {
|
const baseCommand = {
|
||||||
name: 'Sweep',
|
name: 'Sweep',
|
||||||
groupId: 'modeling',
|
groupId: 'modeling',
|
||||||
}
|
}
|
||||||
if (
|
if (operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall') {
|
||||||
(operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall') ||
|
return { reason: 'Wrong operation type' }
|
||||||
!operation.labeledArgs ||
|
|
||||||
!operation.unlabeledArg ||
|
|
||||||
!('sectional' in operation.labeledArgs) ||
|
|
||||||
!operation.labeledArgs.sectional
|
|
||||||
) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!artifact ||
|
|
||||||
!('pathId' in artifact) ||
|
|
||||||
(operation.type !== 'StdLibCall' && operation.type !== 'KclStdLibCall')
|
|
||||||
) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to go a little roundabout to get from the original artifact
|
// 1. Map the unlabeled arguments to solid2d selections
|
||||||
// to the solid2DId that we need to pass to the Sweep command, just like Extrude.
|
const sketches = getSketchSelectionsFromOperation(
|
||||||
const pathArtifact = getArtifactOfTypes(
|
operation,
|
||||||
{
|
|
||||||
key: artifact.pathId,
|
|
||||||
types: ['path'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
|
if (err(sketches)) {
|
||||||
if (
|
return { reason: "Couldn't retrieve sketches" }
|
||||||
err(pathArtifact) ||
|
|
||||||
pathArtifact.type !== 'path' ||
|
|
||||||
!pathArtifact.solid2dId
|
|
||||||
) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetArtifact = getArtifactOfTypes(
|
|
||||||
{
|
|
||||||
key: pathArtifact.solid2dId,
|
|
||||||
types: ['solid2d'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
|
|
||||||
if (err(targetArtifact) || targetArtifact.type !== 'solid2d') {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
const target = {
|
|
||||||
graphSelections: [
|
|
||||||
{
|
|
||||||
artifact: targetArtifact,
|
|
||||||
codeRef: pathArtifact.codeRef,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Prepare labeled arguments
|
||||||
// Same roundabout but twice for 'path' aka trajectory: sketch -> path -> segment
|
// Same roundabout but twice for 'path' aka trajectory: sketch -> path -> segment
|
||||||
if (!('path' in operation.labeledArgs) || !operation.labeledArgs.path) {
|
if (operation.labeledArgs.path?.value.type !== 'Sketch') {
|
||||||
return baseCommand
|
return { reason: "Couldn't retrieve trajectory argument" }
|
||||||
}
|
|
||||||
|
|
||||||
if (operation.labeledArgs.path.value.type !== 'Sketch') {
|
|
||||||
return baseCommand
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const trajectoryPathArtifact = getArtifactOfTypes(
|
const trajectoryPathArtifact = getArtifactOfTypes(
|
||||||
@ -529,7 +457,7 @@ const prepareToEditSweep: PrepareToEditCallback = async ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (err(trajectoryPathArtifact) || trajectoryPathArtifact.type !== 'path') {
|
if (err(trajectoryPathArtifact) || trajectoryPathArtifact.type !== 'path') {
|
||||||
return baseCommand
|
return { reason: "Couldn't retrieve trajectory path artifact" }
|
||||||
}
|
}
|
||||||
|
|
||||||
const trajectoryArtifact = getArtifactOfTypes(
|
const trajectoryArtifact = getArtifactOfTypes(
|
||||||
@ -541,10 +469,11 @@ const prepareToEditSweep: PrepareToEditCallback = async ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (err(trajectoryArtifact) || trajectoryArtifact.type !== 'segment') {
|
if (err(trajectoryArtifact) || trajectoryArtifact.type !== 'segment') {
|
||||||
return baseCommand
|
console.log(trajectoryArtifact)
|
||||||
|
return { reason: "Couldn't retrieve trajectory artifact" }
|
||||||
}
|
}
|
||||||
|
|
||||||
const trajectory = {
|
const path = {
|
||||||
graphSelections: [
|
graphSelections: [
|
||||||
{
|
{
|
||||||
artifact: trajectoryArtifact,
|
artifact: trajectoryArtifact,
|
||||||
@ -554,33 +483,28 @@ const prepareToEditSweep: PrepareToEditCallback = async ({
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
// sectional options boolean arg
|
// sectional argument from a string to a KCL expression
|
||||||
if (
|
let sectional: boolean | undefined
|
||||||
!('sectional' in operation.labeledArgs) ||
|
if ('sectional' in operation.labeledArgs && operation.labeledArgs.sectional) {
|
||||||
!operation.labeledArgs.sectional
|
sectional =
|
||||||
) {
|
codeManager.code.slice(
|
||||||
return baseCommand
|
operation.labeledArgs.sectional.sourceRange[0],
|
||||||
|
operation.labeledArgs.sectional.sourceRange[1]
|
||||||
|
) === 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
const sectional =
|
// 3. Assemble the default argument values for the command,
|
||||||
codeManager.code.slice(
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
operation.labeledArgs.sectional.sourceRange[0],
|
|
||||||
operation.labeledArgs.sectional.sourceRange[1]
|
|
||||||
) === 'true'
|
|
||||||
|
|
||||||
// Assemble the default argument values for the Offset Plane command,
|
|
||||||
// with `nodeToEdit` set, which will let the Offset Plane actor know
|
|
||||||
// to edit the node that corresponds to the StdLibCall.
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
const argDefaultValues: ModelingCommandSchema['Sweep'] = {
|
const argDefaultValues: ModelingCommandSchema['Sweep'] = {
|
||||||
target: target,
|
sketches,
|
||||||
trajectory,
|
path,
|
||||||
sectional,
|
sectional,
|
||||||
nodeToEdit: getNodePathFromSourceRange(
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
sourceRangeFromRust(operation.sourceRange)
|
sourceRangeFromRust(operation.sourceRange)
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...baseCommand,
|
...baseCommand,
|
||||||
argDefaultValues,
|
argDefaultValues,
|
||||||
@ -841,6 +765,10 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather up the argument values for the Revolve command
|
||||||
|
* to be used in the command bar edit flow.
|
||||||
|
*/
|
||||||
const prepareToEditRevolve: PrepareToEditCallback = async ({
|
const prepareToEditRevolve: PrepareToEditCallback = async ({
|
||||||
operation,
|
operation,
|
||||||
artifact,
|
artifact,
|
||||||
@ -851,51 +779,22 @@ const prepareToEditRevolve: PrepareToEditCallback = async ({
|
|||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
!artifact ||
|
!artifact ||
|
||||||
!('pathId' in artifact) ||
|
|
||||||
operation.type !== 'KclStdLibCall' ||
|
operation.type !== 'KclStdLibCall' ||
|
||||||
!operation.labeledArgs
|
!operation.labeledArgs
|
||||||
) {
|
) {
|
||||||
return { reason: 'Wrong operation type or artifact' }
|
return { reason: 'Wrong operation type or artifact' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to go a little roundabout to get from the original artifact
|
// 1. Map the unlabeled arguments to solid2d selections
|
||||||
// to the solid2DId that we need to pass to the command.
|
const sketches = getSketchSelectionsFromOperation(
|
||||||
const pathArtifact = getArtifactOfTypes(
|
operation,
|
||||||
{
|
|
||||||
key: artifact.pathId,
|
|
||||||
types: ['path'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (
|
if (err(sketches)) {
|
||||||
err(pathArtifact) ||
|
return { reason: "Couldn't retrieve sketches" }
|
||||||
pathArtifact.type !== 'path' ||
|
|
||||||
!pathArtifact.solid2dId
|
|
||||||
) {
|
|
||||||
return { reason: "Couldn't find related path artifact" }
|
|
||||||
}
|
|
||||||
|
|
||||||
const solid2DArtifact = getArtifactOfTypes(
|
|
||||||
{
|
|
||||||
key: pathArtifact.solid2dId,
|
|
||||||
types: ['solid2d'],
|
|
||||||
},
|
|
||||||
kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (err(solid2DArtifact) || solid2DArtifact.type !== 'solid2d') {
|
|
||||||
return { reason: "Couldn't find related solid2d artifact" }
|
|
||||||
}
|
|
||||||
|
|
||||||
const selection = {
|
|
||||||
graphSelections: [
|
|
||||||
{
|
|
||||||
artifact: solid2DArtifact,
|
|
||||||
codeRef: pathArtifact.codeRef,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
otherSelections: [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. Prepare labeled arguments
|
||||||
// axis options string arg
|
// axis options string arg
|
||||||
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis) {
|
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis) {
|
||||||
return { reason: "Couldn't find axis argument" }
|
return { reason: "Couldn't find axis argument" }
|
||||||
@ -988,14 +887,14 @@ const prepareToEditRevolve: PrepareToEditCallback = async ({
|
|||||||
return { reason: 'Error in angle argument retrieval' }
|
return { reason: 'Error in angle argument retrieval' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the default argument values for the Offset Plane command,
|
// 3. Assemble the default argument values for the command,
|
||||||
// with `nodeToEdit` set, which will let the Offset Plane actor know
|
// with `nodeToEdit` set, which will let the actor know
|
||||||
// to edit the node that corresponds to the StdLibCall.
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
const argDefaultValues: ModelingCommandSchema['Revolve'] = {
|
const argDefaultValues: ModelingCommandSchema['Revolve'] = {
|
||||||
|
sketches,
|
||||||
axisOrEdge,
|
axisOrEdge,
|
||||||
axis,
|
axis,
|
||||||
edge,
|
edge,
|
||||||
selection,
|
|
||||||
angle,
|
angle,
|
||||||
nodeToEdit: getNodePathFromSourceRange(
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
|
@ -49,11 +49,8 @@ import {
|
|||||||
addHelix,
|
addHelix,
|
||||||
addOffsetPlane,
|
addOffsetPlane,
|
||||||
addShell,
|
addShell,
|
||||||
addSweep,
|
|
||||||
extrudeSketch,
|
|
||||||
insertNamedConstant,
|
insertNamedConstant,
|
||||||
insertVariableAndOffsetPathToNode,
|
insertVariableAndOffsetPathToNode,
|
||||||
loftSketches,
|
|
||||||
} from '@src/lang/modifyAst'
|
} from '@src/lang/modifyAst'
|
||||||
import type {
|
import type {
|
||||||
ChamferParameters,
|
ChamferParameters,
|
||||||
@ -66,9 +63,12 @@ import {
|
|||||||
mutateAstWithTagForSketchSegment,
|
mutateAstWithTagForSketchSegment,
|
||||||
} from '@src/lang/modifyAst/addEdgeTreatment'
|
} from '@src/lang/modifyAst/addEdgeTreatment'
|
||||||
import {
|
import {
|
||||||
|
addExtrude,
|
||||||
|
addLoft,
|
||||||
|
addRevolve,
|
||||||
|
addSweep,
|
||||||
getAxisExpressionAndIndex,
|
getAxisExpressionAndIndex,
|
||||||
revolveSketch,
|
} from '@src/lang/modifyAst/addSweep'
|
||||||
} from '@src/lang/modifyAst/addRevolve'
|
|
||||||
import {
|
import {
|
||||||
applyIntersectFromTargetOperatorSelections,
|
applyIntersectFromTargetOperatorSelections,
|
||||||
applySubtractFromTargetOperatorSelections,
|
applySubtractFromTargetOperatorSelections,
|
||||||
@ -94,7 +94,6 @@ import {
|
|||||||
updatePathToNodesAfterEdit,
|
updatePathToNodesAfterEdit,
|
||||||
valueOrVariable,
|
valueOrVariable,
|
||||||
} from '@src/lang/queryAst'
|
} from '@src/lang/queryAst'
|
||||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
|
||||||
import {
|
import {
|
||||||
getFaceCodeRef,
|
getFaceCodeRef,
|
||||||
getPathsFromPlaneArtifact,
|
getPathsFromPlaneArtifact,
|
||||||
@ -133,7 +132,7 @@ import {
|
|||||||
} from '@src/lib/singletons'
|
} from '@src/lib/singletons'
|
||||||
import type { ToolbarModeName } from '@src/lib/toolbar'
|
import type { ToolbarModeName } from '@src/lib/toolbar'
|
||||||
import { err, reportRejection, trap } from '@src/lib/trap'
|
import { err, reportRejection, trap } from '@src/lib/trap'
|
||||||
import { isArray, uuidv4 } from '@src/lib/utils'
|
import { uuidv4 } from '@src/lib/utils'
|
||||||
import { deleteNodeInExtrudePipe } from '@src/lang/modifyAst/deleteNodeInExtrudePipe'
|
import { deleteNodeInExtrudePipe } from '@src/lang/modifyAst/deleteNodeInExtrudePipe'
|
||||||
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
import type { ImportStatement } from '@rust/kcl-lib/bindings/ImportStatement'
|
||||||
|
|
||||||
@ -1793,69 +1792,20 @@ export const modelingMachine = setup({
|
|||||||
unknown,
|
unknown,
|
||||||
ModelingCommandSchema['Extrude'] | undefined
|
ModelingCommandSchema['Extrude'] | undefined
|
||||||
>(async ({ input }) => {
|
>(async ({ input }) => {
|
||||||
if (!input) return new Error('No input provided')
|
if (!input) return Promise.reject(new Error('No input provided'))
|
||||||
const { selection, distance, nodeToEdit } = input
|
const { nodeToEdit, sketches, length } = input
|
||||||
const isEditing =
|
const { ast } = kclManager
|
||||||
nodeToEdit !== undefined && typeof nodeToEdit[1][0] === 'number'
|
const astResult = addExtrude({
|
||||||
let ast = structuredClone(kclManager.ast)
|
|
||||||
let extrudeName: string | undefined = undefined
|
|
||||||
|
|
||||||
// If this is an edit flow, first we're going to remove the old extrusion
|
|
||||||
if (isEditing) {
|
|
||||||
// Extract the plane name from the node to edit
|
|
||||||
const extrudeNameNode = getNodeFromPath<VariableDeclaration>(
|
|
||||||
ast,
|
|
||||||
nodeToEdit,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(extrudeNameNode)) {
|
|
||||||
console.error('Error extracting plane name')
|
|
||||||
} else {
|
|
||||||
extrudeName = extrudeNameNode.node.declaration.id.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing the old extrusion statement
|
|
||||||
const newBody = [...ast.body]
|
|
||||||
newBody.splice(nodeToEdit[1][0] as number, 1)
|
|
||||||
ast.body = newBody
|
|
||||||
}
|
|
||||||
|
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
|
||||||
ast,
|
ast,
|
||||||
selection.graphSelections[0]?.codeRef.range
|
sketches,
|
||||||
)
|
length,
|
||||||
// Add an extrude statement to the AST
|
nodeToEdit,
|
||||||
const extrudeSketchRes = extrudeSketch({
|
|
||||||
node: ast,
|
|
||||||
pathToNode,
|
|
||||||
artifact: selection.graphSelections[0].artifact,
|
|
||||||
artifactGraph: kclManager.artifactGraph,
|
|
||||||
distance:
|
|
||||||
'variableName' in distance
|
|
||||||
? distance.variableIdentifierAst
|
|
||||||
: distance.valueAst,
|
|
||||||
extrudeName,
|
|
||||||
})
|
})
|
||||||
if (err(extrudeSketchRes)) return extrudeSketchRes
|
if (err(astResult)) {
|
||||||
const { modifiedAst, pathToExtrudeArg } = extrudeSketchRes
|
return Promise.reject(new Error("Couldn't add extrude statement"))
|
||||||
|
|
||||||
// Insert the distance variable if the user has provided a variable name
|
|
||||||
if (
|
|
||||||
'variableName' in distance &&
|
|
||||||
distance.variableName &&
|
|
||||||
typeof pathToExtrudeArg[1][0] === 'number'
|
|
||||||
) {
|
|
||||||
const insertIndex = Math.min(
|
|
||||||
pathToExtrudeArg[1][0],
|
|
||||||
distance.insertIndex
|
|
||||||
)
|
|
||||||
const newBody = [...modifiedAst.body]
|
|
||||||
newBody.splice(insertIndex, 0, distance.variableDeclarationAst)
|
|
||||||
modifiedAst.body = newBody
|
|
||||||
// Since we inserted a new variable, we need to update the path to the extrude argument
|
|
||||||
pathToExtrudeArg[1][0]++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { modifiedAst, pathToNode } = astResult
|
||||||
await updateModelingState(
|
await updateModelingState(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
EXECUTION_TYPE_REAL,
|
EXECUTION_TYPE_REAL,
|
||||||
@ -1865,73 +1815,92 @@ export const modelingMachine = setup({
|
|||||||
codeManager,
|
codeManager,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
focusPath: [pathToExtrudeArg],
|
focusPath: [pathToNode],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
sweepAstMod: fromPromise<
|
||||||
|
unknown,
|
||||||
|
ModelingCommandSchema['Sweep'] | undefined
|
||||||
|
>(async ({ input }) => {
|
||||||
|
if (!input) return Promise.reject(new Error('No input provided'))
|
||||||
|
const { nodeToEdit, sketches, path, sectional } = input
|
||||||
|
const { ast } = kclManager
|
||||||
|
const astResult = addSweep({
|
||||||
|
ast,
|
||||||
|
sketches,
|
||||||
|
path,
|
||||||
|
sectional,
|
||||||
|
nodeToEdit,
|
||||||
|
})
|
||||||
|
if (err(astResult)) {
|
||||||
|
return Promise.reject(astResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { modifiedAst, pathToNode } = astResult
|
||||||
|
await updateModelingState(
|
||||||
|
modifiedAst,
|
||||||
|
EXECUTION_TYPE_REAL,
|
||||||
|
{
|
||||||
|
kclManager,
|
||||||
|
editorManager,
|
||||||
|
codeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
focusPath: [pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
loftAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Loft'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) return Promise.reject(new Error('No input provided'))
|
||||||
|
const { sketches } = input
|
||||||
|
const { ast } = kclManager
|
||||||
|
const astResult = addLoft({ ast, sketches })
|
||||||
|
if (err(astResult)) {
|
||||||
|
return Promise.reject(astResult)
|
||||||
|
}
|
||||||
|
|
||||||
|
const { modifiedAst, pathToNode } = astResult
|
||||||
|
await updateModelingState(
|
||||||
|
modifiedAst,
|
||||||
|
EXECUTION_TYPE_REAL,
|
||||||
|
{
|
||||||
|
kclManager,
|
||||||
|
editorManager,
|
||||||
|
codeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
focusPath: [pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
revolveAstMod: fromPromise<
|
revolveAstMod: fromPromise<
|
||||||
unknown,
|
unknown,
|
||||||
ModelingCommandSchema['Revolve'] | undefined
|
ModelingCommandSchema['Revolve'] | undefined
|
||||||
>(async ({ input }) => {
|
>(async ({ input }) => {
|
||||||
if (!input) return new Error('No input provided')
|
if (!input) return Promise.reject(new Error('No input provided'))
|
||||||
const { nodeToEdit, selection, angle, axis, edge, axisOrEdge } = input
|
const { nodeToEdit, sketches, angle, axis, edge, axisOrEdge } = input
|
||||||
let ast = kclManager.ast
|
const { ast } = kclManager
|
||||||
let variableName: string | undefined = undefined
|
const astResult = addRevolve({
|
||||||
let insertIndex: number | undefined = undefined
|
|
||||||
|
|
||||||
// If this is an edit flow, first we're going to remove the old extrusion
|
|
||||||
if (nodeToEdit && typeof nodeToEdit[1][0] === 'number') {
|
|
||||||
// Extract the plane name from the node to edit
|
|
||||||
const nameNode = getNodeFromPath<VariableDeclaration>(
|
|
||||||
ast,
|
|
||||||
nodeToEdit,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(nameNode)) {
|
|
||||||
console.error('Error extracting plane name')
|
|
||||||
} else {
|
|
||||||
variableName = nameNode.node.declaration.id.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing the old extrusion statement
|
|
||||||
const newBody = [...ast.body]
|
|
||||||
newBody.splice(nodeToEdit[1][0], 1)
|
|
||||||
ast.body = newBody
|
|
||||||
insertIndex = nodeToEdit[1][0]
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
'variableName' in angle &&
|
|
||||||
angle.variableName &&
|
|
||||||
angle.insertIndex !== undefined
|
|
||||||
) {
|
|
||||||
const newBody = [...ast.body]
|
|
||||||
newBody.splice(angle.insertIndex, 0, angle.variableDeclarationAst)
|
|
||||||
ast.body = newBody
|
|
||||||
if (insertIndex) {
|
|
||||||
// if editing need to offset that new var
|
|
||||||
insertIndex += 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is the selection of the sketch that will be revolved
|
|
||||||
const pathToNode = getNodePathFromSourceRange(
|
|
||||||
ast,
|
ast,
|
||||||
selection.graphSelections[0]?.codeRef.range
|
sketches,
|
||||||
)
|
angle,
|
||||||
|
|
||||||
const revolveSketchRes = revolveSketch(
|
|
||||||
ast,
|
|
||||||
pathToNode,
|
|
||||||
'variableName' in angle ? angle.variableIdentifierAst : angle.valueAst,
|
|
||||||
axisOrEdge,
|
axisOrEdge,
|
||||||
axis,
|
axis,
|
||||||
edge,
|
edge,
|
||||||
variableName,
|
nodeToEdit,
|
||||||
insertIndex
|
})
|
||||||
)
|
if (err(astResult)) {
|
||||||
if (trap(revolveSketchRes)) return
|
return Promise.reject(astResult)
|
||||||
const { modifiedAst, pathToRevolveArg } = revolveSketchRes
|
}
|
||||||
|
|
||||||
|
const { modifiedAst, pathToNode } = astResult
|
||||||
await updateModelingState(
|
await updateModelingState(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
EXECUTION_TYPE_REAL,
|
EXECUTION_TYPE_REAL,
|
||||||
@ -1941,7 +1910,7 @@ export const modelingMachine = setup({
|
|||||||
codeManager,
|
codeManager,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
focusPath: [pathToRevolveArg],
|
focusPath: [pathToNode],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
@ -2169,141 +2138,6 @@ export const modelingMachine = setup({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
sweepAstMod: fromPromise(
|
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
input: ModelingCommandSchema['Sweep'] | undefined
|
|
||||||
}) => {
|
|
||||||
if (!input) return new Error('No input provided')
|
|
||||||
// Extract inputs
|
|
||||||
const ast = kclManager.ast
|
|
||||||
const { target, trajectory, sectional, nodeToEdit } = input
|
|
||||||
let variableName: string | undefined = undefined
|
|
||||||
let insertIndex: number | undefined = undefined
|
|
||||||
|
|
||||||
// If this is an edit flow, first we're going to remove the old one
|
|
||||||
if (nodeToEdit !== undefined && typeof nodeToEdit[1][0] === 'number') {
|
|
||||||
// Extract the plane name from the node to edit
|
|
||||||
const variableNode = getNodeFromPath<VariableDeclaration>(
|
|
||||||
ast,
|
|
||||||
nodeToEdit,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
|
|
||||||
if (err(variableNode)) {
|
|
||||||
console.error('Error extracting name')
|
|
||||||
} else {
|
|
||||||
variableName = variableNode.node.declaration.id.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing the old statement
|
|
||||||
const newBody = [...ast.body]
|
|
||||||
newBody.splice(nodeToEdit[1][0], 1)
|
|
||||||
ast.body = newBody
|
|
||||||
insertIndex = nodeToEdit[1][0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the target declaration
|
|
||||||
const targetNodePath = getNodePathFromSourceRange(
|
|
||||||
ast,
|
|
||||||
target.graphSelections[0].codeRef.range
|
|
||||||
)
|
|
||||||
// Gotchas, not sure why
|
|
||||||
// - it seems like in some cases we get a list on edit, especially the state that e2e hits
|
|
||||||
// - looking for a VariableDeclaration seems more robust than VariableDeclarator
|
|
||||||
const targetNode = getNodeFromPath<
|
|
||||||
VariableDeclaration | VariableDeclaration[]
|
|
||||||
>(ast, targetNodePath, 'VariableDeclaration')
|
|
||||||
if (err(targetNode)) {
|
|
||||||
return new Error("Couldn't parse profile selection")
|
|
||||||
}
|
|
||||||
|
|
||||||
const targetDeclarator = isArray(targetNode.node)
|
|
||||||
? targetNode.node[0].declaration
|
|
||||||
: targetNode.node.declaration
|
|
||||||
|
|
||||||
// Find the trajectory (or path) declaration
|
|
||||||
const trajectoryNodePath = getNodePathFromSourceRange(
|
|
||||||
ast,
|
|
||||||
trajectory.graphSelections[0].codeRef.range
|
|
||||||
)
|
|
||||||
// Also looking for VariableDeclaration for consistency here
|
|
||||||
const trajectoryNode = getNodeFromPath<VariableDeclaration>(
|
|
||||||
ast,
|
|
||||||
trajectoryNodePath,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(trajectoryNode)) {
|
|
||||||
return new Error("Couldn't parse path selection")
|
|
||||||
}
|
|
||||||
|
|
||||||
const trajectoryDeclarator = trajectoryNode.node.declaration
|
|
||||||
|
|
||||||
// Perform the sweep
|
|
||||||
const { modifiedAst, pathToNode } = addSweep({
|
|
||||||
node: ast,
|
|
||||||
targetDeclarator,
|
|
||||||
trajectoryDeclarator,
|
|
||||||
sectional,
|
|
||||||
variableName,
|
|
||||||
insertIndex,
|
|
||||||
})
|
|
||||||
await updateModelingState(
|
|
||||||
modifiedAst,
|
|
||||||
EXECUTION_TYPE_REAL,
|
|
||||||
{
|
|
||||||
kclManager,
|
|
||||||
editorManager,
|
|
||||||
codeManager,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
focusPath: [pathToNode],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
loftAstMod: fromPromise(
|
|
||||||
async ({
|
|
||||||
input,
|
|
||||||
}: {
|
|
||||||
input: ModelingCommandSchema['Loft'] | undefined
|
|
||||||
}) => {
|
|
||||||
if (!input) return new Error('No input provided')
|
|
||||||
// Extract inputs
|
|
||||||
const ast = kclManager.ast
|
|
||||||
const { selection } = input
|
|
||||||
const declarators = selection.graphSelections.flatMap((s) => {
|
|
||||||
const path = getNodePathFromSourceRange(ast, s?.codeRef.range)
|
|
||||||
const nodeFromPath = getNodeFromPath<VariableDeclarator>(
|
|
||||||
ast,
|
|
||||||
path,
|
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
|
||||||
return err(nodeFromPath) ? [] : nodeFromPath.node
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: add better validation on selection
|
|
||||||
if (!(declarators && declarators.length > 1)) {
|
|
||||||
trap('Not enough sketches selected')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform the loft
|
|
||||||
const loftSketchesRes = loftSketches(ast, declarators)
|
|
||||||
await updateModelingState(
|
|
||||||
loftSketchesRes.modifiedAst,
|
|
||||||
EXECUTION_TYPE_REAL,
|
|
||||||
{
|
|
||||||
kclManager,
|
|
||||||
editorManager,
|
|
||||||
codeManager,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
focusPath: [loftSketchesRes.pathToNode],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
shellAstMod: fromPromise(
|
shellAstMod: fromPromise(
|
||||||
async ({
|
async ({
|
||||||
input,
|
input,
|
||||||
|
Reference in New Issue
Block a user