Refactor addFillet into addEdgeTreatment Function Supporting Chamfers (#4593)
* refactor code mod and tests * tsc * make lint happy * remove dumby data Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch> --------- Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
This commit is contained in:
@ -82,7 +82,7 @@ import { getVarNameModal } from 'hooks/useToolbarGuards'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { modelingMachineEvent } from 'editor/manager'
|
||||
import { hasValidFilletSelection } from 'lang/modifyAst/addFillet'
|
||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addFillet'
|
||||
import {
|
||||
ExportIntent,
|
||||
EngineConnectionStateType,
|
||||
@ -576,8 +576,10 @@ export const ModelingMachineProvider = ({
|
||||
if (selectionRanges.graphSelections.length <= 0) return false
|
||||
return true
|
||||
},
|
||||
'has valid fillet selection': ({ context: { selectionRanges } }) => {
|
||||
return hasValidFilletSelection({
|
||||
'has valid edge treatment selection': ({
|
||||
context: { selectionRanges },
|
||||
}) => {
|
||||
return hasValidEdgeTreatmentSelection({
|
||||
selectionRanges,
|
||||
ast: kclManager.ast,
|
||||
code: codeManager.code,
|
||||
|
@ -10,10 +10,14 @@ import {
|
||||
VariableDeclarator,
|
||||
} from '../wasm'
|
||||
import {
|
||||
EdgeTreatmentType,
|
||||
getPathToExtrudeForSegmentSelection,
|
||||
hasValidFilletSelection,
|
||||
isTagUsedInFillet,
|
||||
modifyAstWithFilletAndTag,
|
||||
hasValidEdgeTreatmentSelection,
|
||||
isTagUsedInEdgeTreatment,
|
||||
modifyAstWithEdgeTreatmentAndTag,
|
||||
FilletParameters,
|
||||
ChamferParameters,
|
||||
EdgeTreatmentParameters,
|
||||
} from './addFillet'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||
import { createLiteral } from 'lang/modifyAst'
|
||||
@ -21,7 +25,6 @@ import { err } from 'lib/trap'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { VITE_KC_DEV_TOKEN } from 'env'
|
||||
import { KclCommandValue } from 'lib/commandTypes'
|
||||
import { isOverlap } from 'lib/utils'
|
||||
import { codeRefFromRange } from 'lang/std/artifactGraph'
|
||||
|
||||
@ -253,10 +256,10 @@ extrude003 = extrude(-15, sketch003)`
|
||||
})
|
||||
})
|
||||
|
||||
const runModifyAstCloneWithFilletAndTag = async (
|
||||
const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
||||
code: string,
|
||||
selectionSnippets: Array<string>,
|
||||
radiusValue: number,
|
||||
parameters: EdgeTreatmentParameters,
|
||||
expectedCode: string
|
||||
) => {
|
||||
// ast
|
||||
@ -274,13 +277,6 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
]
|
||||
)
|
||||
|
||||
// radius
|
||||
const radius: KclCommandValue = {
|
||||
valueAst: createLiteral(radiusValue),
|
||||
valueText: radiusValue.toString(),
|
||||
valueCalculated: radiusValue.toString(),
|
||||
}
|
||||
|
||||
// executeAst
|
||||
await kclManager.executeAst({ ast })
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -299,8 +295,8 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
otherSelections: [],
|
||||
}
|
||||
|
||||
// apply fillet to selection
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
// apply edge treatment to seleciton
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
if (err(result)) {
|
||||
return result
|
||||
}
|
||||
@ -310,8 +306,41 @@ const runModifyAstCloneWithFilletAndTag = async (
|
||||
|
||||
expect(newCode).toContain(expectedCode)
|
||||
}
|
||||
describe('Testing applyFilletToSelection', () => {
|
||||
it('should add a fillet to a specific segment', async () => {
|
||||
const createFilletParameters = (radiusValue: number): FilletParameters => ({
|
||||
type: EdgeTreatmentType.Fillet,
|
||||
radius: {
|
||||
valueAst: createLiteral(radiusValue),
|
||||
valueText: radiusValue.toString(),
|
||||
valueCalculated: radiusValue.toString(),
|
||||
},
|
||||
})
|
||||
const createChamferParameters = (lengthValue: number): ChamferParameters => ({
|
||||
type: EdgeTreatmentType.Chamfer,
|
||||
length: {
|
||||
valueAst: createLiteral(lengthValue),
|
||||
valueText: lengthValue.toString(),
|
||||
valueCalculated: lengthValue.toString(),
|
||||
},
|
||||
})
|
||||
// Iterate tests over all edge treatment types
|
||||
Object.values(EdgeTreatmentType).forEach(
|
||||
(edgeTreatmentType: EdgeTreatmentType) => {
|
||||
// create parameters based on the edge treatment type
|
||||
let parameterName: string
|
||||
let parameters: EdgeTreatmentParameters
|
||||
if (edgeTreatmentType === EdgeTreatmentType.Fillet) {
|
||||
parameterName = 'radius'
|
||||
parameters = createFilletParameters(3)
|
||||
} else if (edgeTreatmentType === EdgeTreatmentType.Chamfer) {
|
||||
parameterName = 'length'
|
||||
parameters = createChamferParameters(3)
|
||||
} else {
|
||||
// Handle future edge treatments
|
||||
return new Error(`Unsupported edge treatment type: ${edgeTreatmentType}`)
|
||||
}
|
||||
// run tests
|
||||
describe(`Testing modifyAstCloneWithEdgeTreatmentAndTag with ${edgeTreatmentType}s`, () => {
|
||||
it(`should add a ${edgeTreatmentType} to a specific segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -321,7 +350,6 @@ describe('Testing applyFilletToSelection', () => {
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -330,16 +358,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to the sketch pipe', async () => {
|
||||
it(`should add a ${edgeTreatmentType} to the sketch pipe`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -349,7 +377,6 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)`
|
||||
const segmentSnippets = ['line([0, -20], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -358,16 +385,16 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(-15, %)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to an already tagged segment', async () => {
|
||||
it(`should add a ${edgeTreatmentType} to an already tagged segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -377,7 +404,6 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([0, -20], %, $seg01)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -386,16 +412,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet with existing tag on other segment', async () => {
|
||||
it(`should add a ${edgeTreatmentType} with existing tag on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -405,7 +431,6 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -414,16 +439,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet with existing fillet on other segment', async () => {
|
||||
it(`should add a ${edgeTreatmentType} with existing fillet on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -434,7 +459,6 @@ extrude001 = extrude(-15, sketch001)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -444,16 +468,45 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 5, tags = [seg01] }, %)
|
||||
|> fillet({ radius = 3, tags = [seg02] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add a fillet to two segments of a single extrusion', async () => {
|
||||
it(`should add a ${edgeTreatmentType} with existing chamfer on other segment`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)`
|
||||
const segmentSnippets = ['line([-20, 0], %)']
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
|> line([0, -20], %)
|
||||
|> line([-20, 0], %, $seg02)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> chamfer({ length: 5, tags: [seg01] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName}: 3, tags: [seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it(`should add a ${edgeTreatmentType} to two segments of a single extrusion`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -463,7 +516,6 @@ extrude001 = extrude(-15, sketch001)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)`
|
||||
const segmentSnippets = ['line([20, 0], %)', 'line([-20, 0], %)']
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -472,16 +524,16 @@ extrude001 = extrude(-15, sketch001)`
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)`
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)`
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
it('should add fillets to two bodies', async () => {
|
||||
it(`should add ${edgeTreatmentType}s to two bodies`, async () => {
|
||||
const code = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %)
|
||||
@ -503,7 +555,6 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
'line([-20, 0], %)',
|
||||
'line([0, -15], %)',
|
||||
]
|
||||
const radiusValue = 3
|
||||
const expectedCode = `sketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([-10, 10], %)
|
||||
|> line([20, 0], %, $seg01)
|
||||
@ -512,7 +563,7 @@ extrude002 = extrude(-25, sketch002)` // <--- body 2
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(-15, sketch001)
|
||||
|> fillet({ radius = 3, tags = [seg01, seg02] }, %)
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg01, seg02] }, %)
|
||||
sketch002 = startSketchOn('XY')
|
||||
|> startProfileAt([30, 10], %)
|
||||
|> line([15, 0], %)
|
||||
@ -521,18 +572,20 @@ sketch002 = startSketchOn('XY')
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
extrude002 = extrude(-25, sketch002)
|
||||
|> fillet({ radius = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|> ${edgeTreatmentType}({ ${parameterName} = 3, tags = [seg03] }, %)` // <-- able to add a new one
|
||||
|
||||
await runModifyAstCloneWithFilletAndTag(
|
||||
await runModifyAstCloneWithEdgeTreatmentAndTag(
|
||||
code,
|
||||
segmentSnippets,
|
||||
radiusValue,
|
||||
parameters,
|
||||
expectedCode
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
describe('Testing isTagUsedInFillet', () => {
|
||||
describe('Testing isTagUsedInEdgeTreatment', () => {
|
||||
const code = `sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([7.72, 4.13], %)
|
||||
|> line([7.11, 3.48], %, $seg01)
|
||||
@ -565,7 +618,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getOppositeEdge', 'baseEdge'])
|
||||
})
|
||||
it('should correctly identify getPreviousAdjacentEdge edges', () => {
|
||||
@ -584,7 +637,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual(['getPreviousAdjacentEdge'])
|
||||
})
|
||||
it('should correctly identify no edges', () => {
|
||||
@ -603,7 +656,7 @@ extrude001 = extrude(-5, sketch001)
|
||||
'CallExpression'
|
||||
)
|
||||
if (err(callExp)) return
|
||||
const edges = isTagUsedInFillet({ ast, callExp: callExp.node })
|
||||
const edges = isTagUsedInEdgeTreatment({ ast, callExp: callExp.node })
|
||||
expect(edges).toEqual([])
|
||||
})
|
||||
})
|
||||
@ -638,7 +691,7 @@ describe('Testing button states', () => {
|
||||
}
|
||||
|
||||
// state
|
||||
const buttonState = hasValidFilletSelection({
|
||||
const buttonState = hasValidEdgeTreatmentSelection({
|
||||
ast,
|
||||
selectionRanges,
|
||||
code,
|
||||
|
@ -44,32 +44,49 @@ import {
|
||||
} from 'lib/singletons'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
// Apply Fillet To Selection
|
||||
// Edge Treatment Types
|
||||
export enum EdgeTreatmentType {
|
||||
Chamfer = 'chamfer',
|
||||
Fillet = 'fillet',
|
||||
}
|
||||
|
||||
export function applyFilletToSelection(
|
||||
export interface ChamferParameters {
|
||||
type: EdgeTreatmentType.Chamfer
|
||||
length: KclCommandValue
|
||||
}
|
||||
export interface FilletParameters {
|
||||
type: EdgeTreatmentType.Fillet
|
||||
radius: KclCommandValue
|
||||
}
|
||||
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||
|
||||
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||
export function applyEdgeTreatmentToSelection(
|
||||
ast: Node<Program>,
|
||||
selection: Selections,
|
||||
radius: KclCommandValue
|
||||
parameters: EdgeTreatmentParameters
|
||||
): void | Error {
|
||||
// 1. clone and modify with fillet and tag
|
||||
const result = modifyAstWithFilletAndTag(ast, selection, radius)
|
||||
// 1. clone and modify with edge treatment and tag
|
||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||
if (err(result)) return result
|
||||
const { modifiedAst, pathToFilletNode } = result
|
||||
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||
|
||||
// 2. update ast
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
updateAstAndFocus(modifiedAst, pathToFilletNode)
|
||||
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||
}
|
||||
|
||||
export function modifyAstWithFilletAndTag(
|
||||
export function modifyAstWithEdgeTreatmentAndTag(
|
||||
ast: Node<Program>,
|
||||
selections: Selections,
|
||||
radius: KclCommandValue
|
||||
): { modifiedAst: Node<Program>; pathToFilletNode: Array<PathToNode> } | Error {
|
||||
parameters: EdgeTreatmentParameters
|
||||
):
|
||||
| { modifiedAst: Node<Program>; pathToEdgeTreatmentNode: Array<PathToNode> }
|
||||
| Error {
|
||||
let clonedAst = structuredClone(ast)
|
||||
const clonedAstForGetExtrude = structuredClone(ast)
|
||||
|
||||
const astResult = insertRadiusIntoAst(clonedAst, radius)
|
||||
const astResult = insertParametersIntoAst(clonedAst, parameters)
|
||||
if (err(astResult)) return astResult
|
||||
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
@ -119,21 +136,26 @@ export function modifyAstWithFilletAndTag(
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Apply fillet(s) for each extrude node (body)
|
||||
let pathToFilletNodes: Array<PathToNode> = []
|
||||
// Step 2: Apply edge treatments for each extrude node (body)
|
||||
let pathToEdgeTreatmentNodes: Array<PathToNode> = []
|
||||
for (const [pathToExtrudeNode, tagInfos] of extrudeToTagsMap.entries()) {
|
||||
// Create a fillet expression with multiple tags
|
||||
const radiusValue =
|
||||
'variableName' in radius ? radius.variableIdentifierAst : radius.valueAst
|
||||
// Create an edge treatment expression with multiple tags
|
||||
|
||||
// edge treatment parameter
|
||||
const parameterResult = getParameterNameAndValue(parameters)
|
||||
if (err(parameterResult)) return parameterResult
|
||||
const { parameterName, parameterValue } = parameterResult
|
||||
|
||||
// tag calls
|
||||
const tagCalls = tagInfos.map(({ tag, artifact }) => {
|
||||
return getEdgeTagCall(tag, artifact)
|
||||
})
|
||||
const firstTag = tagCalls[0] // can be Identifier or CallExpression (for opposite and adjacent edges)
|
||||
|
||||
const filletCall = createCallExpressionStdLib('fillet', [
|
||||
// edge treatment call
|
||||
const edgeTreatmentCall = createCallExpressionStdLib(parameters.type, [
|
||||
createObjectExpression({
|
||||
radius: radiusValue,
|
||||
[parameterName]: parameterValue,
|
||||
tags: createArrayExpression(tagCalls),
|
||||
}),
|
||||
createPipeSubstitution(),
|
||||
@ -147,64 +169,89 @@ export function modifyAstWithFilletAndTag(
|
||||
if (err(locatedExtrudeDeclarator)) return locatedExtrudeDeclarator
|
||||
const { extrudeDeclarator } = locatedExtrudeDeclarator
|
||||
|
||||
// Modify the extrude expression to include this fillet expression
|
||||
// CallExpression - no fillet
|
||||
// PipeExpression - fillet exists or extrude in sketch pipe
|
||||
// Modify the extrude expression to include this edge treatment expression
|
||||
// CallExpression - no edge treatment
|
||||
// PipeExpression - edge treatment exists or body in sketch pipe
|
||||
|
||||
let pathToFilletNode: PathToNode = []
|
||||
let pathToEdgeTreatmentNode: PathToNode
|
||||
|
||||
if (extrudeDeclarator.init.type === 'CallExpression') {
|
||||
// 1. case when no fillet exists
|
||||
// 1. case when no edge treatment exists
|
||||
|
||||
// modify ast with new fillet call by mutating the extrude node
|
||||
// modify ast with new edge treatment call by mutating the extrude node
|
||||
extrudeDeclarator.init = createPipeExpression([
|
||||
extrudeDeclarator.init,
|
||||
filletCall,
|
||||
edgeTreatmentCall,
|
||||
])
|
||||
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
} else if (extrudeDeclarator.init.type === 'PipeExpression') {
|
||||
// 2. case when fillet exists or extrude in sketch pipe
|
||||
// 2. case when edge treatment exists or extrude in sketch pipe
|
||||
|
||||
// mutate the extrude node with the new fillet call
|
||||
extrudeDeclarator.init.body.push(filletCall)
|
||||
// mutate the extrude node with the new edge treatment call
|
||||
extrudeDeclarator.init.body.push(edgeTreatmentCall)
|
||||
|
||||
// get path to the fillet node
|
||||
pathToFilletNode = getPathToNodeOfFilletLiteral(
|
||||
// get path to the edge treatment node
|
||||
pathToEdgeTreatmentNode = getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode,
|
||||
extrudeDeclarator,
|
||||
firstTag
|
||||
firstTag,
|
||||
parameters
|
||||
)
|
||||
pathToFilletNodes.push(pathToFilletNode)
|
||||
pathToEdgeTreatmentNodes.push(pathToEdgeTreatmentNode)
|
||||
} else {
|
||||
return new Error('Unsupported extrude type.')
|
||||
}
|
||||
}
|
||||
return { modifiedAst: clonedAst, pathToFilletNode: pathToFilletNodes }
|
||||
return {
|
||||
modifiedAst: clonedAst,
|
||||
pathToEdgeTreatmentNode: pathToEdgeTreatmentNodes,
|
||||
}
|
||||
}
|
||||
|
||||
function insertRadiusIntoAst(
|
||||
function insertParametersIntoAst(
|
||||
ast: Program,
|
||||
radius: KclCommandValue
|
||||
parameters: EdgeTreatmentParameters
|
||||
): { ast: Program } | Error {
|
||||
try {
|
||||
// Validate and update AST
|
||||
if (
|
||||
'variableName' in radius &&
|
||||
radius.variableName &&
|
||||
radius.insertIndex !== undefined
|
||||
) {
|
||||
const newAst = structuredClone(ast)
|
||||
newAst.body.splice(radius.insertIndex, 0, radius.variableDeclarationAst)
|
||||
return { ast: newAst }
|
||||
|
||||
// handle radius parameter
|
||||
if (
|
||||
parameters.type === EdgeTreatmentType.Fillet &&
|
||||
'variableName' in parameters.radius &&
|
||||
parameters.radius.variableName &&
|
||||
parameters.radius.insertIndex !== undefined
|
||||
) {
|
||||
newAst.body.splice(
|
||||
parameters.radius.insertIndex,
|
||||
0,
|
||||
parameters.radius.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
return { ast }
|
||||
// handle length parameter
|
||||
if (
|
||||
parameters.type === EdgeTreatmentType.Chamfer &&
|
||||
'variableName' in parameters.length &&
|
||||
parameters.length.variableName &&
|
||||
parameters.length.insertIndex !== undefined
|
||||
) {
|
||||
newAst.body.splice(
|
||||
parameters.length.insertIndex,
|
||||
0,
|
||||
parameters.length.variableDeclarationAst
|
||||
)
|
||||
}
|
||||
|
||||
// handle upcoming parameters here (for blend, bevel, etc.)
|
||||
return { ast: newAst }
|
||||
} catch (error) {
|
||||
return new Error(`Failed to handle AST: ${(error as Error).message}`)
|
||||
}
|
||||
@ -248,10 +295,10 @@ export function getPathToExtrudeForSegmentSelection(
|
||||
|
||||
async function updateAstAndFocus(
|
||||
modifiedAst: Node<Program>,
|
||||
pathToFilletNode: Array<PathToNode>
|
||||
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||
) {
|
||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||
focusPath: pathToFilletNode,
|
||||
focusPath: pathToEdgeTreatmentNode,
|
||||
})
|
||||
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(updatedAst.newAst)
|
||||
@ -340,27 +387,38 @@ function locateExtrudeDeclarator(
|
||||
return { extrudeDeclarator }
|
||||
}
|
||||
|
||||
function getPathToNodeOfFilletLiteral(
|
||||
function getPathToNodeOfEdgeTreatmentLiteral(
|
||||
pathToExtrudeNode: PathToNode,
|
||||
extrudeDeclarator: VariableDeclarator,
|
||||
tag: Identifier | CallExpression
|
||||
tag: Identifier | CallExpression,
|
||||
parameters: EdgeTreatmentParameters
|
||||
): PathToNode {
|
||||
let pathToFilletObj: PathToNode = []
|
||||
let inFillet = false
|
||||
let pathToEdgeTreatmentObj: PathToNode = []
|
||||
let inEdgeTreatment = false
|
||||
|
||||
traverse(extrudeDeclarator.init, {
|
||||
enter(node, path) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (!hasTag(node, tag)) return false
|
||||
pathToFilletObj = getPathToRadiusLiteral(node, path)
|
||||
pathToEdgeTreatmentObj = getPathToEdgeTreatmentParameterLiteral(
|
||||
node,
|
||||
path,
|
||||
parameters
|
||||
)
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === parameters.type
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
},
|
||||
})
|
||||
@ -375,7 +433,7 @@ function getPathToNodeOfFilletLiteral(
|
||||
|
||||
return [
|
||||
...pathToExtrudeNode.slice(0, indexOfPipeExpression),
|
||||
...pathToFilletObj,
|
||||
...pathToEdgeTreatmentObj,
|
||||
]
|
||||
}
|
||||
|
||||
@ -408,23 +466,62 @@ function hasTag(
|
||||
})
|
||||
}
|
||||
|
||||
function getPathToRadiusLiteral(node: ObjectExpression, path: any): PathToNode {
|
||||
let pathToFilletObj = path
|
||||
function getPathToEdgeTreatmentParameterLiteral(
|
||||
node: ObjectExpression,
|
||||
path: any,
|
||||
parameters: EdgeTreatmentParameters
|
||||
): PathToNode {
|
||||
let pathToEdgeTreatmentObj = path
|
||||
const parameterResult = getParameterNameAndValue(parameters)
|
||||
if (err(parameterResult)) return pathToEdgeTreatmentObj
|
||||
const { parameterName } = parameterResult
|
||||
|
||||
node.properties.forEach((prop, index) => {
|
||||
if (prop.key.name === 'radius') {
|
||||
pathToFilletObj.push(
|
||||
if (prop.key.name === parameterName) {
|
||||
pathToEdgeTreatmentObj.push(
|
||||
['properties', 'ObjectExpression'],
|
||||
[index, 'index'],
|
||||
['value', 'Property']
|
||||
)
|
||||
}
|
||||
})
|
||||
return pathToFilletObj
|
||||
return pathToEdgeTreatmentObj
|
||||
}
|
||||
|
||||
function getParameterNameAndValue(
|
||||
parameters: EdgeTreatmentParameters
|
||||
): { parameterName: string; parameterValue: Expr } | Error {
|
||||
if (parameters.type === EdgeTreatmentType.Fillet) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.radius
|
||||
? parameters.radius.variableIdentifierAst
|
||||
: parameters.radius.valueAst
|
||||
return { parameterName: 'radius', parameterValue }
|
||||
} else if (parameters.type === EdgeTreatmentType.Chamfer) {
|
||||
const parameterValue =
|
||||
'variableName' in parameters.length
|
||||
? parameters.length.variableIdentifierAst
|
||||
: parameters.length.valueAst
|
||||
return { parameterName: 'length', parameterValue }
|
||||
} else {
|
||||
return new Error('Unsupported edge treatment type}')
|
||||
}
|
||||
}
|
||||
|
||||
// Type Guards
|
||||
function isEdgeTreatmentType(name: string): name is EdgeTreatmentType {
|
||||
return name === EdgeTreatmentType.Chamfer || name === EdgeTreatmentType.Fillet
|
||||
}
|
||||
function isEdgeType(name: string): name is EdgeTypes {
|
||||
return (
|
||||
name === 'getNextAdjacentEdge' ||
|
||||
name === 'getPreviousAdjacentEdge' ||
|
||||
name === 'getOppositeEdge'
|
||||
)
|
||||
}
|
||||
|
||||
// Button states
|
||||
|
||||
export const hasValidFilletSelection = ({
|
||||
export const hasValidEdgeTreatmentSelection = ({
|
||||
selectionRanges,
|
||||
ast,
|
||||
code,
|
||||
@ -433,11 +530,14 @@ export const hasValidFilletSelection = ({
|
||||
ast: Node<Program>
|
||||
code: string
|
||||
}) => {
|
||||
// check if there is anything filletable in the scene
|
||||
// check if there is anything valid for the edge treatment in the scene
|
||||
let extrudeExists = false
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'extrude') {
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve')
|
||||
) {
|
||||
extrudeExists = true
|
||||
}
|
||||
},
|
||||
@ -494,32 +594,39 @@ export const hasValidFilletSelection = ({
|
||||
},
|
||||
})
|
||||
|
||||
// check if tag is used in fillet
|
||||
// check if tag is used in edge treatment
|
||||
if (tagExists && selection.artifact) {
|
||||
// create tag call
|
||||
let tagCall: Expr = getEdgeTagCall(tag, selection.artifact)
|
||||
|
||||
// check if tag is used in fillet
|
||||
let inFillet = false
|
||||
let tagUsedInFillet = false
|
||||
// check if tag is used in edge treatment
|
||||
let inEdgeTreatment = false
|
||||
let tagUsedInEdgeTreatment = false
|
||||
|
||||
traverse(ast, {
|
||||
enter(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
if (hasTag(node, tagCall)) {
|
||||
tagUsedInFillet = true
|
||||
tagUsedInEdgeTreatment = true
|
||||
}
|
||||
}
|
||||
},
|
||||
leave(node) {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
},
|
||||
})
|
||||
if (tagUsedInFillet) {
|
||||
if (tagUsedInEdgeTreatment) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -533,7 +640,7 @@ type EdgeTypes =
|
||||
| 'getPreviousAdjacentEdge'
|
||||
| 'getOppositeEdge'
|
||||
|
||||
export const isTagUsedInFillet = ({
|
||||
export const isTagUsedInEdgeTreatment = ({
|
||||
ast,
|
||||
callExp,
|
||||
}: {
|
||||
@ -543,16 +650,21 @@ export const isTagUsedInFillet = ({
|
||||
const tag = getTagFromCallExpression(callExp)
|
||||
if (err(tag)) return []
|
||||
|
||||
let inFillet = false
|
||||
let inEdgeTreatment = false
|
||||
let inObj = false
|
||||
let inTagHelper: EdgeTypes | '' = ''
|
||||
const edges: Array<EdgeTypes> = []
|
||||
|
||||
traverse(ast, {
|
||||
enter: (node) => {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = true
|
||||
// Check if we are entering an edge treatment call
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = true
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -564,17 +676,15 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
isEdgeType(node.callee.name)
|
||||
) {
|
||||
inTagHelper = node.callee.name
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
!inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -583,7 +693,7 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
inTagHelper &&
|
||||
node.type === 'Identifier' &&
|
||||
node.name === tag
|
||||
@ -592,10 +702,13 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
},
|
||||
leave: (node) => {
|
||||
if (node.type === 'CallExpression' && node.callee.name === 'fillet') {
|
||||
inFillet = false
|
||||
if (
|
||||
node.type === 'CallExpression' &&
|
||||
isEdgeTreatmentType(node.callee.name)
|
||||
) {
|
||||
inEdgeTreatment = false
|
||||
}
|
||||
if (inFillet && node.type === 'ObjectExpression') {
|
||||
if (inEdgeTreatment && node.type === 'ObjectExpression') {
|
||||
node.properties.forEach((prop) => {
|
||||
if (
|
||||
prop.key.name === 'tags' &&
|
||||
@ -607,11 +720,9 @@ export const isTagUsedInFillet = ({
|
||||
}
|
||||
if (
|
||||
inObj &&
|
||||
inFillet &&
|
||||
inEdgeTreatment &&
|
||||
node.type === 'CallExpression' &&
|
||||
(node.callee.name === 'getOppositeEdge' ||
|
||||
node.callee.name === 'getNextAdjacentEdge' ||
|
||||
node.callee.name === 'getPreviousAdjacentEdge')
|
||||
isEdgeType(node.callee.name)
|
||||
) {
|
||||
inTagHelper = ''
|
||||
}
|
||||
|
@ -46,7 +46,11 @@ import {
|
||||
extrudeSketch,
|
||||
revolveSketch,
|
||||
} from 'lang/modifyAst'
|
||||
import { applyFilletToSelection } from 'lang/modifyAst/addFillet'
|
||||
import {
|
||||
applyEdgeTreatmentToSelection,
|
||||
EdgeTreatmentType,
|
||||
FilletParameters,
|
||||
} from 'lang/modifyAst/addFillet'
|
||||
import { getNodeFromPath } from '../lang/queryAst'
|
||||
import {
|
||||
applyConstraintEqualAngle,
|
||||
@ -383,7 +387,7 @@ export const modelingMachine = setup({
|
||||
guards: {
|
||||
'Selection is on face': () => false,
|
||||
'has valid sweep selection': () => false,
|
||||
'has valid fillet selection': () => false,
|
||||
'has valid edge treatment selection': () => false,
|
||||
'Has exportable geometry': () => false,
|
||||
'has valid selection for deletion': () => false,
|
||||
'has made first point': ({ context }) => {
|
||||
@ -739,14 +743,19 @@ export const modelingMachine = setup({
|
||||
// Extract inputs
|
||||
const ast = kclManager.ast
|
||||
const { selection, radius } = event.data
|
||||
const parameters: FilletParameters = {
|
||||
type: EdgeTreatmentType.Fillet,
|
||||
radius,
|
||||
}
|
||||
|
||||
// Apply fillet to selection
|
||||
const applyFilletToSelectionResult = applyFilletToSelection(
|
||||
const applyEdgeTreatmentToSelectionResult = applyEdgeTreatmentToSelection(
|
||||
ast,
|
||||
selection,
|
||||
radius
|
||||
parameters
|
||||
)
|
||||
if (err(applyFilletToSelectionResult)) return applyFilletToSelectionResult
|
||||
if (err(applyEdgeTreatmentToSelectionResult))
|
||||
return applyEdgeTreatmentToSelectionResult
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
@ -1563,7 +1572,7 @@ export const modelingMachine = setup({
|
||||
|
||||
Fillet: {
|
||||
target: 'idle',
|
||||
guard: 'has valid fillet selection', // TODO: fix selections
|
||||
guard: 'has valid edge treatment selection',
|
||||
actions: ['AST fillet'],
|
||||
reenter: false,
|
||||
},
|
||||
|
Reference in New Issue
Block a user