diff --git a/e2e/playwright/point-click.spec.ts b/e2e/playwright/point-click.spec.ts index 1c64cfe76..94eb12b35 100644 --- a/e2e/playwright/point-click.spec.ts +++ b/e2e/playwright/point-click.spec.ts @@ -1068,7 +1068,7 @@ openSketch = startSketchOn('XY') }) }) - test('Helix point-and-click', async ({ + test('Helix point-and-click on default axis', async ({ context, page, homePage, @@ -1084,9 +1084,6 @@ openSketch = startSketchOn('XY') 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({ @@ -1177,6 +1174,165 @@ openSketch = startSketchOn('XY') }) }) + const helixCases = [ + { + selectionType: 'segment', + testPoint: { x: 513, y: 221 }, + expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, counterClockWise = true, radius = 1, axis = seg01, length = 100,)`, + expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, counterClockWise = true, radius = 1, axis = seg01, length = 50,)`, + }, + { + selectionType: 'sweepEdge', + testPoint: { x: 564, y: 364 }, + expectedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, counterClockWise = true, radius = 1, axis = getOppositeEdge(seg01), length = 100,)`, + expectedEditedOutput: `helix001 = helix( revolutions = 20, angleStart = 0, counterClockWise = true, radius = 1, axis = getOppositeEdge(seg01), length = 50,)`, + }, + ] + helixCases.map( + ({ selectionType, testPoint, expectedOutput, expectedEditedOutput }) => { + test(`Helix point-and-click around ${selectionType}`, async ({ + context, + page, + homePage, + scene, + editor, + toolbar, + cmdBar, + }) => { + page.on('console', console.log) + const initialCode = `sketch001 = startSketchOn('XZ') + profile001 = startProfileAt([0, 0], sketch001) + |> yLine(length = 100) + |> line(endAbsolute = [100, 0]) + |> line(endAbsolute = [profileStartX(%), profileStartY(%)]) + |> close() + extrude001 = extrude(profile001, length = 100)` + + // One dumb hardcoded screen pixel value + const [clickOnEdge] = scene.makeMouseHelpers(testPoint.x, testPoint.y) + + await context.addInitScript((initialCode) => { + localStorage.setItem('persistCode', initialCode) + }, initialCode) + await page.setBodyDimensions({ width: 1000, height: 500 }) + await homePage.goToModelingScene() + + await test.step(`Go through the command bar flow`, async () => { + await toolbar.closePane('code') + await toolbar.helixButton.click() + await cmdBar.expectState({ + stage: 'arguments', + currentArgKey: 'axisOrEdge', + currentArgValue: '', + headerArguments: { + AngleStart: '', + AxisOrEdge: '', + CounterClockWise: '', + Length: '', + Radius: '', + Revolutions: '', + }, + highlightedHeaderArg: 'axisOrEdge', + commandName: 'Helix', + }) + await cmdBar.selectOption({ name: 'Edge' }).click() + await clickOnEdge() + await cmdBar.progressCmdBar() + await cmdBar.argumentInput.focus() + await page.keyboard.insertText('20') + await cmdBar.progressCmdBar() + await page.keyboard.insertText('0') + await cmdBar.progressCmdBar() + await cmdBar.selectOption({ name: 'True' }).click() + await page.keyboard.insertText('1') + await cmdBar.progressCmdBar() + await page.keyboard.insertText('100') + await cmdBar.progressCmdBar() + await cmdBar.expectState({ + stage: 'review', + headerArguments: { + AngleStart: '0', + AxisOrEdge: 'Edge', + Edge: `1 ${selectionType}`, + CounterClockWise: '', + Length: '100', + Radius: '1', + Revolutions: '20', + }, + commandName: 'Helix', + }) + await cmdBar.progressCmdBar() + }) + + await test.step(`Confirm code is added to the editor, scene has changed`, async () => { + await toolbar.openPane('code') + await editor.expectEditor.toContain(expectedOutput) + await toolbar.closePane('code') + }) + + await test.step(`Edit helix through the feature tree`, async () => { + await toolbar.openPane('feature-tree') + const operationButton = await toolbar.getFeatureTreeOperation( + 'Helix', + 0 + ) + await operationButton.dblclick() + const initialInput = '100' + const newInput = '50' + await cmdBar.expectState({ + commandName: 'Helix', + stage: 'arguments', + currentArgKey: 'length', + currentArgValue: initialInput, + headerArguments: { + AngleStart: '0', + CounterClockWise: '', + Length: initialInput, + Radius: '1', + Revolutions: '20', + }, + highlightedHeaderArg: 'length', + }) + await expect(cmdBar.currentArgumentInput).toBeVisible() + await cmdBar.currentArgumentInput + .locator('.cm-content') + .fill(newInput) + await cmdBar.progressCmdBar() + await cmdBar.expectState({ + stage: 'review', + headerArguments: { + AngleStart: '0', + CounterClockWise: '', + Length: newInput, + Radius: '1', + Revolutions: '20', + }, + commandName: 'Helix', + }) + await cmdBar.progressCmdBar() + await toolbar.closePane('feature-tree') + await toolbar.openPane('code') + await editor.expectEditor.toContain(expectedEditedOutput) + await toolbar.closePane('code') + }) + + 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 editor.expectEditor.not.toContain(expectedEditedOutput) + expect( + await toolbar.getFeatureTreeOperation('Helix', 0) + ).not.toBeVisible() + }) + }) + } + ) + const loftPointAndClickCases = [ { shouldPreselect: true }, { shouldPreselect: false }, diff --git a/src/lib/commandBarConfigs/modelingCommandConfig.ts b/src/lib/commandBarConfigs/modelingCommandConfig.ts index aae61aa78..b9c531908 100644 --- a/src/lib/commandBarConfigs/modelingCommandConfig.ts +++ b/src/lib/commandBarConfigs/modelingCommandConfig.ts @@ -529,9 +529,8 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig< commandContext.argumentsToSubmit.axisOrEdge as string ), inputType: 'selection', - selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'], + selectionTypes: ['segment', 'sweepEdge'], multiple: false, - skip: true, hidden: (context) => Boolean(context.argumentsToSubmit.nodeToEdit), }, revolutions: { diff --git a/src/lib/operations.ts b/src/lib/operations.ts index e82b37a6a..eb613b468 100644 --- a/src/lib/operations.ts +++ b/src/lib/operations.ts @@ -3,6 +3,7 @@ import { Artifact, getArtifactOfTypes, getCapCodeRef, + getSweepEdgeCodeRef, } from 'lang/std/artifactGraph' import { Operation } from '@rust/kcl-lib/bindings/Operation' import { codeManager, engineCommandManager, kclManager } from './singletons' @@ -332,9 +333,11 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { let axis: string | undefined let edge: Selections | undefined if (axisValue.type === 'String') { + // default axis casee axisOrEdge = 'Axis' axis = axisValue.value } else if (axisValue.type === 'TagIdentifier' && axisValue.artifact_id) { + // segment case axisOrEdge = 'Edge' const artifact = getArtifactOfTypes( { @@ -356,6 +359,37 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { ], otherSelections: [], } + } else if (axisValue.type === 'Uuid') { + // sweepEdge case + axisOrEdge = 'Edge' + const artifact = getArtifactOfTypes( + { + key: axisValue.value, + types: ['sweepEdge'], + }, + engineCommandManager.artifactGraph + ) + if (err(artifact)) { + return baseCommand + } + + const codeRef = getSweepEdgeCodeRef( + artifact, + engineCommandManager.artifactGraph + ) + if (err(codeRef)) { + return baseCommand + } + + edge = { + graphSelections: [ + { + artifact, + codeRef, + }, + ], + otherSelections: [], + } } else { return baseCommand } @@ -364,8 +398,9 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { if ( !('revolutions' in operation.labeledArgs) || !operation.labeledArgs.revolutions - ) + ) { return baseCommand + } const revolutions = await stringToKclExpression( codeManager.code.slice( operation.labeledArgs.revolutions.sourceRange[0], @@ -378,22 +413,26 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { if ( !('angleStart' in operation.labeledArgs) || !operation.labeledArgs.angleStart - ) + ) { return baseCommand + } const angleStart = await stringToKclExpression( codeManager.code.slice( operation.labeledArgs.angleStart.sourceRange[0], operation.labeledArgs.angleStart.sourceRange[1] ) ) - if (err(angleStart) || 'errors' in angleStart) return baseCommand + if (err(angleStart) || 'errors' in angleStart) { + return baseCommand + } // counterClockWise options boolean arg if ( !('counterClockWise' in operation.labeledArgs) || !operation.labeledArgs.counterClockWise - ) + ) { return baseCommand + } const counterClockWise = codeManager.code.slice( operation.labeledArgs.counterClockWise.sourceRange[0], @@ -401,26 +440,35 @@ const prepareToEditHelix: PrepareToEditCallback = async ({ operation }) => { ) === 'true' // radius kcl arg - if (!('radius' in operation.labeledArgs) || !operation.labeledArgs.radius) + if (!('radius' in operation.labeledArgs) || !operation.labeledArgs.radius) { + console.log( + "!('radius' in operation.labeledArgs) || !operation.labeledArgs.radius" + ) return baseCommand + } const radius = await stringToKclExpression( codeManager.code.slice( operation.labeledArgs.radius.sourceRange[0], operation.labeledArgs.radius.sourceRange[1] ) ) - if (err(radius) || 'errors' in radius) return baseCommand + if (err(radius) || 'errors' in radius) { + return baseCommand + } // length kcl arg - if (!('length' in operation.labeledArgs) || !operation.labeledArgs.length) + if (!('length' in operation.labeledArgs) || !operation.labeledArgs.length) { return baseCommand + } const length = await stringToKclExpression( codeManager.code.slice( operation.labeledArgs.length.sourceRange[0], operation.labeledArgs.length.sourceRange[1] ) ) - if (err(length) || 'errors' in length) return baseCommand + if (err(length) || 'errors' in length) { + return baseCommand + } // Assemble the default argument values for the Offset Plane command, // with `nodeToEdit` set, which will let the Offset Plane actor know diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index e6dbfa407..ca9831d18 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -1940,15 +1940,17 @@ export const modelingMachine = setup({ ast ) if (err(getAxisResult)) return getAxisResult - const { generatedAxis, axisIndexIfAxis } = 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 + // *or* after the sweep that consumes the sketch, in which case the below code doesn't work // If an axis was selected in KCL, find the max index to insert the revolve command - if (axisIndexIfAxis) { - opInsertIndex = axisIndexIfAxis + 1 - } + // if (axisIndexIfAxis) { + // opInsertIndex = axisIndexIfAxis + 1 + // } for (const variable of [revolutions, angleStart, radius, length]) { // Insert the variable if it exists