multi-profile follow up. (#4802)

* multi-profile work

* fix enter sketch on cap

* fix coderef problem for walls and caps

* allow sketch mode entry from circle

* clean up

* update snapshot

* Look at this (photo)Graph *in the voice of Nickelback*

* trigger CI

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* add test

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)

* fix how expression index is corrected, to make compatible with offset planes

* another test

* tweak test

* more test tweaks

* break up test to fix it hopfully

* fix onboarding test

* remove bad comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Kurt Hutten
2024-12-16 18:36:48 +11:00
committed by GitHub
parent 96652a0c48
commit 2b2ed470c1
12 changed files with 538 additions and 98 deletions

View File

@ -216,7 +216,7 @@ export class SceneFixture {
}
expectPixelColor = async (
colour: [number, number, number],
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
) => {
@ -237,22 +237,36 @@ export class SceneFixture {
}
}
function isColourArray(
colour: [number, number, number] | [number, number, number][]
): colour is [number, number, number][] {
return Array.isArray(colour[0])
}
export async function expectPixelColor(
page: Page,
colour: [number, number, number],
colour: [number, number, number] | [number, number, number][],
coords: { x: number; y: number },
diff: number
) {
let finalValue = colour
await expect
.poll(async () => {
.poll(
async () => {
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
if (!pixel) return null
finalValue = pixel
if (!isColourArray(colour)) {
return pixel.every(
(channel, index) => Math.abs(channel - colour[index]) < diff
)
})
}
return colour.some((c) =>
c.every((channel, index) => Math.abs(pixel[index] - channel) < diff)
)
},
{ timeout: 10_000 }
)
.toBeTruthy()
.catch((cause) => {
throw new Error(

View File

@ -7,6 +7,7 @@ import {
PERSIST_MODELING_CONTEXT,
setup,
tearDown,
TEST_COLORS,
} from './test-utils'
import { uuidv4, roundOff } from 'lib/utils'
@ -1350,7 +1351,7 @@ test2.describe('Sketch mode should be toleratant to syntax errors', () => {
const [objClick] = scene.makeMouseHelpers(600, 250)
const arrowHeadLocation = { x: 604, y: 129 } as const
const arrowHeadWhite: [number, number, number] = [255, 255, 255]
const arrowHeadWhite = TEST_COLORS.WHITE
const backgroundGray: [number, number, number] = [28, 28, 28]
const verifyArrowHeadColor = async (c: [number, number, number]) =>
scene.expectPixelColor(c, arrowHeadLocation, 15)
@ -1993,4 +1994,334 @@ extrude001 = extrude(75, thePart)
)
}
)
test2(
'Can enter sketch on sketch of wall and cap for segment, solid2d, extrude-wall, extrude-cap selections',
async ({ app, scene, toolbar, editor }) => {
// TODO this test should include a test for selecting revolve walls and caps
await app.initialise(`sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([6.71, -3.66], sketch001)
|> line([2.65, 9.02], %, $seg02)
|> line([3.73, -9.36], %, $seg01)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude001 = extrude(20, profile001)
sketch002 = startSketchOn(extrude001, seg01)
profile002 = startProfileAt([0.75, 13.46], sketch002)
|> line([4.52, 3.79], %)
|> line([5.98, -2.81], %)
profile003 = startProfileAt([3.19, 13.3], sketch002)
|> angledLine([0, 6.64], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
2.81
], %)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
profile004 = startProfileAt([3.15, 9.39], sketch002)
|> xLine(6.92, %)
|> line([-7.41, -2.85], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
profile005 = circle({ center = [5.15, 4.34], radius = 1.66 }, sketch002)
profile006 = startProfileAt([9.65, 3.82], sketch002)
|> line([2.38, 5.62], %)
|> line([2.13, -5.57], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
revolve001 = revolve({
angle = 45,
axis = getNextAdjacentEdge(seg01)
}, profile004)
extrude002 = extrude(4, profile006)
sketch003 = startSketchOn('-XZ')
profile007 = startProfileAt([4.8, 7.55], sketch003)
|> line([7.39, 2.58], %)
|> line([7.02, -2.85], %)
profile008 = startProfileAt([5.54, 5.49], sketch003)
|> line([6.34, 2.64], %)
|> line([6.33, -2.96], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
profile009 = startProfileAt([5.23, 1.95], sketch003)
|> line([6.8, 2.17], %)
|> line([7.34, -2.75], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
profile010 = circle({
center = [7.18, -2.11],
radius = 2.67
}, sketch003)
profile011 = startProfileAt([5.07, -6.39], sketch003)
|> angledLine([0, 4.54], %, $rectangleSegmentA002)
|> angledLine([
segAng(rectangleSegmentA002) - 90,
4.17
], %)
|> angledLine([
segAng(rectangleSegmentA002),
-segLen(rectangleSegmentA002)
], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
extrude003 = extrude(2.5, profile011)
revolve002 = revolve({ angle = 45, axis = seg02 }, profile008)
`)
const camPositionForSelectingSketchOnWallProfiles = () =>
scene.moveCameraTo(
{ x: 834, y: -680, z: 534 },
{ x: -54, y: -476, z: 148 }
)
const camPositionForSelectingSketchOnCapProfiles = () =>
scene.moveCameraTo(
{ x: 404, y: 690, z: 38 },
{ x: 16, y: -140, z: -10 }
)
const wallSelectionOptions = [
{
title: 'select wall segment',
selectClick: scene.makeMouseHelpers(598, 211)[0],
},
{
title: 'select wall solid 2d',
selectClick: scene.makeMouseHelpers(677, 236)[0],
},
{
title: 'select wall circle',
selectClick: scene.makeMouseHelpers(811, 247)[0],
},
{
title: 'select wall extrude wall',
selectClick: scene.makeMouseHelpers(793, 136)[0],
},
{
title: 'select wall extrude cap',
selectClick: scene.makeMouseHelpers(836, 103)[0],
},
] as const
const capSelectionOptions = [
{
title: 'select cap segment',
selectClick: scene.makeMouseHelpers(688, 91)[0],
},
{
title: 'select cap solid 2d',
selectClick: scene.makeMouseHelpers(733, 204)[0],
},
// TODO keeps failing
// {
// title: 'select cap circle',
// selectClick: scene.makeMouseHelpers(679, 290)[0],
// },
{
title: 'select cap extrude wall',
selectClick: scene.makeMouseHelpers(649, 402)[0],
},
{
title: 'select cap extrude cap',
selectClick: scene.makeMouseHelpers(693, 408)[0],
},
] as const
const verifyWallProfilesAreDrawn = async () =>
test2.step('verify wall profiles are drawn', async () => {
// open polygon
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 599, y: 168 },
15
)
// closed polygon
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 656, y: 171 },
15
)
// revolved profile
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 655, y: 264 },
15
)
// extruded profile
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 808, y: 396 },
15
)
// circle
await scene.expectPixelColor(
[
TEST_COLORS.WHITE,
TEST_COLORS.BLUE, // When entering via the circle, it's selected and therefore blue
],
{ x: 742, y: 386 },
15
)
})
const verifyCapProfilesAreDrawn = async () =>
test2.step('verify wall profiles are drawn', async () => {
// open polygon
await scene.expectPixelColor(
TEST_COLORS.WHITE,
// TEST_COLORS.BLUE, // When entering via the circle, it's selected and therefore blue
{ x: 620, y: 58 },
15
)
// revolved profile
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 641, y: 110 },
15
)
// closed polygon
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 632, y: 200 },
15
)
// extruded profile
await scene.expectPixelColor(
TEST_COLORS.WHITE,
{ x: 628, y: 410 },
15
)
// circle
await scene.expectPixelColor(
[
TEST_COLORS.WHITE,
TEST_COLORS.BLUE, // When entering via the circle, it's selected and therefore blue
],
{ x: 681, y: 303 },
15
)
})
await test2.step('select wall profiles', async () => {
for (const { title, selectClick } of wallSelectionOptions) {
await test2.step(title, async () => {
await camPositionForSelectingSketchOnWallProfiles()
await selectClick()
await toolbar.editSketch()
await app.page.waitForTimeout(600)
await verifyWallProfilesAreDrawn()
await toolbar.exitSketchBtn.click()
await app.page.waitForTimeout(100)
})
}
})
await test2.step('select cap profiles', async () => {
for (const { title, selectClick } of capSelectionOptions) {
await test2.step(title, async () => {
await camPositionForSelectingSketchOnCapProfiles()
await app.page.waitForTimeout(100)
await selectClick()
await app.page.waitForTimeout(100)
await toolbar.editSketch()
await app.page.waitForTimeout(600)
await verifyCapProfilesAreDrawn()
await toolbar.exitSketchBtn.click()
await app.page.waitForTimeout(100)
})
}
})
}
)
test2(
'Can enter sketch loft edges, base and continue sketch',
async ({ app, scene, toolbar, editor }) => {
await app.initialise(`sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([34, 42.66], sketch001)
|> line([102.65, 151.99], %)
|> line([76, -138.66], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
plane001 = offsetPlane('XZ', 50)
sketch002 = startSketchOn(plane001)
profile002 = startProfileAt([39.43, 172.21], sketch002)
|> xLine(183.99, %)
|> line([-77.95, -145.93], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
loft([profile001, profile002])
`)
const [baseProfileEdgeClick] = scene.makeMouseHelpers(621, 292)
const [rect1Crn1] = scene.makeMouseHelpers(592, 283)
const [rect1Crn2] = scene.makeMouseHelpers(797, 268)
await baseProfileEdgeClick()
await toolbar.editSketch()
await app.page.waitForTimeout(600)
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 562, y: 172 }, 15)
await toolbar.rectangleBtn.click()
await app.page.waitForTimeout(100)
await rect1Crn1()
await editor.expectEditor.toContain(
`profile003 = startProfileAt([50.72, -18.19], sketch001)`
)
await rect1Crn2()
await editor.expectEditor.toContain(
`angledLine([0, 113.01], %, $rectangleSegmentA001)`
)
}
)
test2(
'Can enter sketch loft edges offsetPlane and continue sketch',
async ({ app, scene, toolbar, editor }) => {
await app.initialise(`sketch001 = startSketchOn('XZ')
profile001 = startProfileAt([34, 42.66], sketch001)
|> line([102.65, 151.99], %)
|> line([76, -138.66], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
plane001 = offsetPlane('XZ', 50)
sketch002 = startSketchOn(plane001)
profile002 = startProfileAt([39.43, 172.21], sketch002)
|> xLine(183.99, %)
|> line([-77.95, -145.93], %)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
loft([profile001, profile002])
`)
const topProfileEdgeClickCoords = { x: 602, y: 185 } as const
const [topProfileEdgeClick] = scene.makeMouseHelpers(
topProfileEdgeClickCoords.x,
topProfileEdgeClickCoords.y
)
const [rect1Crn1] = scene.makeMouseHelpers(592, 283)
const [rect1Crn2] = scene.makeMouseHelpers(797, 268)
await scene.moveCameraTo(
{ x: 8171, y: -7740, z: 1624 },
{ x: 3302, y: -627, z: 2892 }
)
await topProfileEdgeClick()
await toolbar.editSketch()
await app.page.waitForTimeout(600)
await scene.expectPixelColor(TEST_COLORS.BLUE, { x: 788, y: 188 }, 15)
await toolbar.rectangleBtn.click()
await app.page.waitForTimeout(100)
await rect1Crn1()
await editor.expectEditor.toContain(
`profile003 = startProfileAt([47.76, -17.13], plane001)`
)
await rect1Crn2()
await editor.expectEditor.toContain(
`angledLine([0, 106.42], %, $rectangleSegmentA001)`
)
}
)
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -1776,8 +1776,7 @@ export class SceneEntities {
structuredClone(pathToNode)
nodePathWithCorrectedIndexForTruncatedAst[1][0] =
Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) -
Number(planeNodePath[1][0]) -
1
Number(sketchNodePaths[0][1][0])
const _node = getNodeFromPath<Node<CallExpression>>(
modifiedAst,

View File

@ -2,7 +2,12 @@ import { SVGProps } from 'react'
export const Spinner = (props: SVGProps<SVGSVGElement>) => {
return (
<svg viewBox="0 0 10 10" className={'w-8 h-8'} {...props}>
<svg
data-testid="spinner"
viewBox="0 0 10 10"
className={'w-8 h-8'}
{...props}
>
<circle
cx="5"
cy="5"

View File

@ -5,7 +5,6 @@ import {
PathToNode,
Expr,
CallExpression,
PipeExpression,
VariableDeclarator,
} from 'lang/wasm'
import { Selections } from 'lib/selections'
@ -15,7 +14,6 @@ import {
createCallExpressionStdLib,
createObjectExpression,
createIdentifier,
createPipeExpression,
findUniqueName,
createVariableDeclaration,
} from 'lang/modifyAst'
@ -24,12 +22,13 @@ import {
mutateAstWithTagForSketchSegment,
getEdgeTagCall,
} from 'lang/modifyAst/addEdgeTreatment'
import { Artifact, getPathsFromArtifact } from 'lang/std/artifactGraph'
export function revolveSketch(
ast: Node<Program>,
pathToSketchNode: PathToNode,
shouldPipe = false,
angle: Expr = createLiteral(4),
axis: Selections
axis: Selections,
artifact?: Artifact
):
| {
modifiedAst: Node<Program>
@ -37,6 +36,11 @@ export function revolveSketch(
pathToRevolveArg: PathToNode
}
| Error {
const orderedSketchNodePaths = getPathsFromArtifact({
artifact: artifact,
sketchPathToNode: pathToSketchNode,
})
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
const clonedAst = structuredClone(ast)
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
if (err(sketchNode)) return sketchNode
@ -67,29 +71,13 @@ export function revolveSketch(
if (err(tagResult)) return tagResult
const { tag } = tagResult
/* Original Code */
const { node: sketchExpression } = sketchNode
// determine if sketchExpression is in a pipeExpression or not
const sketchPipeExpressionNode = getNodeFromPath<PipeExpression>(
clonedAst,
pathToSketchNode,
'PipeExpression'
)
if (err(sketchPipeExpressionNode)) return sketchPipeExpressionNode
const { node: sketchPipeExpression } = sketchPipeExpressionNode
const isInPipeExpression = sketchPipeExpression.type === 'PipeExpression'
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
clonedAst,
pathToSketchNode,
'VariableDeclarator'
)
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
const {
node: sketchVariableDeclarator,
shallowPath: sketchPathToDecleration,
} = sketchVariableDeclaratorNode
const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode
const axisSelection = axis?.graphSelections[0]?.artifact
@ -103,37 +91,13 @@ export function revolveSketch(
createIdentifier(sketchVariableDeclarator.id.name),
])
if (shouldPipe) {
const pipeChain = createPipeExpression(
isInPipeExpression
? [...sketchPipeExpression.body, revolveCall]
: [sketchExpression as any, revolveCall]
)
sketchVariableDeclarator.init = pipeChain
const pathToRevolveArg: PathToNode = [
...sketchPathToDecleration,
['init', 'VariableDeclarator'],
['body', ''],
[pipeChain.body.length - 1, 'index'],
['arguments', 'CallExpression'],
[0, 'index'],
]
return {
modifiedAst: clonedAst,
pathToSketchNode,
pathToRevolveArg,
}
}
// We're not creating a pipe expression,
// but rather a separate constant for the extrusion
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
const sketchIndexInPathToNode =
sketchPathToDecleration.findIndex((a) => a[0] === 'body') + 1
const sketchIndexInBody = sketchPathToDecleration[sketchIndexInPathToNode][0]
const lastSketchNodePath =
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
if (typeof sketchIndexInBody !== 'number')
return new Error('expected sketchIndexInBody to be a number')
clonedAst.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)

View File

@ -212,19 +212,7 @@ Map {
"type": "wall",
},
"UUID-10" => {
"codeRef": {
"pathToNode": [
[
"body",
"",
],
],
"range": [
501,
522,
true,
],
},
"codeRef": undefined,
"edgeCutEdgeIds": [],
"id": "UUID",
"pathIds": [

View File

@ -22,6 +22,7 @@ import * as d3 from 'd3-force'
import path from 'path'
import pixelmatch from 'pixelmatch'
import { PNG } from 'pngjs'
import { Node } from 'wasm-lib/kcl/bindings/Node'
/*
Note this is an integration test, these tests connect to our real dev server and make websocket commands.
@ -171,7 +172,7 @@ afterAll(() => {
describe('testing createArtifactGraph', () => {
describe('code with offset planes and a sketch:', () => {
let ast: Program
let ast: Node<Program>
let theMap: ReturnType<typeof createArtifactGraph>
it('setup', () => {
@ -217,7 +218,7 @@ describe('testing createArtifactGraph', () => {
})
})
describe('code with an extrusion, fillet and sketch of face:', () => {
let ast: Program
let ast: Node<Program>
let theMap: ReturnType<typeof createArtifactGraph>
it('setup', () => {
// putting this logic in here because describe blocks runs before beforeAll has finished
@ -312,7 +313,7 @@ describe('testing createArtifactGraph', () => {
})
describe(`code with sketches but no extrusions or other 3D elements`, () => {
let ast: Program
let ast: Node<Program>
let theMap: ReturnType<typeof createArtifactGraph>
it(`setup`, () => {
// putting this logic in here because describe blocks runs before beforeAll has finished
@ -377,7 +378,7 @@ describe('testing createArtifactGraph', () => {
describe('capture graph of sketchOnFaceOnFace...', () => {
describe('code with an extrusion, fillet and sketch of face:', () => {
let ast: Program
let ast: Node<Program>
let theMap: ReturnType<typeof createArtifactGraph>
it('setup', async () => {
// putting this logic in here because describe blocks runs before beforeAll has finished
@ -399,7 +400,9 @@ describe('capture graph of sketchOnFaceOnFace...', () => {
})
})
function getCommands(codeKey: CodeKey): CacheShape[CodeKey] & { ast: Program } {
function getCommands(
codeKey: CodeKey
): CacheShape[CodeKey] & { ast: Node<Program> } {
const ast = assertParse(codeKey)
const file = fs.readFileSync(fullPath, 'utf-8')
const parsed: CacheShape = JSON.parse(file)

View File

@ -1,8 +1,19 @@
import { Expr, PathToNode, Program, SourceRange } from 'lang/wasm'
import {
Expr,
PathToNode,
Program,
SourceRange,
VariableDeclaration,
} from 'lang/wasm'
import { Models } from '@kittycad/lib'
import { getNodePathFromSourceRange } from 'lang/queryAst'
import {
getNodeFromPath,
getNodePathFromSourceRange,
traverse,
} from 'lang/queryAst'
import { err } from 'lib/trap'
import { engineCommandManager, kclManager } from 'lib/singletons'
import { Node } from 'wasm-lib/kcl/bindings/Node'
export type ArtifactId = string
@ -42,7 +53,7 @@ interface Solid2DArtifact extends BaseArtifact {
export interface PathArtifactRich extends BaseArtifact {
type: 'path'
/** A path must always lie on a plane */
plane: PlaneArtifact | WallArtifact
plane: PlaneArtifact | WallArtifact | CapArtifact
/** A path must always contain 0 or more segments */
segments: Array<SegmentArtifact>
/** A path may not result in a sweep artifact */
@ -101,6 +112,9 @@ interface CapArtifact extends BaseArtifact {
edgeCutEdgeIds: Array<ArtifactId>
sweepId: ArtifactId
pathIds: Array<ArtifactId>
// codeRef is for the sketchOnFace plane, not for the wall itself
// traverse to the extrude and or segment to get the wall's codeRef
codeRef?: CodeRef
}
interface SweepEdgeArtifact extends BaseArtifact {
@ -163,7 +177,7 @@ export function createArtifactGraph({
}: {
orderedCommands: Array<OrderedCommand>
responseMap: ResponseMap
ast: Program
ast: Node<Program>
}) {
const myMap = new Map<ArtifactId, Artifact>()
@ -242,7 +256,7 @@ export function getArtifactsToUpdate({
/** Passing in a getter because we don't wan this function to update the map directly */
getArtifact: (id: ArtifactId) => Artifact | undefined
currentPlaneId: ArtifactId
ast: Program
ast: Node<Program>
}): Array<{
id: ArtifactId
artifact: Artifact
@ -278,6 +292,13 @@ export function getArtifactsToUpdate({
plane?.type === 'plane' ? plane?.codeRef : { range, pathToNode }
const existingPlane = getArtifact(currentPlaneId)
if (existingPlane?.type === 'wall') {
let existingPlaneCodeRef = existingPlane.codeRef
if (!existingPlaneCodeRef) {
const astWalkCodeRef = getWallOrCapPlaneCodeRef(ast, codeRef.pathToNode)
if (!err(astWalkCodeRef)) {
existingPlaneCodeRef = astWalkCodeRef
}
}
return [
{
id: currentPlaneId,
@ -288,7 +309,29 @@ export function getArtifactsToUpdate({
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
sweepId: existingPlane.sweepId,
pathIds: existingPlane.pathIds,
codeRef,
codeRef: existingPlaneCodeRef,
},
},
]
} else if (existingPlane?.type === 'cap') {
let existingPlaneCodeRef = existingPlane.codeRef
if (!existingPlaneCodeRef) {
const astWalkCodeRef = getWallOrCapPlaneCodeRef(ast, codeRef.pathToNode)
if (!err(astWalkCodeRef)) {
existingPlaneCodeRef = astWalkCodeRef
}
}
return [
{
id: currentPlaneId,
artifact: {
type: 'cap',
subType: existingPlane.subType,
id: currentPlaneId,
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
sweepId: existingPlane.sweepId,
pathIds: existingPlane.pathIds,
codeRef: existingPlaneCodeRef,
},
},
]
@ -333,6 +376,18 @@ export function getArtifactsToUpdate({
pathIds: [id],
},
})
} else if (plane?.type === 'cap') {
returnArr.push({
id: currentPlaneId,
artifact: {
type: 'cap',
id: currentPlaneId,
subType: plane.subType,
edgeCutEdgeIds: plane.edgeCutEdgeIds,
sweepId: plane.sweepId,
pathIds: [id],
},
})
}
return returnArr
} else if (cmd.type === 'extend_path' || cmd.type === 'close_path') {
@ -880,9 +935,9 @@ export function codeRefFromRange(range: SourceRange, ast: Program): CodeRef {
function getPlaneFromPath(
path: PathArtifact,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
const plane = getArtifactOfTypes(
{ key: path.planeId, types: ['plane', 'wall'] },
{ key: path.planeId, types: ['plane', 'wall', 'cap'] },
graph
)
if (err(plane)) return plane
@ -892,7 +947,7 @@ function getPlaneFromPath(
function getPlaneFromSegment(
segment: SegmentArtifact,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
const path = getArtifactOfTypes(
{ key: segment.pathId, types: ['path'] },
graph
@ -903,7 +958,7 @@ function getPlaneFromSegment(
function getPlaneFromSolid2D(
solid2D: Solid2DArtifact,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
const path = getArtifactOfTypes(
{ key: solid2D.pathId, types: ['path'] },
graph
@ -914,7 +969,7 @@ function getPlaneFromSolid2D(
function getPlaneFromCap(
cap: CapArtifact,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
const sweep = getArtifactOfTypes(
{ key: cap.sweepId, types: ['sweep'] },
graph
@ -927,7 +982,7 @@ function getPlaneFromCap(
function getPlaneFromWall(
wall: WallArtifact,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
const sweep = getArtifactOfTypes(
{ key: wall.sweepId, types: ['sweep'] },
graph
@ -951,7 +1006,7 @@ function getPlaneFromSweepEdge(edge: SweepEdgeArtifact, graph: ArtifactGraph) {
export function getPlaneFromArtifact(
artifact: Artifact | undefined,
graph: ArtifactGraph
): PlaneArtifact | WallArtifact | Error {
): PlaneArtifact | WallArtifact | CapArtifact | Error {
if (!artifact) return new Error(`Artifact is undefined`)
if (artifact.type === 'plane') return artifact
if (artifact.type === 'path') return getPlaneFromPath(artifact, graph)
@ -1075,3 +1130,82 @@ function isNodeSafe(node: Expr): boolean {
}
return false
}
/** {@deprecated} this information should come from the ArtifactGraph not digging around in the AST */
function getWallOrCapPlaneCodeRef(
ast: Node<Program>,
pathToNode: PathToNode
): CodeRef | Error {
const varDec = getNodeFromPath<VariableDeclaration>(
ast,
pathToNode,
'VariableDeclaration'
)
if (err(varDec)) return varDec
if (varDec.node.type !== 'VariableDeclaration')
return new Error('Expected VariableDeclaration')
const init = varDec.node.declaration.init
let varName = ''
if (
init.type === 'CallExpression' &&
init.callee.type === 'Identifier' &&
(init.callee.name === 'circle' || init.callee.name === 'startProfileAt')
) {
const secondArg = init.arguments[1]
if (secondArg.type === 'Identifier') {
varName = secondArg.name
}
} else if (init.type === 'PipeExpression') {
const firstExpr = init.body[0]
if (
firstExpr.type === 'CallExpression' &&
firstExpr.callee.type === 'Identifier' &&
firstExpr.callee.name === 'startProfileAt'
) {
const secondArg = firstExpr.arguments[1]
if (secondArg.type === 'Identifier') {
varName = secondArg.name
}
}
}
if (varName === '') return new Error('Could not find variable name')
let currentVariableName = ''
const planeCodeRef: Array<{
path: PathToNode
sketchName: string
range: SourceRange
}> = []
traverse(ast, {
leave: (node) => {
if (node.type === 'VariableDeclaration') {
currentVariableName = ''
}
},
enter: (node, path) => {
if (node.type === 'VariableDeclaration') {
currentVariableName = node.declaration.id.name
}
if (
// match `${varName} = startSketchOn(...)`
node.type === 'CallExpression' &&
node.callee.name === 'startSketchOn' &&
node.arguments[0].type === 'Identifier' &&
currentVariableName === varName
) {
planeCodeRef.push({
path,
sketchName: currentVariableName,
range: [node.start, node.end, true],
})
}
},
})
if (!planeCodeRef.length)
return new Error('No paths found depending on extrude')
return {
pathToNode: planeCodeRef[0].path,
range: planeCodeRef[0].range,
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 568 KiB

After

Width:  |  Height:  |  Size: 560 KiB

View File

@ -37,6 +37,7 @@ import { KclManager } from 'lang/KclSingleton'
import { reportRejection } from 'lib/trap'
import { markOnce } from 'lib/performance'
import { MachineManager } from 'components/MachineManagerProvider'
import { Node } from 'wasm-lib/kcl/bindings/Node'
// TODO(paultag): This ought to be tweakable.
const pingIntervalMs = 5_000
@ -2115,7 +2116,7 @@ export class EngineCommandManager extends EventTarget {
Object.values(this.pendingCommands).map((a) => a.promise)
)
}
updateArtifactGraph(ast: Program) {
updateArtifactGraph(ast: Node<Program>) {
this.artifactGraph = createArtifactGraph({
orderedCommands: this.orderedCommands,
responseMap: this.responseMap,

File diff suppressed because one or more lines are too long