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:
@ -25,7 +25,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||
import {
|
||||
isCursorInSketchCommandRange,
|
||||
updatePathToNodeFromMap,
|
||||
updateSketchDetailsNodePaths,
|
||||
} from 'lang/util'
|
||||
import {
|
||||
kclManager,
|
||||
@ -65,17 +65,31 @@ import {
|
||||
replaceValueAtNodePath,
|
||||
sketchOnExtrudedFace,
|
||||
sketchOnOffsetPlane,
|
||||
splitPipedProfile,
|
||||
startSketchOnDefault,
|
||||
} from 'lang/modifyAst'
|
||||
import { PathToNode, Program, parse, recast, resultIsOk } from 'lang/wasm'
|
||||
import { artifactIsPlaneWithPaths, isSingleCursorInPipe } from 'lang/queryAst'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
import {
|
||||
KclValue,
|
||||
PathToNode,
|
||||
Program,
|
||||
VariableDeclaration,
|
||||
parse,
|
||||
recast,
|
||||
resultIsOk,
|
||||
} from 'lang/wasm'
|
||||
import {
|
||||
artifactIsPlaneWithPaths,
|
||||
doesSketchPipeNeedSplitting,
|
||||
getNodeFromPath,
|
||||
isCursorInFunctionDefinition,
|
||||
traverse,
|
||||
} from 'lang/queryAst'
|
||||
import { exportFromEngine } from 'lib/exportFromEngine'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import toast from 'react-hot-toast'
|
||||
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||
import { err, reportRejection, trap } from 'lib/trap'
|
||||
import { err, reportRejection, trap, reject } from 'lib/trap'
|
||||
import {
|
||||
ExportIntent,
|
||||
EngineConnectionStateType,
|
||||
@ -86,10 +100,16 @@ import { useFileContext } from 'hooks/useFileContext'
|
||||
import { uuidv4 } from 'lib/utils'
|
||||
import { IndexLoaderData } from 'lib/types'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import {
|
||||
getFaceCodeRef,
|
||||
getPathsFromArtifact,
|
||||
getPlaneFromArtifact,
|
||||
} from 'lang/std/artifactGraph'
|
||||
import { promptToEditFlow } from 'lib/promptToEdit'
|
||||
import { kclEditorActor } from 'machines/kclEditorMachine'
|
||||
import { commandBarActor } from 'machines/commandBarMachine'
|
||||
import { useToken } from 'machines/appMachine'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -254,7 +274,11 @@ export const ModelingMachineProvider = ({
|
||||
'Set Segment Overlays': assign({
|
||||
segmentOverlays: ({ context: { segmentOverlays }, event }) => {
|
||||
if (event.type !== 'Set Segment Overlays') return {}
|
||||
if (event.data.type === 'set-many') return event.data.overlays
|
||||
if (event.data.type === 'add-many')
|
||||
return {
|
||||
...segmentOverlays,
|
||||
...event.data.overlays,
|
||||
}
|
||||
if (event.data.type === 'set-one')
|
||||
return {
|
||||
...segmentOverlays,
|
||||
@ -287,7 +311,7 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
sketchDetails: {
|
||||
...sketchDetails,
|
||||
sketchPathToNode: event.data,
|
||||
sketchEntryNodePath: event.data,
|
||||
},
|
||||
}
|
||||
}),
|
||||
@ -483,9 +507,17 @@ export const ModelingMachineProvider = ({
|
||||
selectionRanges: setSelections.selection,
|
||||
sketchDetails: {
|
||||
...sketchDetails,
|
||||
sketchPathToNode:
|
||||
setSelections.updatedPathToNode ||
|
||||
sketchDetails?.sketchPathToNode ||
|
||||
sketchEntryNodePath:
|
||||
setSelections.updatedSketchEntryNodePath ||
|
||||
sketchDetails?.sketchEntryNodePath ||
|
||||
[],
|
||||
sketchNodePaths:
|
||||
setSelections.updatedSketchNodePaths ||
|
||||
sketchDetails?.sketchNodePaths ||
|
||||
[],
|
||||
planeNodePath:
|
||||
setSelections.updatedPlaneNodePath ||
|
||||
sketchDetails?.planeNodePath ||
|
||||
[],
|
||||
},
|
||||
}
|
||||
@ -638,7 +670,12 @@ export const ModelingMachineProvider = ({
|
||||
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
||||
return true
|
||||
}
|
||||
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||
if (
|
||||
isCursorInFunctionDefinition(
|
||||
kclManager.ast,
|
||||
selectionRanges.graphSelections[0]
|
||||
)
|
||||
)
|
||||
return false
|
||||
return !!isCursorInSketchCommandRange(
|
||||
engineCommandManager.artifactGraph,
|
||||
@ -666,13 +703,33 @@ export const ModelingMachineProvider = ({
|
||||
async ({ input: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return
|
||||
if (kclManager.ast.body.length) {
|
||||
// this assumes no changes have been made to the sketch besides what we did when entering the sketch
|
||||
// i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
|
||||
const newAst = structuredClone(kclManager.ast)
|
||||
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
||||
const varDecIndex = sketchDetails.planeNodePath[1][0]
|
||||
|
||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||
newAst,
|
||||
sketchDetails.planeNodePath,
|
||||
'VariableDeclaration'
|
||||
)
|
||||
if (err(varDec)) return reject(new Error('No varDec'))
|
||||
const variableName = varDec.node.declaration.id.name
|
||||
let isIdentifierUsed = false
|
||||
traverse(newAst, {
|
||||
enter: (node) => {
|
||||
if (
|
||||
node.type === 'Identifier' &&
|
||||
node.name === variableName
|
||||
) {
|
||||
isIdentifierUsed = true
|
||||
}
|
||||
},
|
||||
})
|
||||
if (isIdentifierUsed) return
|
||||
|
||||
// remove body item at varDecIndex
|
||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||
await kclManager.executeAstMock(newAst)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(newAst)
|
||||
}
|
||||
sceneInfra.setCallbacks({
|
||||
onClick: () => {},
|
||||
@ -682,7 +739,7 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
),
|
||||
'animate-to-face': fromPromise(async ({ input }) => {
|
||||
if (!input) return undefined
|
||||
if (!input) return null
|
||||
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
||||
const sketched =
|
||||
input.type === 'extrudeFace'
|
||||
@ -709,7 +766,9 @@ export const ModelingMachineProvider = ({
|
||||
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||
return {
|
||||
sketchPathToNode: pathToNewSketchNode,
|
||||
sketchEntryNodePath: [],
|
||||
planeNodePath: pathToNewSketchNode,
|
||||
sketchNodePaths: [],
|
||||
zAxis: input.zAxis,
|
||||
yAxis: input.yAxis,
|
||||
origin: input.position,
|
||||
@ -730,7 +789,9 @@ export const ModelingMachineProvider = ({
|
||||
)
|
||||
|
||||
return {
|
||||
sketchPathToNode: pathToNode,
|
||||
sketchEntryNodePath: [],
|
||||
planeNodePath: pathToNode,
|
||||
sketchNodePaths: [],
|
||||
zAxis: input.zAxis,
|
||||
yAxis: input.yAxis,
|
||||
origin: [0, 0, 0],
|
||||
@ -739,21 +800,70 @@ export const ModelingMachineProvider = ({
|
||||
}),
|
||||
'animate-to-sketch': fromPromise(
|
||||
async ({ input: { selectionRanges } }) => {
|
||||
const sourceRange =
|
||||
selectionRanges.graphSelections[0]?.codeRef?.range
|
||||
const sketchPathToNode = getNodePathFromSourceRange(
|
||||
kclManager.ast,
|
||||
sourceRange
|
||||
)
|
||||
const info = await getSketchOrientationDetails(
|
||||
sketchPathToNode || []
|
||||
const plane = getPlaneFromArtifact(
|
||||
selectionRanges.graphSelections[0].artifact,
|
||||
engineCommandManager.artifactGraph
|
||||
)
|
||||
if (err(plane)) return Promise.reject(plane)
|
||||
let sketch: KclValue | null = null
|
||||
for (const variable of Object.values(
|
||||
kclManager.execState.variables
|
||||
)) {
|
||||
// find programMemory that matches path artifact
|
||||
if (
|
||||
variable?.type === 'Sketch' &&
|
||||
variable.value.artifactId === plane.pathIds[0]
|
||||
) {
|
||||
sketch = variable
|
||||
break
|
||||
}
|
||||
if (
|
||||
// if the variable is an sweep, check if the underlying sketch matches the artifact
|
||||
variable?.type === 'Solid' &&
|
||||
variable.value.sketch.on.type === 'plane' &&
|
||||
variable.value.sketch.artifactId === plane.pathIds[0]
|
||||
) {
|
||||
sketch = {
|
||||
type: 'Sketch',
|
||||
value: variable.value.sketch,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!sketch || sketch.type !== 'Sketch')
|
||||
return Promise.reject(new Error('No sketch'))
|
||||
if (!sketch || sketch.type !== 'Sketch')
|
||||
return Promise.reject(new Error('No sketch'))
|
||||
const info = await getSketchOrientationDetails(sketch.value)
|
||||
|
||||
await letEngineAnimateAndSyncCamAfter(
|
||||
engineCommandManager,
|
||||
info?.sketchDetails?.faceId || ''
|
||||
)
|
||||
|
||||
const sketchArtifact = engineCommandManager.artifactGraph.get(
|
||||
plane.pathIds[0]
|
||||
)
|
||||
if (sketchArtifact?.type !== 'path')
|
||||
return Promise.reject(new Error('No sketch artifact'))
|
||||
const sketchPaths = getPathsFromArtifact({
|
||||
artifact: engineCommandManager.artifactGraph.get(plane.id),
|
||||
sketchPathToNode: sketchArtifact?.codeRef?.pathToNode,
|
||||
artifactGraph: engineCommandManager.artifactGraph,
|
||||
ast: kclManager.ast,
|
||||
})
|
||||
if (err(sketchPaths)) return Promise.reject(sketchPaths)
|
||||
let codeRef = getFaceCodeRef(plane)
|
||||
if (!codeRef) return Promise.reject(new Error('No plane codeRef'))
|
||||
// codeRef.pathToNode is not always populated correctly
|
||||
const planeNodePath = getNodePathFromSourceRange(
|
||||
kclManager.ast,
|
||||
codeRef.range
|
||||
)
|
||||
return {
|
||||
sketchPathToNode: sketchPathToNode || [],
|
||||
sketchEntryNodePath: sketchArtifact.codeRef.pathToNode || [],
|
||||
sketchNodePaths: sketchPaths,
|
||||
planeNodePath,
|
||||
zAxis: info.sketchDetails.zAxis || null,
|
||||
yAxis: info.sketchDetails.yAxis || null,
|
||||
origin: info.sketchDetails.origin.map(
|
||||
@ -766,7 +876,7 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
'Get horizontal info': fromPromise(
|
||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||
const { modifiedAst, pathToNodeMap } =
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await applyConstraintHorzVertDistance({
|
||||
constraint: 'setHorzDistance',
|
||||
selectionRanges,
|
||||
@ -778,13 +888,23 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -805,13 +925,15 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
'Get vertical info': fromPromise(
|
||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||
const { modifiedAst, pathToNodeMap } =
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await applyConstraintHorzVertDistance({
|
||||
constraint: 'setVertDistance',
|
||||
selectionRanges,
|
||||
@ -822,13 +944,23 @@ export const ModelingMachineProvider = ({
|
||||
const _modifiedAst = pResult.program
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -849,7 +981,9 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
@ -859,14 +993,15 @@ export const ModelingMachineProvider = ({
|
||||
selectionRanges,
|
||||
})
|
||||
if (err(info)) return Promise.reject(info)
|
||||
const { modifiedAst, pathToNodeMap } = await (info.enabled
|
||||
? applyConstraintAngleBetween({
|
||||
selectionRanges,
|
||||
})
|
||||
: applyConstraintAngleLength({
|
||||
selectionRanges,
|
||||
angleOrLength: 'setAngle',
|
||||
}))
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await (info.enabled
|
||||
? applyConstraintAngleBetween({
|
||||
selectionRanges,
|
||||
})
|
||||
: applyConstraintAngleLength({
|
||||
selectionRanges,
|
||||
angleOrLength: 'setAngle',
|
||||
}))
|
||||
const pResult = parse(recast(modifiedAst))
|
||||
if (trap(pResult) || !resultIsOk(pResult))
|
||||
return Promise.reject(new Error('Unexpected compilation error'))
|
||||
@ -875,13 +1010,23 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -902,7 +1047,9 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
@ -917,20 +1064,30 @@ export const ModelingMachineProvider = ({
|
||||
length: lengthValue,
|
||||
})
|
||||
if (err(constraintResult)) return Promise.reject(constraintResult)
|
||||
const { modifiedAst, pathToNodeMap } = constraintResult
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
constraintResult
|
||||
const pResult = parse(recast(modifiedAst))
|
||||
if (trap(pResult) || !resultIsOk(pResult))
|
||||
return Promise.reject(new Error('Unexpected compilation error'))
|
||||
const _modifiedAst = pResult.program
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -951,13 +1108,15 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
'Get perpendicular distance info': fromPromise(
|
||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||
const { modifiedAst, pathToNodeMap } =
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await applyConstraintIntersect({
|
||||
selectionRanges,
|
||||
})
|
||||
@ -967,13 +1126,22 @@ export const ModelingMachineProvider = ({
|
||||
const _modifiedAst = pResult.program
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -994,13 +1162,15 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
'Get ABS X info': fromPromise(
|
||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||
const { modifiedAst, pathToNodeMap } =
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await applyConstraintAbsDistance({
|
||||
constraint: 'xAbs',
|
||||
selectionRanges,
|
||||
@ -1011,13 +1181,22 @@ export const ModelingMachineProvider = ({
|
||||
const _modifiedAst = pResult.program
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -1038,13 +1217,15 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
'Get ABS Y info': fromPromise(
|
||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||
const { modifiedAst, pathToNodeMap } =
|
||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||
await applyConstraintAbsDistance({
|
||||
constraint: 'yAbs',
|
||||
selectionRanges,
|
||||
@ -1055,13 +1236,22 @@ export const ModelingMachineProvider = ({
|
||||
const _modifiedAst = pResult.program
|
||||
if (!sketchDetails)
|
||||
return Promise.reject(new Error('No sketch details'))
|
||||
const updatedPathToNode = updatePathToNodeFromMap(
|
||||
sketchDetails.sketchPathToNode,
|
||||
pathToNodeMap
|
||||
)
|
||||
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex,
|
||||
})
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
_modifiedAst,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -1082,7 +1272,9 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
@ -1102,9 +1294,11 @@ export const ModelingMachineProvider = ({
|
||||
let result: {
|
||||
modifiedAst: Node<Program>
|
||||
pathToReplaced: PathToNode | null
|
||||
exprInsertIndex: number
|
||||
} = {
|
||||
modifiedAst: parsed,
|
||||
pathToReplaced: null,
|
||||
exprInsertIndex: -1,
|
||||
}
|
||||
// If the user provided a constant name,
|
||||
// we need to insert the named constant
|
||||
@ -1134,6 +1328,7 @@ export const ModelingMachineProvider = ({
|
||||
result = {
|
||||
modifiedAst: parseResultAfterInsertion.program,
|
||||
pathToReplaced: astAfterReplacement.pathToReplaced,
|
||||
exprInsertIndex: astAfterReplacement.exprInsertIndex,
|
||||
}
|
||||
} else if ('valueText' in data.namedValue) {
|
||||
// If they didn't provide a constant name,
|
||||
@ -1164,10 +1359,22 @@ export const ModelingMachineProvider = ({
|
||||
parsed = parsed as Node<Program>
|
||||
if (!result.pathToReplaced)
|
||||
return Promise.reject(new Error('No path to replaced node'))
|
||||
const {
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
} = updateSketchDetailsNodePaths({
|
||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
exprInsertIndex: result.exprInsertIndex,
|
||||
})
|
||||
|
||||
const updatedAst =
|
||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||
result.pathToReplaced || [],
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
parsed,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
@ -1188,7 +1395,194 @@ export const ModelingMachineProvider = ({
|
||||
return {
|
||||
selectionType: 'completeSelection',
|
||||
selection,
|
||||
updatedPathToNode: result.pathToReplaced,
|
||||
updatedSketchEntryNodePath,
|
||||
updatedSketchNodePaths,
|
||||
updatedPlaneNodePath,
|
||||
}
|
||||
}
|
||||
),
|
||||
'set-up-draft-circle': fromPromise(
|
||||
async ({ input: { sketchDetails, data } }) => {
|
||||
if (!sketchDetails || !data)
|
||||
return reject('No sketch details or data')
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
|
||||
const result = await sceneEntitiesManager.setupDraftCircle(
|
||||
sketchDetails.sketchEntryNodePath,
|
||||
sketchDetails.sketchNodePaths,
|
||||
sketchDetails.planeNodePath,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
sketchDetails.origin,
|
||||
data
|
||||
)
|
||||
if (err(result)) return reject(result)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
|
||||
return result
|
||||
}
|
||||
),
|
||||
'set-up-draft-circle-three-point': fromPromise(
|
||||
async ({ input: { sketchDetails, data } }) => {
|
||||
if (!sketchDetails || !data)
|
||||
return reject('No sketch details or data')
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
|
||||
const result =
|
||||
await sceneEntitiesManager.setupDraftCircleThreePoint(
|
||||
sketchDetails.sketchEntryNodePath,
|
||||
sketchDetails.sketchNodePaths,
|
||||
sketchDetails.planeNodePath,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
sketchDetails.origin,
|
||||
data.p1,
|
||||
data.p2
|
||||
)
|
||||
if (err(result)) return reject(result)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
|
||||
return result
|
||||
}
|
||||
),
|
||||
'set-up-draft-rectangle': fromPromise(
|
||||
async ({ input: { sketchDetails, data } }) => {
|
||||
if (!sketchDetails || !data)
|
||||
return reject('No sketch details or data')
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
|
||||
const result = await sceneEntitiesManager.setupDraftRectangle(
|
||||
sketchDetails.sketchEntryNodePath,
|
||||
sketchDetails.sketchNodePaths,
|
||||
sketchDetails.planeNodePath,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
sketchDetails.origin,
|
||||
data
|
||||
)
|
||||
if (err(result)) return reject(result)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
|
||||
return result
|
||||
}
|
||||
),
|
||||
'set-up-draft-center-rectangle': fromPromise(
|
||||
async ({ input: { sketchDetails, data } }) => {
|
||||
if (!sketchDetails || !data)
|
||||
return reject('No sketch details or data')
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
const result = await sceneEntitiesManager.setupDraftCenterRectangle(
|
||||
sketchDetails.sketchEntryNodePath,
|
||||
sketchDetails.sketchNodePaths,
|
||||
sketchDetails.planeNodePath,
|
||||
sketchDetails.zAxis,
|
||||
sketchDetails.yAxis,
|
||||
sketchDetails.origin,
|
||||
data
|
||||
)
|
||||
if (err(result)) return reject(result)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||
|
||||
return result
|
||||
}
|
||||
),
|
||||
'setup-client-side-sketch-segments': fromPromise(
|
||||
async ({ input: { sketchDetails, selectionRanges } }) => {
|
||||
if (!sketchDetails) return
|
||||
if (!sketchDetails.sketchEntryNodePath.length) return
|
||||
if (Object.keys(sceneEntitiesManager.activeSegments).length > 0) {
|
||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||
}
|
||||
sceneInfra.resetMouseListeners()
|
||||
await sceneEntitiesManager.setupSketch({
|
||||
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
forward: sketchDetails.zAxis,
|
||||
up: sketchDetails.yAxis,
|
||||
position: sketchDetails.origin,
|
||||
maybeModdedAst: kclManager.ast,
|
||||
selectionRanges,
|
||||
})
|
||||
sceneInfra.resetMouseListeners()
|
||||
|
||||
sceneEntitiesManager.setupSketchIdleCallbacks({
|
||||
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||
forward: sketchDetails.zAxis,
|
||||
up: sketchDetails.yAxis,
|
||||
position: sketchDetails.origin,
|
||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
planeNodePath: sketchDetails.planeNodePath,
|
||||
// We will want to pass sketchTools here
|
||||
// to add their interactions
|
||||
})
|
||||
|
||||
// We will want to update the context with sketchTools.
|
||||
// They'll be used for their .destroy() in tearDownSketch
|
||||
return undefined
|
||||
}
|
||||
),
|
||||
'split-sketch-pipe-if-needed': fromPromise(
|
||||
async ({ input: { sketchDetails } }) => {
|
||||
if (!sketchDetails) return reject('No sketch details')
|
||||
const existingSketchInfoNoOp = {
|
||||
updatedEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||
updatedSketchNodePaths: sketchDetails.sketchNodePaths,
|
||||
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
||||
expressionIndexToDelete: -1,
|
||||
} as const
|
||||
if (
|
||||
!sketchDetails.sketchNodePaths.length &&
|
||||
sketchDetails.planeNodePath.length
|
||||
) {
|
||||
// new sketch, no profiles yet
|
||||
return existingSketchInfoNoOp
|
||||
}
|
||||
const doesNeedSplitting = doesSketchPipeNeedSplitting(
|
||||
kclManager.ast,
|
||||
sketchDetails.sketchEntryNodePath
|
||||
)
|
||||
if (err(doesNeedSplitting)) return reject(doesNeedSplitting)
|
||||
let moddedAst: Program = structuredClone(kclManager.ast)
|
||||
let pathToProfile = sketchDetails.sketchEntryNodePath
|
||||
let updatedSketchNodePaths = sketchDetails.sketchNodePaths
|
||||
if (doesNeedSplitting) {
|
||||
const splitResult = splitPipedProfile(
|
||||
moddedAst,
|
||||
sketchDetails.sketchEntryNodePath
|
||||
)
|
||||
if (err(splitResult)) return reject(splitResult)
|
||||
moddedAst = splitResult.modifiedAst
|
||||
pathToProfile = splitResult.pathToProfile
|
||||
updatedSketchNodePaths = [pathToProfile]
|
||||
}
|
||||
|
||||
const indexToDelete = sketchDetails?.expressionIndexToDelete || -1
|
||||
if (indexToDelete >= 0) {
|
||||
// this is the expression that was added when as sketch tool was used but not completed
|
||||
// i.e first click for the center of the circle, but not the second click for the radius
|
||||
// we added a circle to editor, but they bailed out early so we should remove it
|
||||
moddedAst.body.splice(indexToDelete, 1)
|
||||
// make sure the deleted expression is removed from the sketchNodePaths
|
||||
updatedSketchNodePaths = updatedSketchNodePaths.filter(
|
||||
(path) => path[1][0] !== indexToDelete
|
||||
)
|
||||
// if the deleted expression was the entryNodePath, we should just make it the first sketchNodePath
|
||||
// as a safe default
|
||||
pathToProfile =
|
||||
pathToProfile[1][0] !== indexToDelete
|
||||
? pathToProfile
|
||||
: updatedSketchNodePaths[0]
|
||||
}
|
||||
|
||||
if (doesNeedSplitting) {
|
||||
await kclManager.executeAstMock(moddedAst)
|
||||
await codeManager.updateEditorWithAstAndWriteToFile(moddedAst)
|
||||
}
|
||||
return {
|
||||
updatedEntryNodePath: pathToProfile,
|
||||
updatedSketchNodePaths: updatedSketchNodePaths,
|
||||
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
||||
expressionIndexToDelete: -1,
|
||||
}
|
||||
}
|
||||
),
|
||||
|
||||
@ -13,12 +13,7 @@ import {
|
||||
getOperationLabel,
|
||||
stdLibMap,
|
||||
} from 'lib/operations'
|
||||
import {
|
||||
codeManager,
|
||||
editorManager,
|
||||
engineCommandManager,
|
||||
kclManager,
|
||||
} from 'lib/singletons'
|
||||
import { editorManager, engineCommandManager, kclManager } from 'lib/singletons'
|
||||
import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||
import { Actor, Prop } from 'xstate'
|
||||
@ -67,7 +62,7 @@ export const FeatureTreePane = () => {
|
||||
)
|
||||
: null
|
||||
|
||||
if (!artifact || !('codeRef' in artifact)) {
|
||||
if (!artifact) {
|
||||
modelingSend({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -136,6 +136,7 @@ export async function applyConstraintIntersect({
|
||||
}): Promise<{
|
||||
modifiedAst: Node<Program>
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const info = intersectInfo({
|
||||
selectionRanges,
|
||||
@ -174,6 +175,7 @@ export async function applyConstraintIntersect({
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex: -1,
|
||||
}
|
||||
}
|
||||
// transform again but forcing certain values
|
||||
@ -192,6 +194,7 @@ export async function applyConstraintIntersect({
|
||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||
transform2
|
||||
|
||||
let exprInsertIndex = -1
|
||||
if (variableName) {
|
||||
const newBody = [..._modifiedAst.body]
|
||||
newBody.splice(
|
||||
@ -204,9 +207,11 @@ export async function applyConstraintIntersect({
|
||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||
})
|
||||
exprInsertIndex = newVariableInsertIndex
|
||||
}
|
||||
return {
|
||||
modifiedAst: _modifiedAst,
|
||||
pathToNodeMap: _pathToNodeMap,
|
||||
exprInsertIndex,
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ export function removeConstrainingValuesInfo({
|
||||
| Error {
|
||||
const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => {
|
||||
const tmp = getNodeFromPath<Expr>(kclManager.ast, codeRef.pathToNode)
|
||||
if (err(tmp)) return tmp
|
||||
if (tmp instanceof Error) return tmp
|
||||
return tmp.node
|
||||
})
|
||||
const _err1 = _nodes.find(err)
|
||||
|
||||
@ -92,6 +92,7 @@ export async function applyConstraintAbsDistance({
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const info = absDistanceInfo({
|
||||
selectionRanges,
|
||||
@ -131,6 +132,7 @@ export async function applyConstraintAbsDistance({
|
||||
if (err(transform2)) return Promise.reject(transform2)
|
||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transform2
|
||||
|
||||
let exprInsertIndex = -1
|
||||
if (variableName) {
|
||||
const newBody = [..._modifiedAst.body]
|
||||
newBody.splice(
|
||||
@ -143,8 +145,9 @@ export async function applyConstraintAbsDistance({
|
||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||
})
|
||||
exprInsertIndex = newVariableInsertIndex
|
||||
}
|
||||
return { modifiedAst: _modifiedAst, pathToNodeMap }
|
||||
return { modifiedAst: _modifiedAst, pathToNodeMap, exprInsertIndex }
|
||||
}
|
||||
|
||||
export function applyConstraintAxisAlign({
|
||||
|
||||
@ -86,6 +86,7 @@ export async function applyConstraintAngleBetween({
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const info = angleBetweenInfo({ selectionRanges })
|
||||
if (err(info)) return Promise.reject(info)
|
||||
@ -122,6 +123,7 @@ export async function applyConstraintAngleBetween({
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex: -1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,6 +143,7 @@ export async function applyConstraintAngleBetween({
|
||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||
transformed2
|
||||
|
||||
let exprInsertIndex = -1
|
||||
if (variableName) {
|
||||
const newBody = [..._modifiedAst.body]
|
||||
newBody.splice(
|
||||
@ -153,9 +156,11 @@ export async function applyConstraintAngleBetween({
|
||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||
})
|
||||
exprInsertIndex = newVariableInsertIndex
|
||||
}
|
||||
return {
|
||||
modifiedAst: _modifiedAst,
|
||||
pathToNodeMap: _pathToNodeMap,
|
||||
exprInsertIndex,
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,15 +87,13 @@ export function horzVertDistanceInfo({
|
||||
export async function applyConstraintHorzVertDistance({
|
||||
selectionRanges,
|
||||
constraint,
|
||||
// TODO align will always be false (covered by synconous applyConstraintHorzVertAlign), remove it
|
||||
isAlign = false,
|
||||
}: {
|
||||
selectionRanges: Selections
|
||||
constraint: 'setHorzDistance' | 'setVertDistance'
|
||||
isAlign?: false
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const info = horzVertDistanceInfo({
|
||||
selectionRanges: selectionRanges,
|
||||
@ -133,13 +131,12 @@ export async function applyConstraintHorzVertDistance({
|
||||
return {
|
||||
modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex: -1,
|
||||
}
|
||||
} else {
|
||||
if (!isExprBinaryPart(valueNode))
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
let finalValue = isAlign
|
||||
? createLiteral(0)
|
||||
: removeDoubleNegatives(valueNode, sign, variableName)
|
||||
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||
// transform again but forcing certain values
|
||||
const transformed = transformSecondarySketchLinesTagFirst({
|
||||
ast: kclManager.ast,
|
||||
@ -152,6 +149,7 @@ export async function applyConstraintHorzVertDistance({
|
||||
|
||||
if (err(transformed)) return Promise.reject(transformed)
|
||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transformed
|
||||
let exprInsertIndex = -1
|
||||
if (variableName) {
|
||||
const newBody = [..._modifiedAst.body]
|
||||
newBody.splice(
|
||||
@ -164,10 +162,12 @@ export async function applyConstraintHorzVertDistance({
|
||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||
})
|
||||
exprInsertIndex = newVariableInsertIndex
|
||||
}
|
||||
return {
|
||||
modifiedAst: _modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,10 +74,14 @@ export async function applyConstraintLength({
|
||||
}: {
|
||||
length: KclCommandValue
|
||||
selectionRanges: Selections
|
||||
}) {
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const ast = kclManager.ast
|
||||
const angleLength = angleLengthInfo({ selectionRanges })
|
||||
if (err(angleLength)) return angleLength
|
||||
if (err(angleLength)) return Promise.reject(angleLength)
|
||||
const { transforms } = angleLength
|
||||
|
||||
let distanceExpression: Expr = length.valueAst
|
||||
@ -98,7 +102,7 @@ export async function applyConstraintLength({
|
||||
}
|
||||
|
||||
if (!isExprBinaryPart(distanceExpression)) {
|
||||
return new Error('Invalid valueNode, is not a BinaryPart')
|
||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||
}
|
||||
|
||||
const retval = transformAstSketchLines({
|
||||
@ -116,6 +120,12 @@ export async function applyConstraintLength({
|
||||
return {
|
||||
modifiedAst: _modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex:
|
||||
'variableName' in length &&
|
||||
length.variableName &&
|
||||
length.insertIndex !== undefined
|
||||
? length.insertIndex
|
||||
: -1,
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,6 +138,7 @@ export async function applyConstraintAngleLength({
|
||||
}): Promise<{
|
||||
modifiedAst: Program
|
||||
pathToNodeMap: PathToNodeMap
|
||||
exprInsertIndex: number
|
||||
}> {
|
||||
const angleLength = angleLengthInfo({ selectionRanges, angleOrLength })
|
||||
if (err(angleLength)) return Promise.reject(angleLength)
|
||||
@ -212,5 +223,6 @@ export async function applyConstraintAngleLength({
|
||||
return {
|
||||
modifiedAst: _modifiedAst,
|
||||
pathToNodeMap,
|
||||
exprInsertIndex: variableName ? newVariableInsertIndex : -1,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user