Fix Second-Body Extrude Selection (#5456)
* getSweepArtifactFromSelection * update getPathToExtrudeForSegmentSelection * update shell * add tests and update selection * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * add support for wall and cap * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * fmt * add CallExpressionKw * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) * A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores) --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
@ -116,7 +116,11 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
}
|
}
|
||||||
if (!extrudeInSketchPipe) {
|
if (!extrudeInSketchPipe) {
|
||||||
const init = expectedExtrudeNode.init
|
const init = expectedExtrudeNode.init
|
||||||
if (init.type !== 'CallExpression' && init.type !== 'PipeExpression') {
|
if (
|
||||||
|
init.type !== 'CallExpression' &&
|
||||||
|
init.type !== 'CallExpressionKw' &&
|
||||||
|
init.type !== 'PipeExpression'
|
||||||
|
) {
|
||||||
return new Error(
|
return new Error(
|
||||||
'Expected extrude expression is not a CallExpression or PipeExpression'
|
'Expected extrude expression is not a CallExpression or PipeExpression'
|
||||||
)
|
)
|
||||||
@ -129,25 +133,33 @@ const runGetPathToExtrudeForSegmentSelectionTest = async (
|
|||||||
// ast
|
// ast
|
||||||
const ast = assertParse(code)
|
const ast = assertParse(code)
|
||||||
|
|
||||||
// selection
|
// range
|
||||||
const segmentRange = topLevelRange(
|
const segmentRange = topLevelRange(
|
||||||
code.indexOf(selectedSegmentSnippet),
|
code.indexOf(selectedSegmentSnippet),
|
||||||
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length
|
code.indexOf(selectedSegmentSnippet) + selectedSegmentSnippet.length
|
||||||
)
|
)
|
||||||
const selection: Selection = {
|
|
||||||
codeRef: codeRefFromRange(segmentRange, ast),
|
|
||||||
}
|
|
||||||
|
|
||||||
// executeAst and artifactGraph
|
// executeAst and artifactGraph
|
||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
const artifactGraph = engineCommandManager.artifactGraph
|
const artifactGraph = engineCommandManager.artifactGraph
|
||||||
|
|
||||||
|
// find artifact
|
||||||
|
const maybeArtifact = [...artifactGraph].find(([, artifact]) => {
|
||||||
|
if (!('codeRef' in artifact && artifact.codeRef)) return false
|
||||||
|
return isOverlap(artifact.codeRef.range, segmentRange)
|
||||||
|
})
|
||||||
|
|
||||||
|
// build selection
|
||||||
|
const selection: Selection = {
|
||||||
|
codeRef: codeRefFromRange(segmentRange, ast),
|
||||||
|
artifact: maybeArtifact ? maybeArtifact[1] : undefined,
|
||||||
|
}
|
||||||
|
|
||||||
// get extrude expression
|
// get extrude expression
|
||||||
const pathResult = getPathToExtrudeForSegmentSelection(
|
const pathResult = getPathToExtrudeForSegmentSelection(
|
||||||
ast,
|
ast,
|
||||||
selection,
|
selection,
|
||||||
artifactGraph,
|
artifactGraph
|
||||||
dependencies
|
|
||||||
)
|
)
|
||||||
if (err(pathResult)) return pathResult
|
if (err(pathResult)) return pathResult
|
||||||
const { pathToExtrudeNode } = pathResult
|
const { pathToExtrudeNode } = pathResult
|
||||||
@ -234,6 +246,56 @@ extrude003 = extrude(sketch003, length = -15)`
|
|||||||
expectedExtrudeSnippet
|
expectedExtrudeSnippet
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
it('should return the correct paths for a (piped) extrude based on the other body (face)', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-25, -25], %)
|
||||||
|
|> yLine(50, %)
|
||||||
|
|> xLine(50, %)
|
||||||
|
|> yLine(-50, %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
|> extrude(length = 50)
|
||||||
|
sketch002 = startSketchOn(sketch001, 'END')
|
||||||
|
|> startProfileAt([-15, -15], %)
|
||||||
|
|> yLine(30, %)
|
||||||
|
|> xLine(30, %)
|
||||||
|
|> yLine(-30, %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
|> extrude(length = 30)`
|
||||||
|
const selectedSegmentSnippet = `xLine(30, %)`
|
||||||
|
const expectedExtrudeSnippet = `extrude(length = 30)`
|
||||||
|
await runGetPathToExtrudeForSegmentSelectionTest(
|
||||||
|
code,
|
||||||
|
selectedSegmentSnippet,
|
||||||
|
expectedExtrudeSnippet
|
||||||
|
)
|
||||||
|
})
|
||||||
|
it('should return the correct paths for a (non-piped) extrude based on the other body (face)', async () => {
|
||||||
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-25, -25], %)
|
||||||
|
|> yLine(50, %)
|
||||||
|
|> xLine(50, %)
|
||||||
|
|> yLine(-50, %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
extrude001 = extrude(sketch001, length = 50)
|
||||||
|
sketch002 = startSketchOn(extrude001, 'END')
|
||||||
|
|> startProfileAt([-15, -15], %)
|
||||||
|
|> yLine(30, %)
|
||||||
|
|> xLine(30, %)
|
||||||
|
|> yLine(-30, %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
extrude002 = extrude(sketch002, length = 30)`
|
||||||
|
const selectedSegmentSnippet = `xLine(30, %)`
|
||||||
|
const expectedExtrudeSnippet = `extrude002 = extrude(sketch002, length = 30)`
|
||||||
|
await runGetPathToExtrudeForSegmentSelectionTest(
|
||||||
|
code,
|
||||||
|
selectedSegmentSnippet,
|
||||||
|
expectedExtrudeSnippet
|
||||||
|
)
|
||||||
|
})
|
||||||
it('should not return any path for missing extrusion', async () => {
|
it('should not return any path for missing extrusion', async () => {
|
||||||
const code = `sketch001 = startSketchOn('XY')
|
const code = `sketch001 = startSketchOn('XY')
|
||||||
|> startProfileAt([-30, 30], %)
|
|> startProfileAt([-30, 30], %)
|
||||||
|
@ -10,7 +10,6 @@ import {
|
|||||||
Program,
|
Program,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
sketchFromKclValue,
|
|
||||||
} from '../wasm'
|
} from '../wasm'
|
||||||
import {
|
import {
|
||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
@ -35,11 +34,11 @@ import {
|
|||||||
sketchLineHelperMap,
|
sketchLineHelperMap,
|
||||||
sketchLineHelperMapKw,
|
sketchLineHelperMapKw,
|
||||||
} from '../std/sketch'
|
} from '../std/sketch'
|
||||||
import { err, trap } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { Selection, Selections } from 'lib/selections'
|
import { Selection, Selections } from 'lib/selections'
|
||||||
import { KclCommandValue } from 'lib/commandTypes'
|
import { KclCommandValue } from 'lib/commandTypes'
|
||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
import { Artifact, getSweepFromSuspectedPath } from 'lang/std/artifactGraph'
|
import { Artifact, getSweepArtifactFromSelection } from 'lang/std/artifactGraph'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { findKwArg } from 'lang/util'
|
import { findKwArg } from 'lang/util'
|
||||||
import { KclManager } from 'lang/KclSingleton'
|
import { KclManager } from 'lang/KclSingleton'
|
||||||
@ -121,8 +120,7 @@ export function modifyAstWithEdgeTreatmentAndTag(
|
|||||||
const result = getPathToExtrudeForSegmentSelection(
|
const result = getPathToExtrudeForSegmentSelection(
|
||||||
clonedAstForGetExtrude,
|
clonedAstForGetExtrude,
|
||||||
selection,
|
selection,
|
||||||
artifactGraph,
|
artifactGraph
|
||||||
dependencies
|
|
||||||
)
|
)
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { pathToSegmentNode, pathToExtrudeNode } = result
|
const { pathToSegmentNode, pathToExtrudeNode } = result
|
||||||
@ -279,39 +277,19 @@ function insertParametersIntoAst(
|
|||||||
export function getPathToExtrudeForSegmentSelection(
|
export function getPathToExtrudeForSegmentSelection(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
artifactGraph: ArtifactGraph,
|
artifactGraph: ArtifactGraph
|
||||||
dependencies: {
|
|
||||||
kclManager: KclManager
|
|
||||||
engineCommandManager: EngineCommandManager
|
|
||||||
editorManager: EditorManager
|
|
||||||
codeManager: CodeManager
|
|
||||||
}
|
|
||||||
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
|
): { pathToSegmentNode: PathToNode; pathToExtrudeNode: PathToNode } | Error {
|
||||||
const pathToSegmentNode = getNodePathFromSourceRange(
|
const pathToSegmentNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
selection.codeRef?.range
|
selection.codeRef?.range
|
||||||
)
|
)
|
||||||
|
|
||||||
const varDecNode = getNodeFromPath<VariableDeclaration>(
|
const sweepArtifact = getSweepArtifactFromSelection(selection, artifactGraph)
|
||||||
ast,
|
if (err(sweepArtifact)) return sweepArtifact
|
||||||
pathToSegmentNode,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(varDecNode)) return varDecNode
|
|
||||||
const sketchVar = varDecNode.node.declaration.id.name
|
|
||||||
|
|
||||||
const sketch = sketchFromKclValue(
|
|
||||||
dependencies.kclManager.variables[sketchVar],
|
|
||||||
sketchVar
|
|
||||||
)
|
|
||||||
if (trap(sketch)) return sketch
|
|
||||||
|
|
||||||
const extrusion = getSweepFromSuspectedPath(sketch.id, artifactGraph)
|
|
||||||
if (err(extrusion)) return extrusion
|
|
||||||
|
|
||||||
const pathToExtrudeNode = getNodePathFromSourceRange(
|
const pathToExtrudeNode = getNodePathFromSourceRange(
|
||||||
ast,
|
ast,
|
||||||
extrusion.codeRef.range
|
sweepArtifact.codeRef.range
|
||||||
)
|
)
|
||||||
if (err(pathToExtrudeNode)) return pathToExtrudeNode
|
if (err(pathToExtrudeNode)) return pathToExtrudeNode
|
||||||
|
|
||||||
|
@ -13,36 +13,23 @@ import {
|
|||||||
createLiteral,
|
createLiteral,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
findUniqueName,
|
findUniqueName,
|
||||||
createCallExpressionStdLib,
|
|
||||||
createObjectExpression,
|
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
createVariableDeclaration,
|
createVariableDeclaration,
|
||||||
createCallExpressionStdLibKw,
|
createCallExpressionStdLibKw,
|
||||||
createLabeledArg,
|
createLabeledArg,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||||
import { KclManager } from 'lang/KclSingleton'
|
|
||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
|
||||||
import EditorManager from 'editor/manager'
|
|
||||||
import CodeManager from 'lang/codeManager'
|
|
||||||
|
|
||||||
export function addShell({
|
export function addShell({
|
||||||
node,
|
node,
|
||||||
selection,
|
selection,
|
||||||
artifactGraph,
|
artifactGraph,
|
||||||
thickness,
|
thickness,
|
||||||
dependencies,
|
|
||||||
}: {
|
}: {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
selection: Selections
|
selection: Selections
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
thickness: Expr
|
thickness: Expr
|
||||||
dependencies: {
|
|
||||||
kclManager: KclManager
|
|
||||||
engineCommandManager: EngineCommandManager
|
|
||||||
editorManager: EditorManager
|
|
||||||
codeManager: CodeManager
|
|
||||||
}
|
|
||||||
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
}): Error | { modifiedAst: Node<Program>; pathToNode: PathToNode } {
|
||||||
const modifiedAst = structuredClone(node)
|
const modifiedAst = structuredClone(node)
|
||||||
|
|
||||||
@ -55,8 +42,7 @@ export function addShell({
|
|||||||
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
|
const extrudeLookupResult = getPathToExtrudeForSegmentSelection(
|
||||||
clonedAstForGetExtrude,
|
clonedAstForGetExtrude,
|
||||||
graphSelection,
|
graphSelection,
|
||||||
artifactGraph,
|
artifactGraph
|
||||||
dependencies
|
|
||||||
)
|
)
|
||||||
if (err(extrudeLookupResult)) {
|
if (err(extrudeLookupResult)) {
|
||||||
return new Error("Couldn't find extrude")
|
return new Error("Couldn't find extrude")
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
|
import { Selection } from 'lib/selections'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { Cap, Plane, Wall } from 'wasm-lib/kcl/bindings/Artifact'
|
import { Cap, Plane, Wall } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
import { CapSubType } from 'wasm-lib/kcl/bindings/Artifact'
|
import { CapSubType } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
@ -455,6 +456,47 @@ export function getSweepFromSuspectedPath(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSweepArtifactFromSelection(
|
||||||
|
selection: Selection,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): SweepArtifact | Error {
|
||||||
|
let sweepArtifact: Artifact | null = null
|
||||||
|
if (selection.artifact?.type === 'sweepEdge') {
|
||||||
|
const _artifact = getArtifactOfTypes(
|
||||||
|
{ key: selection.artifact.sweepId, types: ['sweep'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(_artifact)) return _artifact
|
||||||
|
sweepArtifact = _artifact
|
||||||
|
} else if (selection.artifact?.type === 'segment') {
|
||||||
|
const _pathArtifact = getArtifactOfTypes(
|
||||||
|
{ key: selection.artifact.pathId, types: ['path'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(_pathArtifact)) return _pathArtifact
|
||||||
|
if (!_pathArtifact.sweepId) return new Error('Path does not have a sweepId')
|
||||||
|
const _artifact = getArtifactOfTypes(
|
||||||
|
{ key: _pathArtifact.sweepId, types: ['sweep'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(_artifact)) return _artifact
|
||||||
|
sweepArtifact = _artifact
|
||||||
|
} else if (
|
||||||
|
selection.artifact?.type === 'cap' ||
|
||||||
|
selection.artifact?.type === 'wall'
|
||||||
|
) {
|
||||||
|
const _artifact = getArtifactOfTypes(
|
||||||
|
{ key: selection.artifact.sweepId, types: ['sweep'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
if (err(_artifact)) return _artifact
|
||||||
|
sweepArtifact = _artifact
|
||||||
|
}
|
||||||
|
if (!sweepArtifact) return new Error('No sweep artifact found')
|
||||||
|
|
||||||
|
return sweepArtifact
|
||||||
|
}
|
||||||
|
|
||||||
export function getCodeRefsByArtifactId(
|
export function getCodeRefsByArtifactId(
|
||||||
id: string,
|
id: string,
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
|
@ -1995,12 +1995,6 @@ export const modelingMachine = setup({
|
|||||||
// Extract inputs
|
// Extract inputs
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const { selection, thickness } = input
|
const { selection, thickness } = input
|
||||||
const dependencies = {
|
|
||||||
kclManager,
|
|
||||||
engineCommandManager,
|
|
||||||
editorManager,
|
|
||||||
codeManager,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert the thickness variable if it exists
|
// Insert the thickness variable if it exists
|
||||||
if (
|
if (
|
||||||
@ -2026,7 +2020,6 @@ export const modelingMachine = setup({
|
|||||||
'variableName' in thickness
|
'variableName' in thickness
|
||||||
? thickness.variableIdentifierAst
|
? thickness.variableIdentifierAst
|
||||||
: thickness.valueAst,
|
: thickness.valueAst,
|
||||||
dependencies,
|
|
||||||
})
|
})
|
||||||
if (err(shellResult)) {
|
if (err(shellResult)) {
|
||||||
return err(shellResult)
|
return err(shellResult)
|
||||||
|
Reference in New Issue
Block a user