Working edit flow

This commit is contained in:
Pierre Jacquier
2025-02-24 17:29:54 -05:00
parent d1563ead8c
commit f68310898b
4 changed files with 178 additions and 17 deletions

View File

@ -447,13 +447,15 @@ export function addSweep(
node: Node<Program>,
profileDeclarator: VariableDeclarator,
pathDeclarator: VariableDeclarator,
sectional: boolean
sectional: boolean,
variableName: string | undefined
): {
modifiedAst: Node<Program>
pathToNode: PathToNode
} {
const modifiedAst = structuredClone(node)
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
const name =
variableName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
const sweep = createCallExpressionStdLibKw(
'sweep',
createIdentifier(profileDeclarator.id.name),

View File

@ -51,6 +51,9 @@ export type ModelingCommandSchema = {
distance: KclCommandValue
}
Sweep: {
// Enables editing workflow
nodeToEdit?: PathToNode
// Arguments
target: Selections
trajectory: Selections
sectional: boolean
@ -341,6 +344,13 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
icon: 'sweep',
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,
},
target: {
inputType: 'selection',
selectionTypes: ['solid2d'],
@ -350,7 +360,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
},
trajectory: {
inputType: 'selection',
selectionTypes: ['segment', 'path'],
selectionTypes: ['segment'],
required: true,
skip: true,
multiple: false,
@ -365,7 +375,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
{ name: 'True', value: true },
{ name: 'False', value: false },
],
validation: sweepValidator,
// validation: sweepValidator,
},
},
},

View File

@ -191,6 +191,133 @@ const prepareToEditOffsetPlane: PrepareToEditCallback = async ({
}
}
const prepareToEditSweep: PrepareToEditCallback = async ({
artifact,
operation,
}) => {
const baseCommand = {
name: 'Sweep',
groupId: 'modeling',
}
if (
operation.type !== 'StdLibCall' ||
!operation.labeledArgs ||
!operation.unlabeledArg ||
!('sectional' in operation.labeledArgs) ||
!operation.labeledArgs.sectional
) {
return baseCommand
}
if (!artifact || !('pathId' in artifact) || operation.type !== 'StdLibCall') {
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'],
},
engineCommandManager.artifactGraph
)
if (
err(pathArtifact) ||
pathArtifact.type !== 'path' ||
!pathArtifact.solid2dId
)
return baseCommand
const targetArtifact = getArtifactOfTypes(
{
key: pathArtifact.solid2dId,
types: ['solid2d'],
},
engineCommandManager.artifactGraph
)
if (err(targetArtifact) || targetArtifact.type !== 'solid2d') {
return baseCommand
}
const target = {
graphSelections: [
{
artifact: targetArtifact,
codeRef: pathArtifact.codeRef,
},
],
otherSelections: [],
}
// Same roundabout but twice for 'path' aka trajectory: sketch -> path -> segment
if (!('path' in operation.labeledArgs) || !operation.labeledArgs.path)
return baseCommand
if (operation.labeledArgs.path.value.type !== 'Sketch') {
return baseCommand
}
const trajectoryPathArtifact = getArtifactOfTypes(
{
key: operation.labeledArgs.path.value.value.artifactId,
types: ['path'],
},
engineCommandManager.artifactGraph
)
if (err(trajectoryPathArtifact) || trajectoryPathArtifact.type !== 'path') {
return baseCommand
}
const trajectoryArtifact = getArtifactOfTypes(
{
key: trajectoryPathArtifact.segIds[0],
types: ['segment'],
},
engineCommandManager.artifactGraph
)
if (err(trajectoryArtifact) || trajectoryArtifact.type !== 'segment') {
return baseCommand
}
const trajectory = {
graphSelections: [
{
artifact: trajectoryArtifact,
codeRef: trajectoryArtifact.codeRef,
},
],
otherSelections: [],
}
// sectional options boolean arg
if (
!('sectional' in operation.labeledArgs) ||
!operation.labeledArgs.sectional
)
return baseCommand
const sectional =
codeManager.code.slice(
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.
const argDefaultValues: ModelingCommandSchema['Sweep'] = {
target: target,
trajectory,
sectional,
nodeToEdit: getNodePathFromSourceRange(
kclManager.ast,
sourceRangeFromRust(operation.sourceRange)
),
}
return {
...baseCommand,
argDefaultValues,
}
}
/**
* A map of standard library calls to their corresponding information
* for use in the feature tree UI.
@ -285,6 +412,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
sweep: {
label: 'Sweep',
icon: 'sweep',
prepareToEdit: prepareToEditSweep,
supportsAppearance: true,
},
}

View File

@ -1861,9 +1861,31 @@ export const modelingMachine = setup({
if (!input) return new Error('No input provided')
// Extract inputs
const ast = kclManager.ast
const { target, trajectory, sectional } = input
const { target, trajectory, sectional, nodeToEdit } = input
const isEditing = nodeToEdit !== undefined // && typeof nodeToEdit[1][0] === 'number'
let variableName: string | undefined = undefined
// Find the profile declaration
// 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 variableNode = getNodeFromPath<VariableDeclaration>(
ast,
nodeToEdit,
'VariableDeclaration'
)
if (err(variableNode)) {
console.error('Error extracting name')
} else {
variableName = variableNode.node.declaration.id.name
}
// Removing the old extrusion statement
const newBody = [...ast.body]
newBody.splice(nodeToEdit[1][0] as number, 1)
ast.body = newBody
}
// Find the target declaration
const targetNodePath = getNodePathFromSourceRange(
ast,
target.graphSelections[0].codeRef.range
@ -1878,7 +1900,7 @@ export const modelingMachine = setup({
}
const targetDeclarator = targetNode.node
// Find the path declaration
// Find the trajectory (or path) declaration
const trajectoryNodePath = getNodePathFromSourceRange(
ast,
trajectory.graphSelections[0].codeRef.range
@ -1894,26 +1916,25 @@ export const modelingMachine = setup({
const trajectoryDeclarator = trajectoryNode.node
// Perform the sweep
const sweepRes = addSweep(
const result = addSweep(
ast,
targetDeclarator,
trajectoryDeclarator,
sectional
sectional,
variableName
)
const updateAstResult = await kclManager.updateAst(
sweepRes.modifiedAst,
const updatedAst = await kclManager.updateAst(
result.modifiedAst,
true,
{
focusPath: [sweepRes.pathToNode],
focusPath: [result.pathToNode],
}
)
await codeManager.updateEditorWithAstAndWriteToFile(
updateAstResult.newAst
)
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
if (updateAstResult?.selections) {
editorManager.selectRange(updateAstResult?.selections)
if (updatedAst?.selections) {
editorManager.selectRange(updatedAst?.selections)
}
}
),