Compare commits
17 Commits
nested_dir
...
max/undele
Author | SHA1 | Date | |
---|---|---|---|
792a101a2f | |||
b0371ccc20 | |||
f5fdad05a4 | |||
19d4ad6332 | |||
66fba3b6fb | |||
dc49105606 | |||
4434e8abbe | |||
1da7d09ab3 | |||
a2343fcfda | |||
f6a02357ca | |||
6875ad5022 | |||
d2c5e7d6ce | |||
51e1e940d3 | |||
17d0f8cf30 | |||
8ccc5653ab | |||
0c0165d515 | |||
168672588d |
@ -2388,6 +2388,7 @@ fillet001 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
// Code samples
|
||||
const initialCode = `sketch001 = startSketchOn(XY)
|
||||
@ -2401,14 +2402,14 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
|> fillet(radius = 5, tags = [seg01]) // fillet01
|
||||
|> fillet(radius = 5, tags = [seg02]) // fillet02
|
||||
fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])
|
||||
fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
`
|
||||
const pipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
|
||||
const firstPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg01])'
|
||||
const secondPipedFilletDeclaration = 'fillet(radius = 5, tags = [seg02])'
|
||||
const standaloneFilletDeclaration =
|
||||
const standaloneAssignedFilletDeclaration =
|
||||
'fillet03 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg01)])'
|
||||
const secondStandaloneFilletDeclaration =
|
||||
'fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])'
|
||||
const standaloneUnassignedFilletDeclaration =
|
||||
'fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])'
|
||||
|
||||
// Locators
|
||||
const pipedFilletEdgeLocation = { x: 600, y: 193 }
|
||||
@ -2430,6 +2431,7 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
}, initialCode)
|
||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||
await homePage.goToModelingScene()
|
||||
await scene.settled(cmdBar)
|
||||
|
||||
// verify modeling scene is loaded
|
||||
await scene.expectPixelColor(
|
||||
@ -2446,15 +2448,19 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
await test.step('Delete fillet via feature tree selection', async () => {
|
||||
await test.step('Open Feature Tree Pane', async () => {
|
||||
await toolbar.openPane('feature-tree')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
|
||||
await test.step('Delete piped fillet via feature tree selection', async () => {
|
||||
await test.step('Verify all fillets are present in the editor', async () => {
|
||||
await editor.expectEditor.toContain(pipedFilletDeclaration)
|
||||
await editor.expectEditor.toContain(firstPipedFilletDeclaration)
|
||||
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||
await editor.expectEditor.toContain(standaloneFilletDeclaration)
|
||||
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneAssignedFilletDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneUnassignedFilletDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify test fillets are present in the scene', async () => {
|
||||
await scene.expectPixelColor(
|
||||
@ -2475,13 +2481,17 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify piped fillet is deleted but other fillets are not (in the editor)', async () => {
|
||||
await editor.expectEditor.not.toContain(pipedFilletDeclaration)
|
||||
await editor.expectEditor.not.toContain(firstPipedFilletDeclaration)
|
||||
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||
await editor.expectEditor.toContain(standaloneFilletDeclaration)
|
||||
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneAssignedFilletDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneUnassignedFilletDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify piped fillet is deleted but non-piped is not (in the scene)', async () => {
|
||||
await scene.expectPixelColor(
|
||||
@ -2497,22 +2507,51 @@ fillet04 = fillet(extrude001, radius = 5, tags = [getOppositeEdge(seg02)])
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Delete non-piped fillet via feature tree selection', async () => {
|
||||
await test.step('Delete non-piped fillet', async () => {
|
||||
await test.step('Delete standalone assigned fillet via feature tree selection', async () => {
|
||||
await test.step('Delete standalone assigned fillet', async () => {
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Fillet',
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify non-piped fillet is deleted but other two fillets are not (in the editor)', async () => {
|
||||
await test.step('Verify standalone assigned fillet is deleted but other two fillets are not (in the editor)', async () => {
|
||||
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||
await editor.expectEditor.not.toContain(standaloneFilletDeclaration)
|
||||
await editor.expectEditor.toContain(secondStandaloneFilletDeclaration)
|
||||
await editor.expectEditor.not.toContain(
|
||||
standaloneAssignedFilletDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneUnassignedFilletDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify non-piped fillet is deleted but piped is not (in the scene)', async () => {
|
||||
await test.step('Verify standalone assigned fillet is deleted but piped is not (in the scene)', async () => {
|
||||
await scene.expectPixelColor(
|
||||
edgeColorWhite,
|
||||
standaloneFilletEdgeLocation,
|
||||
lowTolerance
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Delete standalone unassigned fillet via feature tree selection', async () => {
|
||||
await test.step('Delete standalone unassigned fillet', async () => {
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Fillet',
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify standalone unassigned fillet is deleted but other fillet is not (in the editor)', async () => {
|
||||
await editor.expectEditor.toContain(secondPipedFilletDeclaration)
|
||||
await editor.expectEditor.not.toContain(
|
||||
standaloneUnassignedFilletDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify standalone unassigned fillet is deleted but piped is not (in the scene)', async () => {
|
||||
await scene.expectPixelColor(
|
||||
edgeColorWhite,
|
||||
standaloneFilletEdgeLocation,
|
||||
@ -2964,14 +3003,14 @@ extrude001 = extrude(sketch001, length = -12)
|
||||
|> chamfer(length = 5, tags = [seg01]) // chamfer01
|
||||
|> chamfer(length = 5, tags = [seg02]) // chamfer02
|
||||
chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])
|
||||
chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
`
|
||||
const pipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
|
||||
const firstPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg01])'
|
||||
const secondPipedChamferDeclaration = 'chamfer(length = 5, tags = [seg02])'
|
||||
const standaloneChamferDeclaration =
|
||||
const standaloneAssignedChamferDeclaration =
|
||||
'chamfer03 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg01)])'
|
||||
const secondStandaloneChamferDeclaration =
|
||||
'chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])'
|
||||
const standaloneUnassignedChamferDeclaration =
|
||||
'chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])'
|
||||
|
||||
// Locators
|
||||
const pipedChamferEdgeLocation = { x: 600, y: 193 }
|
||||
@ -3010,16 +3049,18 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
await test.step('Delete chamfer via feature tree selection', async () => {
|
||||
await test.step('Open Feature Tree Pane', async () => {
|
||||
await toolbar.openPane('feature-tree')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
|
||||
await test.step('Delete piped chamfer via feature tree selection', async () => {
|
||||
await test.step('Verify all chamfers are present in the editor', async () => {
|
||||
await editor.expectEditor.toContain(pipedChamferDeclaration)
|
||||
await editor.expectEditor.toContain(firstPipedChamferDeclaration)
|
||||
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||
await editor.expectEditor.toContain(standaloneChamferDeclaration)
|
||||
await editor.expectEditor.toContain(
|
||||
secondStandaloneChamferDeclaration
|
||||
standaloneAssignedChamferDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneUnassignedChamferDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify test chamfers are present in the scene', async () => {
|
||||
@ -3041,14 +3082,16 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify piped chamfer is deleted but other chamfers are not (in the editor)', async () => {
|
||||
await editor.expectEditor.not.toContain(pipedChamferDeclaration)
|
||||
await editor.expectEditor.not.toContain(firstPipedChamferDeclaration)
|
||||
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||
await editor.expectEditor.toContain(standaloneChamferDeclaration)
|
||||
await editor.expectEditor.toContain(
|
||||
secondStandaloneChamferDeclaration
|
||||
standaloneAssignedChamferDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
standaloneUnassignedChamferDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify piped chamfer is deleted but non-piped is not (in the scene)', async () => {
|
||||
@ -3065,24 +3108,51 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Delete non-piped chamfer via feature tree selection', async () => {
|
||||
await test.step('Delete non-piped chamfer', async () => {
|
||||
await test.step('Delete standalone assigned chamfer via feature tree selection', async () => {
|
||||
await test.step('Delete standalone assigned chamfer', async () => {
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Chamfer',
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await page.waitForTimeout(500)
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify non-piped chamfer is deleted but other two chamfers are not (in the editor)', async () => {
|
||||
await test.step('Verify standalone assigned chamfer is deleted but other two chamfers are not (in the editor)', async () => {
|
||||
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||
await editor.expectEditor.not.toContain(standaloneChamferDeclaration)
|
||||
await editor.expectEditor.not.toContain(
|
||||
standaloneAssignedChamferDeclaration
|
||||
)
|
||||
await editor.expectEditor.toContain(
|
||||
secondStandaloneChamferDeclaration
|
||||
standaloneUnassignedChamferDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify non-piped chamfer is deleted but piped is not (in the scene)', async () => {
|
||||
await test.step('Verify standalone assigned chamfer is deleted but piped is not (in the scene)', async () => {
|
||||
await scene.expectPixelColor(
|
||||
edgeColorWhite,
|
||||
standaloneChamferEdgeLocation,
|
||||
lowTolerance
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
await test.step('Delete standalone unassigned chamfer via feature tree selection', async () => {
|
||||
await test.step('Delete standalone unassigned chamfer', async () => {
|
||||
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||
'Chamfer',
|
||||
1
|
||||
)
|
||||
await operationButton.click({ button: 'left' })
|
||||
await page.keyboard.press('Delete')
|
||||
await scene.settled(cmdBar)
|
||||
})
|
||||
await test.step('Verify standalone unassigned chamfer is deleted but piped chamfer is not (in the editor)', async () => {
|
||||
await editor.expectEditor.toContain(secondPipedChamferDeclaration)
|
||||
await editor.expectEditor.not.toContain(
|
||||
standaloneUnassignedChamferDeclaration
|
||||
)
|
||||
})
|
||||
await test.step('Verify standalone unassigned chamfer is deleted but piped is not (in the scene)', async () => {
|
||||
await scene.expectPixelColor(
|
||||
edgeColorWhite,
|
||||
standaloneChamferEdgeLocation,
|
||||
|
@ -64,7 +64,7 @@ import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
|
||||
import type { DefaultPlaneStr } from '@src/lib/planes'
|
||||
|
||||
import { err, trap } from '@src/lib/trap'
|
||||
import { isOverlap, roundOff } from '@src/lib/utils'
|
||||
import { isArray, isOverlap, roundOff } from '@src/lib/utils'
|
||||
import type { ExtrudeFacePlane } from '@src/machines/modelingMachine'
|
||||
import { ARG_AT } from '@src/lang/constants'
|
||||
|
||||
@ -949,6 +949,27 @@ export function deleteSegmentFromPipeExpression(
|
||||
return _modifiedAst
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a standalone top level statement from the AST
|
||||
* Used for removing both unassigned statements and variable declarations
|
||||
*
|
||||
* @param ast The AST to modify
|
||||
* @param pathToNode The path to the node to delete
|
||||
*/
|
||||
export function deleteTopLevelStatement(
|
||||
ast: Node<Program>,
|
||||
pathToNode: PathToNode
|
||||
): Error | void {
|
||||
const pathStep = pathToNode[1]
|
||||
if (!isArray(pathStep) || typeof pathStep[0] !== 'number') {
|
||||
return new Error(
|
||||
'Invalid pathToNode structure: expected a number at path[1][0]'
|
||||
)
|
||||
}
|
||||
const statementIndex: number = pathStep[0]
|
||||
ast.body.splice(statementIndex, 1)
|
||||
}
|
||||
|
||||
export function removeSingleConstraintInfo(
|
||||
pathToCallExp: PathToNode,
|
||||
argDetails: SimplifiedArgDetails,
|
||||
|
@ -801,7 +801,7 @@ extrude001 = extrude(sketch001, length = -15)`
|
||||
expectedCode
|
||||
)
|
||||
}, 10_000)
|
||||
it(`should delete a non-piped ${edgeTreatmentType} from a single segment`, async () => {
|
||||
it(`should delete a standalone assigned ${edgeTreatmentType} from a single segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [-10, 10])
|
||||
|> line(end = [20, 0])
|
||||
@ -810,8 +810,34 @@ extrude001 = extrude(sketch001, length = -15)`
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -15)
|
||||
fillet001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
|
||||
const edgeTreatmentSnippet = `fillet001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
|
||||
${edgeTreatmentType}001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
|
||||
const edgeTreatmentSnippet = `${edgeTreatmentType}001 = ${edgeTreatmentType}(extrude001, ${parameterName} = 3, tags = [seg01])`
|
||||
const expectedCode = `sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [-10, 10])
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, -20])
|
||||
|> line(end = [-20, 0], tag = $seg01)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -15)`
|
||||
|
||||
await runDeleteEdgeTreatmentTest(
|
||||
code,
|
||||
edgeTreatmentSnippet,
|
||||
expectedCode
|
||||
)
|
||||
}, 10_000)
|
||||
it(`should delete a standalone ${edgeTreatmentType} without assignment from a single segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [-10, 10])
|
||||
|> line(end = [20, 0])
|
||||
|> line(end = [0, -20])
|
||||
|> line(end = [-20, 0], tag = $seg01)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(sketch001, length = -15)
|
||||
${edgeTreatmentType}(extrude001, ${parameterName} = 5, tags = [seg01])`
|
||||
const edgeTreatmentSnippet = `${edgeTreatmentType}(extrude001, ${parameterName} = 5, tags = [seg01])`
|
||||
const expectedCode = `sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [-10, 10])
|
||||
|> line(end = [20, 0])
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
getNodeFromPath,
|
||||
hasSketchPipeBeenExtruded,
|
||||
traverse,
|
||||
locateVariableWithCallOrPipe,
|
||||
} from '@src/lang/queryAst'
|
||||
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
||||
import type { Artifact } from '@src/lang/std/artifactGraph'
|
||||
@ -26,25 +27,25 @@ import {
|
||||
sketchLineHelperMapKw,
|
||||
} from '@src/lang/std/sketch'
|
||||
import { findKwArg } from '@src/lang/util'
|
||||
import type {
|
||||
ArtifactGraph,
|
||||
CallExpressionKw,
|
||||
Expr,
|
||||
ObjectExpression,
|
||||
PathToNode,
|
||||
PipeExpression,
|
||||
Program,
|
||||
VariableDeclaration,
|
||||
VariableDeclarator,
|
||||
import {
|
||||
type ArtifactGraph,
|
||||
type CallExpressionKw,
|
||||
type Expr,
|
||||
type ObjectExpression,
|
||||
type PathToNode,
|
||||
type Program,
|
||||
type VariableDeclarator,
|
||||
type ExpressionStatement,
|
||||
} from '@src/lang/wasm'
|
||||
import type { KclCommandValue } from '@src/lib/commandTypes'
|
||||
import type { Selection, Selections } from '@src/lib/selections'
|
||||
import { err } from '@src/lib/trap'
|
||||
import { isArray } from '@src/lib/utils'
|
||||
import {
|
||||
createTagExpressions,
|
||||
modifyAstWithTagsForSelection,
|
||||
} from '@src/lang/modifyAst/tagManagement'
|
||||
import { deleteNodeInExtrudePipe } from '@src/lang/modifyAst/deleteNodeInExtrudePipe'
|
||||
import { deleteTopLevelStatement } from '@src/lang/modifyAst'
|
||||
|
||||
// Edge Treatment Types
|
||||
export enum EdgeTreatmentType {
|
||||
@ -164,12 +165,12 @@ export async function modifyAstWithEdgeTreatmentAndTag(
|
||||
)
|
||||
|
||||
// Locate the extrude call
|
||||
const locatedExtrudeDeclarator = locateExtrudeDeclarator(
|
||||
const locatedExtrudeDeclarator = locateVariableWithCallOrPipe(
|
||||
clonedAst,
|
||||
pathToExtrudeNode
|
||||
)
|
||||
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
||||
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
||||
const { variableDeclarator } = locatedExtrudeDeclarator
|
||||
|
||||
// Modify the extrude expression to include this edge treatment expression
|
||||
// CallExpression - no edge treatment
|
||||
@ -177,33 +178,33 @@ export async function modifyAstWithEdgeTreatmentAndTag(
|
||||
|
||||
let pathToEdgeTreatmentNode: PathToNode
|
||||
|
||||
if (extrudeDeclarator.init.type === 'CallExpressionKw') {
|
||||
if (variableDeclarator.init.type === 'CallExpressionKw') {
|
||||
// 1. case when no edge treatment exists
|
||||
|
||||
// modify ast with new edge treatment call by mutating the extrude node
|
||||
extrudeDeclarator.init = createPipeExpression([
|
||||
extrudeDeclarator.init,
|
||||
variableDeclarator.init = createPipeExpression([
|
||||
variableDeclarator.init,
|
||||
edgeTreatmentCall,
|
||||
])
|
||||
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
variableDeclarator,
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
||||
} else if (variableDeclarator.init.type === 'PipeExpression') {
|
||||
// 2. case when edge treatment exists or extrude in sketch pipe
|
||||
|
||||
// mutate the extrude node with the new edge treatment call
|
||||
extrudeDeclarator.init.body.push(edgeTreatmentCall)
|
||||
variableDeclarator.init.body.push(edgeTreatmentCall)
|
||||
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
variableDeclarator,
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
@ -330,38 +331,6 @@ export function getEdgeTagCall(
|
||||
return tagCall
|
||||
}
|
||||
|
||||
export function locateExtrudeDeclarator(
|
||||
node: Program,
|
||||
pathToExtrudeNode: PathToNode
|
||||
): { extrudeDeclarator: VariableDeclarator; shallowPath: PathToNode } | Error {
|
||||
const nodeOfExtrudeCall = getNodeFromPath<VariableDeclaration>(
|
||||
node,
|
||||
pathToExtrudeNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(nodeOfExtrudeCall)) return nodeOfExtrudeCall
|
||||
|
||||
const { node: extrudeVarDecl } = nodeOfExtrudeCall
|
||||
const extrudeDeclarator = extrudeVarDecl.declaration
|
||||
if (!extrudeDeclarator) {
|
||||
return new Error('Extrude Declarator not found.')
|
||||
}
|
||||
|
||||
const extrudeInit = extrudeDeclarator?.init
|
||||
if (!extrudeInit) {
|
||||
return new Error('Extrude Init not found.')
|
||||
}
|
||||
|
||||
if (
|
||||
extrudeInit.type !== 'CallExpressionKw' &&
|
||||
extrudeInit.type !== 'PipeExpression'
|
||||
) {
|
||||
return new Error('Extrude must be a PipeExpression or CallExpressionKw')
|
||||
}
|
||||
|
||||
return { extrudeDeclarator, shallowPath: nodeOfExtrudeCall.shallowPath }
|
||||
}
|
||||
|
||||
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode: PathToNode,
|
||||
extrudeDeclarator: VariableDeclarator,
|
||||
@ -484,7 +453,7 @@ function getParameterNameAndValue(
|
||||
: parameters.length.valueAst
|
||||
return { parameterName: 'length', parameterValue }
|
||||
} else {
|
||||
return new Error('Unsupported edge treatment type}')
|
||||
return new Error('Unsupported edge treatment type')
|
||||
}
|
||||
}
|
||||
|
||||
@ -614,14 +583,11 @@ export async function deleteEdgeTreatment(
|
||||
selection: Selection
|
||||
): Promise<Node<Program> | Error> {
|
||||
/**
|
||||
* Deletes an edge treatment (fillet or chamfer)
|
||||
* from the AST based on the selection.
|
||||
* Handles both standalone treatments
|
||||
* and those within a PipeExpression.
|
||||
* Deletes an edge treatment (fillet or chamfer) from the AST
|
||||
*
|
||||
* Supported cases:
|
||||
* [+] fillet and chamfer
|
||||
* [+] piped and non-piped edge treatments
|
||||
* [+] piped, standalone (assigned and unassigned) edge treatments
|
||||
* [-] delete single tag from array of tags (currently whole expression is deleted)
|
||||
* [-] multiple selections with different edge treatments (currently single selection is supported)
|
||||
*/
|
||||
@ -632,119 +598,49 @@ export async function deleteEdgeTreatment(
|
||||
return new Error('Selection is not an edge cut')
|
||||
}
|
||||
|
||||
const { subType: edgeTreatmentType } = artifact
|
||||
if (
|
||||
!edgeTreatmentType ||
|
||||
!['fillet', 'chamfer'].includes(edgeTreatmentType)
|
||||
) {
|
||||
const { subType } = artifact
|
||||
if (!isEdgeTreatmentType(subType)) {
|
||||
return new Error('Unsupported or missing edge treatment type')
|
||||
}
|
||||
|
||||
// 2. Clone ast and retrieve the VariableDeclarator
|
||||
// 2. Clone ast and retrieve the edge treatment node
|
||||
const astClone = structuredClone(ast)
|
||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
selection?.codeRef?.pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(varDec)) return varDec
|
||||
const edgeTreatmentNode = getNodeFromPath<
|
||||
VariableDeclarator | ExpressionStatement
|
||||
>(astClone, selection?.codeRef?.pathToNode, [
|
||||
'VariableDeclarator',
|
||||
'ExpressionStatement',
|
||||
])
|
||||
if (err(edgeTreatmentNode)) return edgeTreatmentNode
|
||||
|
||||
// 3: Check if edge treatment is in a pipe
|
||||
const inPipe = varDec.node.init.type === 'PipeExpression'
|
||||
// 3: Delete edge treatments
|
||||
// There 3 possible cases:
|
||||
// - piped: const body = extrude(...) |> fillet(...)
|
||||
// - assigned to variables: fillet0001 = fillet(...)
|
||||
// - unassigned standalone statements: fillet(...)
|
||||
// piped and assigned nodes are in the variable declarator
|
||||
// unassigned nodes are in the expression statement
|
||||
|
||||
// 4A. Handle standalone edge treatment
|
||||
if (!inPipe) {
|
||||
const varDecPathStep = varDec.shallowPath[1]
|
||||
|
||||
if (!isArray(varDecPathStep) || typeof varDecPathStep[0] !== 'number') {
|
||||
return new Error(
|
||||
'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
|
||||
)
|
||||
}
|
||||
|
||||
const varDecIndex: number = varDecPathStep[0]
|
||||
|
||||
// Remove entire VariableDeclarator from the ast
|
||||
astClone.body.splice(varDecIndex, 1)
|
||||
return astClone
|
||||
}
|
||||
|
||||
// 4B. Handle edge treatment within pipe
|
||||
if (inPipe) {
|
||||
// Retrieve the CallExpression path
|
||||
const callExp =
|
||||
getNodeFromPath<CallExpressionKw>(
|
||||
ast,
|
||||
selection?.codeRef?.pathToNode,
|
||||
'CallExpressionKw'
|
||||
) ?? null
|
||||
if (err(callExp)) return callExp
|
||||
|
||||
const shallowPath = callExp.shallowPath
|
||||
|
||||
// Initialize variables to hold the PipeExpression path and callIndex
|
||||
let pipeExpressionPath: PathToNode | null = null
|
||||
let callIndex: number | null = null
|
||||
|
||||
// Iterate through the shallowPath to find the PipeExpression and callIndex
|
||||
for (let i = 0; i < shallowPath.length - 1; i++) {
|
||||
const [key, value] = shallowPath[i]
|
||||
|
||||
if (key === 'body' && value === 'PipeExpression') {
|
||||
pipeExpressionPath = shallowPath.slice(0, i + 1)
|
||||
|
||||
const nextStep = shallowPath[i + 1]
|
||||
if (
|
||||
nextStep &&
|
||||
nextStep[1] === 'index' &&
|
||||
typeof nextStep[0] === 'number'
|
||||
edgeTreatmentNode.node.type === 'ExpressionStatement' || // unassigned
|
||||
(edgeTreatmentNode.node.type === 'VariableDeclarator' && // assigned
|
||||
edgeTreatmentNode.node.init?.type !== 'PipeExpression')
|
||||
) {
|
||||
callIndex = nextStep[0]
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!pipeExpressionPath) {
|
||||
return new Error('PipeExpression not found in path')
|
||||
}
|
||||
|
||||
if (callIndex === null) {
|
||||
return new Error('Failed to extract CallExpressionKw index')
|
||||
}
|
||||
// Retrieve the PipeExpression node
|
||||
const pipeExpressionNode = getNodeFromPath<PipeExpression>(
|
||||
// Handle both standalone cases (assigned and unassigned)
|
||||
const deleteResult = deleteTopLevelStatement(
|
||||
astClone,
|
||||
pipeExpressionPath,
|
||||
'PipeExpression'
|
||||
selection.codeRef.pathToNode
|
||||
)
|
||||
if (err(pipeExpressionNode)) return pipeExpressionNode
|
||||
|
||||
// Ensure that the PipeExpression.body is an array
|
||||
if (!isArray(pipeExpressionNode.node.body)) {
|
||||
return new Error('PipeExpression body is not an array')
|
||||
}
|
||||
|
||||
// Remove the CallExpression at the specified index
|
||||
pipeExpressionNode.node.body.splice(callIndex, 1)
|
||||
|
||||
// Remove VariableDeclarator if PipeExpression.body is empty
|
||||
if (pipeExpressionNode.node.body.length === 0) {
|
||||
const varDecPathStep = varDec.shallowPath[1]
|
||||
if (!isArray(varDecPathStep) || typeof varDecPathStep[0] !== 'number') {
|
||||
return new Error(
|
||||
'Invalid shallowPath structure: expected a number at shallowPath[1][0]'
|
||||
if (err(deleteResult)) return deleteResult
|
||||
return astClone
|
||||
} else {
|
||||
const deleteResult = deleteNodeInExtrudePipe(
|
||||
astClone,
|
||||
selection.codeRef.pathToNode
|
||||
)
|
||||
}
|
||||
const varDecIndex: number = varDecPathStep[0]
|
||||
astClone.body.splice(varDecIndex, 1)
|
||||
}
|
||||
|
||||
if (err(deleteResult)) return deleteResult
|
||||
return astClone
|
||||
}
|
||||
|
||||
return Error('Delete fillets not implemented')
|
||||
}
|
||||
|
||||
// Edit Edge Treatment
|
||||
@ -786,7 +682,7 @@ export async function editEdgeTreatment(
|
||||
edgeTreatmentCall.node.arguments[index] = newArg
|
||||
}
|
||||
|
||||
let pathToEdgeTreatmentNode = selection?.codeRef?.pathToNode
|
||||
const pathToEdgeTreatmentNode = selection?.codeRef?.pathToNode
|
||||
|
||||
return { modifiedAst, pathToEdgeTreatmentNode }
|
||||
}
|
||||
|
@ -1,26 +1,26 @@
|
||||
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
||||
|
||||
import type { PathToNode, Program } from '@src/lang/wasm'
|
||||
import { locateExtrudeDeclarator } from '@src/lang/modifyAst/addEdgeTreatment'
|
||||
import { locateVariableWithCallOrPipe } from '@src/lang/queryAst'
|
||||
import { err } from '@src/lib/trap'
|
||||
|
||||
export function deleteNodeInExtrudePipe(
|
||||
node: PathToNode,
|
||||
ast: Node<Program>
|
||||
ast: Node<Program>,
|
||||
node: PathToNode
|
||||
): Error | void {
|
||||
const pipeIndex = node.findIndex(([_, type]) => type === 'PipeExpression') + 1
|
||||
if (!(node[pipeIndex][0] && typeof node[pipeIndex][0] === 'number')) {
|
||||
return new Error("Couldn't find node to delete in ast")
|
||||
}
|
||||
|
||||
const lookup = locateExtrudeDeclarator(ast, node)
|
||||
const lookup = locateVariableWithCallOrPipe(ast, node)
|
||||
if (err(lookup)) {
|
||||
return lookup
|
||||
}
|
||||
|
||||
if (lookup.extrudeDeclarator.init.type !== 'PipeExpression') {
|
||||
if (lookup.variableDeclarator.init.type !== 'PipeExpression') {
|
||||
return new Error("Couldn't find node to delete in looked up extrusion")
|
||||
}
|
||||
|
||||
lookup.extrudeDeclarator.init.body.splice(node[pipeIndex][0], 1)
|
||||
lookup.variableDeclarator.init.body.splice(node[pipeIndex][0], 1)
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
createPipeExpression,
|
||||
createPipeSubstitution,
|
||||
} from '@src/lang/create'
|
||||
import { locateExtrudeDeclarator } from '@src/lang/modifyAst/addEdgeTreatment'
|
||||
import { locateVariableWithCallOrPipe } from '@src/lang/queryAst'
|
||||
import type { PathToNode, Program } from '@src/lang/wasm'
|
||||
import { COMMAND_APPEARANCE_COLOR_DEFAULT } from '@src/lib/commandBarConfigs/modelingCommandConfig'
|
||||
import { err } from '@src/lib/trap'
|
||||
@ -23,13 +23,13 @@ export function setAppearance({
|
||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||
const modifiedAst = structuredClone(ast)
|
||||
|
||||
// Locate the call (not necessarily an extrude here)
|
||||
const result = locateExtrudeDeclarator(modifiedAst, nodeToEdit)
|
||||
// Locate the call
|
||||
const result = locateVariableWithCallOrPipe(modifiedAst, nodeToEdit)
|
||||
if (err(result)) {
|
||||
return result
|
||||
}
|
||||
|
||||
const declarator = result.extrudeDeclarator
|
||||
const declarator = result.variableDeclarator
|
||||
const call = createCallExpressionStdLibKw(
|
||||
'appearance',
|
||||
createPipeSubstitution(),
|
||||
|
@ -1175,6 +1175,41 @@ export function getSketchSelectionsFromOperation(
|
||||
}
|
||||
}
|
||||
|
||||
export function locateVariableWithCallOrPipe(
|
||||
ast: Program,
|
||||
pathToNode: PathToNode
|
||||
): { variableDeclarator: VariableDeclarator; shallowPath: PathToNode } | Error {
|
||||
const variableDeclarationNode = getNodeFromPath<VariableDeclaration>(
|
||||
ast,
|
||||
pathToNode,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(variableDeclarationNode)) return variableDeclarationNode
|
||||
|
||||
const { node: variableDecl } = variableDeclarationNode
|
||||
const variableDeclarator = variableDecl.declaration
|
||||
if (!variableDeclarator) {
|
||||
return new Error('Variable Declarator not found.')
|
||||
}
|
||||
|
||||
const initializer = variableDeclarator?.init
|
||||
if (!initializer) {
|
||||
return new Error('Initializer not found.')
|
||||
}
|
||||
|
||||
if (
|
||||
initializer.type !== 'CallExpressionKw' &&
|
||||
initializer.type !== 'PipeExpression'
|
||||
) {
|
||||
return new Error('Initializer must be a PipeExpression or CallExpressionKw')
|
||||
}
|
||||
|
||||
return {
|
||||
variableDeclarator,
|
||||
shallowPath: variableDeclarationNode.shallowPath,
|
||||
}
|
||||
}
|
||||
|
||||
export function findImportNodeAndAlias(
|
||||
ast: Node<Program>,
|
||||
pathToNode: PathToNode
|
||||
|
Reference in New Issue
Block a user