Allow multiple profiles in the same sketch (#5196)
* Revert "Revert multi-profile (#4812)"
This reverts commit efe8089b08
.
* fix poor 1000ms wait UX
* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest-8-cores)
* trigger CI
* Add Rust side artifacts for startSketchOn face or plane (#4834)
* Add Rust side artifacts for startSketchOn face or plane
* move ast digging
---------
Co-authored-by: Kurt Hutten Irev-Dev <k.hutten@protonmail.ch>
* lint
* lint
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-macos-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: windows-16-cores)
* trigger CI
* chore: disabled file watcher which prevents faster file write (#4835)
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* partial fixes
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Trigger CI
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Trigger CI
* Fix up all the tests
* Fix partial execution
* wip
* WIP
* wip
* rust changes to make three point confrom to same as others since we're not ready with name params yet
* most of the fix for 3 point circle
* get overlays working for circle three point
* fmt
* fix types
* cargo fmt
* add face codef ref for walls and caps
* fix sketch on face after updates to rust side artifact graph
* some things needed for multi-profile tests
* bad attempts at fixing rust
* more
* more
* fix rust
* more rust fixes
* overlay fix
* remove duplicate test
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* lint and typing
* maybe fix a unit test
* small thing
* fix circ dep
* fix unit test
* fix some tests
* fix sweep point-and-click test
* fix more tests and add a fix me
* fix more tests
* fix electron specific test
* tsc
* more test tweaks
* update docs
* commint snaps?
* is clippy happy now?
* clippy again
* test works now without me changing anything big-fixed-itself
* small bug
* make three point have cross hair to make it consistent with othe rtools
* fix up state diagram
* fmt
* add draft point for first click of three point circ
* 1 test for three point circle
* 2 test for three point circle
* clean up
* 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)
* remove bad doc comment
* remove test skip
* remove onboarding test changes
* Update src/lang/modifyAst.ts
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
* Update output from simulation tests
* Fix to use correct source ranges
This also reduces cloning.
* Change back to skipping face cap none and both
* Update output after changing back to skipping none and both
* Fix clippy warning
* fix profile start snap bug
* add path ids to cap
* fix going into edit sketch
* make other startSketchOn's work
* fix snapshot test
* explain function name
* Update src/lib/rectangleTool.ts
Co-authored-by: Frank Noirot <frank@zoo.dev>
* rename error
* remove file tree from diff
* Update src/clientSideScene/segments.ts
Co-authored-by: Frank Noirot <frank@zoo.dev>
* nit
* Prevent double write to KCL code on revolve
* Update output after adding cap-to-path graph edge
* Fix edit/select sketch-on-cap via feature tree
* clean up for face codeRef
* fix changing tools part way through circle/rect tools
* fix delete of circle profile
* fix close profiles
* fix closing profile bug (tangentArcTo being ignored)
* remove stale comment
* Delete paths associated with sketch when the sketch plane is deleted
* Add support for deleting sketches on caps (not walls)
* get delet working for walls
* make delet of extrusions work for multi profile
* A snapshot a day keeps the bugs away! 📷🐛 (OS: namespace-profile-ubuntu-8-cores)
* Delete the sketch statement too on the cap and wall cases
* Don't write to file in `split-sketch-pipe-if-needed` unless necessary
* Don't wait for file write to complete within `updateEditorWithAstAndWriteToFile`
It is already debounced internally. If we await it, we will have to wait for a debounced timeout
* docs
* fix circ dep
* tsc
* fix selection enter sketch weirdness
* test fixes
* comment out and fixme for delete related tests
* add skip wins
* try and get last test to pass
---------
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
Co-authored-by: Kevin Nadro <nadr0@users.noreply.github.com>
Co-authored-by: Pierre Jacquier <pierre@zoo.dev>
Co-authored-by: 49lf <ircsurfer33@gmail.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Frank Noirot <frankjohnson1993@gmail.com>
This commit is contained in:
@ -21,8 +21,11 @@ import {
|
||||
SourceRange,
|
||||
sketchFromKclValue,
|
||||
isPathToNodeNumber,
|
||||
parse,
|
||||
formatNumber,
|
||||
ArtifactGraph,
|
||||
VariableMap,
|
||||
KclValue,
|
||||
} from './wasm'
|
||||
import {
|
||||
isNodeSafeToReplacePath,
|
||||
@ -31,6 +34,8 @@ import {
|
||||
getNodeFromPath,
|
||||
isNodeSafeToReplace,
|
||||
traverse,
|
||||
getBodyIndex,
|
||||
isCallExprWithName,
|
||||
ARG_INDEX_FIELD,
|
||||
LABELED_ARG_FIELD,
|
||||
UNLABELED_ARG,
|
||||
@ -49,7 +54,7 @@ import {
|
||||
transformAstSketchLines,
|
||||
} from './std/sketchcombos'
|
||||
import { DefaultPlaneStr } from 'lib/planes'
|
||||
import { isOverlap, roundOff } from 'lib/utils'
|
||||
import { isArray, isOverlap, roundOff } from 'lib/utils'
|
||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||
@ -57,6 +62,17 @@ import { Models } from '@kittycad/lib'
|
||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
||||
import {
|
||||
Artifact,
|
||||
expandCap,
|
||||
expandPlane,
|
||||
expandWall,
|
||||
getArtifactOfTypes,
|
||||
getArtifactsOfTypes,
|
||||
getFaceCodeRef,
|
||||
getPathsFromArtifact,
|
||||
} from './std/artifactGraph'
|
||||
import { BodyItem } from 'wasm-lib/kcl/bindings/BodyItem'
|
||||
import { findKwArg } from './util'
|
||||
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
||||
|
||||
@ -91,41 +107,54 @@ export function startSketchOnDefault(
|
||||
}
|
||||
}
|
||||
|
||||
export function addStartProfileAt(
|
||||
export function insertNewStartProfileAt(
|
||||
node: Node<Program>,
|
||||
pathToNode: PathToNode,
|
||||
at: [number, number]
|
||||
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||
sketchEntryNodePath: PathToNode,
|
||||
sketchNodePaths: PathToNode[],
|
||||
planeNodePath: PathToNode,
|
||||
at: [number, number],
|
||||
insertType: 'start' | 'end' = 'end'
|
||||
):
|
||||
| {
|
||||
modifiedAst: Node<Program>
|
||||
updatedSketchNodePaths: PathToNode[]
|
||||
updatedEntryNodePath: PathToNode
|
||||
}
|
||||
| Error {
|
||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||
node,
|
||||
pathToNode,
|
||||
'VariableDeclaration'
|
||||
planeNodePath,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_node1)) return _node1
|
||||
const variableDeclaration = _node1.node
|
||||
if (variableDeclaration.type !== 'VariableDeclaration') {
|
||||
return new Error('variableDeclaration.init.type !== PipeExpression')
|
||||
}
|
||||
const _node = { ...node }
|
||||
const init = variableDeclaration.declaration.init
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
createArrayExpression([
|
||||
createLiteral(roundOff(at[0])),
|
||||
createLiteral(roundOff(at[1])),
|
||||
]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
if (init.type === 'PipeExpression') {
|
||||
init.body.splice(1, 0, startProfileAt)
|
||||
} else {
|
||||
variableDeclaration.declaration.init = createPipeExpression([
|
||||
init,
|
||||
startProfileAt,
|
||||
if (err(varDec)) return varDec
|
||||
if (varDec.node.type !== 'VariableDeclarator') return new Error('not a var')
|
||||
|
||||
const newExpression = createVariableDeclaration(
|
||||
findUniqueName(node, 'profile'),
|
||||
createCallExpressionStdLib('startProfileAt', [
|
||||
createArrayExpression([
|
||||
createLiteral(roundOff(at[0])),
|
||||
createLiteral(roundOff(at[1])),
|
||||
]),
|
||||
createIdentifier(varDec.node.id.name),
|
||||
])
|
||||
}
|
||||
)
|
||||
const insertIndex = getInsertIndex(sketchNodePaths, planeNodePath, insertType)
|
||||
|
||||
const _node = structuredClone(node)
|
||||
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
||||
_node.body.splice(insertIndex, 0, newExpression)
|
||||
|
||||
const { updatedEntryNodePath, updatedSketchNodePaths } =
|
||||
updateSketchNodePathsWithInsertIndex({
|
||||
insertIndex,
|
||||
insertType,
|
||||
sketchNodePaths,
|
||||
})
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
updatedSketchNodePaths,
|
||||
updatedEntryNodePath,
|
||||
}
|
||||
}
|
||||
|
||||
@ -225,8 +254,21 @@ export function mutateKwArg(
|
||||
for (let i = 0; i < node.arguments.length; i++) {
|
||||
const arg = node.arguments[i]
|
||||
if (arg.label.name === label) {
|
||||
node.arguments[i].arg = val
|
||||
return true
|
||||
if (isLiteralArrayOrStatic(val) && isLiteralArrayOrStatic(arg.arg)) {
|
||||
node.arguments[i].arg = val
|
||||
return true
|
||||
} else if (
|
||||
arg.arg.type === 'ArrayExpression' &&
|
||||
val.type === 'ArrayExpression'
|
||||
) {
|
||||
const arrExp = arg.arg
|
||||
arrExp.elements.forEach((element, i) => {
|
||||
if (isLiteralArrayOrStatic(element)) {
|
||||
arrExp.elements[i] = val.elements[i]
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
node.arguments.push(createLabeledArg(label, val))
|
||||
@ -289,15 +331,17 @@ export function mutateObjExpProp(
|
||||
export function extrudeSketch({
|
||||
node,
|
||||
pathToNode,
|
||||
shouldPipe = false,
|
||||
distance = createLiteral(4),
|
||||
extrudeName,
|
||||
artifact,
|
||||
artifactGraph,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
pathToNode: PathToNode
|
||||
shouldPipe?: boolean
|
||||
distance: Expr
|
||||
extrudeName?: string
|
||||
artifactGraph: ArtifactGraph
|
||||
artifact?: Artifact
|
||||
}):
|
||||
| {
|
||||
modifiedAst: Node<Program>
|
||||
@ -305,10 +349,16 @@ export function extrudeSketch({
|
||||
pathToExtrudeArg: PathToNode
|
||||
}
|
||||
| Error {
|
||||
const orderedSketchNodePaths = getPathsFromArtifact({
|
||||
artifact: artifact,
|
||||
sketchPathToNode: pathToNode,
|
||||
artifactGraph,
|
||||
ast: node,
|
||||
})
|
||||
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
||||
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>(
|
||||
@ -317,9 +367,6 @@ export function extrudeSketch({
|
||||
'PipeExpression'
|
||||
)
|
||||
if (err(_node2)) return _node2
|
||||
const { node: pipeExpression } = _node2
|
||||
|
||||
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||
|
||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||
_node,
|
||||
@ -327,54 +374,27 @@ export function extrudeSketch({
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(_node3)) return _node3
|
||||
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
||||
const { node: variableDeclarator } = _node3
|
||||
|
||||
const sketchToExtrude = shouldPipe
|
||||
? createPipeSubstitution()
|
||||
: createIdentifier(variableDeclarator.id.name)
|
||||
const extrudeCall = createCallExpressionStdLibKw('extrude', sketchToExtrude, [
|
||||
createLabeledArg('length', distance),
|
||||
])
|
||||
const extrudeCall = createCallExpressionStdLibKw(
|
||||
'extrude',
|
||||
createIdentifier(variableDeclarator.id.name),
|
||||
[createLabeledArg('length', distance)]
|
||||
)
|
||||
// index of the 'length' arg above. If you reorder the labeled args above,
|
||||
// make sure to update this too.
|
||||
const argIndex = 0
|
||||
|
||||
if (shouldPipe) {
|
||||
const pipeChain = createPipeExpression(
|
||||
isInPipeExpression
|
||||
? [...pipeExpression.body, extrudeCall]
|
||||
: [sketchExpression as any, extrudeCall]
|
||||
)
|
||||
|
||||
variableDeclarator.init = pipeChain
|
||||
const pathToExtrudeArg: PathToNode = [
|
||||
...pathToDecleration,
|
||||
['init', 'VariableDeclarator'],
|
||||
['body', ''],
|
||||
[pipeChain.body.length - 1, 'index'],
|
||||
['arguments', 'CallExpressionKw'],
|
||||
[argIndex, ARG_INDEX_FIELD],
|
||||
['arg', LABELED_ARG_FIELD],
|
||||
]
|
||||
|
||||
return {
|
||||
modifiedAst: _node,
|
||||
pathToNode,
|
||||
pathToExtrudeArg,
|
||||
}
|
||||
}
|
||||
|
||||
// We're not creating a pipe expression,
|
||||
// but rather a separate constant for the extrusion
|
||||
const name =
|
||||
extrudeName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
||||
|
||||
const sketchIndexInPathToNode =
|
||||
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
||||
const sketchIndexInBody = pathToDecleration[
|
||||
sketchIndexInPathToNode
|
||||
][0] as number
|
||||
const lastSketchNodePath =
|
||||
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
||||
|
||||
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
||||
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||
|
||||
const pathToExtrudeArg: PathToNode = [
|
||||
@ -1372,10 +1392,53 @@ export async function deleteFromSelection(
|
||||
ast: Node<Program>,
|
||||
selection: Selection,
|
||||
variables: VariableMap,
|
||||
artifactGraph: ArtifactGraph,
|
||||
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
||||
({} as any)
|
||||
): Promise<Node<Program> | Error> {
|
||||
const astClone = structuredClone(ast)
|
||||
if (
|
||||
(selection.artifact?.type === 'plane' ||
|
||||
selection.artifact?.type === 'cap' ||
|
||||
selection.artifact?.type === 'wall') &&
|
||||
selection.artifact?.pathIds?.length
|
||||
) {
|
||||
const plane =
|
||||
selection.artifact.type === 'plane'
|
||||
? expandPlane(selection.artifact, artifactGraph)
|
||||
: selection.artifact.type === 'wall'
|
||||
? expandWall(selection.artifact, artifactGraph)
|
||||
: expandCap(selection.artifact, artifactGraph)
|
||||
for (const path of plane.paths.sort(
|
||||
(a, b) => b.codeRef.range?.[0] - a.codeRef.range?.[0]
|
||||
)) {
|
||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
path.codeRef.pathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
if (err(varDec)) return varDec
|
||||
const bodyIndex = Number(varDec.shallowPath[1][0])
|
||||
astClone.body.splice(bodyIndex, 1)
|
||||
}
|
||||
// If it's a cap, we're not going to continue and try to
|
||||
// delete the extrusion
|
||||
if (
|
||||
selection.artifact.type === 'cap' ||
|
||||
selection.artifact.type === 'wall'
|
||||
) {
|
||||
// Delete the sketch node, which would not work if
|
||||
// we continued down the traditional code path below.
|
||||
// faceCodeRef's pathToNode is empty for some reason
|
||||
// so using source range instead
|
||||
const codeRef = getFaceCodeRef(selection.artifact)
|
||||
if (!codeRef) return new Error('Could not find face code ref')
|
||||
const sketchVarDec = getNodePathFromSourceRange(astClone, codeRef.range)
|
||||
const sketchBodyIndex = Number(sketchVarDec[1][0])
|
||||
astClone.body.splice(sketchBodyIndex, 1)
|
||||
return astClone
|
||||
}
|
||||
}
|
||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||
ast,
|
||||
selection?.codeRef?.pathToNode,
|
||||
@ -1454,59 +1517,108 @@ export async function deleteFromSelection(
|
||||
if (extrudeNameToDelete) {
|
||||
await new Promise((resolve) => {
|
||||
;(async () => {
|
||||
let currentVariableName = ''
|
||||
const pathsDependingOnExtrude: Array<{
|
||||
path: PathToNode
|
||||
sketchName: string
|
||||
variable: KclValue
|
||||
}> = []
|
||||
traverse(astClone, {
|
||||
leave: (node) => {
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
currentVariableName = ''
|
||||
}
|
||||
},
|
||||
enter: (node, path) => {
|
||||
;(async () => {
|
||||
if (node.type === 'VariableDeclaration') {
|
||||
currentVariableName = node.declaration.id.name
|
||||
}
|
||||
if (
|
||||
// match startSketchOn(${extrudeNameToDelete})
|
||||
node.type === 'CallExpression' &&
|
||||
node.callee.name === 'startSketchOn' &&
|
||||
node.arguments[0].type === 'Identifier' &&
|
||||
node.arguments[0].name === extrudeNameToDelete
|
||||
) {
|
||||
pathsDependingOnExtrude.push({
|
||||
path,
|
||||
sketchName: currentVariableName,
|
||||
})
|
||||
}
|
||||
})().catch(reportRejection)
|
||||
},
|
||||
})
|
||||
const roundLiteral = (x: number) => createLiteral(roundOff(x))
|
||||
const modificationDetails: {
|
||||
parent: PipeExpression['body']
|
||||
parentPipe: PipeExpression['body']
|
||||
parentInit: VariableDeclarator
|
||||
faceDetails: Models['FaceIsPlanar_type']
|
||||
lastKey: number
|
||||
lastKey: number | string
|
||||
}[] = []
|
||||
for (const { path, sketchName } of pathsDependingOnExtrude) {
|
||||
const parent = getNodeFromPath<PipeExpression['body']>(
|
||||
const wallArtifact =
|
||||
selection.artifact?.type === 'wall'
|
||||
? selection.artifact
|
||||
: selection.artifact?.type === 'segment' &&
|
||||
selection.artifact.surfaceId
|
||||
? getArtifactOfTypes(
|
||||
{ key: selection.artifact.surfaceId, types: ['wall'] },
|
||||
artifactGraph
|
||||
)
|
||||
: null
|
||||
if (err(wallArtifact)) return
|
||||
if (wallArtifact) {
|
||||
const sweep = getArtifactOfTypes(
|
||||
{ key: wallArtifact.sweepId, types: ['sweep'] },
|
||||
artifactGraph
|
||||
)
|
||||
if (err(sweep)) return
|
||||
const wallsWithDependencies = Array.from(
|
||||
getArtifactsOfTypes(
|
||||
{ keys: sweep.surfaceIds, types: ['wall', 'cap'] },
|
||||
artifactGraph
|
||||
).values()
|
||||
).filter((wall) => wall?.pathIds?.length)
|
||||
const wallIds = wallsWithDependencies.map((wall) => wall.id)
|
||||
Object.entries(variables).forEach(([key, _var]) => {
|
||||
if (
|
||||
_var?.type === 'Face' &&
|
||||
wallIds.includes(_var.value.artifactId)
|
||||
) {
|
||||
const pathToStartSketchOn = getNodePathFromSourceRange(
|
||||
astClone,
|
||||
_var.value.__meta[0].sourceRange
|
||||
)
|
||||
pathsDependingOnExtrude.push({
|
||||
path: pathToStartSketchOn,
|
||||
variable: _var,
|
||||
})
|
||||
}
|
||||
if (
|
||||
_var?.type === 'Sketch' &&
|
||||
_var.value.on.type === 'face' &&
|
||||
wallIds.includes(_var.value.on.artifactId)
|
||||
) {
|
||||
const pathToStartSketchOn = getNodePathFromSourceRange(
|
||||
astClone,
|
||||
_var.value.on.__meta[0].sourceRange
|
||||
)
|
||||
pathsDependingOnExtrude.push({
|
||||
path: pathToStartSketchOn,
|
||||
variable: {
|
||||
type: 'Face',
|
||||
value: _var.value.on,
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
for (const { path, variable } of pathsDependingOnExtrude) {
|
||||
// `parentPipe` and `parentInit` are the exact same node, but because it could either be an array or on object node
|
||||
// putting them in two different variables was the only way to get TypeScript to stop complaining
|
||||
// the reason why we're grabbing the parent and the last key is because we want to mutate the ast
|
||||
// so `parent[lastKey]` does the trick, if there's a better way of doing this I'm all years
|
||||
const parentPipe = getNodeFromPath<PipeExpression['body']>(
|
||||
astClone,
|
||||
path.slice(0, -1)
|
||||
)
|
||||
if (err(parent)) {
|
||||
const parentInit = getNodeFromPath<VariableDeclarator>(
|
||||
astClone,
|
||||
path.slice(0, -1)
|
||||
)
|
||||
if (err(parentPipe) || err(parentInit)) {
|
||||
return
|
||||
}
|
||||
const sketchToPreserve = sketchFromKclValue(
|
||||
variables[sketchName],
|
||||
sketchName
|
||||
)
|
||||
if (err(sketchToPreserve)) return sketchToPreserve
|
||||
if (!variable) return new Error('Could not find sketch')
|
||||
const artifactId =
|
||||
variable.type === 'Sketch'
|
||||
? variable.value.artifactId
|
||||
: variable.type === 'Face'
|
||||
? variable.value.artifactId
|
||||
: ''
|
||||
if (!artifactId) return new Error('Sketch not on anything')
|
||||
const onId =
|
||||
variable.type === 'Sketch'
|
||||
? variable.value.on.id
|
||||
: variable.type === 'Face'
|
||||
? variable.value.id
|
||||
: ''
|
||||
if (!onId) return new Error('Sketch not on anything')
|
||||
// Can't kick off multiple requests at once as getFaceDetails
|
||||
// is three engine calls in one and they conflict
|
||||
const faceDetails = await getFaceDetails(sketchToPreserve.on.id)
|
||||
const faceDetails = await getFaceDetails(onId)
|
||||
if (
|
||||
!(
|
||||
faceDetails.origin &&
|
||||
@ -1517,14 +1629,20 @@ export async function deleteFromSelection(
|
||||
) {
|
||||
return
|
||||
}
|
||||
const lastKey = Number(path.slice(-1)[0][0])
|
||||
const lastKey = path.slice(-1)[0][0]
|
||||
modificationDetails.push({
|
||||
parent: parent.node,
|
||||
parentPipe: parentPipe.node,
|
||||
parentInit: parentInit.node,
|
||||
faceDetails,
|
||||
lastKey,
|
||||
})
|
||||
}
|
||||
for (const { parent, faceDetails, lastKey } of modificationDetails) {
|
||||
for (const {
|
||||
parentInit,
|
||||
parentPipe,
|
||||
faceDetails,
|
||||
lastKey,
|
||||
} of modificationDetails) {
|
||||
if (
|
||||
!(
|
||||
faceDetails.origin &&
|
||||
@ -1535,7 +1653,7 @@ export async function deleteFromSelection(
|
||||
) {
|
||||
continue
|
||||
}
|
||||
parent[lastKey] = createCallExpressionStdLib('startSketchOn', [
|
||||
const expression = createCallExpressionStdLib('startSketchOn', [
|
||||
createObjectExpression({
|
||||
plane: createObjectExpression({
|
||||
origin: createObjectExpression({
|
||||
@ -1561,6 +1679,14 @@ export async function deleteFromSelection(
|
||||
}),
|
||||
}),
|
||||
])
|
||||
if (
|
||||
parentInit.type === 'VariableDeclarator' &&
|
||||
lastKey === 'init'
|
||||
) {
|
||||
parentInit[lastKey] = expression
|
||||
} else if (isArray(parentPipe) && typeof lastKey === 'number') {
|
||||
parentPipe[lastKey] = expression
|
||||
}
|
||||
}
|
||||
resolve(true)
|
||||
})().catch(reportRejection)
|
||||
@ -1572,15 +1698,29 @@ export async function deleteFromSelection(
|
||||
return deleteEdgeTreatment(astClone, selection)
|
||||
} else if (varDec.node.init.type === 'PipeExpression') {
|
||||
const pipeBody = varDec.node.init.body
|
||||
const doNotDeleteProfileIfItHasBeenExtruded = !(
|
||||
selection?.artifact?.type === 'segment' && selection?.artifact?.surfaceId
|
||||
)
|
||||
if (
|
||||
pipeBody[0].type === 'CallExpression' &&
|
||||
pipeBody[0].callee.name === 'startSketchOn'
|
||||
doNotDeleteProfileIfItHasBeenExtruded &&
|
||||
(pipeBody[0].callee.name === 'startSketchOn' ||
|
||||
pipeBody[0].callee.name === 'startProfileAt')
|
||||
) {
|
||||
// remove varDec
|
||||
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||
astClone.body.splice(varDecIndex, 1)
|
||||
return astClone
|
||||
}
|
||||
} else if (
|
||||
// single expression profiles
|
||||
(varDec.node.init.type === 'CallExpressionKw' ||
|
||||
varDec.node.init.type === 'CallExpression') &&
|
||||
['circleThreePoint', 'circle'].includes(varDec.node.init.callee.name)
|
||||
) {
|
||||
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||
astClone.body.splice(varDecIndex, 1)
|
||||
return astClone
|
||||
}
|
||||
|
||||
return new Error('Selection not recognised, could not delete')
|
||||
@ -1590,6 +1730,167 @@ export const nonCodeMetaEmpty = () => {
|
||||
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
||||
}
|
||||
|
||||
export const createLabeledArg = (name: string, arg: Expr): LabeledArg => {
|
||||
return { label: createIdentifier(name), arg, type: 'LabeledArg' }
|
||||
export function getInsertIndex(
|
||||
sketchNodePaths: PathToNode[],
|
||||
planeNodePath: PathToNode,
|
||||
insertType: 'start' | 'end'
|
||||
) {
|
||||
let minIndex = 0
|
||||
let maxIndex = 0
|
||||
for (const path of sketchNodePaths) {
|
||||
const index = Number(path[1][0])
|
||||
if (index < minIndex) minIndex = index
|
||||
if (index > maxIndex) maxIndex = index
|
||||
}
|
||||
|
||||
const insertIndex = !sketchNodePaths.length
|
||||
? Number(planeNodePath[1][0]) + 1
|
||||
: insertType === 'start'
|
||||
? minIndex
|
||||
: maxIndex + 1
|
||||
return insertIndex
|
||||
}
|
||||
|
||||
export function updateSketchNodePathsWithInsertIndex({
|
||||
insertIndex,
|
||||
insertType,
|
||||
sketchNodePaths,
|
||||
}: {
|
||||
insertIndex: number
|
||||
insertType: 'start' | 'end'
|
||||
sketchNodePaths: PathToNode[]
|
||||
}): {
|
||||
updatedEntryNodePath: PathToNode
|
||||
updatedSketchNodePaths: PathToNode[]
|
||||
} {
|
||||
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
||||
const newExpressionPathToNode: PathToNode = [
|
||||
['body', ''],
|
||||
[insertIndex, 'index'],
|
||||
['declaration', 'VariableDeclaration'],
|
||||
['init', 'VariableDeclarator'],
|
||||
]
|
||||
let updatedSketchNodePaths = structuredClone(sketchNodePaths)
|
||||
if (insertType === 'start') {
|
||||
updatedSketchNodePaths = updatedSketchNodePaths.map((path) => {
|
||||
path[1][0] = Number(path[1][0]) + 1
|
||||
return path
|
||||
})
|
||||
updatedSketchNodePaths.unshift(newExpressionPathToNode)
|
||||
} else {
|
||||
updatedSketchNodePaths.push(newExpressionPathToNode)
|
||||
}
|
||||
return {
|
||||
updatedSketchNodePaths,
|
||||
updatedEntryNodePath: newExpressionPathToNode,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Split the following pipe expression into
|
||||
* ```ts
|
||||
* part001 = startSketchOn('XZ')
|
||||
|> startProfileAt([1, 2], %)
|
||||
|> line([3, 4], %)
|
||||
|> line([5, 6], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5, part001)
|
||||
```
|
||||
into
|
||||
```ts
|
||||
sketch001 = startSketchOn('XZ')
|
||||
part001 = startProfileAt([1, 2], sketch001)
|
||||
|> line([3, 4], %)
|
||||
|> line([5, 6], %)
|
||||
|> close(%)
|
||||
extrude001 = extrude(5, part001)
|
||||
```
|
||||
Notice that the `startSketchOn` is what gets the new variable name, this is so part001 still has the same data as before
|
||||
making it safe for later code that uses part001 (the extrude in this example)
|
||||
*
|
||||
*/
|
||||
export function splitPipedProfile(
|
||||
ast: Program,
|
||||
pathToPipe: PathToNode
|
||||
):
|
||||
| {
|
||||
modifiedAst: Program
|
||||
pathToProfile: PathToNode
|
||||
pathToPlane: PathToNode
|
||||
}
|
||||
| Error {
|
||||
const _ast = structuredClone(ast)
|
||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||
_ast,
|
||||
pathToPipe,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(varDec)) return varDec
|
||||
if (
|
||||
varDec.node.type !== 'VariableDeclaration' ||
|
||||
varDec.node.declaration.init.type !== 'PipeExpression'
|
||||
) {
|
||||
return new Error('pathToNode does not point to pipe')
|
||||
}
|
||||
const init = varDec.node.declaration.init
|
||||
const firstCall = init.body[0]
|
||||
if (!isCallExprWithName(firstCall, 'startSketchOn'))
|
||||
return new Error('First call is not startSketchOn')
|
||||
const secondCall = init.body[1]
|
||||
if (!isCallExprWithName(secondCall, 'startProfileAt'))
|
||||
return new Error('Second call is not startProfileAt')
|
||||
|
||||
const varName = varDec.node.declaration.id.name
|
||||
const newVarName = findUniqueName(_ast, 'sketch')
|
||||
const secondCallArgs = structuredClone(secondCall.arguments)
|
||||
secondCallArgs[1] = createIdentifier(newVarName)
|
||||
const firstCallOfNewPipe = createCallExpression(
|
||||
'startProfileAt',
|
||||
secondCallArgs
|
||||
)
|
||||
const newSketch = createVariableDeclaration(
|
||||
newVarName,
|
||||
varDec.node.declaration.init.body[0]
|
||||
)
|
||||
const newProfile = createVariableDeclaration(
|
||||
varName,
|
||||
varDec.node.declaration.init.body.length <= 2
|
||||
? firstCallOfNewPipe
|
||||
: createPipeExpression([
|
||||
firstCallOfNewPipe,
|
||||
...varDec.node.declaration.init.body.slice(2),
|
||||
])
|
||||
)
|
||||
const index = getBodyIndex(pathToPipe)
|
||||
if (err(index)) return index
|
||||
_ast.body.splice(index, 1, newSketch, newProfile)
|
||||
const pathToPlane = structuredClone(pathToPipe)
|
||||
const pathToProfile = structuredClone(pathToPipe)
|
||||
pathToProfile[1][0] = index + 1
|
||||
|
||||
return {
|
||||
modifiedAst: _ast,
|
||||
pathToProfile,
|
||||
pathToPlane,
|
||||
}
|
||||
}
|
||||
|
||||
export function createNodeFromExprSnippet(
|
||||
strings: TemplateStringsArray,
|
||||
...expressions: any[]
|
||||
): Node<BodyItem> | Error {
|
||||
const code = strings.reduce(
|
||||
(acc, str, i) => acc + str + (expressions[i] || ''),
|
||||
''
|
||||
)
|
||||
let program = parse(code)
|
||||
if (err(program)) return program
|
||||
const node = program.program?.body[0]
|
||||
if (!node) return new Error('No node found')
|
||||
return node
|
||||
}
|
||||
|
||||
export const createLabeledArg = (label: string, arg: Expr): LabeledArg => {
|
||||
return { label: createIdentifier(label), arg, type: 'LabeledArg' }
|
||||
}
|
||||
|
Reference in New Issue
Block a user