Add edit flow for Revolve point-and-click (#5951)

* WIP: Add edge and segment selection in point-and-click Helix flow
Fixes #5393

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Working edge based helix edit

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Add utility function for shared code between revolve and helix

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Use updateModelingState in codemod

* A snapshot a day keeps the bugs away! 📷🐛

* Add skip: true for edge helix to be consistent with axis as options

* A snapshot a day keeps the bugs away! 📷🐛

* Add support for sweepEdge and tests

* Lint

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Clean up snapshots

* WIP: Add edit flow for Revolve
Fixes #5504

* A snapshot a day keeps the bugs away! 📷🐛

* Clean up, add edit steps to three e2e tests

* Fix up tests after ccw change

* Use displayName: 'CounterClockWise' cause ccw not cutting it

* Fix tsc

* Remove uneeded return

* Update 2020 snapshots after helix change

* Update 2020 snapshots after helix change

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Another one :djkhaled:

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Move to fromPromise actor, fix test

* A snapshot a day keeps the bugs away! 📷🐛

* Clean up

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* A snapshot a day keeps the bugs away! 📷🐛

* Lint

* Fix third test

* A snapshot a day keeps the bugs away! 📷🐛

* Lint

* Fix edit insert order

* Fix axis, edge cases with new var, edit new var

* Add test case for new variable creation

* Clean up addRevolve with getSafeInsertIndex

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Pierre Jacquier
2025-03-31 18:13:34 -04:00
committed by GitHub
parent eb3ceba497
commit 822f2ffc73
7 changed files with 437 additions and 154 deletions

View File

@ -3476,6 +3476,39 @@ segAng(rectangleSegmentA002),
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()
// Edit flow
const newAngle = '90'
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Revolve',
0
)
await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({
commandName: 'Revolve',
currentArgKey: 'angle',
currentArgValue: '360',
headerArguments: {
Angle: '360',
},
highlightedHeaderArg: 'angle',
stage: 'arguments',
})
await page.keyboard.insertText(newAngle)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Angle: newAngle,
},
commandName: 'Revolve',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await editor.expectEditor.toContain(
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
)
}) })
test('revolve surface around edge from an extruded solid2d', async ({ test('revolve surface around edge from an extruded solid2d', async ({
context, context,
@ -3486,26 +3519,22 @@ segAng(rectangleSegmentA002),
toolbar, toolbar,
cmdBar, cmdBar,
}) => { }) => {
const initialCode = ` const initialCode = `sketch001 = startSketchOn(XZ)
sketch001 = startSketchOn(XZ) |> startProfileAt([-102.57, 101.72], %)
|> startProfileAt([-102.57, 101.72], %) |> angledLine([0, 202.6], %, $rectangleSegmentA001)
|> angledLine([0, 202.6], %, $rectangleSegmentA001) |> angledLine([
|> angledLine([ segAng(rectangleSegmentA001) - 90,
segAng(rectangleSegmentA001) - 90, 202.6
202.6 ], %, $rectangleSegmentB001)
], %, $rectangleSegmentB001) |> angledLine([
|> angledLine([ segAng(rectangleSegmentA001),
segAng(rectangleSegmentA001), -segLen(rectangleSegmentA001)
-segLen(rectangleSegmentA001) ], %, $rectangleSegmentC001)
], %, $rectangleSegmentC001) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> close()
|> close()
extrude001 = extrude(sketch001, length = 50) extrude001 = extrude(sketch001, length = 50)
sketch002 = startSketchOn(extrude001, rectangleSegmentA001) sketch002 = startSketchOn(extrude001, rectangleSegmentA001)
|> circle( |> circle(center = [-11.34, 10.0], radius = 8.69)
center = [-11.34, 10.0],
radius = 8.69
)
` `
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
localStorage.setItem('persistCode', initialCode) localStorage.setItem('persistCode', initialCode)
@ -3523,9 +3552,49 @@ radius = 8.69
const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)` const lineCodeToSelection = `|> angledLine([0, 202.6], %, $rectangleSegmentA001)`
await page.getByText(lineCodeToSelection).click() await page.getByText(lineCodeToSelection).click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = getOppositeEdge(rectangleSegmentA001)) ` const newCodeToFind = `revolve001 = revolve(sketch002, angle = 360, axis = rectangleSegmentA001)`
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() await editor.expectEditor.toContain(newCodeToFind)
// Edit flow
const newAngle = '180'
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Revolve',
0
)
await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({
commandName: 'Revolve',
currentArgKey: 'angle',
currentArgValue: '360',
headerArguments: {
Angle: '360',
},
highlightedHeaderArg: 'angle',
stage: 'arguments',
})
await page.keyboard.insertText(newAngle)
await page.getByRole('button', { name: 'Create new variable' }).click()
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
'angle001'
)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Angle: newAngle,
},
commandName: 'Revolve',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await editor.expectEditor.toContain('angle001 = ' + newAngle)
await editor.expectEditor.toContain(
newCodeToFind.replace('angle = 360', 'angle = angle001')
)
}) })
test('revolve sketch circle around line segment from startProfileAt sketch', async ({ test('revolve sketch circle around line segment from startProfileAt sketch', async ({
context, context,
@ -3536,11 +3605,10 @@ radius = 8.69
toolbar, toolbar,
cmdBar, cmdBar,
}) => { }) => {
const initialCode = ` const initialCode = `sketch002 = startSketchOn(XY)
sketch002 = startSketchOn(XY)
|> startProfileAt([-2.02, 1.79], %) |> startProfileAt([-2.02, 1.79], %)
|> xLine(length = 2.6) |> xLine(length = 2.6)
sketch001 = startSketchOn('-XY') sketch001 = startSketchOn(-XY)
|> startProfileAt([-0.48, 1.25], %) |> startProfileAt([-0.48, 1.25], %)
|> angledLine([0, 2.38], %, $rectangleSegmentA001) |> angledLine([0, 2.38], %, $rectangleSegmentA001)
|> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001) |> angledLine([segAng(rectangleSegmentA001) - 90, 2.4], %, $rectangleSegmentB001)
@ -3550,12 +3618,9 @@ radius = 8.69
], %, $rectangleSegmentC001) ], %, $rectangleSegmentC001)
|> line(endAbsolute = [profileStartX(%), profileStartY(%)]) |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|> close() |> close()
extrude001 = extrude(sketch001, length = 5) extrude001 = extrude(sketch001, length = 5)
sketch003 = startSketchOn(extrude001, 'START') sketch003 = startSketchOn(extrude001, 'START')
|> circle( |> circle(center = [-0.69, 0.56], radius = 0.28)
center = [-0.69, 0.56],
radius = 0.28
)
` `
await context.addInitScript((initialCode) => { await context.addInitScript((initialCode) => {
@ -3574,9 +3639,44 @@ radius = 8.69
const lineCodeToSelection = `|> xLine(length = 2.6)` const lineCodeToSelection = `|> xLine(length = 2.6)`
await page.getByText(lineCodeToSelection).click() await page.getByText(lineCodeToSelection).click()
await cmdBar.progressCmdBar() await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
const newCodeToFind = `revolve001 = revolve(sketch003, angle = 360, axis = seg01)` const newCodeToFind = `revolve001 = revolve(sketch003, angle = 360, axis = seg01)`
expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy() expect(editor.expectEditor.toContain(newCodeToFind)).toBeTruthy()
// Edit flow
const newAngle = '270'
await toolbar.openPane('feature-tree')
const operationButton = await toolbar.getFeatureTreeOperation(
'Revolve',
0
)
await operationButton.dblclick({ button: 'left' })
await cmdBar.expectState({
commandName: 'Revolve',
currentArgKey: 'angle',
currentArgValue: '360',
headerArguments: {
Angle: '360',
},
highlightedHeaderArg: 'angle',
stage: 'arguments',
})
await page.keyboard.insertText(newAngle)
await cmdBar.progressCmdBar()
await cmdBar.expectState({
stage: 'review',
headerArguments: {
Angle: newAngle,
},
commandName: 'Revolve',
})
await cmdBar.progressCmdBar()
await toolbar.closePane('feature-tree')
await editor.expectEditor.toContain(
newCodeToFind.replace('angle = 360', 'angle = ' + newAngle)
)
}) })
}) })

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 49 KiB

View File

@ -320,6 +320,7 @@ export function mutateAstWithTagForSketchSegment(
// Check whether selection is a valid segment // Check whether selection is a valid segment
if ( if (
!segmentNode.node.callee ||
!( !(
segmentNode.node.callee.name.name in sketchLineHelperMap || segmentNode.node.callee.name.name in sketchLineHelperMap ||
segmentNode.node.callee.name.name in sketchLineHelperMapKw segmentNode.node.callee.name.name in sketchLineHelperMapKw

View File

@ -1,14 +1,6 @@
import { err } from 'lib/trap' import { err } from 'lib/trap'
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants' import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
import { import { Program, PathToNode, Expr, VariableDeclarator } from 'lang/wasm'
Program,
PathToNode,
Expr,
CallExpression,
VariableDeclarator,
CallExpressionKw,
ArtifactGraph,
} from 'lang/wasm'
import { Selections } from 'lib/selections' import { Selections } from 'lib/selections'
import { Node } from '@rust/kcl-lib/bindings/Node' import { Node } from '@rust/kcl-lib/bindings/Node'
import { import {
@ -29,8 +21,7 @@ import {
mutateAstWithTagForSketchSegment, mutateAstWithTagForSketchSegment,
getEdgeTagCall, getEdgeTagCall,
} from 'lang/modifyAst/addEdgeTreatment' } from 'lang/modifyAst/addEdgeTreatment'
import { Artifact, getPathsFromArtifact } from 'lang/std/artifactGraph' import { getSafeInsertIndex } from 'lang/queryAst/getSafeInsertIndex'
import { kclManager } from 'lib/singletons'
export function getAxisExpressionAndIndex( export function getAxisExpressionAndIndex(
axisOrEdge: 'Axis' | 'Edge', axisOrEdge: 'Axis' | 'Edge',
@ -47,13 +38,6 @@ export function getAxisExpressionAndIndex(
ast, ast,
edge.graphSelections[0]?.codeRef.range edge.graphSelections[0]?.codeRef.range
) )
const lineNode = getNodeFromPath<CallExpression | CallExpressionKw>(
ast,
pathToAxisSelection,
['CallExpression', 'CallExpressionKw']
)
if (err(lineNode)) return lineNode
const tagResult = mutateAstWithTagForSketchSegment(ast, pathToAxisSelection) const tagResult = mutateAstWithTagForSketchSegment(ast, pathToAxisSelection)
// Have the tag whether it is already created or a new one is generated // Have the tag whether it is already created or a new one is generated
@ -90,12 +74,12 @@ export function getAxisExpressionAndIndex(
export function revolveSketch( export function revolveSketch(
ast: Node<Program>, ast: Node<Program>,
pathToSketchNode: PathToNode, pathToSketchNode: PathToNode,
angle: Expr = createLiteral(4), angle: Expr,
axisOrEdge: 'Axis' | 'Edge', axisOrEdge: 'Axis' | 'Edge',
axis: string, axis: string | undefined,
edge: Selections, edge: Selections | undefined,
artifactGraph: ArtifactGraph, variableName?: string,
artifact?: Artifact insertIndex?: number
): ):
| { | {
modifiedAst: Node<Program> modifiedAst: Node<Program>
@ -103,16 +87,7 @@ export function revolveSketch(
pathToRevolveArg: PathToNode pathToRevolveArg: PathToNode
} }
| Error { | Error {
const orderedSketchNodePaths = getPathsFromArtifact({
artifact: artifact,
sketchPathToNode: pathToSketchNode,
artifactGraph,
ast: kclManager.ast,
})
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
const clonedAst = structuredClone(ast) const clonedAst = structuredClone(ast)
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
if (err(sketchNode)) return sketchNode
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>( const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
clonedAst, clonedAst,
pathToSketchNode, pathToSketchNode,
@ -121,9 +96,14 @@ export function revolveSketch(
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode
const getAxisResult = getAxisExpressionAndIndex(axisOrEdge, axis, edge, ast) const getAxisResult = getAxisExpressionAndIndex(
axisOrEdge,
axis,
edge,
clonedAst
)
if (err(getAxisResult)) return getAxisResult if (err(getAxisResult)) return getAxisResult
const { generatedAxis, axisIndexIfAxis } = getAxisResult const { generatedAxis } = getAxisResult
if (!generatedAxis) return new Error('Generated axis selection is missing.') if (!generatedAxis) return new Error('Generated axis selection is missing.')
const revolveCall = createCallExpressionStdLibKw( const revolveCall = createCallExpressionStdLibKw(
@ -134,29 +114,21 @@ export function revolveSketch(
// We're not creating a pipe expression, // We're not creating a pipe expression,
// but rather a separate constant for the extrusion // but rather a separate constant for the extrusion
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE) const name =
const VariableDeclaration = createVariableDeclaration(name, revolveCall) variableName ??
const lastSketchNodePath = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
orderedSketchNodePaths[orderedSketchNodePaths.length - 1] const variableDeclaration = createVariableDeclaration(name, revolveCall)
let sketchIndexInBody = Number(lastSketchNodePath[1][0]) const bodyInsertIndex =
if (typeof sketchIndexInBody !== 'number') { insertIndex ?? getSafeInsertIndex(revolveCall, clonedAst)
return new Error('expected sketchIndexInBody to be a number') clonedAst.body.splice(bodyInsertIndex, 0, variableDeclaration)
} const argIndex = 0
// If an axis was selected in KCL, find the max index to insert the revolve command
if (axisIndexIfAxis) {
sketchIndexInBody = Math.max(sketchIndexInBody, axisIndexIfAxis)
}
clonedAst.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
const pathToRevolveArg: PathToNode = [ const pathToRevolveArg: PathToNode = [
['body', ''], ['body', ''],
[sketchIndexInBody + 1, 'index'], [bodyInsertIndex, 'index'],
['declaration', 'VariableDeclaration'], ['declaration', 'VariableDeclaration'],
['init', 'VariableDeclarator'], ['init', 'VariableDeclarator'],
['arguments', 'CallExpressionKw'], ['arguments', 'CallExpressionKw'],
[0, ARG_INDEX_FIELD], [argIndex, ARG_INDEX_FIELD],
['arg', LABELED_ARG_FIELD], ['arg', LABELED_ARG_FIELD],
] ]

View File

@ -77,11 +77,15 @@ export type ModelingCommandSchema = {
thickness: KclCommandValue thickness: KclCommandValue
} }
Revolve: { Revolve: {
// Enables editing workflow
nodeToEdit?: PathToNode
// Flow arg
axisOrEdge: 'Axis' | 'Edge'
// KCL stdlib arguments
selection: Selections selection: Selections
angle: KclCommandValue angle: KclCommandValue
axisOrEdge: 'Axis' | 'Edge' axis: string | undefined
axis: string edge: Selections | undefined
edge: Selections
} }
Fillet: { Fillet: {
// Enables editing workflow // Enables editing workflow
@ -472,6 +476,13 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
icon: 'revolve', icon: 'revolve',
needsReview: true, needsReview: true,
args: { args: {
nodeToEdit: {
description:
'Path to the node in the AST to edit. Never shown to the user.',
skip: true,
inputType: 'text',
required: false,
},
selection: { selection: {
inputType: 'selection', inputType: 'selection',
selectionTypes: ['solid2d', 'segment'], selectionTypes: ['solid2d', 'segment'],
@ -500,6 +511,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
{ name: 'X Axis', isCurrent: true, value: 'X' }, { name: 'X Axis', isCurrent: true, value: 'X' },
{ name: 'Y Axis', isCurrent: false, value: 'Y' }, { name: 'Y Axis', isCurrent: false, value: 'Y' },
], ],
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
}, },
edge: { edge: {
required: (commandContext) => required: (commandContext) =>

View File

@ -800,6 +800,163 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
} }
} }
const prepareToEditRevolve: PrepareToEditCallback = async ({
operation,
artifact,
}) => {
const baseCommand = {
name: 'Revolve',
groupId: 'modeling',
}
if (
!artifact ||
!('pathId' in artifact) ||
operation.type !== 'StdLibCall' ||
!operation.labeledArgs
) {
return { reason: 'Wrong operation type or artifact' }
}
// We have to go a little roundabout to get from the original artifact
// to the solid2DId that we need to pass to the command.
const pathArtifact = getArtifactOfTypes(
{
key: artifact.pathId,
types: ['path'],
},
kclManager.artifactGraph
)
if (
err(pathArtifact) ||
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: [],
}
// axis options string arg
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis) {
return { reason: "Couldn't find axis argument" }
}
const axisValue = operation.labeledArgs.axis.value
let axisOrEdge: 'Axis' | 'Edge' | undefined
let axis: string | undefined
let edge: Selections | undefined
if (axisValue.type === 'String') {
// default axis casee
axisOrEdge = 'Axis'
axis = axisValue.value
} else if (axisValue.type === 'TagIdentifier' && axisValue.artifact_id) {
// segment case
axisOrEdge = 'Edge'
const artifact = getArtifactOfTypes(
{
key: axisValue.artifact_id,
types: ['segment'],
},
kclManager.artifactGraph
)
if (err(artifact)) {
return { reason: "Couldn't find related edge artifact" }
}
edge = {
graphSelections: [
{
artifact,
codeRef: artifact.codeRef,
},
],
otherSelections: [],
}
} else if (axisValue.type === 'Uuid') {
// sweepEdge case
axisOrEdge = 'Edge'
const artifact = getArtifactOfTypes(
{
key: axisValue.value,
types: ['sweepEdge'],
},
kclManager.artifactGraph
)
if (err(artifact)) {
return { reason: "Couldn't find related edge artifact" }
}
const codeRef = getSweepEdgeCodeRef(artifact, kclManager.artifactGraph)
if (err(codeRef)) {
return { reason: "Couldn't find related edge code ref" }
}
edge = {
graphSelections: [
{
artifact,
codeRef,
},
],
otherSelections: [],
}
} else {
return { reason: 'The type of the axis argument is unsupported' }
}
// angle kcl arg
if (!('angle' in operation.labeledArgs) || !operation.labeledArgs.angle) {
return { reason: "Couldn't find angle argument" }
}
const angle = await stringToKclExpression(
codeManager.code.slice(
operation.labeledArgs.angle.sourceRange[0],
operation.labeledArgs.angle.sourceRange[1]
)
)
if (err(angle) || 'errors' in angle) {
return { reason: 'Error in angle argument retrieval' }
}
// 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['Revolve'] = {
axisOrEdge,
axis,
edge,
selection,
angle,
nodeToEdit: getNodePathFromSourceRange(
kclManager.ast,
sourceRangeFromRust(operation.sourceRange)
),
}
return {
...baseCommand,
argDefaultValues,
}
}
/** /**
* A map of standard library calls to their corresponding information * A map of standard library calls to their corresponding information
* for use in the feature tree UI. * for use in the feature tree UI.
@ -872,6 +1029,7 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
revolve: { revolve: {
label: 'Revolve', label: 'Revolve',
icon: 'revolve', icon: 'revolve',
prepareToEdit: prepareToEditRevolve,
supportsAppearance: true, supportsAppearance: true,
}, },
shell: { shell: {

View File

@ -735,62 +735,6 @@ export const modelingMachine = setup({
sketchDetails: event.output, sketchDetails: event.output,
} }
}), }),
'AST revolve': ({ context: { store }, event }) => {
if (event.type !== 'Revolve') return
;(async () => {
if (!event.data) return
const { selection, angle, axis, edge, axisOrEdge } = event.data
let ast = kclManager.ast
if (
'variableName' in angle &&
angle.variableName &&
angle.insertIndex !== undefined
) {
const newBody = [...ast.body]
newBody.splice(angle.insertIndex, 0, angle.variableDeclarationAst)
ast.body = newBody
}
// This is the selection of the sketch that will be revolved
const pathToNode = getNodePathFromSourceRange(
ast,
selection.graphSelections[0]?.codeRef.range
)
const revolveSketchRes = revolveSketch(
ast,
pathToNode,
'variableName' in angle
? angle.variableIdentifierAst
: angle.valueAst,
axisOrEdge,
axis,
edge,
kclManager.artifactGraph,
selection.graphSelections[0]?.artifact
)
if (trap(revolveSketchRes)) return
const { modifiedAst, pathToRevolveArg } = revolveSketchRes
await updateModelingState(
modifiedAst,
EXECUTION_TYPE_REAL,
{
kclManager,
editorManager,
codeManager,
},
{
focusPath: [pathToRevolveArg],
zoomToFit: true,
zoomOnRangeAndType: {
range: selection.graphSelections[0]?.codeRef.range,
type: 'path',
},
}
)
})().catch(reportRejection)
},
'set selection filter to curves only': () => { 'set selection filter to curves only': () => {
;(async () => { ;(async () => {
await engineCommandManager.sendSceneCommand({ await engineCommandManager.sendSceneCommand({
@ -1837,6 +1781,87 @@ export const modelingMachine = setup({
} }
) )
}), }),
revolveAstMod: fromPromise<
unknown,
ModelingCommandSchema['Revolve'] | undefined
>(async ({ input }) => {
if (!input) return new Error('No input provided')
const { nodeToEdit, selection, angle, axis, edge, axisOrEdge } = input
let ast = kclManager.ast
let variableName: string | undefined = undefined
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,
selection.graphSelections[0]?.codeRef.range
)
const revolveSketchRes = revolveSketch(
ast,
pathToNode,
'variableName' in angle ? angle.variableIdentifierAst : angle.valueAst,
axisOrEdge,
axis,
edge,
variableName,
insertIndex
)
if (trap(revolveSketchRes)) return
const { modifiedAst, pathToRevolveArg } = revolveSketchRes
await updateModelingState(
modifiedAst,
EXECUTION_TYPE_REAL,
{
kclManager,
editorManager,
codeManager,
},
{
focusPath: [pathToRevolveArg],
zoomToFit: true,
zoomOnRangeAndType: {
range: selection.graphSelections[0]?.codeRef.range,
type: 'path',
},
}
)
}),
offsetPlaneAstMod: fromPromise( offsetPlaneAstMod: fromPromise(
async ({ async ({
input, input,
@ -2767,9 +2792,8 @@ export const modelingMachine = setup({
}, },
Revolve: { Revolve: {
target: 'idle', target: 'Applying revolve',
actions: ['AST revolve'], reenter: true,
reenter: false,
}, },
Sweep: { Sweep: {
@ -4023,6 +4047,22 @@ export const modelingMachine = setup({
}, },
}, },
'Applying revolve': {
invoke: {
src: 'revolveAstMod',
id: 'revolveAstMod',
input: ({ event }) => {
if (event.type !== 'Revolve') return undefined
return event.data
},
onDone: ['idle'],
onError: {
target: 'idle',
actions: 'toastError',
},
},
},
'Applying offset plane': { 'Applying offset plane': {
invoke: { invoke: {
src: 'offsetPlaneAstMod', src: 'offsetPlaneAstMod',