Assemblies: Set translate and rotate via point-and-click (#6167)

* WIP: Add point-and-click Import for geometry
Will eventually fix #6120
Right now the whole loop is there but the codemod doesn't work yet

* Better pathToNOde, log on non-working cm dispatch call

* Add workaround to updateModelingState not working

* Back to updateModelingState with a skip flag

* Better todo

* Change working from Import to Insert, cleanups

* Sister command in kclCommands to populate file options

* Improve path selector

* Unsure: move importAstMod to kclCommands onSubmit 😶

* Add e2e test

* Clean up for review

* Add native file menu entry and test

* No await yo lint said so

* WIP: UX improvements around foreign file imports
Fixes #6152

* WIP: Set translate and rotate via point-and-click on imports. Boilerplate code
Will eventually close #6020

* Full working loop of rotate and translate pipe mutation, including edits, only on module imports. VERY VERBOSE

* Add first e2e test for set transform. Bunch of caveats listed as TODOs

* @lrev-Dev's suggestion to remove a comment

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Update to scene.settled(cmdBar)

* Add partNNN default name for alias

* Lint

* Lint

* Fix unit tests

* Add sad path insert test
Thanks @Irev-Dev for the suggestion

* Add step insert test

* Lint

* Add test for second foreign import thru file tree click

* WIP: Add point-and-click Load to copy files from outside the project into the project
Towards #6210

* Move Insert button to modeling toolbar, update menus and toolbars

* Add default value for local name alias

* Aligning tests

* Fix tests

* Add padding for filenames starting with a digit

* Lint

* Lint

* Update snapshots

* Merge branch 'main' into pierremtb/issue6210-Add-point-and-click-Load-to-copy-files-from-outside-the-project-into-the-project

* Add disabled transform subbutton

* Allow start of Transform flow from toolbar with selection

* Merge kcl-samples and local disk load into one 'Load external model' command

* Fix em tests

* Fix test

* Add test for file pick import, better input

* Fix non .kcl loading

* Lint

* Update snapshots

* Fix issue leading to test failure

* Fix clone test

* Add note

* Fix nested clone issue

* Clean up for review

* Add valueSummary for path

* Fix test after path change

* Clean up for review

* Support much wider range for transform

* Set display names

* Bug fixed itself moment...

* Add test for extrude tranform

* Oops missed a thing

* Clean up selection arg

* More tests incl for variable stuff

* Fix imports

* Add supportsTransform: true on all solids returning operations

* Fix edit flow on variables, add test

* Split transform command into translate and rotate

* Clean up and comment

* Clean up operations.ts

* Add comment

* Improve assemblies test

* Support more things

* Typo

* Fix test after unit change on import

* Last clean up for review

* Fix remaining test

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit is contained in:
Pierre Jacquier
2025-04-17 11:44:31 -04:00
committed by GitHub
parent 056a4d4a22
commit 6f2d127c4f
17 changed files with 1496 additions and 48 deletions

View File

@ -52,6 +52,7 @@ import {
deleteNodeInExtrudePipe,
extrudeSketch,
insertNamedConstant,
insertVariableAndOffsetPathToNode,
loftSketches,
} from '@src/lang/modifyAst'
import type {
@ -72,17 +73,21 @@ import {
applyIntersectFromTargetOperatorSelections,
applySubtractFromTargetOperatorSelections,
applyUnionFromTargetOperatorSelections,
findAllChildrenAndOrderByPlaceInCode,
getLastVariable,
} from '@src/lang/modifyAst/boolean'
import {
deleteSelectionPromise,
deletionErrorMessage,
} from '@src/lang/modifyAst/deleteSelection'
import { setAppearance } from '@src/lang/modifyAst/setAppearance'
import { setTranslate, setRotate } from '@src/lang/modifyAst/setTransform'
import {
getNodeFromPath,
isNodeSafeToReplacePath,
stringifyPathToNode,
updatePathToNodesAfterEdit,
valueOrVariable,
} from '@src/lang/queryAst'
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
import {
@ -373,6 +378,8 @@ export type ModelingMachineEvent =
data: ModelingCommandSchema['Delete selection']
}
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
| {
type:
| 'Add circle origin'
@ -2031,12 +2038,6 @@ export const modelingMachine = setup({
}
}
const valueOrVariable = (variable: KclCommandValue) => {
return 'variableName' in variable
? variable.variableIdentifierAst
: variable.valueAst
}
const { modifiedAst, pathToNode } = addHelix({
node: ast,
revolutions: valueOrVariable(revolutions),
@ -2651,6 +2652,120 @@ export const modelingMachine = setup({
)
}
),
translateAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Translate'] | undefined
}) => {
if (!input) return new Error('No input provided')
const ast = kclManager.ast
const modifiedAst = structuredClone(ast)
const { x, y, z, nodeToEdit, selection } = input
let pathToNode = nodeToEdit
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
if (selection?.graphSelections[0].artifact) {
const children = findAllChildrenAndOrderByPlaceInCode(
selection?.graphSelections[0].artifact,
kclManager.artifactGraph
)
const variable = getLastVariable(children, modifiedAst)
if (!variable) {
return new Error("Couldn't find corresponding path to node")
}
pathToNode = variable.pathToNode
} else if (selection?.graphSelections[0].codeRef.pathToNode) {
pathToNode = selection?.graphSelections[0].codeRef.pathToNode
} else {
return new Error("Couldn't find corresponding path to node")
}
}
insertVariableAndOffsetPathToNode(x, modifiedAst, pathToNode)
insertVariableAndOffsetPathToNode(y, modifiedAst, pathToNode)
insertVariableAndOffsetPathToNode(z, modifiedAst, pathToNode)
const result = setTranslate({
pathToNode,
modifiedAst,
x: valueOrVariable(x),
y: valueOrVariable(y),
z: valueOrVariable(z),
})
if (err(result)) {
return err(result)
}
await updateModelingState(
result.modifiedAst,
EXECUTION_TYPE_REAL,
{
kclManager,
editorManager,
codeManager,
},
{
focusPath: [result.pathToNode],
}
)
}
),
rotateAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Rotate'] | undefined
}) => {
if (!input) return new Error('No input provided')
const ast = kclManager.ast
const modifiedAst = structuredClone(ast)
const { roll, pitch, yaw, nodeToEdit, selection } = input
let pathToNode = nodeToEdit
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
if (selection?.graphSelections[0].artifact) {
const children = findAllChildrenAndOrderByPlaceInCode(
selection?.graphSelections[0].artifact,
kclManager.artifactGraph
)
const variable = getLastVariable(children, modifiedAst)
if (!variable) {
return new Error("Couldn't find corresponding path to node")
}
pathToNode = variable.pathToNode
} else if (selection?.graphSelections[0].codeRef.pathToNode) {
pathToNode = selection?.graphSelections[0].codeRef.pathToNode
} else {
return new Error("Couldn't find corresponding path to node")
}
}
insertVariableAndOffsetPathToNode(roll, modifiedAst, pathToNode)
insertVariableAndOffsetPathToNode(pitch, modifiedAst, pathToNode)
insertVariableAndOffsetPathToNode(yaw, modifiedAst, pathToNode)
const result = setRotate({
pathToNode,
modifiedAst,
roll: valueOrVariable(roll),
pitch: valueOrVariable(pitch),
yaw: valueOrVariable(yaw),
})
if (err(result)) {
return err(result)
}
await updateModelingState(
result.modifiedAst,
EXECUTION_TYPE_REAL,
{
kclManager,
editorManager,
codeManager,
},
{
focusPath: [result.pathToNode],
}
)
}
),
exportFromEngine: fromPromise(
async ({}: { input?: ModelingCommandSchema['Export'] }) => {
return undefined as Error | undefined
@ -2919,6 +3034,16 @@ export const modelingMachine = setup({
reenter: true,
},
Translate: {
target: 'Applying translate',
reenter: true,
},
Rotate: {
target: 'Applying rotate',
reenter: true,
},
'Boolean Subtract': 'Boolean subtracting',
'Boolean Union': 'Boolean uniting',
'Boolean Intersect': 'Boolean intersecting',
@ -4325,6 +4450,32 @@ export const modelingMachine = setup({
},
},
'Applying translate': {
invoke: {
src: 'translateAstMod',
id: 'translateAstMod',
input: ({ event }) => {
if (event.type !== 'Translate') return undefined
return event.data
},
onDone: ['idle'],
onError: ['idle'],
},
},
'Applying rotate': {
invoke: {
src: 'rotateAstMod',
id: 'rotateAstMod',
input: ({ event }) => {
if (event.type !== 'Rotate') return undefined
return event.data
},
onDone: ['idle'],
onError: ['idle'],
},
},
Exporting: {
invoke: {
src: 'exportFromEngine',