Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic axis case for Helix point-and-click #5240

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
a758f22
Move Helix button to a section with offset plane (3d 'construction' e…
pierremtb Feb 3, 2025
f147c47
Add generix axis case for Helix point-and-click
pierremtb Feb 3, 2025
5713d6c
Merge branch 'main' into pierremtb/issue5234-Move-Helix-button-to-a-s…
pierremtb Feb 4, 2025
27da72f
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 4, 2025
4d76915
Trigger CI
pierremtb Feb 4, 2025
c16da6f
Merge branch 'main' into pierremtb/issue5234-Move-Helix-button-to-a-s…
pierremtb Feb 4, 2025
b101bbf
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 4, 2025
4dfec7d
Trigger CI
pierremtb Feb 4, 2025
897e844
Merge branch 'pierremtb/issue5234-Move-Helix-button-to-a-section-with…
pierremtb Feb 4, 2025
6099b85
Clean up
pierremtb Feb 4, 2025
0802158
Merge branch 'main' into pierremtb/issue5234-Move-Helix-button-to-a-s…
pierremtb Feb 4, 2025
397307c
Merge branch 'main' into pierremtb/issue5234-Move-Helix-button-to-a-s…
pierremtb Feb 5, 2025
754abb9
Temp remove point and click test
pierremtb Feb 5, 2025
14eb903
Merge branch 'pierremtb/issue5234-Move-Helix-button-to-a-section-with…
pierremtb Feb 5, 2025
71513c5
Add back point and click test
pierremtb Feb 5, 2025
cd16927
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 5, 2025
6547b6d
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
82869b3
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
ed33938
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
6ab32bb
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
a21e360
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 5, 2025
e0a19ce
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
34834d2
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 5, 2025
acd60ed
Clean up
pierremtb Feb 5, 2025
fb3730f
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 5, 2025
967c284
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 5, 2025
83a445a
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
b814ec6
A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubunt…
github-actions[bot] Feb 5, 2025
b7a6b96
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 6, 2025
093f607
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 6, 2025
8ef72f9
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 6, 2025
8efedac
Fix helix arg after change to kwargs
pierremtb Feb 6, 2025
c21bdcf
Merge branch 'main' into pierremtb/issue5072-add-helix-command-flow-a…
pierremtb Feb 6, 2025
16de555
More fixes wrt helix arg after change to kwargs
pierremtb Feb 6, 2025
fcaa203
Fixed thanks to @adamchalmers
pierremtb Feb 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions e2e/playwright/fixtures/toolbarFixture.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export class ToolbarFixture {
shellButton!: Locator
revolveButton!: Locator
offsetPlaneButton!: Locator
helixButton!: Locator
startSketchBtn!: Locator
lineBtn!: Locator
rectangleBtn!: Locator
Expand Down Expand Up @@ -49,6 +50,7 @@ export class ToolbarFixture {
this.shellButton = page.getByTestId('shell')
this.revolveButton = page.getByTestId('revolve')
this.offsetPlaneButton = page.getByTestId('plane-offset')
this.helixButton = page.getByTestId('helix')
this.startSketchBtn = page.getByTestId('sketch')
this.lineBtn = page.getByTestId('line')
this.rectangleBtn = page.getByTestId('corner-rectangle')
Expand Down
65 changes: 65 additions & 0 deletions e2e/playwright/point-click.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -775,6 +775,71 @@ openSketch = startSketchOn('XY')
})
})

test('Helix point-and-click', async ({
context,
page,
homePage,
scene,
editor,
toolbar,
cmdBar,
}) => {
// One dumb hardcoded screen pixel value
const testPoint = { x: 620, y: 257 }
const expectedOutput = `helix001 = helix(revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5)`

await homePage.goToModelingScene()

await test.step(`Look for the red of the default plane`, async () => {
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
})
await test.step(`Go through the command bar flow`, async () => {
await toolbar.helixButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'revolutions',
currentArgValue: '1',
headerArguments: {
AngleStart: '',
Axis: '',
CounterClockWise: '',
Length: '',
Radius: '',
Revolutions: '',
},
highlightedHeaderArg: 'revolutions',
commandName: 'Helix',
})
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
await cmdBar.progressCmdBar()
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: [expectedOutput],
highlightedCode: '',
})
// Red plane is now gone, white helix is there
await scene.expectPixelColor([250, 250, 250], testPoint, 15)
})

await test.step('Delete offset plane via feature tree selection', async () => {
await editor.closePane()
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
await operationButton.click({ button: 'left' })
await page.keyboard.press('Backspace')
// Red plane is back
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
})
})

const loftPointAndClickCases = [
{ shouldPreselect: true },
{ shouldPreselect: false },
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
57 changes: 57 additions & 0 deletions src/lang/modifyAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,63 @@ export function addOffsetPlane({
}
}

/**
* Append a helix to the AST
*/
export function addHelix({
node,
revolutions,
angleStart,
counterClockWise,
radius,
axis,
length,
}: {
node: Node<Program>
revolutions: Expr
angleStart: Expr
counterClockWise: boolean
radius: Expr
axis: string
length: Expr
}): { modifiedAst: Node<Program>; pathToNode: PathToNode } {
const modifiedAst = structuredClone(node)
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.HELIX)
const variable = createVariableDeclaration(
name,
createCallExpressionStdLibKw(
'helix',
null, // Not in a pipeline
[
createLabeledArg('revolutions', revolutions),
createLabeledArg('angleStart', angleStart),
createLabeledArg('counterClockWise', createLiteral(counterClockWise)),
createLabeledArg('radius', radius),
createLabeledArg('axis', createLiteral(axis)),
createLabeledArg('length', length),
]
)
)

// TODO: figure out smart insertion than just appending at the end
const argIndex = 0
modifiedAst.body.push(variable)
const pathToNode: PathToNode = [
['body', ''],
[modifiedAst.body.length - 1, 'index'],
['declaration', 'VariableDeclaration'],
['init', 'VariableDeclarator'],
['arguments', 'CallExpressionKw'],
[argIndex, ARG_INDEX_FIELD],
['arg', LABELED_ARG_FIELD],
]

return {
modifiedAst,
pathToNode,
}
}

/**
* Return a modified clone of an AST with a named constant inserted into the body
*/
Expand Down
55 changes: 55 additions & 0 deletions src/lib/commandBarConfigs/modelingCommandConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ export type ModelingCommandSchema = {
plane: Selections
distance: KclCommandValue
}
Helix: {
revolutions: KclCommandValue
angleStart: KclCommandValue
counterClockWise: boolean
radius: KclCommandValue
axis: string
length: KclCommandValue
}
'change tool': {
tool: SketchTool
}
Expand Down Expand Up @@ -447,6 +455,53 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
},
},
},
Helix: {
description: 'Create a helix or spiral in 3D about an axis.',
icon: 'helix',
status: 'development',
needsReview: true,
args: {
revolutions: {
inputType: 'kcl',
defaultValue: '1',
required: true,
warningMessage:
'The helix workflow is new and under tested. Please break it and report issues.',
},
angleStart: {
inputType: 'kcl',
defaultValue: KCL_DEFAULT_DEGREE,
required: true,
},
counterClockWise: {
inputType: 'options',
required: true,
options: [
{ name: 'True', isCurrent: false, value: true },
{ name: 'False', isCurrent: true, value: false },
],
},
radius: {
inputType: 'kcl',
defaultValue: KCL_DEFAULT_LENGTH,
required: true,
},
axis: {
inputType: 'options',
required: true,
options: [
{ name: 'X Axis', isCurrent: true, value: 'X' },
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
{ name: 'Z Axis', isCurrent: false, value: 'Z' },
],
},
length: {
inputType: 'kcl',
defaultValue: KCL_DEFAULT_LENGTH,
required: true,
},
},
},
Fillet: {
description: 'Fillet edge',
icon: 'fillet3d',
Expand Down
1 change: 1 addition & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
SEGMENT: 'seg',
REVOLVE: 'revolve',
PLANE: 'plane',
HELIX: 'helix',
} as const
/** The default KCL length expression */
export const KCL_DEFAULT_LENGTH = `5`
Expand Down
10 changes: 8 additions & 2 deletions src/lib/toolbar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,15 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
],
{
id: 'helix',
onClick: () => console.error('Helix not yet implemented'),
onClick: () => {
commandBarActor.send({
type: 'Find and select command',
data: { name: 'Helix', groupId: 'modeling' },
})
},
hotkey: 'H',
icon: 'helix',
status: 'kcl-only',
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
title: 'Helix',
description: 'Create a helix or spiral in 3D about an axis.',
links: [{ label: 'KCL docs', url: 'https://zoo.dev/docs/kcl/helix' }],
Expand Down
87 changes: 87 additions & 0 deletions src/machines/modelingMachine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import {
} from 'components/Toolbar/EqualLength'
import { revolveSketch } from 'lang/modifyAst/addRevolve'
import {
addHelix,
addOffsetPlane,
addSweep,
deleteFromSelection,
Expand Down Expand Up @@ -276,6 +277,7 @@ export type ModelingMachineEvent =
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
| { type: 'Chamfer'; data?: ModelingCommandSchema['Chamfer'] }
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
| { type: 'Helix'; data: ModelingCommandSchema['Helix'] }
| { type: 'Text-to-CAD'; data: ModelingCommandSchema['Text-to-CAD'] }
| { type: 'Prompt-to-edit'; data: ModelingCommandSchema['Prompt-to-edit'] }
| {
Expand Down Expand Up @@ -1615,6 +1617,73 @@ export const modelingMachine = setup({
}
}
),
helixAstMod: fromPromise(
async ({
input,
}: {
input: ModelingCommandSchema['Helix'] | undefined
}) => {
if (!input) return new Error('No input provided')
// Extract inputs
const ast = kclManager.ast
const {
revolutions,
angleStart,
counterClockWise,
radius,
axis,
length,
} = input

for (const variable of [revolutions, angleStart, radius, length]) {
// Insert the variable if it exists
if (
'variableName' in variable &&
variable.variableName &&
variable.insertIndex !== undefined
) {
const newBody = [...ast.body]
newBody.splice(
variable.insertIndex,
0,
variable.variableDeclarationAst
)
ast.body = newBody
}
}

const valueOrVariable = (variable: KclCommandValue) =>
'variableName' in variable
? variable.variableIdentifierAst
: variable.valueAst

const result = addHelix({
node: ast,
revolutions: valueOrVariable(revolutions),
angleStart: valueOrVariable(angleStart),
counterClockWise,
radius: valueOrVariable(radius),
axis,
length: valueOrVariable(length),
})

const updateAstResult = await kclManager.updateAst(
result.modifiedAst,
true,
{
focusPath: [result.pathToNode],
}
)

await codeManager.updateEditorWithAstAndWriteToFile(
updateAstResult.newAst
)

if (updateAstResult?.selections) {
editorManager.selectRange(updateAstResult?.selections)
}
}
),
sweepAstMod: fromPromise(
async ({
input,
Expand Down Expand Up @@ -1974,6 +2043,11 @@ export const modelingMachine = setup({
reenter: true,
},

Helix: {
target: 'Applying helix',
reenter: true,
},

'Prompt-to-edit': 'Applying Prompt-to-edit',
},

Expand Down Expand Up @@ -2734,6 +2808,19 @@ export const modelingMachine = setup({
},
},

'Applying helix': {
invoke: {
src: 'helixAstMod',
id: 'helixAstMod',
input: ({ event }) => {
if (event.type !== 'Helix') return undefined
return event.data
},
onDone: ['idle'],
onError: ['idle'],
},
},

'Applying sweep': {
invoke: {
src: 'sweepAstMod',
Expand Down
Loading