Nadro/3716/mvp revolve (#3728)
* chore: Implemented a executeAst interrupt to stop processing a KCL program * fix: added a catch since this promise was not being caught * fix: fmt formatting, need to fix some tsc errors next. * fix: fixing tsc errors * fix: cleaning up comment * fix: only rejecting pending modeling commands * fix: adding constant for rejection message, adding rejection in WASM send command * fix: tsc, lint, fmt checks * feat: first pass over revolve with basic hard coded X axis * fix: updated revolve status for DEV only * fix: adding some TODOs to warn others about the Revolve MVP * fix: fmt, lint, tsc checks * fix: codespell got me * fix: xstate v5 upgrade * fix: removing this fix for a different PR. Not needed for initial MVP * fix: renaming extrude function to sweep since it fixes extrude and revolve now * fix: updating selection logic to support revolve * fix: renaming extrude to sweep since it adds revolve * fix: swapping as for type in function parameters * fix: updated from object destruct to structuredClone * fix: addressing PR comments * fix: one other typo for return value of revolve
This commit is contained in:
@ -38,7 +38,7 @@ import {
|
|||||||
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
||||||
import {
|
import {
|
||||||
Selections,
|
Selections,
|
||||||
canExtrudeSelection,
|
canSweepSelection,
|
||||||
handleSelectionBatch,
|
handleSelectionBatch,
|
||||||
isSelectionLastLine,
|
isSelectionLastLine,
|
||||||
isRangeInbetweenCharacters,
|
isRangeInbetweenCharacters,
|
||||||
@ -62,8 +62,8 @@ import {
|
|||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { Program, parse, recast } from 'lang/wasm'
|
import { Program, parse, recast } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
|
doesSceneHaveSweepableSketch,
|
||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
hasExtrudableGeometry,
|
|
||||||
isSingleCursorInPipe,
|
isSingleCursorInPipe,
|
||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import { exportFromEngine } from 'lib/exportFromEngine'
|
import { exportFromEngine } from 'lib/exportFromEngine'
|
||||||
@ -528,12 +528,32 @@ export const ModelingMachineProvider = ({
|
|||||||
// they have no selection, we should enable the button
|
// they have no selection, we should enable the button
|
||||||
// so they can select the face through the cmdbar
|
// so they can select the face through the cmdbar
|
||||||
// BUT only if there's extrudable geometry
|
// BUT only if there's extrudable geometry
|
||||||
if (hasExtrudableGeometry(kclManager.ast)) return true
|
if (doesSceneHaveSweepableSketch(kclManager.ast)) return true
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if (!isPipe) return false
|
if (!isPipe) return false
|
||||||
|
|
||||||
return canExtrudeSelection(selectionRanges)
|
return canSweepSelection(selectionRanges)
|
||||||
|
},
|
||||||
|
'has valid revolve selection': ({ context: { selectionRanges } }) => {
|
||||||
|
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
||||||
|
// TODO: I believe this guard only allows for extruding a single face at a time
|
||||||
|
const isPipe = isSketchPipe(selectionRanges)
|
||||||
|
|
||||||
|
if (
|
||||||
|
selectionRanges.codeBasedSelections.length === 0 ||
|
||||||
|
isRangeInbetweenCharacters(selectionRanges) ||
|
||||||
|
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||||
|
) {
|
||||||
|
// they have no selection, we should enable the button
|
||||||
|
// so they can select the face through the cmdbar
|
||||||
|
// BUT only if there's extrudable geometry
|
||||||
|
if (doesSceneHaveSweepableSketch(kclManager.ast)) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (!isPipe) return false
|
||||||
|
|
||||||
|
return canSweepSelection(selectionRanges)
|
||||||
},
|
},
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
context: { selectionRanges },
|
context: { selectionRanges },
|
||||||
|
@ -251,7 +251,7 @@ export function extrudeSketch(
|
|||||||
node: Program,
|
node: Program,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
shouldPipe = false,
|
shouldPipe = false,
|
||||||
distance = createLiteral(4) as Expr
|
distance: Expr = createLiteral(4)
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
@ -259,7 +259,7 @@ export function extrudeSketch(
|
|||||||
pathToExtrudeArg: PathToNode
|
pathToExtrudeArg: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
const _node = { ...node }
|
const _node = structuredClone(node)
|
||||||
const _node1 = getNodeFromPath(_node, pathToNode)
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
||||||
if (err(_node1)) return _node1
|
if (err(_node1)) return _node1
|
||||||
const { node: sketchExpression } = _node1
|
const { node: sketchExpression } = _node1
|
||||||
@ -342,6 +342,102 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function revolveSketch(
|
||||||
|
node: Program,
|
||||||
|
pathToNode: PathToNode,
|
||||||
|
shouldPipe = false,
|
||||||
|
angle: Expr = createLiteral(4)
|
||||||
|
):
|
||||||
|
| {
|
||||||
|
modifiedAst: Program
|
||||||
|
pathToNode: PathToNode
|
||||||
|
pathToRevolveArg: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
const _node = structuredClone(node)
|
||||||
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
||||||
|
if (err(_node1)) return _node1
|
||||||
|
const { node: sketchExpression } = _node1
|
||||||
|
|
||||||
|
// determine if sketchExpression is in a pipeExpression or not
|
||||||
|
const _node2 = getNodeFromPath<PipeExpression>(
|
||||||
|
_node,
|
||||||
|
pathToNode,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
if (err(_node2)) return _node2
|
||||||
|
const { node: pipeExpression } = _node2
|
||||||
|
|
||||||
|
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||||
|
|
||||||
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||||
|
_node,
|
||||||
|
pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(_node3)) return _node3
|
||||||
|
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
||||||
|
|
||||||
|
const revolveCall = createCallExpressionStdLib('revolve', [
|
||||||
|
createObjectExpression({
|
||||||
|
angle: angle,
|
||||||
|
// TODO: hard coded 'X' axis for revolve MVP, should be changed.
|
||||||
|
axis: createLiteral('X'),
|
||||||
|
}),
|
||||||
|
createIdentifier(variableDeclarator.id.name),
|
||||||
|
])
|
||||||
|
|
||||||
|
if (shouldPipe) {
|
||||||
|
const pipeChain = createPipeExpression(
|
||||||
|
isInPipeExpression
|
||||||
|
? [...pipeExpression.body, revolveCall]
|
||||||
|
: [sketchExpression as any, revolveCall]
|
||||||
|
)
|
||||||
|
|
||||||
|
variableDeclarator.init = pipeChain
|
||||||
|
const pathToRevolveArg: PathToNode = [
|
||||||
|
...pathToDecleration,
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['body', ''],
|
||||||
|
[pipeChain.body.length - 1, 'index'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
pathToRevolveArg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're not creating a pipe expression,
|
||||||
|
// but rather a separate constant for the extrusion
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
||||||
|
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
||||||
|
const sketchIndexInPathToNode =
|
||||||
|
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
||||||
|
const sketchIndexInBody = pathToDecleration[sketchIndexInPathToNode][0]
|
||||||
|
if (typeof sketchIndexInBody !== 'number')
|
||||||
|
return new Error('expected sketchIndexInBody to be a number')
|
||||||
|
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||||
|
|
||||||
|
const pathToRevolveArg: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[sketchIndexInBody + 1, 'index'],
|
||||||
|
['declarations', 'VariableDeclaration'],
|
||||||
|
[0, 'index'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode: [...pathToNode.slice(0, -1), [-1, 'index']],
|
||||||
|
pathToRevolveArg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function sketchOnExtrudedFace(
|
export function sketchOnExtrudedFace(
|
||||||
node: Program,
|
node: Program,
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
hasExtrudeSketchGroup,
|
hasExtrudeSketchGroup,
|
||||||
findUsesOfTagInPipe,
|
findUsesOfTagInPipe,
|
||||||
hasSketchPipeBeenExtruded,
|
hasSketchPipeBeenExtruded,
|
||||||
hasExtrudableGeometry,
|
doesSceneHaveSweepableSketch,
|
||||||
traverse,
|
traverse,
|
||||||
} from './queryAst'
|
} from './queryAst'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
@ -488,7 +488,7 @@ const sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Testing hasExtrudableGeometry', () => {
|
describe('Testing doesSceneHaveSweepableSketch', () => {
|
||||||
it('finds sketch001 pipe to be extruded', async () => {
|
it('finds sketch001 pipe to be extruded', async () => {
|
||||||
const exampleCode = `const sketch001 = startSketchOn('XZ')
|
const exampleCode = `const sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([3.29, 7.86], %)
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
@ -506,7 +506,7 @@ const sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = parse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const extrudable = hasExtrudableGeometry(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeTruthy()
|
expect(extrudable).toBeTruthy()
|
||||||
})
|
})
|
||||||
it('find sketch002 NOT pipe to be extruded', async () => {
|
it('find sketch002 NOT pipe to be extruded', async () => {
|
||||||
@ -520,7 +520,7 @@ const extrude001 = extrude(10, sketch001)
|
|||||||
`
|
`
|
||||||
const ast = parse(exampleCode)
|
const ast = parse(exampleCode)
|
||||||
if (err(ast)) throw ast
|
if (err(ast)) throw ast
|
||||||
const extrudable = hasExtrudableGeometry(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeFalsy()
|
expect(extrudable).toBeFalsy()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -880,7 +880,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
if (
|
if (
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
node.callee.type === 'Identifier' &&
|
node.callee.type === 'Identifier' &&
|
||||||
node.callee.name === 'extrude' &&
|
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
||||||
node.arguments?.[1]?.type === 'Identifier' &&
|
node.arguments?.[1]?.type === 'Identifier' &&
|
||||||
node.arguments[1].name === varDec.id.name
|
node.arguments[1].name === varDec.id.name
|
||||||
) {
|
) {
|
||||||
@ -892,7 +892,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File must contain at least one sketch that has not been extruded already */
|
/** File must contain at least one sketch that has not been extruded already */
|
||||||
export function hasExtrudableGeometry(ast: Program) {
|
export function doesSceneHaveSweepableSketch(ast: Program) {
|
||||||
const theMap: any = {}
|
const theMap: any = {}
|
||||||
traverse(ast as any, {
|
traverse(ast as any, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
@ -925,7 +925,7 @@ export function hasExtrudableGeometry(ast: Program) {
|
|||||||
}
|
}
|
||||||
} else if (
|
} else if (
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
node.callee.name === 'extrude' &&
|
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
||||||
node.arguments[1]?.type === 'Identifier' &&
|
node.arguments[1]?.type === 'Identifier' &&
|
||||||
theMap?.[node?.arguments?.[1]?.name]
|
theMap?.[node?.arguments?.[1]?.name]
|
||||||
) {
|
) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
import { StateMachineCommandSetConfig, KclCommandValue } from 'lib/commandTypes'
|
||||||
import { KCL_DEFAULT_LENGTH } from 'lib/constants'
|
import { KCL_DEFAULT_LENGTH, KCL_DEFAULT_DEGREE } from 'lib/constants'
|
||||||
import { components } from 'lib/machine-api'
|
import { components } from 'lib/machine-api'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { machineManager } from 'lib/machineManager'
|
import { machineManager } from 'lib/machineManager'
|
||||||
@ -32,6 +32,10 @@ export type ModelingCommandSchema = {
|
|||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||||
distance: KclCommandValue
|
distance: KclCommandValue
|
||||||
}
|
}
|
||||||
|
Revolve: {
|
||||||
|
selection: Selections
|
||||||
|
angle: KclCommandValue
|
||||||
|
}
|
||||||
Fillet: {
|
Fillet: {
|
||||||
// todo
|
// todo
|
||||||
selection: Selections
|
selection: Selections
|
||||||
@ -209,6 +213,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
args: {
|
args: {
|
||||||
selection: {
|
selection: {
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
|
// TODO: These are products of an extrude
|
||||||
selectionTypes: ['extrude-wall', 'start-cap', 'end-cap'],
|
selectionTypes: ['extrude-wall', 'start-cap', 'end-cap'],
|
||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
@ -232,6 +237,26 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
||||||
|
Revolve: {
|
||||||
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
|
icon: 'revolve',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
selection: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['extrude-wall', 'start-cap', 'end-cap'],
|
||||||
|
multiple: false, // TODO: multiple selection
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
},
|
||||||
|
angle: {
|
||||||
|
inputType: 'kcl',
|
||||||
|
defaultValue: KCL_DEFAULT_DEGREE,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Fillet: {
|
Fillet: {
|
||||||
// todo
|
// todo
|
||||||
description: 'Fillet edge',
|
description: 'Fillet edge',
|
||||||
|
@ -53,9 +53,14 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
|||||||
SKETCH: 'sketch',
|
SKETCH: 'sketch',
|
||||||
EXTRUDE: 'extrude',
|
EXTRUDE: 'extrude',
|
||||||
SEGMENT: 'seg',
|
SEGMENT: 'seg',
|
||||||
|
REVOLVE: 'revolve',
|
||||||
} as const
|
} as const
|
||||||
/** The default KCL length expression */
|
/** The default KCL length expression */
|
||||||
export const KCL_DEFAULT_LENGTH = `5`
|
export const KCL_DEFAULT_LENGTH = `5`
|
||||||
|
|
||||||
|
/** The default KCL degree expression */
|
||||||
|
export const KCL_DEFAULT_DEGREE = `360`
|
||||||
|
|
||||||
/** localStorage key for the playwright test-specific app settings file */
|
/** localStorage key for the playwright test-specific app settings file */
|
||||||
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
export const TEST_SETTINGS_FILE_KEY = 'playwright-test-settings'
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@ export function createMachineCommand<
|
|||||||
| Command<T, typeof type, S[typeof type]>[]
|
| Command<T, typeof type, S[typeof type]>[]
|
||||||
| null {
|
| null {
|
||||||
const commandConfig = commandBarConfig && commandBarConfig[type]
|
const commandConfig = commandBarConfig && commandBarConfig[type]
|
||||||
|
|
||||||
// There may be no command config for this event type,
|
// There may be no command config for this event type,
|
||||||
// or there may be multiple commands to create.
|
// or there may be multiple commands to create.
|
||||||
if (!commandConfig) {
|
if (!commandConfig) {
|
||||||
|
@ -395,10 +395,16 @@ function buildCommonNodeFromSelection(selectionRanges: Selections, i: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function nodeHasExtrude(node: CommonASTNode) {
|
function nodeHasExtrude(node: CommonASTNode) {
|
||||||
return doesPipeHaveCallExp({
|
return (
|
||||||
|
doesPipeHaveCallExp({
|
||||||
calleeName: 'extrude',
|
calleeName: 'extrude',
|
||||||
...node,
|
...node,
|
||||||
|
}) ||
|
||||||
|
doesPipeHaveCallExp({
|
||||||
|
calleeName: 'revolve',
|
||||||
|
...node,
|
||||||
})
|
})
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeHasClose(node: CommonASTNode) {
|
function nodeHasClose(node: CommonASTNode) {
|
||||||
@ -408,7 +414,7 @@ function nodeHasClose(node: CommonASTNode) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function canExtrudeSelection(selection: Selections) {
|
export function canSweepSelection(selection: Selections) {
|
||||||
const commonNodes = selection.codeBasedSelections.map((_, i) =>
|
const commonNodes = selection.codeBasedSelections.map((_, i) =>
|
||||||
buildCommonNodeFromSelection(selection, i)
|
buildCommonNodeFromSelection(selection, i)
|
||||||
)
|
)
|
||||||
|
@ -94,9 +94,16 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'revolve',
|
id: 'revolve',
|
||||||
onClick: () => console.error('Revolve not yet implemented'),
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Revolve', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
// TODO: disabled
|
||||||
|
// Who's state is this?
|
||||||
|
disabled: (state) => !state.can({ type: 'Revolve' }),
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: 'kcl-only',
|
status: DEV ? 'available' : 'kcl-only',
|
||||||
title: 'Revolve',
|
title: 'Revolve',
|
||||||
hotkey: 'R',
|
hotkey: 'R',
|
||||||
description:
|
description:
|
||||||
|
@ -33,7 +33,11 @@ import {
|
|||||||
applyConstraintEqualLength,
|
applyConstraintEqualLength,
|
||||||
setEqualLengthInfo,
|
setEqualLengthInfo,
|
||||||
} from 'components/Toolbar/EqualLength'
|
} from 'components/Toolbar/EqualLength'
|
||||||
import { deleteFromSelection, extrudeSketch } from 'lang/modifyAst'
|
import {
|
||||||
|
deleteFromSelection,
|
||||||
|
extrudeSketch,
|
||||||
|
revolveSketch,
|
||||||
|
} from 'lang/modifyAst'
|
||||||
import { applyFilletToSelection } from 'lang/modifyAst/addFillet'
|
import { applyFilletToSelection } from 'lang/modifyAst/addFillet'
|
||||||
import { getNodeFromPath } from '../lang/queryAst'
|
import { getNodeFromPath } from '../lang/queryAst'
|
||||||
import {
|
import {
|
||||||
@ -202,6 +206,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
||||||
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
||||||
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
||||||
|
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
||||||
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
||||||
| { type: 'Text-to-CAD'; data: ModelingCommandSchema['Text-to-CAD'] }
|
| { type: 'Text-to-CAD'; data: ModelingCommandSchema['Text-to-CAD'] }
|
||||||
| {
|
| {
|
||||||
@ -310,6 +315,7 @@ export const modelingMachine = setup({
|
|||||||
guards: {
|
guards: {
|
||||||
'Selection is on face': () => false,
|
'Selection is on face': () => false,
|
||||||
'has valid extrude selection': () => false,
|
'has valid extrude selection': () => false,
|
||||||
|
'has valid revolve selection': () => false,
|
||||||
'has valid fillet selection': () => false,
|
'has valid fillet selection': () => false,
|
||||||
'Has exportable geometry': () => false,
|
'Has exportable geometry': () => false,
|
||||||
'has valid selection for deletion': () => false,
|
'has valid selection for deletion': () => false,
|
||||||
@ -566,6 +572,53 @@ export const modelingMachine = setup({
|
|||||||
}
|
}
|
||||||
})().catch(reportRejection)
|
})().catch(reportRejection)
|
||||||
},
|
},
|
||||||
|
'AST revolve': ({ context: { store }, event }) => {
|
||||||
|
if (event.type !== 'Revolve') return
|
||||||
|
;(async () => {
|
||||||
|
if (!event.data) return
|
||||||
|
const { selection, angle } = event.data
|
||||||
|
let ast = kclManager.ast
|
||||||
|
if (
|
||||||
|
'variableName' in angle &&
|
||||||
|
angle.variableName &&
|
||||||
|
angle.insertIndex !== undefined
|
||||||
|
) {
|
||||||
|
const newBody = [...ast.body]
|
||||||
|
newBody.splice(angle.insertIndex, 0, angle.variableDeclarationAst)
|
||||||
|
ast.body = newBody
|
||||||
|
}
|
||||||
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
|
ast,
|
||||||
|
selection.codeBasedSelections[0].range
|
||||||
|
)
|
||||||
|
const revolveSketchRes = revolveSketch(
|
||||||
|
ast,
|
||||||
|
pathToNode,
|
||||||
|
false,
|
||||||
|
'variableName' in angle ? angle.variableIdentifierAst : angle.valueAst
|
||||||
|
)
|
||||||
|
if (trap(revolveSketchRes)) return
|
||||||
|
const { modifiedAst, pathToRevolveArg } = revolveSketchRes
|
||||||
|
|
||||||
|
store.videoElement?.pause()
|
||||||
|
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||||
|
focusPath: pathToRevolveArg,
|
||||||
|
zoomToFit: true,
|
||||||
|
zoomOnRangeAndType: {
|
||||||
|
range: selection.codeBasedSelections[0].range,
|
||||||
|
type: 'path',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (!engineCommandManager.engineConnection?.idleMode) {
|
||||||
|
store.videoElement?.play().catch((e) => {
|
||||||
|
console.warn('Video playing was prevented', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (updatedAst?.selections) {
|
||||||
|
editorManager.selectRange(updatedAst?.selections)
|
||||||
|
}
|
||||||
|
})().catch(reportRejection)
|
||||||
|
},
|
||||||
'AST delete selection': ({ context: { selectionRanges } }) => {
|
'AST delete selection': ({ context: { selectionRanges } }) => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
let ast = kclManager.ast
|
let ast = kclManager.ast
|
||||||
@ -1238,6 +1291,13 @@ export const modelingMachine = setup({
|
|||||||
reenter: false,
|
reenter: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Revolve: {
|
||||||
|
target: 'idle',
|
||||||
|
guard: 'has valid revolve selection',
|
||||||
|
actions: ['AST revolve'],
|
||||||
|
reenter: false,
|
||||||
|
},
|
||||||
|
|
||||||
Fillet: {
|
Fillet: {
|
||||||
target: 'idle',
|
target: 'idle',
|
||||||
guard: 'has valid fillet selection', // TODO: fix selections
|
guard: 'has valid fillet selection', // TODO: fix selections
|
||||||
|
Reference in New Issue
Block a user