WIP: Add edge and segment selection in point-and-click Helix flow

Fixes #5393
This commit is contained in:
Pierre Jacquier
2025-03-18 13:10:00 -04:00
parent 6d72104faa
commit 03e4de6099
5 changed files with 117 additions and 31 deletions

View File

@ -1093,17 +1093,17 @@ openSketch = startSketchOn('XY')
await toolbar.helixButton.click() await toolbar.helixButton.click()
await cmdBar.expectState({ await cmdBar.expectState({
stage: 'arguments', stage: 'arguments',
currentArgKey: 'revolutions', currentArgKey: 'axisOrEdge',
currentArgValue: '1', currentArgValue: '',
headerArguments: { headerArguments: {
AngleStart: '', AngleStart: '',
Axis: '', AxisOrEdge: '',
CounterClockWise: '', CounterClockWise: '',
Length: '', Length: '',
Radius: '', Radius: '',
Revolutions: '', Revolutions: '',
}, },
highlightedHeaderArg: 'revolutions', highlightedHeaderArg: 'axisOrEdge',
commandName: 'Helix', commandName: 'Helix',
}) })
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
@ -1113,6 +1113,7 @@ openSketch = startSketchOn('XY')
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
}) })
await test.step(`Confirm code is added to the editor, scene has changed`, async () => { await test.step(`Confirm code is added to the editor, scene has changed`, async () => {

View File

@ -795,7 +795,7 @@ export function addHelix({
angleStart: Expr angleStart: Expr
counterClockWise: boolean counterClockWise: boolean
radius: Expr radius: Expr
axis: string axis: Node<Literal> | Node<Identifier | CallExpression | CallExpressionKw>
length: Expr length: Expr
insertIndex?: number insertIndex?: number
variableName?: string variableName?: string
@ -813,7 +813,7 @@ export function addHelix({
createLabeledArg('angleStart', angleStart), createLabeledArg('angleStart', angleStart),
createLabeledArg('counterClockWise', createLiteral(counterClockWise)), createLabeledArg('counterClockWise', createLiteral(counterClockWise)),
createLabeledArg('radius', radius), createLabeledArg('radius', radius),
createLabeledArg('axis', createLiteral(axis)), createLabeledArg('axis', axis),
createLabeledArg('length', length), createLabeledArg('length', length),
] ]
) )

View File

@ -84,12 +84,15 @@ export type ModelingCommandSchema = {
Helix: { Helix: {
// Enables editing workflow // Enables editing workflow
nodeToEdit?: PathToNode nodeToEdit?: PathToNode
// Flow arg
axisOrEdge: 'Axis' | 'Edge'
// KCL stdlib arguments // KCL stdlib arguments
axis: string
edge: Selections
revolutions: KclCommandValue revolutions: KclCommandValue
angleStart: KclCommandValue angleStart: KclCommandValue
counterClockWise: boolean counterClockWise: boolean
radius: KclCommandValue radius: KclCommandValue
axis: string
length: KclCommandValue length: KclCommandValue
} }
'change tool': { 'change tool': {
@ -495,6 +498,38 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
required: false, required: false,
hidden: true, hidden: true,
}, },
axisOrEdge: {
inputType: 'options',
required: true,
defaultValue: 'Axis',
options: [
{ name: 'Axis', isCurrent: true, value: 'Axis' },
{ name: 'Edge', isCurrent: false, value: 'Edge' },
],
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
},
axis: {
inputType: 'options',
required: (commandContext) =>
['Axis'].includes(
commandContext.argumentsToSubmit.axisOrEdge as string
),
options: [
{ name: 'X Axis', value: 'X' },
{ name: 'Y Axis', value: 'Y' },
{ name: 'Z Axis', value: 'Z' },
],
},
edge: {
required: (commandContext) =>
['Edge'].includes(
commandContext.argumentsToSubmit.axisOrEdge as string
),
inputType: 'selection',
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
multiple: false,
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
},
revolutions: { revolutions: {
inputType: 'kcl', inputType: 'kcl',
defaultValue: '1', defaultValue: '1',
@ -521,16 +556,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
defaultValue: KCL_DEFAULT_LENGTH, defaultValue: KCL_DEFAULT_LENGTH,
required: true, required: true,
}, },
axis: {
inputType: 'options',
required: true,
defaultValue: 'X',
options: [
{ name: 'X Axis', value: 'X' },
{ name: 'Y Axis', value: 'Y' },
{ name: 'Z Axis', value: 'Z' },
],
},
length: { length: {
inputType: 'kcl', inputType: 'kcl',
defaultValue: KCL_DEFAULT_LENGTH, defaultValue: KCL_DEFAULT_LENGTH,

View File

@ -311,7 +311,10 @@ const prepareToEditOffsetPlane: PrepareToEditCallback = async ({
} }
} }
const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { const prepareToEditHelix: PrepareToEditCallback = async ({
operation,
artifact,
}) => {
const baseCommand = { const baseCommand = {
name: 'Helix', name: 'Helix',
groupId: 'modeling', groupId: 'modeling',
@ -321,6 +324,25 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
} }
// TODO: find a way to loop over the arguments while keeping it safe // TODO: find a way to loop over the arguments while keeping it safe
// axis options string arg
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis)
return baseCommand
const axisValue = codeManager.code
.slice(
operation.labeledArgs.axis.sourceRange[0],
operation.labeledArgs.axis.sourceRange[1]
)
.replaceAll("'", '') // TODO: fix this crap
console.log('preparedToEditHelix axis', axisValue)
console.log('preparedToEditHelix artifact', artifact)
console.log('preparedToEditHelix operation', operation)
// TODO: replace with constants
const isAxis = axisValue === 'X' || axisValue === 'Y' || axisValue === 'Z'
const axisOrEdge = isAxis ? 'Axis' : 'Edge'
let axis = isAxis ? axisValue : undefined
// TODO: massage selection in for edge edits
let edge: Selections | undefined = undefined
// revolutions kcl arg // revolutions kcl arg
if ( if (
!('revolutions' in operation.labeledArgs) || !('revolutions' in operation.labeledArgs) ||
@ -372,16 +394,6 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
) )
if (err(radius) || 'errors' in radius) return baseCommand if (err(radius) || 'errors' in radius) return baseCommand
// axis options string arg
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis)
return baseCommand
const axis = codeManager.code
.slice(
operation.labeledArgs.axis.sourceRange[0],
operation.labeledArgs.axis.sourceRange[1]
)
.replaceAll("'", '') // TODO: fix this crap
// length kcl arg // length kcl arg
if (!('length' in operation.labeledArgs) || !operation.labeledArgs.length) if (!('length' in operation.labeledArgs) || !operation.labeledArgs.length)
return baseCommand return baseCommand
@ -397,11 +409,13 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
// with `nodeToEdit` set, which will let the Offset Plane actor know // 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['Helix'] = { const argDefaultValues: ModelingCommandSchema['Helix'] = {
axisOrEdge,
axis,
edge,
revolutions, revolutions,
angleStart, angleStart,
counterClockWise, counterClockWise,
radius, radius,
axis,
length, length,
nodeToEdit: getNodePathFromSourceRange( nodeToEdit: getNodePathFromSourceRange(
kclManager.ast, kclManager.ast,

View File

@ -1,4 +1,6 @@
import { import {
CallExpression,
CallExpressionKw,
Expr, Expr,
PathToNode, PathToNode,
VariableDeclaration, VariableDeclaration,
@ -57,6 +59,7 @@ import {
ChamferParameters, ChamferParameters,
EdgeTreatmentType, EdgeTreatmentType,
FilletParameters, FilletParameters,
getEdgeTagCall,
getPathToExtrudeForSegmentSelection, getPathToExtrudeForSegmentSelection,
mutateAstWithTagForSketchSegment, mutateAstWithTagForSketchSegment,
} from 'lang/modifyAst/addEdgeTreatment' } from 'lang/modifyAst/addEdgeTreatment'
@ -1892,11 +1895,13 @@ export const modelingMachine = setup({
// Extract inputs // Extract inputs
const ast = kclManager.ast const ast = kclManager.ast
const { const {
axisOrEdge,
axis,
edge,
revolutions, revolutions,
angleStart, angleStart,
counterClockWise, counterClockWise,
radius, radius,
axis,
length, length,
nodeToEdit, nodeToEdit,
} = input } = input
@ -1924,6 +1929,47 @@ export const modelingMachine = setup({
opInsertIndex = nodeToEdit[1][0] opInsertIndex = nodeToEdit[1][0]
} }
let generatedAxis
let axisDeclaration: PathToNode | null = null
if (axisOrEdge === 'Edge') {
const pathToAxisSelection = getNodePathFromSourceRange(
ast,
edge.graphSelections[0]?.codeRef.range
)
const lineNode = getNodeFromPath<CallExpression | CallExpressionKw>(
ast,
pathToAxisSelection,
['CallExpression', 'CallExpressionKw']
)
if (err(lineNode)) return lineNode
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
}
} else {
generatedAxis = createLiteral(axis)
}
if (!generatedAxis)
return new Error('Generated axis selection is missing.')
for (const variable of [revolutions, angleStart, radius, length]) { for (const variable of [revolutions, angleStart, radius, length]) {
// Insert the variable if it exists // Insert the variable if it exists
if ( if (
@ -1952,7 +1998,7 @@ export const modelingMachine = setup({
angleStart: valueOrVariable(angleStart), angleStart: valueOrVariable(angleStart),
counterClockWise, counterClockWise,
radius: valueOrVariable(radius), radius: valueOrVariable(radius),
axis, axis: generatedAxis,
length: valueOrVariable(length), length: valueOrVariable(length),
insertIndex: opInsertIndex, insertIndex: opInsertIndex,
variableName: opVariableName, variableName: opVariableName,