Allow point-and-click Substract to take in multiple solids and tools (#7614)
* Allow point-and-click Substract to take in multiple tools Fixes #7612 * Change target to solids for consistency and make it support multi select too * Improve err message * Update src/lang/modifyAst/boolean.ts Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> * Update src/lang/modifyAst/boolean.ts Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch> * Good bot * Reduce array to single value if len 1 * Remove console.log --------- Co-authored-by: graphite-app[bot] <96075541+graphite-app[bot]@users.noreply.github.com> Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit is contained in:
@ -12,7 +12,7 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'subtract',
|
name: 'subtract',
|
||||||
code: 'subtract([extrude001], tools = [extrude006])',
|
code: 'subtract(extrude001, tools = extrude006)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'intersect',
|
name: 'intersect',
|
||||||
@ -81,6 +81,8 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
if (operationName !== 'subtract') {
|
if (operationName !== 'subtract') {
|
||||||
// should down shift key to select multiple objects
|
// should down shift key to select multiple objects
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
|
} else {
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select second object
|
// Select second object
|
||||||
@ -103,8 +105,8 @@ test.describe('Point and click for boolean workflows', () => {
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
Tool: '1 path',
|
Solids: '1 path',
|
||||||
Target: '1 path',
|
Tools: '1 path',
|
||||||
},
|
},
|
||||||
commandName,
|
commandName,
|
||||||
})
|
})
|
||||||
|
@ -23,13 +23,13 @@ import type {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
} from '@src/lang/wasm'
|
} from '@src/lang/wasm'
|
||||||
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
|
import { EXECUTION_TYPE_REAL } from '@src/lib/constants'
|
||||||
import type { Selection, Selections } from '@src/lib/selections'
|
import type { Selections } from '@src/lib/selections'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
import { isArray } from '@src/lib/utils'
|
import { isArray } from '@src/lib/utils'
|
||||||
|
|
||||||
export async function applySubtractFromTargetOperatorSelections(
|
export async function applySubtractFromTargetOperatorSelections(
|
||||||
target: Selection,
|
solids: Selections,
|
||||||
tool: Selection,
|
tools: Selections,
|
||||||
dependencies: {
|
dependencies: {
|
||||||
kclManager: KclManager
|
kclManager: KclManager
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
@ -38,28 +38,28 @@ export async function applySubtractFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
if (!target.artifact || !tool.artifact) {
|
const lastSolidsVars = getLastVariableDeclarationsFromSelections(
|
||||||
return new Error('No artifact found')
|
solids,
|
||||||
}
|
ast,
|
||||||
const orderedChildrenTarget = findAllChildrenAndOrderByPlaceInCode(
|
|
||||||
target.artifact,
|
|
||||||
dependencies.kclManager.artifactGraph
|
dependencies.kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
const orderedChildrenTool = findAllChildrenAndOrderByPlaceInCode(
|
if (err(lastSolidsVars) || lastSolidsVars.length < 1) {
|
||||||
tool.artifact,
|
return new Error('Not enough or invalid solids variables found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastToolsVars = getLastVariableDeclarationsFromSelections(
|
||||||
|
tools,
|
||||||
|
ast,
|
||||||
dependencies.kclManager.artifactGraph
|
dependencies.kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
|
if (err(lastToolsVars) || lastToolsVars.length < 1) {
|
||||||
const lastVarTarget = getLastVariable(orderedChildrenTarget, ast)
|
return new Error('Not enough or invalid tools variables found')
|
||||||
const lastVarTool = getLastVariable(orderedChildrenTool, ast)
|
|
||||||
|
|
||||||
if (!lastVarTarget || !lastVarTool) {
|
|
||||||
return new Error('No variable found')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifiedAst = booleanSubtractAstMod({
|
const modifiedAst = booleanSubtractAstMod({
|
||||||
ast,
|
ast,
|
||||||
targets: [lastVarTarget?.variableDeclaration?.node],
|
solids: lastSolidsVars,
|
||||||
tools: [lastVarTool?.variableDeclaration.node],
|
tools: lastToolsVars,
|
||||||
})
|
})
|
||||||
|
|
||||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||||
@ -75,34 +75,13 @@ export async function applyUnionFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
|
const lastVars = getLastVariableDeclarationsFromSelections(
|
||||||
const artifacts: Artifact[] = []
|
solids,
|
||||||
for (const selection of solids.graphSelections) {
|
ast,
|
||||||
if (selection.artifact) {
|
|
||||||
artifacts.push(selection.artifact)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (artifacts.length < 2) {
|
|
||||||
return new Error('Not enough artifacts selected')
|
|
||||||
}
|
|
||||||
|
|
||||||
const orderedChildrenEach = artifacts.map((artifact) =>
|
|
||||||
findAllChildrenAndOrderByPlaceInCode(
|
|
||||||
artifact,
|
|
||||||
dependencies.kclManager.artifactGraph
|
dependencies.kclManager.artifactGraph
|
||||||
)
|
)
|
||||||
)
|
if (err(lastVars) || lastVars.length < 2) {
|
||||||
|
return new Error('Not enough or invalid solids variables found')
|
||||||
const lastVars: VariableDeclaration[] = []
|
|
||||||
for (const orderedArtifactLeaves of orderedChildrenEach) {
|
|
||||||
const lastVar = getLastVariable(orderedArtifactLeaves, ast)
|
|
||||||
if (!lastVar) continue
|
|
||||||
lastVars.push(lastVar.variableDeclaration.node)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastVars.length < 2) {
|
|
||||||
return new Error('Not enough variables found')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const modifiedAst = booleanUnionAstMod({
|
const modifiedAst = booleanUnionAstMod({
|
||||||
@ -122,23 +101,36 @@ export async function applyIntersectFromTargetOperatorSelections(
|
|||||||
}
|
}
|
||||||
): Promise<Error | void> {
|
): Promise<Error | void> {
|
||||||
const ast = dependencies.kclManager.ast
|
const ast = dependencies.kclManager.ast
|
||||||
|
const lastVars = getLastVariableDeclarationsFromSelections(
|
||||||
|
solids,
|
||||||
|
ast,
|
||||||
|
dependencies.kclManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(lastVars) || lastVars.length < 2) {
|
||||||
|
return new Error('Not enough or invalid solids variables found')
|
||||||
|
}
|
||||||
|
|
||||||
|
const modifiedAst = booleanIntersectAstMod({
|
||||||
|
ast,
|
||||||
|
solids: lastVars,
|
||||||
|
})
|
||||||
|
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLastVariableDeclarationsFromSelections(
|
||||||
|
selections: Selections,
|
||||||
|
ast: Node<Program>,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): Error | VariableDeclaration[] {
|
||||||
const artifacts: Artifact[] = []
|
const artifacts: Artifact[] = []
|
||||||
for (const selection of solids.graphSelections) {
|
for (const selection of selections.graphSelections) {
|
||||||
if (selection.artifact) {
|
if (selection.artifact) {
|
||||||
artifacts.push(selection.artifact)
|
artifacts.push(selection.artifact)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (artifacts.length < 2) {
|
|
||||||
return new Error('Not enough artifacts selected')
|
|
||||||
}
|
|
||||||
|
|
||||||
const orderedChildrenEach = artifacts.map((artifact) =>
|
const orderedChildrenEach = artifacts.map((artifact) =>
|
||||||
findAllChildrenAndOrderByPlaceInCode(
|
findAllChildrenAndOrderByPlaceInCode(artifact, artifactGraph)
|
||||||
artifact,
|
|
||||||
dependencies.kclManager.artifactGraph
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const lastVars: VariableDeclaration[] = []
|
const lastVars: VariableDeclaration[] = []
|
||||||
@ -148,15 +140,7 @@ export async function applyIntersectFromTargetOperatorSelections(
|
|||||||
lastVars.push(lastVar.variableDeclaration.node)
|
lastVars.push(lastVar.variableDeclaration.node)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastVars.length < 2) {
|
return lastVars
|
||||||
return new Error('Not enough variables found')
|
|
||||||
}
|
|
||||||
|
|
||||||
const modifiedAst = booleanIntersectAstMod({
|
|
||||||
ast,
|
|
||||||
solids: lastVars,
|
|
||||||
})
|
|
||||||
await updateModelingState(modifiedAst, EXECUTION_TYPE_REAL, dependencies)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** returns all children of a given artifact, and sorts them DESC by start sourceRange
|
/** returns all children of a given artifact, and sorts them DESC by start sourceRange
|
||||||
@ -271,25 +255,27 @@ export function getLastVariable(
|
|||||||
|
|
||||||
export function booleanSubtractAstMod({
|
export function booleanSubtractAstMod({
|
||||||
ast,
|
ast,
|
||||||
targets,
|
solids,
|
||||||
tools,
|
tools,
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
targets: VariableDeclaration[]
|
solids: VariableDeclaration[]
|
||||||
tools: VariableDeclaration[]
|
tools: VariableDeclaration[]
|
||||||
}): Node<Program> {
|
}): Node<Program> {
|
||||||
const newAst = structuredClone(ast)
|
const newAst = structuredClone(ast)
|
||||||
const newVarName = findUniqueName(newAst, 'solid')
|
const newVarName = findUniqueName(newAst, 'solid')
|
||||||
const createArrExpr = (varDecs: VariableDeclaration[]) =>
|
const createArrExpr = (varDecs: VariableDeclaration[]) => {
|
||||||
createArrayExpression(
|
const names = varDecs.map((varDec) =>
|
||||||
varDecs.map((varDec) => createLocalName(varDec.declaration.id.name))
|
createLocalName(varDec.declaration.id.name)
|
||||||
)
|
)
|
||||||
const targetsArrayExpression = createArrExpr(targets)
|
return names.length === 1 ? names[0] : createArrayExpression(names)
|
||||||
|
}
|
||||||
|
const solidsArrayExpression = createArrExpr(solids)
|
||||||
const toolsArrayExpression = createArrExpr(tools)
|
const toolsArrayExpression = createArrExpr(tools)
|
||||||
|
|
||||||
const newVarDec = createVariableDeclaration(
|
const newVarDec = createVariableDeclaration(
|
||||||
newVarName,
|
newVarName,
|
||||||
createCallExpressionStdLibKw('subtract', targetsArrayExpression, [
|
createCallExpressionStdLibKw('subtract', solidsArrayExpression, [
|
||||||
createLabeledArg('tools', toolsArrayExpression),
|
createLabeledArg('tools', toolsArrayExpression),
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
@ -204,8 +204,8 @@ export type ModelingCommandSchema = {
|
|||||||
variableName: string
|
variableName: string
|
||||||
}
|
}
|
||||||
'Boolean Subtract': {
|
'Boolean Subtract': {
|
||||||
target: Selections
|
solids: Selections
|
||||||
tool: Selections
|
tools: Selections
|
||||||
}
|
}
|
||||||
'Boolean Union': {
|
'Boolean Union': {
|
||||||
solids: Selections
|
solids: Selections
|
||||||
@ -595,23 +595,21 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
icon: 'booleanSubtract',
|
icon: 'booleanSubtract',
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
target: {
|
solids: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['path'],
|
selectionTypes: ['path'],
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
multiple: false,
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
tool: {
|
tools: {
|
||||||
clearSelectionFirst: true,
|
clearSelectionFirst: true,
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['path'],
|
selectionTypes: ['path'],
|
||||||
selectionFilter: ['object'],
|
selectionFilter: ['object'],
|
||||||
multiple: false,
|
multiple: true,
|
||||||
required: true,
|
required: true,
|
||||||
skip: false,
|
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -3570,17 +3570,17 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||||
}
|
}
|
||||||
|
|
||||||
const { target, tool } = input
|
const { solids, tools } = input
|
||||||
if (
|
if (
|
||||||
!target.graphSelections[0].artifact ||
|
!solids.graphSelections.some((selection) => selection.artifact) ||
|
||||||
!tool.graphSelections[0].artifact
|
!tools.graphSelections.some((selection) => selection.artifact)
|
||||||
) {
|
) {
|
||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
await applySubtractFromTargetOperatorSelections(
|
const result = await applySubtractFromTargetOperatorSelections(
|
||||||
target.graphSelections[0],
|
solids,
|
||||||
tool.graphSelections[0],
|
tools,
|
||||||
{
|
{
|
||||||
kclManager,
|
kclManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
@ -3588,6 +3588,9 @@ export const modelingMachine = setup({
|
|||||||
editorManager,
|
editorManager,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return Promise.reject(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
boolUnionAstMod: fromPromise(
|
boolUnionAstMod: fromPromise(
|
||||||
@ -3605,12 +3608,15 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
await applyUnionFromTargetOperatorSelections(solids, {
|
const result = await applyUnionFromTargetOperatorSelections(solids, {
|
||||||
kclManager,
|
kclManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
editorManager,
|
editorManager,
|
||||||
})
|
})
|
||||||
|
if (err(result)) {
|
||||||
|
return Promise.reject(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
boolIntersectAstMod: fromPromise(
|
boolIntersectAstMod: fromPromise(
|
||||||
@ -3628,12 +3634,18 @@ export const modelingMachine = setup({
|
|||||||
return Promise.reject(new Error('No artifact in selections found'))
|
return Promise.reject(new Error('No artifact in selections found'))
|
||||||
}
|
}
|
||||||
|
|
||||||
await applyIntersectFromTargetOperatorSelections(solids, {
|
const result = await applyIntersectFromTargetOperatorSelections(
|
||||||
|
solids,
|
||||||
|
{
|
||||||
kclManager,
|
kclManager,
|
||||||
codeManager,
|
codeManager,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
editorManager,
|
editorManager,
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
if (err(result)) {
|
||||||
|
return Promise.reject(result)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user