3036 tests add fillet (#3530)

* addFillet.ts - refactor existing code

* move logic from modelingMachine to addFillet

* rename getPathForSelection into getPathToExtrudeForSegmentSelection

* stuck with kclManager

* stuck 2

* remove engineless exe from fillet test

* pathToExtrudeNode properly tested

* resolve conflicts

* engine initialization update

* cleanup comments

* passed ExecuteArgs instead of Program to executeAst

* afterAll engineCommandManager.tearDown

* resolve conflicts

* mutateAstForRadiusInsertion

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* save banner from hulk mutations

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* sweet errors

* purging the as

* make type of getNodeFromPath safe again

* as cleaning part 2

* cleared mutation logic

* last bits

* make the linter happy

---------

Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
max
2024-08-26 08:07:20 +02:00
committed by GitHub
parent 199722c505
commit c2e9d18f92
4 changed files with 528 additions and 198 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

@ -6,9 +6,13 @@ import {
Expr,
Program,
CallExpression,
makeDefaultPlanes,
PipeExpression,
VariableDeclaration,
} from '../wasm'
import {
addFillet,
getPathToExtrudeForSegmentSelection,
hasValidFilletSelection,
isTagUsedInFillet,
} from './addFillet'
@ -16,9 +20,204 @@ import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
import { createLiteral } from 'lang/modifyAst'
import { err } from 'lib/trap'
import { Selections } from 'lib/selections'
import { engineCommandManager, kclManager } from 'lib/singletons'
import { VITE_KC_DEV_TOKEN } from 'env'
beforeAll(async () => {
await initPromise // Initialize the WASM environment before running tests
await initPromise
// THESE TEST WILL FAIL without VITE_KC_DEV_TOKEN set in .env.development.local
await new Promise((resolve) => {
engineCommandManager.start({
token: VITE_KC_DEV_TOKEN,
width: 256,
height: 256,
makeDefaultPlanes: () => makeDefaultPlanes(engineCommandManager),
setMediaStream: () => {},
setIsStreamReady: () => {},
modifyGrid: async () => {},
callbackOnEngineLiteConnect: async () => {
resolve(true)
},
})
})
}, 20_000)
afterAll(() => {
engineCommandManager.tearDown()
})
const runGetPathToExtrudeForSegmentSelectionTest = async (
code: string,
selectedSegmentSnippet: string,
expectedExtrudeSnippet: string
) => {
// helpers
function getExtrudeExpression(
ast: Program,
pathToExtrudeNode: PathToNode
): CallExpression | PipeExpression | undefined | Error {
if (pathToExtrudeNode.length === 0) return undefined // no extrude node
const extrudeNodeResult = getNodeFromPath(ast, pathToExtrudeNode)
if (err(extrudeNodeResult)) {
return extrudeNodeResult
}
return extrudeNodeResult.node as CallExpression | PipeExpression
}
function getExpectedExtrudeExpression(
ast: Program,
code: string,
expectedExtrudeSnippet: string
): CallExpression | PipeExpression | Error {
const extrudeRange: [number, number] = [
code.indexOf(expectedExtrudeSnippet),
code.indexOf(expectedExtrudeSnippet) + expectedExtrudeSnippet.length,
]
const expedtedExtrudePath = getNodePathFromSourceRange(ast, extrudeRange)
const expedtedExtrudeNodeResult = getNodeFromPath(ast, expedtedExtrudePath)
if (err(expedtedExtrudeNodeResult)) {
return expedtedExtrudeNodeResult
}
const expectedExtrudeNode =
expedtedExtrudeNodeResult.node as VariableDeclaration
return expectedExtrudeNode.declarations[0].init as
| CallExpression
| PipeExpression
}
// ast
const astOrError = parse(code)
if (err(astOrError)) return new Error('AST not found')
const ast = astOrError as Program
// selection
const segmentRange: [number, number] = [
code.indexOf(selectedSegmentSnippet),
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length,
]
const selection: Selections = {
codeBasedSelections: [
{
range: segmentRange,
type: 'default',
},
],
otherSelections: [],
}
// programMemory and artifactGraph
await kclManager.executeAst({ ast })
const programMemory = kclManager.programMemory
const artifactGraph = engineCommandManager.artifactGraph
// get extrude expression
const pathResult = getPathToExtrudeForSegmentSelection(
ast,
selection,
programMemory,
artifactGraph
)
if (err(pathResult)) return pathResult
const { pathToExtrudeNode } = pathResult
const extrudeExpression = getExtrudeExpression(ast, pathToExtrudeNode)
// test
if (expectedExtrudeSnippet) {
const expectedExtrudeExpression = getExpectedExtrudeExpression(
ast,
code,
expectedExtrudeSnippet
)
if (err(expectedExtrudeExpression)) return expectedExtrudeExpression
expect(extrudeExpression).toEqual(expectedExtrudeExpression)
} else {
expect(extrudeExpression).toBeUndefined()
}
}
describe('Testing getPathToExtrudeForSegmentSelection', () => {
it('should return the correct paths for a valid selection and extrusion', async () => {
const code = `const sketch001 = startSketchOn('XY')
|> startProfileAt([-10, 10], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(-15, sketch001)`
const selectedSegmentSnippet = `line([20, 0], %)`
const expectedExtrudeSnippet = `const extrude001 = extrude(-15, sketch001)`
await runGetPathToExtrudeForSegmentSelectionTest(
code,
selectedSegmentSnippet,
expectedExtrudeSnippet
)
})
it('should return the correct paths for a valid selection and extrusion in case of several extrusions and sketches', async () => {
const code = `const sketch001 = startSketchOn('XY')
|> startProfileAt([-30, 30], %)
|> line([15, 0], %)
|> line([0, -15], %)
|> line([-15, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const sketch002 = startSketchOn('XY')
|> startProfileAt([30, 30], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const sketch003 = startSketchOn('XY')
|> startProfileAt([30, -30], %)
|> line([25, 0], %)
|> line([0, -25], %)
|> line([-25, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(-15, sketch001)
const extrude002 = extrude(-15, sketch002)
const extrude003 = extrude(-15, sketch003)`
const selectedSegmentSnippet = `line([20, 0], %)`
const expectedExtrudeSnippet = `const extrude002 = extrude(-15, sketch002)`
await runGetPathToExtrudeForSegmentSelectionTest(
code,
selectedSegmentSnippet,
expectedExtrudeSnippet
)
})
it('should not return any path for missing extrusion', async () => {
const code = `const sketch001 = startSketchOn('XY')
|> startProfileAt([-30, 30], %)
|> line([15, 0], %)
|> line([0, -15], %)
|> line([-15, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const sketch002 = startSketchOn('XY')
|> startProfileAt([30, 30], %)
|> line([20, 0], %)
|> line([0, -20], %)
|> line([-20, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const sketch003 = startSketchOn('XY')
|> startProfileAt([30, -30], %)
|> line([25, 0], %)
|> line([0, -25], %)
|> line([-25, 0], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
const extrude001 = extrude(-15, sketch001)
const extrude003 = extrude(-15, sketch003)`
const selectedSegmentSnippet = `line([20, 0], %)`
const expectedExtrudeSnippet = ``
await runGetPathToExtrudeForSegmentSelectionTest(
code,
selectedSegmentSnippet,
expectedExtrudeSnippet
)
})
})
const runFilletTest = async (
@ -57,8 +256,6 @@ const runFilletTest = async (
return new Error('Path to extrude node not found')
}
// const radius = createLiteral(5) as Expr
const result = addFillet(ast, pathToSegmentNode, pathToExtrudeNode, radius)
if (err(result)) {
return result
@ -68,7 +265,6 @@ const runFilletTest = async (
expect(newCode).toContain(expectedCode)
}
describe('Testing addFillet', () => {
/**
* 1. Ideal Case

View File

@ -4,9 +4,11 @@ import {
ObjectExpression,
PathToNode,
Program,
ProgramMemory,
Expr,
VariableDeclaration,
VariableDeclarator,
sketchGroupFromKclValue,
} from '../wasm'
import {
createCallExpressionStdLib,
@ -28,62 +30,210 @@ import {
getTagFromCallExpression,
sketchLineHelperMap,
} from '../std/sketch'
import { err } from 'lib/trap'
import { err, trap } from 'lib/trap'
import { Selections, canFilletSelection } from 'lib/selections'
import { KclCommandValue } from 'lib/commandTypes'
import {
ArtifactGraph,
getExtrusionFromSuspectedPath,
} from 'lang/std/artifactGraph'
import { kclManager, engineCommandManager, editorManager } from 'lib/singletons'
export function addFillet(
node: Program,
pathToSegmentNode: PathToNode,
pathToExtrudeNode: PathToNode,
radius = createLiteral(5) as Expr
// shouldPipe = false, // TODO: Implement this feature
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error {
// clone ast to make mutations safe
let _node = structuredClone(node)
/**
* Add Tag to the Segment Expression
/**
* Apply Fillet To Selection
*/
// Find the specific sketch segment to tag with the new tag
const sketchSegmentChunk = getNodeFromPath(
_node,
export function applyFilletToSelection(
selection: Selections,
radius: KclCommandValue
): void | Error {
// 1. get AST
let ast = kclManager.ast
const astResult = insertRadiusIntoAst(ast, radius)
if (err(astResult)) return astResult
// 2. get path
const programMemory = kclManager.programMemory
const artifactGraph = engineCommandManager.artifactGraph
const getPathToExtrudeForSegmentSelectionResult =
getPathToExtrudeForSegmentSelection(
ast,
selection,
programMemory,
artifactGraph
)
if (err(getPathToExtrudeForSegmentSelectionResult))
return getPathToExtrudeForSegmentSelectionResult
const { pathToSegmentNode, pathToExtrudeNode } =
getPathToExtrudeForSegmentSelectionResult
// 3. add fillet
const addFilletResult = addFillet(
ast,
pathToSegmentNode,
pathToExtrudeNode,
'variableName' in radius ? radius.variableIdentifierAst : radius.valueAst
)
if (trap(addFilletResult)) return addFilletResult
const { modifiedAst, pathToFilletNode } = addFilletResult
// 4. update ast
updateAstAndFocus(modifiedAst, pathToFilletNode)
}
function insertRadiusIntoAst(
ast: Program,
radius: KclCommandValue
): { ast: Program } | Error {
try {
// Validate and update AST
if (
'variableName' in radius &&
radius.variableName &&
radius.insertIndex !== undefined
) {
const newAst = structuredClone(ast)
newAst.body.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
return { ast: newAst }
}
return { ast }
} catch (error) {
return new Error(`Failed to handle AST: ${(error as Error).message}`)
}
}
export function getPathToExtrudeForSegmentSelection(
ast: Program,
selection: Selections,
programMemory: ProgramMemory,
artifactGraph: ArtifactGraph
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
const pathToSegmentNode = getNodePathFromSourceRange(
ast,
selection.codeBasedSelections[0].range
)
const varDecNode = getNodeFromPath<VariableDeclaration>(
ast,
pathToSegmentNode,
'VariableDeclaration'
)
if (err(varDecNode)) return varDecNode
const sketchVar = varDecNode.node.declarations[0].id.name
const sketchGroup = sketchGroupFromKclValue(
kclManager.programMemory.get(sketchVar),
sketchVar
)
if (trap(sketchGroup)) return sketchGroup
const extrusion = getExtrusionFromSuspectedPath(sketchGroup.id, artifactGraph)
if (err(extrusion)) return extrusion
const pathToExtrudeNode = getNodePathFromSourceRange(
ast,
extrusion.codeRef.range
)
if (err(pathToExtrudeNode)) return pathToExtrudeNode
return { pathToSegmentNode, pathToExtrudeNode }
}
async function updateAstAndFocus(
modifiedAst: Program,
pathToFilletNode: PathToNode
) {
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: pathToFilletNode,
})
if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections)
}
}
/**
* Add Fillet
*/
export function addFillet(
ast: Program,
pathToSegmentNode: PathToNode,
pathToExtrudeNode: PathToNode,
radius: Expr = createLiteral(5)
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error {
// Clone AST to ensure safe mutations
const astClone = structuredClone(ast)
// Modify AST clone : TAG the sketch segment and retrieve tag
const segmentResult = mutateAstWithTagForSketchSegment(
astClone,
pathToSegmentNode
)
if (err(segmentResult)) return segmentResult
const { tag } = segmentResult
// Modify AST clone : Insert FILLET node and retrieve path to fillet
const filletResult = mutateAstWithFilletNode(
astClone,
pathToExtrudeNode,
radius,
tag
)
if (err(filletResult)) return filletResult
const { pathToFilletNode } = filletResult
return { modifiedAst: astClone, pathToFilletNode }
}
function mutateAstWithTagForSketchSegment(
astClone: Program,
pathToSegmentNode: PathToNode
): { modifiedAst: Program; tag: string } | Error {
const segmentNode = getNodeFromPath<CallExpression>(
astClone,
pathToSegmentNode,
'CallExpression'
)
if (err(sketchSegmentChunk)) return sketchSegmentChunk
const { node: sketchSegmentNode } = sketchSegmentChunk as {
node: CallExpression
}
if (err(segmentNode)) return segmentNode
// Check whether selection is a valid segment from sketchLineHelpersMap
if (!(sketchSegmentNode.callee.name in sketchLineHelperMap)) {
// Check whether selection is a valid segment
if (!(segmentNode.node.callee.name in sketchLineHelperMap)) {
return new Error('Selection is not a sketch segment')
}
// Add tag to the sketch segment or use existing tag
// a helper function that creates the updated node and applies the changes to the AST
const taggedSegment = addTagForSketchOnFace(
{
// previousProgramMemory: programMemory,
pathToNode: pathToSegmentNode,
node: _node,
node: astClone,
},
sketchSegmentNode.callee.name
segmentNode.node.callee.name
)
if (err(taggedSegment)) return taggedSegment
const { tag } = taggedSegment
/**
* Find Extrude Expression automatically
*/
return { modifiedAst: astClone, tag }
}
// 1. Get the sketch name
function mutateAstWithFilletNode(
astClone: Program,
pathToExtrudeNode: PathToNode,
radius: Expr,
tag: string
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error {
// Locate the extrude call
const locatedExtrudeDeclarator = locateExtrudeDeclarator(
astClone,
pathToExtrudeNode
)
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
const { extrudeDeclarator } = locatedExtrudeDeclarator
/**
* Add Fillet to the Extrude expression
* Prepare changes to the AST
*/
// Create the fillet call expression in one line
const filletCall = createCallExpressionStdLib('fillet', [
createObjectExpression({
radius: radius,
@ -92,65 +242,113 @@ export function addFillet(
createPipeSubstitution(),
])
// Locate the extrude call
const extrudeChunk = getNodeFromPath<VariableDeclaration>(
_node,
pathToExtrudeNode,
'VariableDeclaration'
)
if (err(extrudeChunk)) return extrudeChunk
const { node: extrudeVarDecl } = extrudeChunk
const extrudeDeclarator = extrudeVarDecl.declarations[0]
const extrudeInit = extrudeDeclarator.init
if (
!extrudeDeclarator ||
(extrudeInit.type !== 'CallExpression' &&
extrudeInit.type !== 'PipeExpression')
) {
return new Error('Extrude PipeExpression / CallExpression not found.')
}
// determine if extrude is in a PipeExpression or CallExpression
/**
* Mutate the AST
*/
// CallExpression - no fillet
// PipeExpression - fillet exists
const getPathToNodeOfFilletLiteral = (
let pathToFilletNode: PathToNode = []
if (extrudeDeclarator.init.type === 'CallExpression') {
// 1. case when no fillet exists
// modify ast with new fillet call by mutating the extrude node
extrudeDeclarator.init = createPipeExpression([
extrudeDeclarator.init,
filletCall,
])
// get path to the fillet node
pathToFilletNode = getPathToNodeOfFilletLiteral(
pathToExtrudeNode,
extrudeDeclarator,
tag
)
return { modifiedAst: astClone, pathToFilletNode }
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
// 2. case when fillet exists
const existingFilletCall = extrudeDeclarator.init.body.find((node) => {
return node.type === 'CallExpression' && node.callee.name === 'fillet'
})
if (!existingFilletCall || existingFilletCall.type !== 'CallExpression') {
return new Error('Fillet CallExpression not found.')
}
// check if the existing fillet has the same tag as the new fillet
const filletTag = getFilletTag(existingFilletCall)
if (filletTag !== tag) {
// mutate the extrude node with the new fillet call
extrudeDeclarator.init.body.push(filletCall)
return {
modifiedAst: astClone,
pathToFilletNode: getPathToNodeOfFilletLiteral(
pathToExtrudeNode,
extrudeDeclarator,
tag
),
}
}
} else {
return new Error('Unsupported extrude type.')
}
return { modifiedAst: astClone, pathToFilletNode }
}
function locateExtrudeDeclarator(
node: Program,
pathToExtrudeNode: PathToNode
): { extrudeDeclarator: VariableDeclarator } | Error {
const extrudeChunk = getNodeFromPath<VariableDeclaration>(
node,
pathToExtrudeNode,
'VariableDeclaration'
)
if (err(extrudeChunk)) return extrudeChunk
const { node: extrudeVarDecl } = extrudeChunk
const extrudeDeclarator = extrudeVarDecl.declarations[0]
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 !== 'CallExpression' &&
extrudeInit.type !== 'PipeExpression'
) {
return new Error('Extrude must be a PipeExpression or CallExpression')
}
return { extrudeDeclarator }
}
function getPathToNodeOfFilletLiteral(
pathToExtrudeNode: PathToNode,
extrudeDeclarator: VariableDeclarator,
tag: string
): PathToNode => {
let pathToFilletObj: any
): PathToNode {
let pathToFilletObj: PathToNode = []
let inFillet = false
traverse(extrudeDeclarator.init, {
enter(node, path) {
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
inFillet = true
}
if (inFillet && node.type === 'ObjectExpression') {
const hasTag = node.properties.some((prop) => {
const isTagProp = prop.key.name === 'tags'
if (isTagProp && prop.value.type === 'ArrayExpression') {
return prop.value.elements.some(
(element) =>
element.type === 'Identifier' && element.name === tag
)
}
return false
})
if (!hasTag) return false
pathToFilletObj = path
node.properties.forEach((prop, index) => {
if (prop.key.name === 'radius') {
pathToFilletObj.push(
['properties', 'ObjectExpression'],
[index, 'index'],
['value', 'Property']
)
}
})
if (!hasTag(node, tag)) return false
pathToFilletObj = getPathToRadiusLiteral(node, path)
}
},
leave(node) {
@ -162,6 +360,7 @@ export function addFillet(
let indexOfPipeExpression = pathToExtrudeNode.findIndex(
(path) => path[1] === 'PipeExpression'
)
indexOfPipeExpression =
indexOfPipeExpression === -1
? pathToExtrudeNode.length
@ -171,34 +370,34 @@ export function addFillet(
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
...pathToFilletObj,
]
}
function hasTag(node: ObjectExpression, tag: string): boolean {
return node.properties.some((prop) => {
if (prop.key.name === 'tags' && prop.value.type === 'ArrayExpression') {
return prop.value.elements.some(
(element) => element.type === 'Identifier' && element.name === tag
)
}
if (extrudeInit.type === 'CallExpression') {
// 1. no fillet case
extrudeDeclarator.init = createPipeExpression([extrudeInit, filletCall])
return {
modifiedAst: _node,
pathToFilletNode: getPathToNodeOfFilletLiteral(
pathToExtrudeNode,
extrudeDeclarator,
tag
),
}
} else if (extrudeInit.type === 'PipeExpression') {
// 2. fillet case
// there are 2 options here:
const existingFilletCall = extrudeInit.body.find((node) => {
return node.type === 'CallExpression' && node.callee.name === 'fillet'
return false
})
}
if (!existingFilletCall || existingFilletCall.type !== 'CallExpression') {
return new Error('Fillet CallExpression not found.')
function getPathToRadiusLiteral(node: ObjectExpression, path: any): PathToNode {
let pathToFilletObj = path
node.properties.forEach((prop, index) => {
if (prop.key.name === 'radius') {
pathToFilletObj.push(
['properties', 'ObjectExpression'],
[index, 'index'],
['value', 'Property']
)
}
})
return pathToFilletObj
}
// check if the existing fillet has the same tag as the new fillet
let filletTag = null
function getFilletTag(existingFilletCall: CallExpression): string | null {
if (existingFilletCall.arguments[0].type === 'ObjectExpression') {
const properties = (existingFilletCall.arguments[0] as ObjectExpression)
.properties
@ -206,31 +405,17 @@ export function addFillet(
if (tagsProperty && tagsProperty.value.type === 'ArrayExpression') {
const elements = (tagsProperty.value as ArrayExpression).elements
if (elements.length > 0 && elements[0].type === 'Identifier') {
filletTag = elements[0].name
return elements[0].name
}
}
} else {
return new Error('Expected an ObjectExpression node')
}
if (filletTag !== tag) {
extrudeInit.body.push(filletCall)
return {
modifiedAst: _node,
pathToFilletNode: getPathToNodeOfFilletLiteral(
pathToExtrudeNode,
extrudeDeclarator,
tag
),
}
}
} else {
return new Error('Unsupported extrude type.')
}
return new Error('Unsupported extrude type.')
return null
}
/**
* Button states
*/
export const hasValidFilletSelection = ({
selectionRanges,
ast,

View File

@ -4,7 +4,6 @@ import {
VariableDeclarator,
parse,
recast,
sketchGroupFromKclValue,
} from 'lang/wasm'
import { Axis, Selection, Selections, updateSelections } from 'lib/selections'
import { assign, createMachine } from 'xstate'
@ -35,7 +34,7 @@ import {
setEqualLengthInfo,
} from 'components/Toolbar/EqualLength'
import { deleteFromSelection, extrudeSketch } from 'lang/modifyAst'
import { addFillet } from 'lang/modifyAst/addFillet'
import { applyFilletToSelection } from 'lang/modifyAst/addFillet'
import { getNodeFromPath } from '../lang/queryAst'
import {
applyConstraintEqualAngle,
@ -59,7 +58,6 @@ import { Coords2d } from 'lang/std/sketch'
import { deleteSegment } from 'clientSideScene/ClientSideSceneComp'
import { executeAst } from 'lang/langHelpers'
import toast from 'react-hot-toast'
import { getExtrusionFromSuspectedPath } from 'lang/std/artifactGraph'
export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY'
@ -1163,65 +1161,16 @@ export const modelingMachine = createMachine(
'AST fillet': async (_, event) => {
if (!event.data) return
// Extract inputs
const { selection, radius } = event.data
let ast = kclManager.ast
if (
'variableName' in radius &&
radius.variableName &&
radius.insertIndex !== undefined
) {
const newBody = [...ast.body]
newBody.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
ast.body = newBody
}
const pathToSegmentNode = getNodePathFromSourceRange(
ast,
selection.codeBasedSelections[0].range
// Apply fillet to selection
const applyFilletToSelectionResult = applyFilletToSelection(
selection,
radius
)
const varDecNode = getNodeFromPath<VariableDeclaration>(
ast,
pathToSegmentNode,
'VariableDeclaration'
)
if (err(varDecNode)) return
const sketchVar = varDecNode.node.declarations[0].id.name
const sketchGroup = sketchGroupFromKclValue(
kclManager.programMemory.get(sketchVar),
sketchVar
)
if (trap(sketchGroup)) return
const extrusion = getExtrusionFromSuspectedPath(
sketchGroup.id,
engineCommandManager.artifactGraph
)
const pathToExtrudeNode = err(extrusion)
? []
: getNodePathFromSourceRange(ast, extrusion.codeRef.range)
// we assume that there is only one body related to the sketch
// and apply the fillet to it
const addFilletResult = addFillet(
ast,
pathToSegmentNode,
pathToExtrudeNode,
'variableName' in radius
? radius.variableIdentifierAst
: radius.valueAst
)
if (trap(addFilletResult)) return
const { modifiedAst, pathToFilletNode } = addFilletResult
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: pathToFilletNode,
})
if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections)
}
if (err(applyFilletToSelectionResult))
return applyFilletToSelectionResult
},
'conditionally equip line tool': (_, { type }) => {
if (type === 'done.invoke.animate-to-face') {