Point-and-click Helix from cylinders (#5979)
@ -1082,8 +1082,8 @@ openSketch = startSketchOn(XY)
|
|||||||
}) => {
|
}) => {
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 620, y: 257 }
|
const testPoint = { x: 620, y: 257 }
|
||||||
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, ccw = false, radius = 5, axis = 'X', length = 5,)`
|
const expectedOutput = `helix001 = helix( axis = 'X', radius = 5, length = 5, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||||
const expectedLine = `revolutions=1,`
|
const expectedLine = `axis='X',`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
@ -1091,17 +1091,17 @@ openSketch = startSketchOn(XY)
|
|||||||
await toolbar.helixButton.click()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'axisOrEdge',
|
currentArgKey: 'mode',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
|
Mode: '',
|
||||||
AngleStart: '',
|
AngleStart: '',
|
||||||
AxisOrEdge: '',
|
Revolutions: '',
|
||||||
CounterClockWise: '',
|
|
||||||
Length: '',
|
Length: '',
|
||||||
Radius: '',
|
Radius: '',
|
||||||
Revolutions: '',
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'axisOrEdge',
|
highlightedHeaderArg: 'mode',
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
@ -1110,7 +1110,19 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: 'Axis',
|
||||||
|
Axis: 'X',
|
||||||
|
AngleStart: '360',
|
||||||
|
Revolutions: '1',
|
||||||
|
Length: '5',
|
||||||
|
Radius: '5',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1134,30 +1146,31 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'length',
|
currentArgKey: 'CounterClockWise',
|
||||||
currentArgValue: initialInput,
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '360',
|
|
||||||
Axis: 'X',
|
Axis: 'X',
|
||||||
CounterClockWise: '',
|
AngleStart: '360',
|
||||||
Length: initialInput,
|
|
||||||
Radius: '5',
|
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
|
Radius: '5',
|
||||||
|
Length: initialInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'length',
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
})
|
})
|
||||||
|
await page.keyboard.press('Shift+Backspace')
|
||||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
await cmdBar.currentArgumentInput.locator('.cm-content').fill(newInput)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '360',
|
|
||||||
Axis: 'X',
|
Axis: 'X',
|
||||||
CounterClockWise: '',
|
AngleStart: '360',
|
||||||
Length: newInput,
|
|
||||||
Radius: '5',
|
|
||||||
Revolutions: '1',
|
Revolutions: '1',
|
||||||
|
Radius: '5',
|
||||||
|
Length: newInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
@ -1181,14 +1194,14 @@ openSketch = startSketchOn(XY)
|
|||||||
{
|
{
|
||||||
selectionType: 'segment',
|
selectionType: 'segment',
|
||||||
testPoint: { x: 513, y: 221 },
|
testPoint: { x: 513, y: 221 },
|
||||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 100,)`,
|
expectedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = seg01, length = 50,)`,
|
expectedEditedOutput: `helix001 = helix( axis = seg01, radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selectionType: 'sweepEdge',
|
selectionType: 'sweepEdge',
|
||||||
testPoint: { x: 564, y: 364 },
|
testPoint: { x: 564, y: 364 },
|
||||||
expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`,
|
expectedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 100, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, ccw = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`,
|
expectedEditedOutput: `helix001 = helix( axis = getOppositeEdge(seg01), radius = 1, length = 50, revolutions = 20, angleStart = 0, ccw = false,)`,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
helixCases.map(
|
helixCases.map(
|
||||||
@ -1225,17 +1238,17 @@ openSketch = startSketchOn(XY)
|
|||||||
await toolbar.helixButton.click()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'axisOrEdge',
|
currentArgKey: 'mode',
|
||||||
currentArgValue: '',
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '',
|
AngleStart: '',
|
||||||
AxisOrEdge: '',
|
Mode: '',
|
||||||
CounterClockWise: '',
|
CounterClockWise: '',
|
||||||
Length: '',
|
Length: '',
|
||||||
Radius: '',
|
Radius: '',
|
||||||
Revolutions: '',
|
Revolutions: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'axisOrEdge',
|
highlightedHeaderArg: 'mode',
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
await cmdBar.selectOption({ name: 'Edge' }).click()
|
await cmdBar.selectOption({ name: 'Edge' }).click()
|
||||||
@ -1246,7 +1259,6 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await page.keyboard.insertText('0')
|
await page.keyboard.insertText('0')
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await cmdBar.selectOption({ name: 'True' }).click()
|
|
||||||
await page.keyboard.insertText('1')
|
await page.keyboard.insertText('1')
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await page.keyboard.insertText('100')
|
await page.keyboard.insertText('100')
|
||||||
@ -1254,13 +1266,13 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '0',
|
Mode: 'Edge',
|
||||||
AxisOrEdge: 'Edge',
|
|
||||||
Edge: `1 ${selectionType}`,
|
Edge: `1 ${selectionType}`,
|
||||||
CounterClockWise: '',
|
AngleStart: '0',
|
||||||
Length: '100',
|
|
||||||
Radius: '1',
|
|
||||||
Revolutions: '20',
|
Revolutions: '20',
|
||||||
|
Radius: '1',
|
||||||
|
Length: '100',
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
@ -1285,17 +1297,18 @@ openSketch = startSketchOn(XY)
|
|||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
stage: 'arguments',
|
stage: 'arguments',
|
||||||
currentArgKey: 'length',
|
currentArgKey: 'CounterClockWise',
|
||||||
currentArgValue: initialInput,
|
currentArgValue: '',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '0',
|
AngleStart: '0',
|
||||||
CounterClockWise: '',
|
|
||||||
Length: initialInput,
|
|
||||||
Radius: '1',
|
|
||||||
Revolutions: '20',
|
Revolutions: '20',
|
||||||
|
Radius: '1',
|
||||||
|
Length: initialInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
highlightedHeaderArg: 'length',
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
})
|
})
|
||||||
|
await page.keyboard.press('Shift+Backspace')
|
||||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||||
await cmdBar.currentArgumentInput
|
await cmdBar.currentArgumentInput
|
||||||
.locator('.cm-content')
|
.locator('.cm-content')
|
||||||
@ -1305,10 +1318,10 @@ openSketch = startSketchOn(XY)
|
|||||||
stage: 'review',
|
stage: 'review',
|
||||||
headerArguments: {
|
headerArguments: {
|
||||||
AngleStart: '0',
|
AngleStart: '0',
|
||||||
CounterClockWise: '',
|
|
||||||
Length: newInput,
|
|
||||||
Radius: '1',
|
|
||||||
Revolutions: '20',
|
Revolutions: '20',
|
||||||
|
Radius: '1',
|
||||||
|
Length: newInput,
|
||||||
|
CounterClockWise: '',
|
||||||
},
|
},
|
||||||
commandName: 'Helix',
|
commandName: 'Helix',
|
||||||
})
|
})
|
||||||
@ -1336,6 +1349,141 @@ openSketch = startSketchOn(XY)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test('Helix point-and-click on cylinder', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn(XY)
|
||||||
|
profile001 = circle(
|
||||||
|
sketch001,
|
||||||
|
center = [0, 0],
|
||||||
|
radius = 100,
|
||||||
|
tag = $seg01,
|
||||||
|
)
|
||||||
|
extrude001 = extrude(profile001, length = 100)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 620, y: 257 }
|
||||||
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const expectedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = false,)`
|
||||||
|
const expectedLine = `cylinder = extrude001,`
|
||||||
|
const expectedEditedOutput = `helix001 = helix( cylinder = extrude001, revolutions = 1, angleStart = 360, ccw = true,)`
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
|
await toolbar.helixButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'mode',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: '',
|
||||||
|
AngleStart: '',
|
||||||
|
Revolutions: '',
|
||||||
|
Length: '',
|
||||||
|
Radius: '',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'mode',
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.selectOption({ name: 'Cylinder' }).click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'cylinder',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: 'Cylinder',
|
||||||
|
Cylinder: '',
|
||||||
|
AngleStart: '',
|
||||||
|
Revolutions: '',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'cylinder',
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await clickOnWall()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Mode: 'Cylinder',
|
||||||
|
Cylinder: '1 face',
|
||||||
|
AngleStart: '360',
|
||||||
|
Revolutions: '1',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(expectedOutput)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [expectedLine],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Edit helix through the feature tree`, async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||||
|
await operationButton.dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Helix',
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'CounterClockWise',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
AngleStart: '360',
|
||||||
|
Revolutions: '1',
|
||||||
|
CounterClockWise: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'CounterClockWise',
|
||||||
|
})
|
||||||
|
await cmdBar.selectOption({ name: 'True' }).click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
AngleStart: '360',
|
||||||
|
Revolutions: '1',
|
||||||
|
CounterClockWise: 'true',
|
||||||
|
},
|
||||||
|
commandName: 'Helix',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(expectedEditedOutput)
|
||||||
|
await editor.closePane()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete helix via feature tree selection', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Delete')
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.not.toContain(expectedEditedOutput)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
const loftPointAndClickCases = [
|
||||||
{ shouldPreselect: true },
|
{ shouldPreselect: true },
|
||||||
{ shouldPreselect: false },
|
{ shouldPreselect: false },
|
||||||
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
@ -80,7 +80,8 @@ function CommandBarKclInput({
|
|||||||
: arg.variableName
|
: arg.variableName
|
||||||
}
|
}
|
||||||
// or derive it from the previously set value or the argument name
|
// or derive it from the previously set value or the argument name
|
||||||
return previouslySetValue && 'variableName' in previouslySetValue
|
return typeof previouslySetValue === 'object' &&
|
||||||
|
'variableName' in previouslySetValue
|
||||||
? previouslySetValue.variableName
|
? previouslySetValue.variableName
|
||||||
: arg.name
|
: arg.name
|
||||||
}, [
|
}, [
|
||||||
@ -96,7 +97,8 @@ function CommandBarKclInput({
|
|||||||
)
|
)
|
||||||
const [value, setValue] = useState(initialValue)
|
const [value, setValue] = useState(initialValue)
|
||||||
const [createNewVariable, setCreateNewVariable] = useState(
|
const [createNewVariable, setCreateNewVariable] = useState(
|
||||||
(previouslySetValue && 'variableName' in previouslySetValue) ||
|
(typeof previouslySetValue === 'object' &&
|
||||||
|
'variableName' in previouslySetValue) ||
|
||||||
arg.createVariable === 'byDefault' ||
|
arg.createVariable === 'byDefault' ||
|
||||||
arg.createVariable === 'force' ||
|
arg.createVariable === 'force' ||
|
||||||
false
|
false
|
||||||
@ -132,7 +134,8 @@ function CommandBarKclInput({
|
|||||||
selection: {
|
selection: {
|
||||||
anchor: 0,
|
anchor: 0,
|
||||||
head:
|
head:
|
||||||
previouslySetValue && 'valueText' in previouslySetValue
|
typeof previouslySetValue === 'object' &&
|
||||||
|
'valueText' in previouslySetValue
|
||||||
? previouslySetValue.valueText.length
|
? previouslySetValue.valueText.length
|
||||||
: defaultValue.length,
|
: defaultValue.length,
|
||||||
},
|
},
|
||||||
|
@ -811,40 +811,53 @@ export function addOffsetPlane({
|
|||||||
*/
|
*/
|
||||||
export function addHelix({
|
export function addHelix({
|
||||||
node,
|
node,
|
||||||
|
axis,
|
||||||
|
cylinder,
|
||||||
revolutions,
|
revolutions,
|
||||||
angleStart,
|
angleStart,
|
||||||
ccw,
|
|
||||||
radius,
|
radius,
|
||||||
axis,
|
|
||||||
length,
|
length,
|
||||||
|
ccw,
|
||||||
insertIndex,
|
insertIndex,
|
||||||
variableName,
|
variableName,
|
||||||
}: {
|
}: {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
|
axis?: Node<Literal> | Node<Name | CallExpression | CallExpressionKw>
|
||||||
|
cylinder?: VariableDeclarator
|
||||||
revolutions: Expr
|
revolutions: Expr
|
||||||
angleStart: Expr
|
angleStart: Expr
|
||||||
|
radius?: Expr
|
||||||
|
length?: Expr
|
||||||
ccw: boolean
|
ccw: boolean
|
||||||
radius: Expr
|
|
||||||
axis: Node<Literal> | Node<Name | CallExpression | CallExpressionKw>
|
|
||||||
length: Expr
|
|
||||||
insertIndex?: number
|
insertIndex?: number
|
||||||
variableName?: string
|
variableName?: string
|
||||||
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
const modifiedAst = structuredClone(node)
|
const modifiedAst = structuredClone(node)
|
||||||
const name =
|
const name =
|
||||||
variableName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.HELIX)
|
variableName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.HELIX)
|
||||||
|
const modeArgs: CallExpressionKw['arguments'] = []
|
||||||
|
if (axis && radius) {
|
||||||
|
modeArgs.push(createLabeledArg('axis', axis))
|
||||||
|
modeArgs.push(createLabeledArg('radius', radius))
|
||||||
|
if (length) {
|
||||||
|
modeArgs.push(createLabeledArg('length', length))
|
||||||
|
}
|
||||||
|
} else if (cylinder) {
|
||||||
|
modeArgs.push(
|
||||||
|
createLabeledArg('cylinder', createLocalName(cylinder.id.name))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const variable = createVariableDeclaration(
|
const variable = createVariableDeclaration(
|
||||||
name,
|
name,
|
||||||
createCallExpressionStdLibKw(
|
createCallExpressionStdLibKw(
|
||||||
'helix',
|
'helix',
|
||||||
null, // Not in a pipeline
|
null, // Not in a pipeline
|
||||||
[
|
[
|
||||||
|
...modeArgs,
|
||||||
createLabeledArg('revolutions', revolutions),
|
createLabeledArg('revolutions', revolutions),
|
||||||
createLabeledArg('angleStart', angleStart),
|
createLabeledArg('angleStart', angleStart),
|
||||||
createLabeledArg('ccw', createLiteral(ccw)),
|
createLabeledArg('ccw', createLiteral(ccw)),
|
||||||
createLabeledArg('radius', radius),
|
|
||||||
createLabeledArg('axis', axis),
|
|
||||||
createLabeledArg('length', length),
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -38,6 +38,8 @@ export const EXTRUSION_RESULTS = [
|
|||||||
|
|
||||||
export const COMMAND_APPEARANCE_COLOR_DEFAULT = 'default'
|
export const COMMAND_APPEARANCE_COLOR_DEFAULT = 'default'
|
||||||
|
|
||||||
|
export type HelixModes = 'Axis' | 'Edge' | 'Cylinder'
|
||||||
|
|
||||||
export type ModelingCommandSchema = {
|
export type ModelingCommandSchema = {
|
||||||
'Enter sketch': {}
|
'Enter sketch': {}
|
||||||
Export: {
|
Export: {
|
||||||
@ -103,15 +105,17 @@ export type ModelingCommandSchema = {
|
|||||||
// Enables editing workflow
|
// Enables editing workflow
|
||||||
nodeToEdit?: PathToNode
|
nodeToEdit?: PathToNode
|
||||||
// Flow arg
|
// Flow arg
|
||||||
axisOrEdge: 'Axis' | 'Edge'
|
mode: HelixModes
|
||||||
|
// Three different arguments depending on mode
|
||||||
|
axis?: string
|
||||||
|
edge?: Selections
|
||||||
|
cylinder?: Selections
|
||||||
// KCL stdlib arguments
|
// KCL stdlib arguments
|
||||||
axis: string | undefined
|
|
||||||
edge: Selections | undefined
|
|
||||||
revolutions: KclCommandValue
|
revolutions: KclCommandValue
|
||||||
angleStart: KclCommandValue
|
angleStart: KclCommandValue
|
||||||
ccw: boolean
|
radius?: KclCommandValue // axis or edge modes only
|
||||||
radius: KclCommandValue
|
length?: KclCommandValue // axis or edge modes only
|
||||||
length: KclCommandValue
|
ccw: boolean // optional boolean argument, default value to false
|
||||||
}
|
}
|
||||||
'event.parameter.create': {
|
'event.parameter.create': {
|
||||||
value: KclCommandValue
|
value: KclCommandValue
|
||||||
@ -530,7 +534,6 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
Helix: {
|
Helix: {
|
||||||
description: 'Create a helix or spiral in 3D about an axis.',
|
description: 'Create a helix or spiral in 3D about an axis.',
|
||||||
icon: 'helix',
|
icon: 'helix',
|
||||||
status: 'development',
|
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
nodeToEdit: {
|
nodeToEdit: {
|
||||||
@ -541,69 +544,91 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
required: false,
|
required: false,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
},
|
},
|
||||||
axisOrEdge: {
|
mode: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
required: true,
|
required: true,
|
||||||
defaultValue: 'Axis',
|
defaultValue: 'Axis',
|
||||||
options: [
|
options: [
|
||||||
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
||||||
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
||||||
|
{ name: 'Cylinder', isCurrent: false, value: 'Cylinder' },
|
||||||
],
|
],
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
axis: {
|
axis: {
|
||||||
inputType: 'options',
|
inputType: 'options',
|
||||||
required: (commandContext) =>
|
required: (commandContext) =>
|
||||||
['Axis'].includes(
|
['Axis'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
|
||||||
),
|
|
||||||
options: [
|
options: [
|
||||||
{ name: 'X Axis', value: 'X' },
|
{ name: 'X Axis', value: 'X' },
|
||||||
{ name: 'Y Axis', value: 'Y' },
|
{ name: 'Y Axis', value: 'Y' },
|
||||||
{ name: 'Z Axis', value: 'Z' },
|
{ name: 'Z Axis', value: 'Z' },
|
||||||
],
|
],
|
||||||
|
hidden: false, // for consistency here, we can actually edit here since it's not a selection
|
||||||
},
|
},
|
||||||
edge: {
|
edge: {
|
||||||
required: (commandContext) =>
|
required: (commandContext) =>
|
||||||
['Edge'].includes(
|
['Edge'].includes(commandContext.argumentsToSubmit.mode as string),
|
||||||
commandContext.argumentsToSubmit.axisOrEdge as string
|
|
||||||
),
|
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment', 'sweepEdge'],
|
selectionTypes: ['segment', 'sweepEdge'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
},
|
},
|
||||||
|
cylinder: {
|
||||||
|
required: (commandContext) =>
|
||||||
|
['Cylinder'].includes(
|
||||||
|
commandContext.argumentsToSubmit.mode as string
|
||||||
|
),
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['wall'],
|
||||||
|
multiple: false,
|
||||||
|
hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit),
|
||||||
|
},
|
||||||
revolutions: {
|
revolutions: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: '1',
|
defaultValue: '1',
|
||||||
required: true,
|
required: true,
|
||||||
warningMessage:
|
|
||||||
'The helix workflow is new and under tested. Please break it and report issues.',
|
|
||||||
},
|
},
|
||||||
angleStart: {
|
angleStart: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_DEGREE,
|
defaultValue: KCL_DEFAULT_DEGREE,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
ccw: {
|
|
||||||
inputType: 'options',
|
|
||||||
required: true,
|
|
||||||
displayName: 'CounterClockWise',
|
|
||||||
defaultValue: false,
|
|
||||||
options: [
|
|
||||||
{ name: 'False', value: false },
|
|
||||||
{ name: 'True', value: true },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
radius: {
|
radius: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
required: true,
|
required: (commandContext) =>
|
||||||
|
!['Cylinder'].includes(
|
||||||
|
commandContext.argumentsToSubmit.mode as string
|
||||||
|
),
|
||||||
},
|
},
|
||||||
length: {
|
length: {
|
||||||
inputType: 'kcl',
|
inputType: 'kcl',
|
||||||
defaultValue: KCL_DEFAULT_LENGTH,
|
defaultValue: KCL_DEFAULT_LENGTH,
|
||||||
|
required: (commandContext) =>
|
||||||
|
!['Cylinder'].includes(
|
||||||
|
commandContext.argumentsToSubmit.mode as string
|
||||||
|
),
|
||||||
|
},
|
||||||
|
ccw: {
|
||||||
|
inputType: 'options',
|
||||||
|
skip: true,
|
||||||
required: true,
|
required: true,
|
||||||
|
defaultValue: false,
|
||||||
|
valueSummary: (value) => String(value),
|
||||||
|
displayName: 'CounterClockWise',
|
||||||
|
options: (commandContext) => [
|
||||||
|
{
|
||||||
|
name: 'False',
|
||||||
|
value: false,
|
||||||
|
isCurrent: !Boolean(commandContext.argumentsToSubmit.ccw),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'True',
|
||||||
|
value: true,
|
||||||
|
isCurrent: Boolean(commandContext.argumentsToSubmit.ccw),
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
getCapCodeRef,
|
getCapCodeRef,
|
||||||
getEdgeCutConsumedCodeRef,
|
getEdgeCutConsumedCodeRef,
|
||||||
getSweepEdgeCodeRef,
|
getSweepEdgeCodeRef,
|
||||||
|
getWallCodeRef,
|
||||||
} from 'lang/std/artifactGraph'
|
} from 'lang/std/artifactGraph'
|
||||||
import { Operation } from '@rust/kcl-lib/bindings/Operation'
|
import { Operation } from '@rust/kcl-lib/bindings/Operation'
|
||||||
import { codeManager, engineCommandManager, kclManager } from './singletons'
|
import { codeManager, engineCommandManager, kclManager } from './singletons'
|
||||||
@ -13,10 +14,14 @@ import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
|||||||
import { sourceRangeFromRust } from 'lang/wasm'
|
import { sourceRangeFromRust } from 'lang/wasm'
|
||||||
import { CommandBarMachineEvent } from 'machines/commandBarMachine'
|
import { CommandBarMachineEvent } from 'machines/commandBarMachine'
|
||||||
import { stringToKclExpression } from './kclHelpers'
|
import { stringToKclExpression } from './kclHelpers'
|
||||||
import { ModelingCommandSchema } from './commandBarConfigs/modelingCommandConfig'
|
import {
|
||||||
|
HelixModes,
|
||||||
|
ModelingCommandSchema,
|
||||||
|
} from './commandBarConfigs/modelingCommandConfig'
|
||||||
import { isDefaultPlaneStr } from './planes'
|
import { isDefaultPlaneStr } from './planes'
|
||||||
import { Selection, Selections } from './selections'
|
import { Selection, Selections } from './selections'
|
||||||
import { rustContext } from './singletons'
|
import { rustContext } from './singletons'
|
||||||
|
import { KclExpression } from './commandTypes'
|
||||||
|
|
||||||
type ExecuteCommandEvent = CommandBarMachineEvent & {
|
type ExecuteCommandEvent = CommandBarMachineEvent & {
|
||||||
type: 'Find and select command'
|
type: 'Find and select command'
|
||||||
@ -571,27 +576,32 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
groupId: 'modeling',
|
groupId: 'modeling',
|
||||||
}
|
}
|
||||||
if (operation.type !== 'StdLibCall' || !operation.labeledArgs) {
|
if (operation.type !== 'StdLibCall' || !operation.labeledArgs) {
|
||||||
return baseCommand
|
return { reason: 'Wrong operation type or arguments' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: find a way to loop over the arguments while keeping it safe
|
// Flow arg
|
||||||
|
let mode: HelixModes | undefined
|
||||||
// axis options string arg
|
// Three different arguments depending on mode
|
||||||
if (!('axis' in operation.labeledArgs) || !operation.labeledArgs.axis) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
|
|
||||||
const axisValue = operation.labeledArgs.axis.value
|
|
||||||
let axisOrEdge: 'Axis' | 'Edge' | undefined
|
|
||||||
let axis: string | undefined
|
let axis: string | undefined
|
||||||
let edge: Selections | undefined
|
let edge: Selections | undefined
|
||||||
|
let cylinder: Selections | undefined
|
||||||
|
// Rest of stdlib args
|
||||||
|
let revolutions: KclExpression | undefined // common to all modes, can't remain undefined
|
||||||
|
let angleStart: KclExpression | undefined // common to all modes, can't remain undefined
|
||||||
|
let length: KclExpression | undefined // axis or edge modes only
|
||||||
|
let radius: KclExpression | undefined // axis or edge modes only
|
||||||
|
let ccw = false // optional boolean argument, default value
|
||||||
|
|
||||||
|
if ('axis' in operation.labeledArgs && operation.labeledArgs.axis) {
|
||||||
|
// axis options string or selection arg
|
||||||
|
const axisValue = operation.labeledArgs.axis.value
|
||||||
if (axisValue.type === 'String') {
|
if (axisValue.type === 'String') {
|
||||||
// default axis casee
|
// default axis casee
|
||||||
axisOrEdge = 'Axis'
|
mode = 'Axis'
|
||||||
axis = axisValue.value
|
axis = axisValue.value
|
||||||
} else if (axisValue.type === 'TagIdentifier' && axisValue.artifact_id) {
|
} else if (axisValue.type === 'TagIdentifier' && axisValue.artifact_id) {
|
||||||
// segment case
|
// segment case
|
||||||
axisOrEdge = 'Edge'
|
mode = 'Edge'
|
||||||
const artifact = getArtifactOfTypes(
|
const artifact = getArtifactOfTypes(
|
||||||
{
|
{
|
||||||
key: axisValue.artifact_id,
|
key: axisValue.artifact_id,
|
||||||
@ -600,7 +610,7 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
engineCommandManager.artifactGraph
|
engineCommandManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (err(artifact)) {
|
if (err(artifact)) {
|
||||||
return baseCommand
|
return { reason: "Couldn't find related edge artifact" }
|
||||||
}
|
}
|
||||||
|
|
||||||
edge = {
|
edge = {
|
||||||
@ -614,7 +624,7 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
}
|
}
|
||||||
} else if (axisValue.type === 'Uuid') {
|
} else if (axisValue.type === 'Uuid') {
|
||||||
// sweepEdge case
|
// sweepEdge case
|
||||||
axisOrEdge = 'Edge'
|
mode = 'Edge'
|
||||||
const artifact = getArtifactOfTypes(
|
const artifact = getArtifactOfTypes(
|
||||||
{
|
{
|
||||||
key: axisValue.value,
|
key: axisValue.value,
|
||||||
@ -623,7 +633,7 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
engineCommandManager.artifactGraph
|
engineCommandManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (err(artifact)) {
|
if (err(artifact)) {
|
||||||
return baseCommand
|
return { reason: "Couldn't find related edge artifact" }
|
||||||
}
|
}
|
||||||
|
|
||||||
const codeRef = getSweepEdgeCodeRef(
|
const codeRef = getSweepEdgeCodeRef(
|
||||||
@ -631,7 +641,7 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
engineCommandManager.artifactGraph
|
engineCommandManager.artifactGraph
|
||||||
)
|
)
|
||||||
if (err(codeRef)) {
|
if (err(codeRef)) {
|
||||||
return baseCommand
|
return { reason: "Couldn't find related edge code ref" }
|
||||||
}
|
}
|
||||||
|
|
||||||
edge = {
|
edge = {
|
||||||
@ -644,94 +654,148 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => {
|
|||||||
otherSelections: [],
|
otherSelections: [],
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return baseCommand
|
return { reason: 'The type of the axis argument is unsupported' }
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
'cylinder' in operation.labeledArgs &&
|
||||||
|
operation.labeledArgs.cylinder
|
||||||
|
) {
|
||||||
|
mode = 'Cylinder'
|
||||||
|
// axis cylinder selection arg
|
||||||
|
if (operation.labeledArgs.cylinder.value.type !== 'Solid') {
|
||||||
|
return { reason: "Cylinder arg found isn't of type Solid" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// revolutions kcl arg
|
const sweepId = operation.labeledArgs.cylinder.value.value.artifactId
|
||||||
if (
|
const wallArtifact = [...engineCommandManager.artifactGraph.values()].find(
|
||||||
!('revolutions' in operation.labeledArgs) ||
|
(p) => p.type === 'wall' && p.sweepId === sweepId
|
||||||
!operation.labeledArgs.revolutions
|
)
|
||||||
) {
|
if (!wallArtifact || wallArtifact.type !== 'wall') {
|
||||||
return baseCommand
|
return {
|
||||||
|
reason: "Cylinder arg found doesn't point to a valid sweep wall",
|
||||||
}
|
}
|
||||||
const revolutions = await stringToKclExpression(
|
}
|
||||||
|
|
||||||
|
const wallCodeRef = getWallCodeRef(
|
||||||
|
wallArtifact,
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(wallCodeRef)) {
|
||||||
|
return {
|
||||||
|
reason: "Cylinder arg found doesn't point to a valid sweep code ref",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cylinder = {
|
||||||
|
graphSelections: [
|
||||||
|
{
|
||||||
|
artifact: wallArtifact,
|
||||||
|
codeRef: wallCodeRef,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
otherSelections: [],
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
reason: "The axis or cylinder arguments couldn't be prepared for edit",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// revolutions kcl arg (common for all)
|
||||||
|
if (
|
||||||
|
'revolutions' in operation.labeledArgs &&
|
||||||
|
operation.labeledArgs.revolutions
|
||||||
|
) {
|
||||||
|
const r = await stringToKclExpression(
|
||||||
codeManager.code.slice(
|
codeManager.code.slice(
|
||||||
operation.labeledArgs.revolutions.sourceRange[0],
|
operation.labeledArgs.revolutions.sourceRange[0],
|
||||||
operation.labeledArgs.revolutions.sourceRange[1]
|
operation.labeledArgs.revolutions.sourceRange[1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (err(revolutions) || 'errors' in revolutions) return baseCommand
|
if (err(r) || 'errors' in r) {
|
||||||
|
return { reason: 'Errors found in revolutions argument' }
|
||||||
// angleStart kcl arg
|
|
||||||
if (
|
|
||||||
!('angleStart' in operation.labeledArgs) ||
|
|
||||||
!operation.labeledArgs.angleStart
|
|
||||||
) {
|
|
||||||
return baseCommand
|
|
||||||
}
|
}
|
||||||
const angleStart = await stringToKclExpression(
|
|
||||||
|
revolutions = r
|
||||||
|
} else {
|
||||||
|
return { reason: "Couldn't find revolutions argument" }
|
||||||
|
}
|
||||||
|
|
||||||
|
// angleStart kcl arg (common for all)
|
||||||
|
if (
|
||||||
|
'angleStart' in operation.labeledArgs &&
|
||||||
|
operation.labeledArgs.angleStart
|
||||||
|
) {
|
||||||
|
const r = await stringToKclExpression(
|
||||||
codeManager.code.slice(
|
codeManager.code.slice(
|
||||||
operation.labeledArgs.angleStart.sourceRange[0],
|
operation.labeledArgs.angleStart.sourceRange[0],
|
||||||
operation.labeledArgs.angleStart.sourceRange[1]
|
operation.labeledArgs.angleStart.sourceRange[1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (err(angleStart) || 'errors' in angleStart) {
|
if (err(r) || 'errors' in r) {
|
||||||
return baseCommand
|
return { reason: 'Errors found in angleStart argument' }
|
||||||
}
|
}
|
||||||
|
|
||||||
// counterClockWise options boolean arg
|
angleStart = r
|
||||||
if (!('ccw' in operation.labeledArgs) || !operation.labeledArgs.ccw) {
|
} else {
|
||||||
return baseCommand
|
return { reason: "Couldn't find angleStart argument" }
|
||||||
}
|
}
|
||||||
const ccw =
|
|
||||||
codeManager.code.slice(
|
|
||||||
operation.labeledArgs.ccw.sourceRange[0],
|
|
||||||
operation.labeledArgs.ccw.sourceRange[1]
|
|
||||||
) === 'true'
|
|
||||||
|
|
||||||
// radius kcl arg
|
// radius and cylinder and kcl arg (only for axis or edge)
|
||||||
if (!('radius' in operation.labeledArgs) || !operation.labeledArgs.radius) {
|
if (mode !== 'Cylinder') {
|
||||||
console.log(
|
if ('radius' in operation.labeledArgs && operation.labeledArgs.radius) {
|
||||||
"!('radius' in operation.labeledArgs) || !operation.labeledArgs.radius"
|
const r = await stringToKclExpression(
|
||||||
)
|
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
const radius = await stringToKclExpression(
|
|
||||||
codeManager.code.slice(
|
codeManager.code.slice(
|
||||||
operation.labeledArgs.radius.sourceRange[0],
|
operation.labeledArgs.radius.sourceRange[0],
|
||||||
operation.labeledArgs.radius.sourceRange[1]
|
operation.labeledArgs.radius.sourceRange[1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (err(radius) || 'errors' in radius) {
|
if (err(r) || 'errors' in r) {
|
||||||
return baseCommand
|
return { reason: 'Error in radius argument retrieval' }
|
||||||
|
}
|
||||||
|
radius = r
|
||||||
|
} else {
|
||||||
|
return { reason: "Couldn't find radius argument" }
|
||||||
}
|
}
|
||||||
|
|
||||||
// length kcl arg
|
if ('length' in operation.labeledArgs && operation.labeledArgs.length) {
|
||||||
if (!('length' in operation.labeledArgs) || !operation.labeledArgs.length) {
|
const r = await stringToKclExpression(
|
||||||
return baseCommand
|
|
||||||
}
|
|
||||||
const length = await stringToKclExpression(
|
|
||||||
codeManager.code.slice(
|
codeManager.code.slice(
|
||||||
operation.labeledArgs.length.sourceRange[0],
|
operation.labeledArgs.length.sourceRange[0],
|
||||||
operation.labeledArgs.length.sourceRange[1]
|
operation.labeledArgs.length.sourceRange[1]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if (err(length) || 'errors' in length) {
|
if (err(r) || 'errors' in r) {
|
||||||
return baseCommand
|
return { reason: 'Error in length argument retrieval' }
|
||||||
|
}
|
||||||
|
length = r
|
||||||
|
} else {
|
||||||
|
return { reason: "Couldn't find length argument" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// counterClockWise boolean arg (optional)
|
||||||
|
if ('ccw' in operation.labeledArgs && operation.labeledArgs.ccw) {
|
||||||
|
ccw =
|
||||||
|
codeManager.code.slice(
|
||||||
|
operation.labeledArgs.ccw.sourceRange[0],
|
||||||
|
operation.labeledArgs.ccw.sourceRange[1]
|
||||||
|
) === 'true'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assemble the default argument values for the Offset Plane command,
|
// Assemble the default argument values for the Offset Plane command,
|
||||||
// with `nodeToEdit` set, which will let the Offset Plane actor know
|
// with `nodeToEdit` set, which will let the Offset Plane actor know
|
||||||
// to edit the node that corresponds to the StdLibCall.
|
// to edit the node that corresponds to the StdLibCall.
|
||||||
const argDefaultValues: ModelingCommandSchema['Helix'] = {
|
const argDefaultValues: ModelingCommandSchema['Helix'] = {
|
||||||
axisOrEdge,
|
mode,
|
||||||
axis,
|
axis,
|
||||||
edge,
|
edge,
|
||||||
|
cylinder,
|
||||||
revolutions,
|
revolutions,
|
||||||
angleStart,
|
angleStart,
|
||||||
ccw,
|
|
||||||
radius,
|
radius,
|
||||||
length,
|
length,
|
||||||
|
ccw,
|
||||||
nodeToEdit: getNodePathFromSourceRange(
|
nodeToEdit: getNodePathFromSourceRange(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
sourceRangeFromRust(operation.sourceRange)
|
sourceRangeFromRust(operation.sourceRange)
|
||||||
|
@ -366,6 +366,7 @@ export const commandBarMachine = setup({
|
|||||||
!(argConfig.inputType === 'kcl' || argConfig.skip)
|
!(argConfig.inputType === 'kcl' || argConfig.skip)
|
||||||
const hasInvalidKclValue =
|
const hasInvalidKclValue =
|
||||||
argConfig.inputType === 'kcl' &&
|
argConfig.inputType === 'kcl' &&
|
||||||
|
isRequired &&
|
||||||
!(argValue as Partial<KclCommandValue> | undefined)?.valueAst
|
!(argValue as Partial<KclCommandValue> | undefined)?.valueAst
|
||||||
const hasInvalidOptionsValue =
|
const hasInvalidOptionsValue =
|
||||||
isRequired &&
|
isRequired &&
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
import {
|
import {
|
||||||
|
CallExpression,
|
||||||
|
CallExpressionKw,
|
||||||
Expr,
|
Expr,
|
||||||
|
Literal,
|
||||||
|
Name,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
@ -1910,11 +1914,13 @@ export const modelingMachine = setup({
|
|||||||
}) => {
|
}) => {
|
||||||
if (!input) return new Error('No input provided')
|
if (!input) return new Error('No input provided')
|
||||||
// Extract inputs
|
// Extract inputs
|
||||||
|
console.log('input', input)
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const {
|
const {
|
||||||
axisOrEdge,
|
mode,
|
||||||
axis,
|
axis,
|
||||||
edge,
|
edge,
|
||||||
|
cylinder,
|
||||||
revolutions,
|
revolutions,
|
||||||
angleStart,
|
angleStart,
|
||||||
ccw,
|
ccw,
|
||||||
@ -1946,16 +1952,50 @@ export const modelingMachine = setup({
|
|||||||
opInsertIndex = nodeToEdit[1][0]
|
opInsertIndex = nodeToEdit[1][0]
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAxisResult = getAxisExpressionAndIndex(
|
let cylinderDeclarator: VariableDeclarator | undefined
|
||||||
axisOrEdge,
|
let axisExpression:
|
||||||
axis,
|
| Node<CallExpression | CallExpressionKw | Name>
|
||||||
edge,
|
| Node<Literal>
|
||||||
ast
|
| undefined
|
||||||
|
|
||||||
|
if (mode === 'Cylinder') {
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
cylinder &&
|
||||||
|
cylinder.graphSelections[0] &&
|
||||||
|
cylinder.graphSelections[0].artifact?.type === 'wall'
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return new Error('Cylinder argument not valid')
|
||||||
|
}
|
||||||
|
const clonedAstForGetExtrude = structuredClone(ast)
|
||||||
|
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
|
||||||
|
clonedAstForGetExtrude,
|
||||||
|
cylinder.graphSelections[0],
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(extrudeLookupResult)) {
|
||||||
|
return extrudeLookupResult
|
||||||
|
}
|
||||||
|
const extrudeNode = getNodeFromPath<VariableDeclaration>(
|
||||||
|
ast,
|
||||||
|
extrudeLookupResult.pathToExtrudeNode,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(extrudeNode)) {
|
||||||
|
return extrudeNode
|
||||||
|
}
|
||||||
|
cylinderDeclarator = extrudeNode.node.declaration
|
||||||
|
} else if (mode === 'Axis' || mode === 'Edge') {
|
||||||
|
const getAxisResult = getAxisExpressionAndIndex(mode, axis, edge, ast)
|
||||||
|
if (err(getAxisResult)) {
|
||||||
|
return getAxisResult
|
||||||
|
}
|
||||||
|
axisExpression = getAxisResult.generatedAxis
|
||||||
|
} else {
|
||||||
|
return new Error(
|
||||||
|
'Generated axis or cylinder declarator selection is missing.'
|
||||||
)
|
)
|
||||||
if (err(getAxisResult)) return getAxisResult
|
|
||||||
const { generatedAxis } = getAxisResult
|
|
||||||
if (!generatedAxis) {
|
|
||||||
return new Error('Generated axis selection is missing.')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: figure out if we want to smart insert after the sketch as below
|
// TODO: figure out if we want to smart insert after the sketch as below
|
||||||
@ -1965,13 +2005,13 @@ export const modelingMachine = setup({
|
|||||||
// opInsertIndex = axisIndexIfAxis + 1
|
// opInsertIndex = axisIndexIfAxis + 1
|
||||||
// }
|
// }
|
||||||
|
|
||||||
for (const variable of [revolutions, angleStart, radius, length]) {
|
for (const v of [revolutions, angleStart, radius, length]) {
|
||||||
|
if (v === undefined) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const variable = v as KclCommandValue
|
||||||
// Insert the variable if it exists
|
// Insert the variable if it exists
|
||||||
if (
|
if ('variableName' in variable && variable.variableName) {
|
||||||
'variableName' in variable &&
|
|
||||||
variable.variableName &&
|
|
||||||
variable.insertIndex !== undefined
|
|
||||||
) {
|
|
||||||
const newBody = [...ast.body]
|
const newBody = [...ast.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
variable.insertIndex,
|
variable.insertIndex,
|
||||||
@ -1982,19 +2022,21 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const valueOrVariable = (variable: KclCommandValue) =>
|
const valueOrVariable = (variable: KclCommandValue) => {
|
||||||
'variableName' in variable
|
return 'variableName' in variable
|
||||||
? variable.variableIdentifierAst
|
? variable.variableIdentifierAst
|
||||||
: variable.valueAst
|
: variable.valueAst
|
||||||
|
}
|
||||||
|
|
||||||
const { modifiedAst, pathToNode } = addHelix({
|
const { modifiedAst, pathToNode } = addHelix({
|
||||||
node: ast,
|
node: ast,
|
||||||
revolutions: valueOrVariable(revolutions),
|
revolutions: valueOrVariable(revolutions),
|
||||||
angleStart: valueOrVariable(angleStart),
|
angleStart: valueOrVariable(angleStart),
|
||||||
ccw,
|
ccw,
|
||||||
radius: valueOrVariable(radius),
|
radius: radius ? valueOrVariable(radius) : undefined,
|
||||||
axis: generatedAxis,
|
axis: axisExpression,
|
||||||
length: valueOrVariable(length),
|
cylinder: cylinderDeclarator,
|
||||||
|
length: length ? valueOrVariable(length) : undefined,
|
||||||
insertIndex: opInsertIndex,
|
insertIndex: opInsertIndex,
|
||||||
variableName: opVariableName,
|
variableName: opVariableName,
|
||||||
})
|
})
|
||||||
|