WIP transforms like sweep

This commit is contained in:
Pierre Jacquier
2025-07-01 15:46:36 -04:00
parent 8d7858978f
commit 50ac0244fd
4 changed files with 73 additions and 77 deletions

View File

@ -7,8 +7,10 @@ import {
createLiteral,
createLocalName,
createPipeExpression,
createVariableDeclaration,
findUniqueName,
} from '@src/lang/create'
import { getNodeFromPath } from '@src/lang/queryAst'
import { getNodeFromPath, getExprsFromSelection, valueOrVariable } from '@src/lang/queryAst'
import type {
ArtifactGraph,
CallExpressionKw,
@ -25,46 +27,88 @@ import {
getLastVariable,
} from '@src/lang/modifyAst/boolean'
import type { Selections } from '@src/lib/selections'
import { KclCommandValue } from '@src/lib/commandTypes'
import { createPathToNode, createSketchExpression } from './addSweep'
import { insertVariableAndOffsetPathToNode } from '../modifyAst'
import { KCL_DEFAULT_CONSTANT_PREFIXES } from '@src/lib/constants'
export function setTranslate({
modifiedAst,
pathToNode,
ast,
objects,
x,
y,
z,
global,
nodeToEdit,
}: {
modifiedAst: Node<Program>
pathToNode: PathToNode
x: Expr
y: Expr
z: Expr
ast: Node<Program>
objects: Selections
x: KclCommandValue
y: KclCommandValue
z: KclCommandValue
global?: boolean
nodeToEdit: PathToNode
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
// 1. Clone the ast so we can edit it
const modifiedAst = structuredClone(ast)
// 2. Prepare unlabeled and labeled arguments
// Map the sketches selection into a list of kcl expressions to be passed as unlabelled argument
const objectsExprList = getExprsFromSelection(
objects,
modifiedAst,
nodeToEdit
)
if (err(objectsExprList)) {
return objectsExprList
}
// Extra labeled args expression
const globalExpr = global
? [createLabeledArg('global', createLiteral(global))]
: []
const noPercentSign = null
const call = createCallExpressionStdLibKw('translate', noPercentSign, [
createLabeledArg('x', x),
createLabeledArg('y', y),
createLabeledArg('z', z),
const objectsExpr = createSketchExpression(objectsExprList)
const call = createCallExpressionStdLibKw('translate', objectsExpr, [
createLabeledArg('x', valueOrVariable(x)),
createLabeledArg('y', valueOrVariable(y)),
createLabeledArg('z', valueOrVariable(z)),
...globalExpr,
])
const potentialPipe = getNodeFromPath<PipeExpression>(
modifiedAst,
pathToNode,
['PipeExpression']
)
if (!err(potentialPipe) && potentialPipe.node.type === 'PipeExpression') {
setTransformInPipe(potentialPipe.node, call)
} else {
const error = createPipeWithTransform(modifiedAst, pathToNode, call)
if (err(error)) {
return error
// Insert variables for labeled arguments if provided
if ('variableName' in x && x.variableName) {
insertVariableAndOffsetPathToNode(x, modifiedAst, nodeToEdit)
}
if ('variableName' in y && y.variableName) {
insertVariableAndOffsetPathToNode(y, modifiedAst, nodeToEdit)
}
if ('variableName' in z && z.variableName) {
insertVariableAndOffsetPathToNode(z, modifiedAst, nodeToEdit)
}
// 3. If edit, we assign the new function call declaration to the existing node,
// otherwise just push to the end
let pathToNode: PathToNode | undefined
if (nodeToEdit) {
const result = getNodeFromPath<CallExpressionKw>(
modifiedAst,
nodeToEdit,
'CallExpressionKw'
)
if (err(result)) {
return result
}
Object.assign(result.node, call)
pathToNode = nodeToEdit
} else {
const name = findUniqueName(
modifiedAst,
KCL_DEFAULT_CONSTANT_PREFIXES.TRANSLATE,
)
const declaration = createVariableDeclaration(name, call)
modifiedAst.body.push(declaration)
pathToNode = createPathToNode(modifiedAst)
}
return {

View File

@ -186,7 +186,7 @@ export type ModelingCommandSchema = {
}
Translate: {
nodeToEdit?: PathToNode
selection: Selections
objects: Selections
x: KclCommandValue
y: KclCommandValue
z: KclCommandValue
@ -1083,7 +1083,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
nodeToEdit: {
...nodeToEditProps,
},
selection: {
objects: {
// selectionMixed allows for feature tree selection of module imports
inputType: 'selectionMixed',
multiple: false,

View File

@ -53,6 +53,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
PLANE: 'plane',
HELIX: 'helix',
CLONE: 'clone',
TRANSLATE: 'translate',
} as const
/** The default KCL length expression */
export const KCL_DEFAULT_LENGTH = `5`

View File

@ -3319,58 +3319,9 @@ export const modelingMachine = setup({
}
const ast = kclManager.ast
const modifiedAst = structuredClone(ast)
const { x, y, z, global, nodeToEdit, selection } = input
let pathToNode = nodeToEdit
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
const result = retrievePathToNodeFromTransformSelection(
selection,
kclManager.artifactGraph,
ast
)
if (err(result)) {
return Promise.reject(result)
}
pathToNode = result
}
// Look for the last pipe with the import alias and a call to translate, with a fallback to rotate.
// Otherwise create one
const importNodeAndAlias = findImportNodeAndAlias(ast, pathToNode)
if (importNodeAndAlias) {
const pipes = findPipesWithImportAlias(ast, pathToNode, 'translate')
const lastPipe = pipes.at(-1)
if (lastPipe && lastPipe.pathToNode) {
pathToNode = lastPipe.pathToNode
} else {
const otherRelevantPipes = findPipesWithImportAlias(
ast,
pathToNode,
'rotate'
)
const lastRelevantPipe = otherRelevantPipes.at(-1)
if (lastRelevantPipe && lastRelevantPipe.pathToNode) {
pathToNode = lastRelevantPipe.pathToNode
} else {
pathToNode = insertExpressionNode(
modifiedAst,
importNodeAndAlias.alias
)
}
}
}
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),
global,
...input,
ast,
})
if (err(result)) {
return Promise.reject(result)