WIP: global optional arg for all point-and-click transforms and add Scale
Closes #7615 and #7634
This commit is contained in:
@ -409,6 +409,18 @@ const OperationItem = (props: {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enterScaleFlow() {
|
||||||
|
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
||||||
|
props.send({
|
||||||
|
type: 'enterScaleFlow',
|
||||||
|
data: {
|
||||||
|
targetSourceRange: sourceRangeFromRust(props.item.sourceRange),
|
||||||
|
currentOperation: props.item,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function enterCloneFlow() {
|
function enterCloneFlow() {
|
||||||
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
if (props.item.type === 'StdLibCall' || props.item.type === 'GroupBegin') {
|
||||||
props.send({
|
props.send({
|
||||||
@ -527,6 +539,16 @@ const OperationItem = (props: {
|
|||||||
>
|
>
|
||||||
Set rotate
|
Set rotate
|
||||||
</ContextMenuItem>,
|
</ContextMenuItem>,
|
||||||
|
<ContextMenuItem
|
||||||
|
onClick={enterScaleFlow}
|
||||||
|
data-testid="context-menu-set-scale"
|
||||||
|
disabled={
|
||||||
|
props.item.type !== 'GroupBegin' &&
|
||||||
|
!stdLibMap[props.item.name]?.supportsTransform
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Set scale
|
||||||
|
</ContextMenuItem>,
|
||||||
<ContextMenuItem
|
<ContextMenuItem
|
||||||
onClick={enterCloneFlow}
|
onClick={enterCloneFlow}
|
||||||
data-testid="context-menu-clone"
|
data-testid="context-menu-clone"
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import {
|
|||||||
createCallExpressionStdLibKw,
|
createCallExpressionStdLibKw,
|
||||||
createExpressionStatement,
|
createExpressionStatement,
|
||||||
createLabeledArg,
|
createLabeledArg,
|
||||||
|
createLiteral,
|
||||||
createLocalName,
|
createLocalName,
|
||||||
createPipeExpression,
|
createPipeExpression,
|
||||||
} from '@src/lang/create'
|
} from '@src/lang/create'
|
||||||
@ -31,18 +32,25 @@ export function setTranslate({
|
|||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
z,
|
z,
|
||||||
|
global,
|
||||||
}: {
|
}: {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
x: Expr
|
x: Expr
|
||||||
y: Expr
|
y: Expr
|
||||||
z: Expr
|
z: Expr
|
||||||
|
global?: boolean
|
||||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// Extra labeled args expression
|
||||||
|
const globalExpr = global
|
||||||
|
? [createLabeledArg('global', createLiteral(global))]
|
||||||
|
: []
|
||||||
const noPercentSign = null
|
const noPercentSign = null
|
||||||
const call = createCallExpressionStdLibKw('translate', noPercentSign, [
|
const call = createCallExpressionStdLibKw('translate', noPercentSign, [
|
||||||
createLabeledArg('x', x),
|
createLabeledArg('x', x),
|
||||||
createLabeledArg('y', y),
|
createLabeledArg('y', y),
|
||||||
createLabeledArg('z', z),
|
createLabeledArg('z', z),
|
||||||
|
...globalExpr,
|
||||||
])
|
])
|
||||||
|
|
||||||
const potentialPipe = getNodeFromPath<PipeExpression>(
|
const potentialPipe = getNodeFromPath<PipeExpression>(
|
||||||
@ -71,18 +79,72 @@ export function setRotate({
|
|||||||
roll,
|
roll,
|
||||||
pitch,
|
pitch,
|
||||||
yaw,
|
yaw,
|
||||||
|
global,
|
||||||
}: {
|
}: {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
roll: Expr
|
roll: Expr
|
||||||
pitch: Expr
|
pitch: Expr
|
||||||
yaw: Expr
|
yaw: Expr
|
||||||
|
global?: boolean
|
||||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// Extra labeled args expression
|
||||||
|
const globalExpr = global
|
||||||
|
? [createLabeledArg('global', createLiteral(global))]
|
||||||
|
: []
|
||||||
const noPercentSign = null
|
const noPercentSign = null
|
||||||
const call = createCallExpressionStdLibKw('rotate', noPercentSign, [
|
const call = createCallExpressionStdLibKw('rotate', noPercentSign, [
|
||||||
createLabeledArg('roll', roll),
|
createLabeledArg('roll', roll),
|
||||||
createLabeledArg('pitch', pitch),
|
createLabeledArg('pitch', pitch),
|
||||||
createLabeledArg('yaw', yaw),
|
createLabeledArg('yaw', yaw),
|
||||||
|
...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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setScale({
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
global,
|
||||||
|
}: {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
x: Expr
|
||||||
|
y: Expr
|
||||||
|
z: Expr
|
||||||
|
global?: boolean
|
||||||
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
|
// Extra labeled args expression
|
||||||
|
const globalExpr = global
|
||||||
|
? [createLabeledArg('global', createLiteral(global))]
|
||||||
|
: []
|
||||||
|
const noPercentSign = null
|
||||||
|
const call = createCallExpressionStdLibKw('scale', noPercentSign, [
|
||||||
|
createLabeledArg('x', x),
|
||||||
|
createLabeledArg('y', y),
|
||||||
|
createLabeledArg('z', z),
|
||||||
|
...globalExpr,
|
||||||
])
|
])
|
||||||
|
|
||||||
const potentialPipe = getNodeFromPath<PipeExpression>(
|
const potentialPipe = getNodeFromPath<PipeExpression>(
|
||||||
|
|||||||
@ -190,6 +190,7 @@ export type ModelingCommandSchema = {
|
|||||||
x: KclCommandValue
|
x: KclCommandValue
|
||||||
y: KclCommandValue
|
y: KclCommandValue
|
||||||
z: KclCommandValue
|
z: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
}
|
}
|
||||||
Rotate: {
|
Rotate: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
@ -197,6 +198,15 @@ export type ModelingCommandSchema = {
|
|||||||
roll: KclCommandValue
|
roll: KclCommandValue
|
||||||
pitch: KclCommandValue
|
pitch: KclCommandValue
|
||||||
yaw: KclCommandValue
|
yaw: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
|
}
|
||||||
|
Scale: {
|
||||||
|
nodeToEdit?: PathToNode
|
||||||
|
selection: Selections
|
||||||
|
x: KclCommandValue
|
||||||
|
y: KclCommandValue
|
||||||
|
z: KclCommandValue
|
||||||
|
global?: boolean
|
||||||
}
|
}
|
||||||
Clone: {
|
Clone: {
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
@ -750,14 +760,8 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: false,
|
required: false,
|
||||||
displayName: 'CounterClockWise',
|
displayName: 'CounterClockWise',
|
||||||
options: [
|
options: [
|
||||||
{
|
{ name: 'False', value: false },
|
||||||
name: 'False',
|
{ name: 'True', value: true },
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'True',
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1104,6 +1108,14 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
defaultValue: KCL_DEFAULT_TRANSFORM,
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Rotate: {
|
Rotate: {
|
||||||
@ -1139,6 +1151,57 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
defaultValue: KCL_DEFAULT_TRANSFORM,
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Scale: {
|
||||||
|
description: 'Set scale on solid or sketch.',
|
||||||
|
icon: 'scale',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
nodeToEdit: {
|
||||||
|
...nodeToEditProps,
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
// selectionMixed allows for feature tree selection of module imports
|
||||||
|
inputType: 'selectionMixed',
|
||||||
|
multiple: false,
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
selectionTypes: ['path'],
|
||||||
|
selectionFilter: ['object'],
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
z: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_TRANSFORM,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
global: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: false,
|
||||||
|
options: [
|
||||||
|
{ name: 'False', value: false },
|
||||||
|
{ name: 'True', value: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Clone: {
|
Clone: {
|
||||||
|
|||||||
@ -1179,6 +1179,8 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
|||||||
scale: {
|
scale: {
|
||||||
label: 'Scale',
|
label: 'Scale',
|
||||||
icon: 'scale',
|
icon: 'scale',
|
||||||
|
prepareToEdit: prepareToEditScale,
|
||||||
|
supportsTransform: true,
|
||||||
},
|
},
|
||||||
shell: {
|
shell: {
|
||||||
label: 'Shell',
|
label: 'Shell',
|
||||||
@ -1540,6 +1542,7 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
|
|||||||
let x: KclExpression | undefined = undefined
|
let x: KclExpression | undefined = undefined
|
||||||
let y: KclExpression | undefined = undefined
|
let y: KclExpression | undefined = undefined
|
||||||
let z: KclExpression | undefined = undefined
|
let z: KclExpression | undefined = undefined
|
||||||
|
let global: boolean | undefined
|
||||||
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
nodeToEdit,
|
nodeToEdit,
|
||||||
@ -1566,12 +1569,21 @@ async function prepareToEditTranslate({ operation }: EnterEditFlowProps) {
|
|||||||
x = await retrieveArgFromPipedCallExpression(translate, 'x')
|
x = await retrieveArgFromPipedCallExpression(translate, 'x')
|
||||||
y = await retrieveArgFromPipedCallExpression(translate, 'y')
|
y = await retrieveArgFromPipedCallExpression(translate, 'y')
|
||||||
z = await retrieveArgFromPipedCallExpression(translate, 'z')
|
z = await retrieveArgFromPipedCallExpression(translate, 'z')
|
||||||
|
|
||||||
|
// optional global argument
|
||||||
|
const result = await retrieveArgFromPipedCallExpression(
|
||||||
|
translate,
|
||||||
|
'global'
|
||||||
|
)
|
||||||
|
if (result) {
|
||||||
|
global = result.valueText === 'true'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Won't be used since we provide nodeToEdit
|
// Won't be used since we provide nodeToEdit
|
||||||
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
||||||
const argDefaultValues = { nodeToEdit, selection, x, y, z }
|
const argDefaultValues = { nodeToEdit, selection, x, y, z, global }
|
||||||
return {
|
return {
|
||||||
...baseCommand,
|
...baseCommand,
|
||||||
argDefaultValues,
|
argDefaultValues,
|
||||||
@ -1592,6 +1604,84 @@ export async function enterTranslateFlow({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function prepareToEditScale({ operation }: EnterEditFlowProps) {
|
||||||
|
const baseCommand = {
|
||||||
|
name: 'Scale',
|
||||||
|
groupId: 'modeling',
|
||||||
|
}
|
||||||
|
const isModuleImport = operation.type === 'GroupBegin'
|
||||||
|
const isSupportedStdLibCall =
|
||||||
|
operation.type === 'StdLibCall' &&
|
||||||
|
stdLibMap[operation.name]?.supportsTransform
|
||||||
|
if (!isModuleImport && !isSupportedStdLibCall) {
|
||||||
|
return {
|
||||||
|
reason: 'Unsupported operation type. Please edit in the code editor.',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeToEdit = pathToNodeFromRustNodePath(operation.nodePath)
|
||||||
|
let x: KclExpression | undefined = undefined
|
||||||
|
let y: KclExpression | undefined = undefined
|
||||||
|
let z: KclExpression | undefined = undefined
|
||||||
|
let global: boolean | undefined
|
||||||
|
const pipeLookupFromOperation = getNodeFromPath<PipeExpression>(
|
||||||
|
kclManager.ast,
|
||||||
|
nodeToEdit,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
let pipe: PipeExpression | undefined
|
||||||
|
const ast = kclManager.ast
|
||||||
|
if (
|
||||||
|
err(pipeLookupFromOperation) ||
|
||||||
|
pipeLookupFromOperation.node.type !== 'PipeExpression'
|
||||||
|
) {
|
||||||
|
// Look for the last pipe with the import alias and a call to scale
|
||||||
|
const pipes = findPipesWithImportAlias(ast, nodeToEdit, 'scale')
|
||||||
|
pipe = pipes.at(-1)?.expression
|
||||||
|
} else {
|
||||||
|
pipe = pipeLookupFromOperation.node
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipe) {
|
||||||
|
const scale = pipe.body.find(
|
||||||
|
(n) => n.type === 'CallExpressionKw' && n.callee.name.name === 'scale'
|
||||||
|
)
|
||||||
|
if (scale?.type === 'CallExpressionKw') {
|
||||||
|
x = await retrieveArgFromPipedCallExpression(scale, 'x')
|
||||||
|
y = await retrieveArgFromPipedCallExpression(scale, 'y')
|
||||||
|
z = await retrieveArgFromPipedCallExpression(scale, 'z')
|
||||||
|
|
||||||
|
// optional global argument
|
||||||
|
const result = await retrieveArgFromPipedCallExpression(scale, 'global')
|
||||||
|
if (result) {
|
||||||
|
global = result.valueText === 'true'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Won't be used since we provide nodeToEdit
|
||||||
|
const selection: Selections = { graphSelections: [], otherSelections: [] }
|
||||||
|
const argDefaultValues = { nodeToEdit, selection, x, y, z, global }
|
||||||
|
return {
|
||||||
|
...baseCommand,
|
||||||
|
argDefaultValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function enterScaleFlow({
|
||||||
|
operation,
|
||||||
|
}: EnterEditFlowProps): Promise<Error | CommandBarMachineEvent> {
|
||||||
|
const data = await prepareToEditScale({ operation })
|
||||||
|
if ('reason' in data) {
|
||||||
|
return new Error(data.reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'Find and select command',
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
|
async function prepareToEditRotate({ operation }: EnterEditFlowProps) {
|
||||||
const baseCommand = {
|
const baseCommand = {
|
||||||
name: 'Rotate',
|
name: 'Rotate',
|
||||||
|
|||||||
@ -432,6 +432,24 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'scale',
|
||||||
|
onClick: () =>
|
||||||
|
commandBarActor.send({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Scale', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
icon: 'scale',
|
||||||
|
status: 'available',
|
||||||
|
title: 'Scale',
|
||||||
|
description: 'Apply scaling to a solid or sketch.',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
label: 'API docs',
|
||||||
|
url: 'https://zoo.dev/docs/kcl-std/functions/std-transform-scale',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'clone',
|
id: 'clone',
|
||||||
onClick: () =>
|
onClick: () =>
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import {
|
|||||||
enterEditFlow,
|
enterEditFlow,
|
||||||
enterTranslateFlow,
|
enterTranslateFlow,
|
||||||
enterRotateFlow,
|
enterRotateFlow,
|
||||||
|
enterScaleFlow,
|
||||||
} from '@src/lib/operations'
|
} from '@src/lib/operations'
|
||||||
import { kclManager } from '@src/lib/singletons'
|
import { kclManager } from '@src/lib/singletons'
|
||||||
import { err } from '@src/lib/trap'
|
import { err } from '@src/lib/trap'
|
||||||
@ -52,6 +53,10 @@ type FeatureTreeEvent =
|
|||||||
type: 'enterRotateFlow'
|
type: 'enterRotateFlow'
|
||||||
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
type: 'enterScaleFlow'
|
||||||
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
|
}
|
||||||
| {
|
| {
|
||||||
type: 'enterCloneFlow'
|
type: 'enterCloneFlow'
|
||||||
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
data: { targetSourceRange: SourceRange; currentOperation: Operation }
|
||||||
@ -172,6 +177,29 @@ export const featureTreeMachine = setup({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
prepareScaleCommand: fromPromise(
|
||||||
|
({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: EnterEditFlowProps & {
|
||||||
|
commandBarSend: (typeof commandBarActor)['send']
|
||||||
|
}
|
||||||
|
}) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const { commandBarSend, ...editFlowProps } = input
|
||||||
|
enterScaleFlow(editFlowProps)
|
||||||
|
.then((result) => {
|
||||||
|
if (err(result)) {
|
||||||
|
reject(result)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
input.commandBarSend(result)
|
||||||
|
resolve(result)
|
||||||
|
})
|
||||||
|
.catch(reject)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
),
|
||||||
prepareCloneCommand: fromPromise(
|
prepareCloneCommand: fromPromise(
|
||||||
({
|
({
|
||||||
input,
|
input,
|
||||||
@ -293,6 +321,11 @@ export const featureTreeMachine = setup({
|
|||||||
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enterScaleFlow: {
|
||||||
|
target: 'enteringScaleFlow',
|
||||||
|
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
||||||
|
},
|
||||||
|
|
||||||
enterCloneFlow: {
|
enterCloneFlow: {
|
||||||
target: 'enteringCloneFlow',
|
target: 'enteringCloneFlow',
|
||||||
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
actions: ['saveTargetSourceRange', 'saveCurrentOperation'],
|
||||||
@ -571,6 +604,60 @@ export const featureTreeMachine = setup({
|
|||||||
exit: ['clearContext'],
|
exit: ['clearContext'],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
enteringScaleFlow: {
|
||||||
|
states: {
|
||||||
|
selecting: {
|
||||||
|
on: {
|
||||||
|
selected: {
|
||||||
|
target: 'prepareScaleCommand',
|
||||||
|
reenter: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
done: {
|
||||||
|
always: '#featureTree.idle',
|
||||||
|
},
|
||||||
|
|
||||||
|
prepareScaleCommand: {
|
||||||
|
invoke: {
|
||||||
|
src: 'prepareScaleCommand',
|
||||||
|
input: ({ context }) => {
|
||||||
|
const artifact = context.targetSourceRange
|
||||||
|
? (getArtifactFromRange(
|
||||||
|
context.targetSourceRange,
|
||||||
|
kclManager.artifactGraph
|
||||||
|
) ?? undefined)
|
||||||
|
: undefined
|
||||||
|
return {
|
||||||
|
// currentOperation is guaranteed to be defined here
|
||||||
|
operation: context.currentOperation!,
|
||||||
|
artifact,
|
||||||
|
commandBarSend: commandBarActor.send,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDone: {
|
||||||
|
target: 'done',
|
||||||
|
reenter: true,
|
||||||
|
},
|
||||||
|
onError: {
|
||||||
|
target: 'done',
|
||||||
|
reenter: true,
|
||||||
|
actions: ({ event }) => {
|
||||||
|
if ('error' in event && err(event.error)) {
|
||||||
|
toast.error(event.error.message)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
initial: 'selecting',
|
||||||
|
entry: 'sendSelectionEvent',
|
||||||
|
exit: ['clearContext'],
|
||||||
|
},
|
||||||
|
|
||||||
enteringCloneFlow: {
|
enteringCloneFlow: {
|
||||||
states: {
|
states: {
|
||||||
selecting: {
|
selecting: {
|
||||||
|
|||||||
@ -88,6 +88,7 @@ import {
|
|||||||
setRotate,
|
setRotate,
|
||||||
insertExpressionNode,
|
insertExpressionNode,
|
||||||
retrievePathToNodeFromTransformSelection,
|
retrievePathToNodeFromTransformSelection,
|
||||||
|
setScale,
|
||||||
} from '@src/lang/modifyAst/setTransform'
|
} from '@src/lang/modifyAst/setTransform'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -403,6 +404,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
| { type: 'Appearance'; data: ModelingCommandSchema['Appearance'] }
|
||||||
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
| { type: 'Translate'; data: ModelingCommandSchema['Translate'] }
|
||||||
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
| { type: 'Rotate'; data: ModelingCommandSchema['Rotate'] }
|
||||||
|
| { type: 'Scale'; data: ModelingCommandSchema['Scale'] }
|
||||||
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
| { type: 'Clone'; data: ModelingCommandSchema['Clone'] }
|
||||||
| {
|
| {
|
||||||
type:
|
type:
|
||||||
@ -3318,7 +3320,7 @@ export const modelingMachine = setup({
|
|||||||
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const modifiedAst = structuredClone(ast)
|
const modifiedAst = structuredClone(ast)
|
||||||
const { x, y, z, nodeToEdit, selection } = input
|
const { x, y, z, global, nodeToEdit, selection } = input
|
||||||
let pathToNode = nodeToEdit
|
let pathToNode = nodeToEdit
|
||||||
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
||||||
const result = retrievePathToNodeFromTransformSelection(
|
const result = retrievePathToNodeFromTransformSelection(
|
||||||
@ -3368,6 +3370,7 @@ export const modelingMachine = setup({
|
|||||||
x: valueOrVariable(x),
|
x: valueOrVariable(x),
|
||||||
y: valueOrVariable(y),
|
y: valueOrVariable(y),
|
||||||
z: valueOrVariable(z),
|
z: valueOrVariable(z),
|
||||||
|
global,
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return Promise.reject(result)
|
return Promise.reject(result)
|
||||||
@ -3399,7 +3402,7 @@ export const modelingMachine = setup({
|
|||||||
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const modifiedAst = structuredClone(ast)
|
const modifiedAst = structuredClone(ast)
|
||||||
const { roll, pitch, yaw, nodeToEdit, selection } = input
|
const { roll, pitch, yaw, global, nodeToEdit, selection } = input
|
||||||
let pathToNode = nodeToEdit
|
let pathToNode = nodeToEdit
|
||||||
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
if (!(pathToNode && typeof pathToNode[1][0] === 'number')) {
|
||||||
const result = retrievePathToNodeFromTransformSelection(
|
const result = retrievePathToNodeFromTransformSelection(
|
||||||
@ -3449,6 +3452,89 @@ export const modelingMachine = setup({
|
|||||||
roll: valueOrVariable(roll),
|
roll: valueOrVariable(roll),
|
||||||
pitch: valueOrVariable(pitch),
|
pitch: valueOrVariable(pitch),
|
||||||
yaw: valueOrVariable(yaw),
|
yaw: valueOrVariable(yaw),
|
||||||
|
global,
|
||||||
|
})
|
||||||
|
if (err(result)) {
|
||||||
|
return Promise.reject(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateModelingState(
|
||||||
|
result.modifiedAst,
|
||||||
|
EXECUTION_TYPE_REAL,
|
||||||
|
{
|
||||||
|
kclManager,
|
||||||
|
editorManager,
|
||||||
|
codeManager,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
focusPath: [result.pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
),
|
||||||
|
scaleAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Scale'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) {
|
||||||
|
return Promise.reject(new Error(NO_INPUT_PROVIDED_MESSAGE))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = setScale({
|
||||||
|
pathToNode,
|
||||||
|
modifiedAst,
|
||||||
|
x: valueOrVariable(x),
|
||||||
|
y: valueOrVariable(y),
|
||||||
|
z: valueOrVariable(z),
|
||||||
|
global,
|
||||||
})
|
})
|
||||||
if (err(result)) {
|
if (err(result)) {
|
||||||
return Promise.reject(result)
|
return Promise.reject(result)
|
||||||
@ -3863,6 +3949,12 @@ export const modelingMachine = setup({
|
|||||||
guard: 'no kcl errors',
|
guard: 'no kcl errors',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Scale: {
|
||||||
|
target: 'Applying scale',
|
||||||
|
reenter: true,
|
||||||
|
guard: 'no kcl errors',
|
||||||
|
},
|
||||||
|
|
||||||
Clone: {
|
Clone: {
|
||||||
target: 'Applying clone',
|
target: 'Applying clone',
|
||||||
reenter: true,
|
reenter: true,
|
||||||
@ -5345,6 +5437,22 @@ export const modelingMachine = setup({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying scale': {
|
||||||
|
invoke: {
|
||||||
|
src: 'scaleAstMod',
|
||||||
|
id: 'scaleAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Scale') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: {
|
||||||
|
target: 'idle',
|
||||||
|
actions: 'toastError',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
'Applying clone': {
|
'Applying clone': {
|
||||||
invoke: {
|
invoke: {
|
||||||
src: 'cloneAstMod',
|
src: 'cloneAstMod',
|
||||||
|
|||||||
Reference in New Issue
Block a user