Compare commits
	
		
			12 Commits
		
	
	
		
			nightly-v2
			...
			jtran/exec
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 437aacf477 | |||
| c6fb56058b | |||
| ca2fc1bf38 | |||
| 8e62f07d71 | |||
| 55ef40d136 | |||
| 3b7bbc1642 | |||
| 1a569af476 | |||
| e9fe455607 | |||
| 35387bbd7d | |||
| b26a0f98fc | |||
| b697258ad5 | |||
| 6c8aa799b4 | 
| @ -44,6 +44,8 @@ import { | |||||||
|   VariableDeclaration, |   VariableDeclaration, | ||||||
|   VariableDeclarator, |   VariableDeclarator, | ||||||
|   sketchGroupFromKclValue, |   sketchGroupFromKclValue, | ||||||
|  |   ExecState, | ||||||
|  |   sketchGroupFromArtifactId, | ||||||
| } from 'lang/wasm' | } from 'lang/wasm' | ||||||
| import { | import { | ||||||
|   engineCommandManager, |   engineCommandManager, | ||||||
| @ -77,7 +79,11 @@ import { | |||||||
|   createPipeSubstitution, |   createPipeSubstitution, | ||||||
|   findUniqueName, |   findUniqueName, | ||||||
| } from 'lang/modifyAst' | } from 'lang/modifyAst' | ||||||
| import { Selections, getEventForSegmentSelection } from 'lib/selections' | import { | ||||||
|  |   Selection, | ||||||
|  |   Selections, | ||||||
|  |   getEventForSegmentSelection, | ||||||
|  | } from 'lib/selections' | ||||||
| import { createGridHelper, orthoScale, perspScale } from './helpers' | import { createGridHelper, orthoScale, perspScale } from './helpers' | ||||||
| import { Models } from '@kittycad/lib' | import { Models } from '@kittycad/lib' | ||||||
| import { uuidv4 } from 'lib/utils' | import { uuidv4 } from 'lib/utils' | ||||||
| @ -90,7 +96,12 @@ import { | |||||||
| import { getThemeColorForThreeJs, Themes } from 'lib/theme' | import { getThemeColorForThreeJs, Themes } from 'lib/theme' | ||||||
| import { err, reportRejection, trap } from 'lib/trap' | import { err, reportRejection, trap } from 'lib/trap' | ||||||
| import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' | import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' | ||||||
| import { Point3d } from 'wasm-lib/kcl/bindings/Point3d' | import { | ||||||
|  |   ArtifactGraph, | ||||||
|  |   ArtifactId, | ||||||
|  |   getPathFromSelection, | ||||||
|  |   getPlaneOrFaceFromSelection, | ||||||
|  | } from 'lang/std/artifactGraph' | ||||||
| import { SegmentInputs } from 'lang/std/stdTypes' | import { SegmentInputs } from 'lang/std/stdTypes' | ||||||
|  |  | ||||||
| type DraftSegment = 'line' | 'tangentialArcTo' | type DraftSegment = 'line' | 'tangentialArcTo' | ||||||
| @ -122,8 +133,6 @@ export const SEGMENT_BODIES_PLUS_PROFILE_START = [ | |||||||
|   PROFILE_START, |   PROFILE_START, | ||||||
| ] | ] | ||||||
|  |  | ||||||
| type Vec3Array = [number, number, number] |  | ||||||
|  |  | ||||||
| // This singleton Class is responsible for all of the things the user sees and interacts with. | // This singleton Class is responsible for all of the things the user sees and interacts with. | ||||||
| // That mostly mean sketch elements. | // That mostly mean sketch elements. | ||||||
| // Cameras, controls, raycasters, etc are handled by sceneInfra | // Cameras, controls, raycasters, etc are handled by sceneInfra | ||||||
| @ -391,17 +400,37 @@ export class SceneEntities { | |||||||
|     const { truncatedAst, programMemoryOverride, variableDeclarationName } = |     const { truncatedAst, programMemoryOverride, variableDeclarationName } = | ||||||
|       prepared |       prepared | ||||||
|  |  | ||||||
|     const { programMemory } = await executeAst({ |     const { execState } = await executeAst({ | ||||||
|       ast: truncatedAst, |       ast: truncatedAst, | ||||||
|       useFakeExecutor: true, |       useFakeExecutor: true, | ||||||
|       engineCommandManager: this.engineCommandManager, |       engineCommandManager: this.engineCommandManager, | ||||||
|       programMemoryOverride, |       programMemoryOverride, | ||||||
|     }) |     }) | ||||||
|     const sketchGroup = sketchGroupFromPathToNode({ |     const programMemory = execState.memory | ||||||
|       pathToNode: sketchPathToNode, |     let sketchGroup: SketchGroup | null | Error = null | ||||||
|       ast: maybeModdedAst, |     if (selectionRanges) { | ||||||
|       programMemory, |       console.warn('setupSketch looking for sketch from selection') | ||||||
|     }) |       sketchGroup = sketchGroupFromSelection({ | ||||||
|  |         selections: selectionRanges, | ||||||
|  |         execState: kclManager.execState, | ||||||
|  |         // execState, | ||||||
|  |         artifactGraph: this.engineCommandManager.artifactGraph, | ||||||
|  |       }) | ||||||
|  |       if (sketchGroup) { | ||||||
|  |         console.warn('setupSketch found sketch from selection') | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     if (!sketchGroup) { | ||||||
|  |       console.warn( | ||||||
|  |         'setupSketch sketch not found from selection; falling back to program memory' | ||||||
|  |       ) | ||||||
|  |       // Fall back to the sketch group from the program memory. | ||||||
|  |       sketchGroup = sketchGroupFromPathToNode({ | ||||||
|  |         pathToNode: sketchPathToNode, | ||||||
|  |         ast: maybeModdedAst, | ||||||
|  |         programMemory, | ||||||
|  |       }) | ||||||
|  |     } | ||||||
|     if (err(sketchGroup)) return Promise.reject(sketchGroup) |     if (err(sketchGroup)) return Promise.reject(sketchGroup) | ||||||
|     if (!sketchGroup) return Promise.reject('sketchGroup not found') |     if (!sketchGroup) return Promise.reject('sketchGroup not found') | ||||||
|  |  | ||||||
| @ -598,11 +627,13 @@ export class SceneEntities { | |||||||
|     const _node1 = getNodeFromPath<VariableDeclaration>( |     const _node1 = getNodeFromPath<VariableDeclaration>( | ||||||
|       _ast, |       _ast, | ||||||
|       sketchPathToNode || [], |       sketchPathToNode || [], | ||||||
|       'VariableDeclaration' |       ['VariableDeclaration', 'ExpressionStatement'] | ||||||
|     ) |     ) as { node: { type: string } } | Error | ||||||
|     if (trap(_node1)) return Promise.reject(_node1) |     if (trap(_node1)) return Promise.reject(_node1) | ||||||
|     const variableDeclarationName = |     const variableDeclarationName = | ||||||
|       _node1.node?.declarations?.[0]?.id?.name || '' |       _node1.node.type === 'VariableDeclaration' | ||||||
|  |         ? (_node1.node as VariableDeclaration).declarations[0]?.id?.name || '' | ||||||
|  |         : '' | ||||||
|  |  | ||||||
|     const sg = sketchGroupFromKclValue( |     const sg = sketchGroupFromKclValue( | ||||||
|       kclManager.programMemory.get(variableDeclarationName), |       kclManager.programMemory.get(variableDeclarationName), | ||||||
| @ -802,12 +833,13 @@ export class SceneEntities { | |||||||
|           updateRectangleSketch(sketchInit, x, y, tags[0]) |           updateRectangleSketch(sketchInit, x, y, tags[0]) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const { programMemory } = await executeAst({ |         const { execState } = await executeAst({ | ||||||
|           ast: truncatedAst, |           ast: truncatedAst, | ||||||
|           useFakeExecutor: true, |           useFakeExecutor: true, | ||||||
|           engineCommandManager: this.engineCommandManager, |           engineCommandManager: this.engineCommandManager, | ||||||
|           programMemoryOverride, |           programMemoryOverride, | ||||||
|         }) |         }) | ||||||
|  |         const programMemory = execState.memory | ||||||
|         this.sceneProgramMemory = programMemory |         this.sceneProgramMemory = programMemory | ||||||
|         const sketchGroup = sketchGroupFromKclValue( |         const sketchGroup = sketchGroupFromKclValue( | ||||||
|           programMemory.get(variableDeclarationName), |           programMemory.get(variableDeclarationName), | ||||||
| @ -856,12 +888,13 @@ export class SceneEntities { | |||||||
|           await kclManager.executeAstMock(_ast) |           await kclManager.executeAstMock(_ast) | ||||||
|           sceneInfra.modelingSend({ type: 'Finish rectangle' }) |           sceneInfra.modelingSend({ type: 'Finish rectangle' }) | ||||||
|  |  | ||||||
|           const { programMemory } = await executeAst({ |           const { execState } = await executeAst({ | ||||||
|             ast: _ast, |             ast: _ast, | ||||||
|             useFakeExecutor: true, |             useFakeExecutor: true, | ||||||
|             engineCommandManager: this.engineCommandManager, |             engineCommandManager: this.engineCommandManager, | ||||||
|             programMemoryOverride, |             programMemoryOverride, | ||||||
|           }) |           }) | ||||||
|  |           const programMemory = execState.memory | ||||||
|  |  | ||||||
|           // Prepare to update the THREEjs scene |           // Prepare to update the THREEjs scene | ||||||
|           this.sceneProgramMemory = programMemory |           this.sceneProgramMemory = programMemory | ||||||
| @ -980,12 +1013,13 @@ export class SceneEntities { | |||||||
|           modded = moddedResult.modifiedAst |           modded = moddedResult.modifiedAst | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         const { programMemory } = await executeAst({ |         const { execState } = await executeAst({ | ||||||
|           ast: modded, |           ast: modded, | ||||||
|           useFakeExecutor: true, |           useFakeExecutor: true, | ||||||
|           engineCommandManager: this.engineCommandManager, |           engineCommandManager: this.engineCommandManager, | ||||||
|           programMemoryOverride, |           programMemoryOverride, | ||||||
|         }) |         }) | ||||||
|  |         const programMemory = execState.memory | ||||||
|         this.sceneProgramMemory = programMemory |         this.sceneProgramMemory = programMemory | ||||||
|         const sketchGroup = sketchGroupFromKclValue( |         const sketchGroup = sketchGroupFromKclValue( | ||||||
|           programMemory.get(variableDeclarationName), |           programMemory.get(variableDeclarationName), | ||||||
| @ -1339,12 +1373,13 @@ export class SceneEntities { | |||||||
|         // don't want to mod the user's code yet as they have't committed to the change yet |         // don't want to mod the user's code yet as they have't committed to the change yet | ||||||
|         // plus this would be the truncated ast being recast, it would be wrong |         // plus this would be the truncated ast being recast, it would be wrong | ||||||
|         codeManager.updateCodeEditor(code) |         codeManager.updateCodeEditor(code) | ||||||
|       const { programMemory } = await executeAst({ |       const { execState } = await executeAst({ | ||||||
|         ast: truncatedAst, |         ast: truncatedAst, | ||||||
|         useFakeExecutor: true, |         useFakeExecutor: true, | ||||||
|         engineCommandManager: this.engineCommandManager, |         engineCommandManager: this.engineCommandManager, | ||||||
|         programMemoryOverride, |         programMemoryOverride, | ||||||
|       }) |       }) | ||||||
|  |       const programMemory = execState.memory | ||||||
|       this.sceneProgramMemory = programMemory |       this.sceneProgramMemory = programMemory | ||||||
|  |  | ||||||
|       const maybeSketchGroup = programMemory.get(variableDeclarationName) |       const maybeSketchGroup = programMemory.get(variableDeclarationName) | ||||||
| @ -1820,6 +1855,70 @@ export function getParentGroup( | |||||||
|   return null |   return null | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export async function planeOrFaceFromSelection({ | ||||||
|  |   artifactGraph, | ||||||
|  |   selection, | ||||||
|  | }: { | ||||||
|  |   artifactGraph: ArtifactGraph | ||||||
|  |   selection: Selection | ||||||
|  | }): Promise<{ | ||||||
|  |   id: ArtifactId | ||||||
|  |   faceDetails: Models['GetSketchModePlane_type'] | ||||||
|  | } | null> { | ||||||
|  |   // If the selection doesn't have an artifactId associated with it, we can't | ||||||
|  |   // do it. | ||||||
|  |   if (!selection.artifactId) return null | ||||||
|  |  | ||||||
|  |   const planeOrFace = getPlaneOrFaceFromSelection( | ||||||
|  |     selection.artifactId, | ||||||
|  |     artifactGraph | ||||||
|  |   ) | ||||||
|  |   if (!planeOrFace) return null | ||||||
|  |   if (planeOrFace?.type === 'plane') { | ||||||
|  |     const faceDetails = await getFaceDetails(planeOrFace.id) | ||||||
|  |     return { id: planeOrFace.id, faceDetails } | ||||||
|  |   } | ||||||
|  |   // TODO: Handle wall or cap artifact. | ||||||
|  |   return null | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function sketchGroupFromSelection({ | ||||||
|  |   selections, | ||||||
|  |   artifactGraph, | ||||||
|  |   execState, | ||||||
|  | }: { | ||||||
|  |   selections: Selections | ||||||
|  |   artifactGraph: ArtifactGraph | ||||||
|  |   execState: ExecState | ||||||
|  | }): SketchGroup | null { | ||||||
|  |   if (selections.codeBasedSelections.length !== 1) { | ||||||
|  |     // Give up if there isn't exactly one selection. | ||||||
|  |     console.warn( | ||||||
|  |       'sketchGroupFromSelection no single selection', | ||||||
|  |       selections.codeBasedSelections.length, | ||||||
|  |       selections | ||||||
|  |     ) | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  |   const selection = selections.codeBasedSelections[0] | ||||||
|  |   const artifactId = selection.artifactId | ||||||
|  |   if (!artifactId) { | ||||||
|  |     console.warn( | ||||||
|  |       'sketchGroupFromSelection artifact ID not found', | ||||||
|  |       selections.codeBasedSelections.length, | ||||||
|  |       selections | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  |   if (!artifactId) return null | ||||||
|  |   const path = getPathFromSelection(artifactId, artifactGraph) | ||||||
|  |   if (!path) { | ||||||
|  |     console.warn('sketchGroupFromSelection path not found', artifactId) | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  |   const sketch = sketchGroupFromArtifactId(execState, path.id) | ||||||
|  |   return sketch | ||||||
|  | } | ||||||
|  |  | ||||||
| export function sketchGroupFromPathToNode({ | export function sketchGroupFromPathToNode({ | ||||||
|   pathToNode, |   pathToNode, | ||||||
|   ast, |   ast, | ||||||
| @ -1829,6 +1928,7 @@ export function sketchGroupFromPathToNode({ | |||||||
|   ast: Program |   ast: Program | ||||||
|   programMemory: ProgramMemory |   programMemory: ProgramMemory | ||||||
| }): SketchGroup | null | Error { | }): SketchGroup | null | Error { | ||||||
|  |   console.warn('**** sketchGroupFromPathToNode **** Deprecated') | ||||||
|   const _varDec = getNodeFromPath<VariableDeclarator>( |   const _varDec = getNodeFromPath<VariableDeclarator>( | ||||||
|     kclManager.ast, |     kclManager.ast, | ||||||
|     pathToNode, |     pathToNode, | ||||||
| @ -1868,27 +1968,55 @@ function colorSegment(object: any, color: number) { | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| export function getSketchQuaternion( |  | ||||||
|   sketchPathToNode: PathToNode, |  | ||||||
|   sketchNormalBackUp: [number, number, number] | null |  | ||||||
| ): Quaternion | Error { |  | ||||||
|   const sketchGroup = sketchGroupFromPathToNode({ |  | ||||||
|     pathToNode: sketchPathToNode, |  | ||||||
|     ast: kclManager.ast, |  | ||||||
|     programMemory: kclManager.programMemory, |  | ||||||
|   }) |  | ||||||
|   if (err(sketchGroup)) return sketchGroup |  | ||||||
|   const zAxis = sketchGroup?.on.zAxis || sketchNormalBackUp |  | ||||||
|   if (!zAxis) return Error('SketchGroup zAxis not found') |  | ||||||
|  |  | ||||||
|   return getQuaternionFromZAxis(massageFormats(zAxis)) |  | ||||||
| } |  | ||||||
| export async function getSketchOrientationDetails( | export async function getSketchOrientationDetails( | ||||||
|  |   artifactGraph: ArtifactGraph, | ||||||
|  |   selection: Selection, | ||||||
|   sketchPathToNode: PathToNode |   sketchPathToNode: PathToNode | ||||||
| ): Promise<{ | ): Promise<{ | ||||||
|   quat: Quaternion |   sketchDetails: { | ||||||
|   sketchDetails: SketchDetails & { faceId?: string } |     zAxis: [number, number, number] | ||||||
|  |     yAxis: [number, number, number] | ||||||
|  |     origin: [number, number, number] | ||||||
|  |     faceId: string | ||||||
|  |   } | ||||||
| }> { | }> { | ||||||
|  |   const plane = await planeOrFaceFromSelection({ | ||||||
|  |     artifactGraph, | ||||||
|  |     selection, | ||||||
|  |   }) | ||||||
|  |   if (plane) { | ||||||
|  |     const details = plane.faceDetails | ||||||
|  |     console.warn('Found plane', plane) | ||||||
|  |     const zAxis: [number, number, number] = [ | ||||||
|  |       details.z_axis.x, | ||||||
|  |       details.z_axis.y, | ||||||
|  |       details.z_axis.z, | ||||||
|  |     ] | ||||||
|  |     const yAxis: [number, number, number] = [ | ||||||
|  |       details.y_axis.x, | ||||||
|  |       details.y_axis.y, | ||||||
|  |       details.y_axis.z, | ||||||
|  |     ] | ||||||
|  |     const origin: [number, number, number] = [ | ||||||
|  |       details.origin.x, | ||||||
|  |       details.origin.y, | ||||||
|  |       details.origin.z, | ||||||
|  |     ] | ||||||
|  |     return { | ||||||
|  |       sketchDetails: { | ||||||
|  |         zAxis, | ||||||
|  |         yAxis, | ||||||
|  |         origin, | ||||||
|  |         faceId: plane.id, | ||||||
|  |       }, | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   // We couldn't find the plane or face, so try to look at the AST, and find it | ||||||
|  |   // through there. | ||||||
|  |   console.warn( | ||||||
|  |     'getSketchOrientationDetails falling back to sketchGroupFromPathToNode' | ||||||
|  |   ) | ||||||
|   const sketchGroup = sketchGroupFromPathToNode({ |   const sketchGroup = sketchGroupFromPathToNode({ | ||||||
|     pathToNode: sketchPathToNode, |     pathToNode: sketchPathToNode, | ||||||
|     ast: kclManager.ast, |     ast: kclManager.ast, | ||||||
| @ -1900,9 +2028,7 @@ export async function getSketchOrientationDetails( | |||||||
|   if (sketchGroup.on.type === 'plane') { |   if (sketchGroup.on.type === 'plane') { | ||||||
|     const zAxis = sketchGroup?.on.zAxis |     const zAxis = sketchGroup?.on.zAxis | ||||||
|     return { |     return { | ||||||
|       quat: getQuaternionFromZAxis(massageFormats(zAxis)), |  | ||||||
|       sketchDetails: { |       sketchDetails: { | ||||||
|         sketchPathToNode, |  | ||||||
|         zAxis: [zAxis.x, zAxis.y, zAxis.z], |         zAxis: [zAxis.x, zAxis.y, zAxis.z], | ||||||
|         yAxis: [ |         yAxis: [ | ||||||
|           sketchGroup.on.yAxis.x, |           sketchGroup.on.yAxis.x, | ||||||
| @ -1921,14 +2047,8 @@ export async function getSketchOrientationDetails( | |||||||
|     if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) |     if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis) | ||||||
|       return Promise.reject('face info') |       return Promise.reject('face info') | ||||||
|     const { z_axis, y_axis, origin } = faceInfo |     const { z_axis, y_axis, origin } = faceInfo | ||||||
|     const quaternion = quaternionFromUpNForward( |  | ||||||
|       new Vector3(y_axis.x, y_axis.y, y_axis.z), |  | ||||||
|       new Vector3(z_axis.x, z_axis.y, z_axis.z) |  | ||||||
|     ) |  | ||||||
|     return { |     return { | ||||||
|       quat: quaternion, |  | ||||||
|       sketchDetails: { |       sketchDetails: { | ||||||
|         sketchPathToNode, |  | ||||||
|         zAxis: [z_axis.x, z_axis.y, z_axis.z], |         zAxis: [z_axis.x, z_axis.y, z_axis.z], | ||||||
|         yAxis: [y_axis.x, y_axis.y, y_axis.z], |         yAxis: [y_axis.x, y_axis.y, y_axis.z], | ||||||
|         origin: [origin.x, origin.y, origin.z], |         origin: [origin.x, origin.y, origin.z], | ||||||
| @ -2003,7 +2123,3 @@ export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion { | |||||||
|   } |   } | ||||||
|   return quaternion |   return quaternion | ||||||
| } | } | ||||||
|  |  | ||||||
| function massageFormats(a: Vec3Array | Point3d): Vector3 { |  | ||||||
|   return isArray(a) ? new Vector3(a[0], a[1], a[2]) : new Vector3(a.x, a.y, a.z) |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -157,7 +157,7 @@ export function useCalc({ | |||||||
|         engineCommandManager, |         engineCommandManager, | ||||||
|         useFakeExecutor: true, |         useFakeExecutor: true, | ||||||
|         programMemoryOverride: kclManager.programMemory.clone(), |         programMemoryOverride: kclManager.programMemory.clone(), | ||||||
|       }).then(({ programMemory }) => { |       }).then(({ execState }) => { | ||||||
|         const resultDeclaration = ast.body.find( |         const resultDeclaration = ast.body.find( | ||||||
|           (a) => |           (a) => | ||||||
|             a.type === 'VariableDeclaration' && |             a.type === 'VariableDeclaration' && | ||||||
| @ -166,7 +166,7 @@ export function useCalc({ | |||||||
|         const init = |         const init = | ||||||
|           resultDeclaration?.type === 'VariableDeclaration' && |           resultDeclaration?.type === 'VariableDeclaration' && | ||||||
|           resultDeclaration?.declarations?.[0]?.init |           resultDeclaration?.declarations?.[0]?.init | ||||||
|         const result = programMemory?.get('__result__')?.value |         const result = execState.memory?.get('__result__')?.value | ||||||
|         setCalcResult(typeof result === 'number' ? String(result) : 'NAN') |         setCalcResult(typeof result === 'number' ? String(result) : 'NAN') | ||||||
|         init && setValueNode(init) |         init && setValueNode(init) | ||||||
|       }) |       }) | ||||||
|  | |||||||
| @ -625,12 +625,15 @@ export const ModelingMachineProvider = ({ | |||||||
|         }), |         }), | ||||||
|         'animate-to-sketch': fromPromise( |         'animate-to-sketch': fromPromise( | ||||||
|           async ({ input: { selectionRanges } }) => { |           async ({ input: { selectionRanges } }) => { | ||||||
|             const sourceRange = selectionRanges.codeBasedSelections[0].range |             const selection = selectionRanges.codeBasedSelections[0] | ||||||
|  |             const sourceRange = selection.range | ||||||
|             const sketchPathToNode = getNodePathFromSourceRange( |             const sketchPathToNode = getNodePathFromSourceRange( | ||||||
|               kclManager.ast, |               kclManager.ast, | ||||||
|               sourceRange |               sourceRange | ||||||
|             ) |             ) | ||||||
|             const info = await getSketchOrientationDetails( |             const info = await getSketchOrientationDetails( | ||||||
|  |               engineCommandManager.artifactGraph, | ||||||
|  |               selection, | ||||||
|               sketchPathToNode || [] |               sketchPathToNode || [] | ||||||
|             ) |             ) | ||||||
|             await letEngineAnimateAndSyncCamAfter( |             await letEngineAnimateAndSyncCamAfter( | ||||||
|  | |||||||
| @ -29,8 +29,8 @@ describe('processMemory', () => { | |||||||
|     |> lineTo([2.15, 4.32], %) |     |> lineTo([2.15, 4.32], %) | ||||||
|     // |> rx(90, %)` |     // |> rx(90, %)` | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     const programMemory = await enginelessExecutor(ast, ProgramMemory.empty()) |     const execState = await enginelessExecutor(ast, ProgramMemory.empty()) | ||||||
|     const output = processMemory(programMemory) |     const output = processMemory(execState.memory) | ||||||
|     expect(output.myVar).toEqual(5) |     expect(output.myVar).toEqual(5) | ||||||
|     expect(output.otherVar).toEqual(3) |     expect(output.otherVar).toEqual(3) | ||||||
|     expect(output).toEqual({ |     expect(output).toEqual({ | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants' | |||||||
|  |  | ||||||
| import { | import { | ||||||
|   CallExpression, |   CallExpression, | ||||||
|  |   emptyExecState, | ||||||
|  |   ExecState, | ||||||
|   initPromise, |   initPromise, | ||||||
|   parse, |   parse, | ||||||
|   PathToNode, |   PathToNode, | ||||||
| @ -19,6 +21,8 @@ import { | |||||||
| import { getNodeFromPath } from './queryAst' | import { getNodeFromPath } from './queryAst' | ||||||
| import { codeManager, editorManager, sceneInfra } from 'lib/singletons' | import { codeManager, editorManager, sceneInfra } from 'lib/singletons' | ||||||
| import { Diagnostic } from '@codemirror/lint' | import { Diagnostic } from '@codemirror/lint' | ||||||
|  | import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId' | ||||||
|  | import { Artifact } from 'wasm-lib/kcl/bindings/Artifact' | ||||||
|  |  | ||||||
| interface ExecuteArgs { | interface ExecuteArgs { | ||||||
|   ast?: Program |   ast?: Program | ||||||
| @ -43,6 +47,7 @@ export class KclManager { | |||||||
|     digest: null, |     digest: null, | ||||||
|   } |   } | ||||||
|   private _programMemory: ProgramMemory = ProgramMemory.empty() |   private _programMemory: ProgramMemory = ProgramMemory.empty() | ||||||
|  |   private _execState: ExecState = emptyExecState() | ||||||
|   private _logs: string[] = [] |   private _logs: string[] = [] | ||||||
|   private _lints: Diagnostic[] = [] |   private _lints: Diagnostic[] = [] | ||||||
|   private _kclErrors: KCLError[] = [] |   private _kclErrors: KCLError[] = [] | ||||||
| @ -71,11 +76,21 @@ export class KclManager { | |||||||
|   get programMemory() { |   get programMemory() { | ||||||
|     return this._programMemory |     return this._programMemory | ||||||
|   } |   } | ||||||
|   set programMemory(programMemory) { |   // This is private because callers should be setting the entire execState. | ||||||
|  |   private set programMemory(programMemory) { | ||||||
|     this._programMemory = programMemory |     this._programMemory = programMemory | ||||||
|     this._programMemoryCallBack(programMemory) |     this._programMemoryCallBack(programMemory) | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   set execState(execState) { | ||||||
|  |     this._execState = execState | ||||||
|  |     this.programMemory = execState.memory | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get execState() { | ||||||
|  |     return this._execState | ||||||
|  |   } | ||||||
|  |  | ||||||
|   get logs() { |   get logs() { | ||||||
|     return this._logs |     return this._logs | ||||||
|   } |   } | ||||||
| @ -252,7 +267,7 @@ export class KclManager { | |||||||
|     // Make sure we clear before starting again. End session will do this. |     // Make sure we clear before starting again. End session will do this. | ||||||
|     this.engineCommandManager?.endSession() |     this.engineCommandManager?.endSession() | ||||||
|     await this.ensureWasmInit() |     await this.ensureWasmInit() | ||||||
|     const { logs, errors, programMemory, isInterrupted } = await executeAst({ |     const { logs, errors, execState, isInterrupted } = await executeAst({ | ||||||
|       ast, |       ast, | ||||||
|       engineCommandManager: this.engineCommandManager, |       engineCommandManager: this.engineCommandManager, | ||||||
|     }) |     }) | ||||||
| @ -263,7 +278,7 @@ export class KclManager { | |||||||
|       this.lints = await lintAst({ ast: ast }) |       this.lints = await lintAst({ ast: ast }) | ||||||
|  |  | ||||||
|       sceneInfra.modelingSend({ type: 'code edit during sketch' }) |       sceneInfra.modelingSend({ type: 'code edit during sketch' }) | ||||||
|       defaultSelectionFilter(programMemory, this.engineCommandManager) |       defaultSelectionFilter(execState.memory, this.engineCommandManager) | ||||||
|  |  | ||||||
|       if (args.zoomToFit) { |       if (args.zoomToFit) { | ||||||
|         let zoomObjectId: string | undefined = '' |         let zoomObjectId: string | undefined = '' | ||||||
| @ -296,7 +311,7 @@ export class KclManager { | |||||||
|     this.logs = logs |     this.logs = logs | ||||||
|     // Do not add the errors since the program was interrupted and the error is not a real KCL error |     // Do not add the errors since the program was interrupted and the error is not a real KCL error | ||||||
|     this.addKclErrors(isInterrupted ? [] : errors) |     this.addKclErrors(isInterrupted ? [] : errors) | ||||||
|     this.programMemory = programMemory |     this.execState = execState | ||||||
|     this.ast = { ...ast } |     this.ast = { ...ast } | ||||||
|     this._executeCallback() |     this._executeCallback() | ||||||
|     this.engineCommandManager.addCommandLog({ |     this.engineCommandManager.addCommandLog({ | ||||||
| @ -333,7 +348,7 @@ export class KclManager { | |||||||
|     await codeManager.writeToFile() |     await codeManager.writeToFile() | ||||||
|     this._ast = { ...newAst } |     this._ast = { ...newAst } | ||||||
|  |  | ||||||
|     const { logs, errors, programMemory } = await executeAst({ |     const { logs, errors, execState } = await executeAst({ | ||||||
|       ast: newAst, |       ast: newAst, | ||||||
|       engineCommandManager: this.engineCommandManager, |       engineCommandManager: this.engineCommandManager, | ||||||
|       useFakeExecutor: true, |       useFakeExecutor: true, | ||||||
| @ -341,7 +356,8 @@ export class KclManager { | |||||||
|  |  | ||||||
|     this._logs = logs |     this._logs = logs | ||||||
|     this._kclErrors = errors |     this._kclErrors = errors | ||||||
|     this._programMemory = programMemory |     this._execState = execState | ||||||
|  |     this._programMemory = execState.memory | ||||||
|     if (updates !== 'artifactRanges') return |     if (updates !== 'artifactRanges') return | ||||||
|  |  | ||||||
|     // TODO the below seems like a work around, I wish there's a comment explaining exactly what |     // TODO the below seems like a work around, I wish there's a comment explaining exactly what | ||||||
|  | |||||||
| @ -445,6 +445,6 @@ async function exe( | |||||||
| ) { | ) { | ||||||
|   const ast = parse(code) |   const ast = parse(code) | ||||||
|  |  | ||||||
|   const result = await enginelessExecutor(ast, programMemory) |   const execState = await enginelessExecutor(ast, programMemory) | ||||||
|   return result |   return execState.memory | ||||||
| } | } | ||||||
|  | |||||||
| @ -4,6 +4,8 @@ import { | |||||||
|   ProgramMemory, |   ProgramMemory, | ||||||
|   programMemoryInit, |   programMemoryInit, | ||||||
|   kclLint, |   kclLint, | ||||||
|  |   emptyExecState, | ||||||
|  |   ExecState, | ||||||
| } from 'lang/wasm' | } from 'lang/wasm' | ||||||
| import { enginelessExecutor } from 'lib/testHelpers' | import { enginelessExecutor } from 'lib/testHelpers' | ||||||
| import { EngineCommandManager } from 'lang/std/engineConnection' | import { EngineCommandManager } from 'lang/std/engineConnection' | ||||||
| @ -56,7 +58,7 @@ export async function executeAst({ | |||||||
| }): Promise<{ | }): Promise<{ | ||||||
|   logs: string[] |   logs: string[] | ||||||
|   errors: KCLError[] |   errors: KCLError[] | ||||||
|   programMemory: ProgramMemory |   execState: ExecState | ||||||
|   isInterrupted: boolean |   isInterrupted: boolean | ||||||
| }> { | }> { | ||||||
|   try { |   try { | ||||||
| @ -65,7 +67,7 @@ export async function executeAst({ | |||||||
|       // eslint-disable-next-line @typescript-eslint/no-floating-promises |       // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||||||
|       engineCommandManager.startNewSession() |       engineCommandManager.startNewSession() | ||||||
|     } |     } | ||||||
|     const programMemory = await (useFakeExecutor |     const execState = await (useFakeExecutor | ||||||
|       ? enginelessExecutor(ast, programMemoryOverride || programMemoryInit()) |       ? enginelessExecutor(ast, programMemoryOverride || programMemoryInit()) | ||||||
|       : _executor(ast, programMemoryInit(), engineCommandManager, false)) |       : _executor(ast, programMemoryInit(), engineCommandManager, false)) | ||||||
|  |  | ||||||
| @ -73,7 +75,7 @@ export async function executeAst({ | |||||||
|     return { |     return { | ||||||
|       logs: [], |       logs: [], | ||||||
|       errors: [], |       errors: [], | ||||||
|       programMemory, |       execState, | ||||||
|       isInterrupted: false, |       isInterrupted: false, | ||||||
|     } |     } | ||||||
|   } catch (e: any) { |   } catch (e: any) { | ||||||
| @ -89,7 +91,7 @@ export async function executeAst({ | |||||||
|       return { |       return { | ||||||
|         errors: [e], |         errors: [e], | ||||||
|         logs: [], |         logs: [], | ||||||
|         programMemory: ProgramMemory.empty(), |         execState: emptyExecState(), | ||||||
|         isInterrupted, |         isInterrupted, | ||||||
|       } |       } | ||||||
|     } else { |     } else { | ||||||
| @ -97,7 +99,7 @@ export async function executeAst({ | |||||||
|       return { |       return { | ||||||
|         logs: [e], |         logs: [e], | ||||||
|         errors: [], |         errors: [], | ||||||
|         programMemory: ProgramMemory.empty(), |         execState: emptyExecState(), | ||||||
|         isInterrupted, |         isInterrupted, | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -220,11 +220,11 @@ const yo2 = hmm([identifierGuy + 5])` | |||||||
|   it('should move a binary expression into a new variable', async () => { |   it('should move a binary expression into a new variable', async () => { | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const startIndex = code.indexOf('100 + 100') + 1 |     const startIndex = code.indexOf('100 + 100') + 1 | ||||||
|     const { modifiedAst } = moveValueIntoNewVariable( |     const { modifiedAst } = moveValueIntoNewVariable( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [startIndex, startIndex], |       [startIndex, startIndex], | ||||||
|       'newVar' |       'newVar' | ||||||
|     ) |     ) | ||||||
| @ -235,11 +235,11 @@ const yo2 = hmm([identifierGuy + 5])` | |||||||
|   it('should move a value into a new variable', async () => { |   it('should move a value into a new variable', async () => { | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const startIndex = code.indexOf('2.8') + 1 |     const startIndex = code.indexOf('2.8') + 1 | ||||||
|     const { modifiedAst } = moveValueIntoNewVariable( |     const { modifiedAst } = moveValueIntoNewVariable( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [startIndex, startIndex], |       [startIndex, startIndex], | ||||||
|       'newVar' |       'newVar' | ||||||
|     ) |     ) | ||||||
| @ -250,11 +250,11 @@ const yo2 = hmm([identifierGuy + 5])` | |||||||
|   it('should move a callExpression into a new variable', async () => { |   it('should move a callExpression into a new variable', async () => { | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const startIndex = code.indexOf('def(') |     const startIndex = code.indexOf('def(') | ||||||
|     const { modifiedAst } = moveValueIntoNewVariable( |     const { modifiedAst } = moveValueIntoNewVariable( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [startIndex, startIndex], |       [startIndex, startIndex], | ||||||
|       'newVar' |       'newVar' | ||||||
|     ) |     ) | ||||||
| @ -265,11 +265,11 @@ const yo2 = hmm([identifierGuy + 5])` | |||||||
|   it('should move a binary expression with call expression into a new variable', async () => { |   it('should move a binary expression with call expression into a new variable', async () => { | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const startIndex = code.indexOf('jkl(') + 1 |     const startIndex = code.indexOf('jkl(') + 1 | ||||||
|     const { modifiedAst } = moveValueIntoNewVariable( |     const { modifiedAst } = moveValueIntoNewVariable( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [startIndex, startIndex], |       [startIndex, startIndex], | ||||||
|       'newVar' |       'newVar' | ||||||
|     ) |     ) | ||||||
| @ -280,11 +280,11 @@ const yo2 = hmm([identifierGuy + 5])` | |||||||
|   it('should move a identifier into a new variable', async () => { |   it('should move a identifier into a new variable', async () => { | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const startIndex = code.indexOf('identifierGuy +') + 1 |     const startIndex = code.indexOf('identifierGuy +') + 1 | ||||||
|     const { modifiedAst } = moveValueIntoNewVariable( |     const { modifiedAst } = moveValueIntoNewVariable( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [startIndex, startIndex], |       [startIndex, startIndex], | ||||||
|       'newVar' |       'newVar' | ||||||
|     ) |     ) | ||||||
| @ -465,7 +465,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => { | |||||||
|   |> line([306.21, 198.87], %)` |   |> line([306.21, 198.87], %)` | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const lineOfInterest = 'line([306.21, 198.85], %, $a)' |     const lineOfInterest = 'line([306.21, 198.85], %, $a)' | ||||||
|     const range: [number, number] = [ |     const range: [number, number] = [ | ||||||
|       code.indexOf(lineOfInterest), |       code.indexOf(lineOfInterest), | ||||||
| @ -475,7 +475,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => { | |||||||
|     const modifiedAst = deleteSegmentFromPipeExpression( |     const modifiedAst = deleteSegmentFromPipeExpression( | ||||||
|       [], |       [], | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       code, |       code, | ||||||
|       pathToNode |       pathToNode | ||||||
|     ) |     ) | ||||||
| @ -543,7 +543,7 @@ ${!replace1 ? `  |> ${line}\n` : ''}  |> angledLine([-65, ${ | |||||||
|       const code = makeCode(line) |       const code = makeCode(line) | ||||||
|       const ast = parse(code) |       const ast = parse(code) | ||||||
|       if (err(ast)) throw ast |       if (err(ast)) throw ast | ||||||
|       const programMemory = await enginelessExecutor(ast) |       const execState = await enginelessExecutor(ast) | ||||||
|       const lineOfInterest = line |       const lineOfInterest = line | ||||||
|       const range: [number, number] = [ |       const range: [number, number] = [ | ||||||
|         code.indexOf(lineOfInterest), |         code.indexOf(lineOfInterest), | ||||||
| @ -554,7 +554,7 @@ ${!replace1 ? `  |> ${line}\n` : ''}  |> angledLine([-65, ${ | |||||||
|       const modifiedAst = deleteSegmentFromPipeExpression( |       const modifiedAst = deleteSegmentFromPipeExpression( | ||||||
|         dependentSegments, |         dependentSegments, | ||||||
|         ast, |         ast, | ||||||
|         programMemory, |         execState.memory, | ||||||
|         code, |         code, | ||||||
|         pathToNode |         pathToNode | ||||||
|       ) |       ) | ||||||
| @ -632,7 +632,7 @@ describe('Testing removeSingleConstraintInfo', () => { | |||||||
|       const ast = parse(code) |       const ast = parse(code) | ||||||
|       if (err(ast)) throw ast |       if (err(ast)) throw ast | ||||||
|  |  | ||||||
|       const programMemory = await enginelessExecutor(ast) |       const execState = await enginelessExecutor(ast) | ||||||
|       const lineOfInterest = expectedFinish.split('(')[0] + '(' |       const lineOfInterest = expectedFinish.split('(')[0] + '(' | ||||||
|       const range: [number, number] = [ |       const range: [number, number] = [ | ||||||
|         code.indexOf(lineOfInterest) + 1, |         code.indexOf(lineOfInterest) + 1, | ||||||
| @ -661,7 +661,7 @@ describe('Testing removeSingleConstraintInfo', () => { | |||||||
|         pathToNode, |         pathToNode, | ||||||
|         argPosition, |         argPosition, | ||||||
|         ast, |         ast, | ||||||
|         programMemory |         execState.memory | ||||||
|       ) |       ) | ||||||
|       if (!mod) return new Error('mod is undefined') |       if (!mod) return new Error('mod is undefined') | ||||||
|       const recastCode = recast(mod.modifiedAst) |       const recastCode = recast(mod.modifiedAst) | ||||||
| @ -686,7 +686,7 @@ describe('Testing removeSingleConstraintInfo', () => { | |||||||
|       const ast = parse(code) |       const ast = parse(code) | ||||||
|       if (err(ast)) throw ast |       if (err(ast)) throw ast | ||||||
|  |  | ||||||
|       const programMemory = await enginelessExecutor(ast) |       const execState = await enginelessExecutor(ast) | ||||||
|       const lineOfInterest = expectedFinish.split('(')[0] + '(' |       const lineOfInterest = expectedFinish.split('(')[0] + '(' | ||||||
|       const range: [number, number] = [ |       const range: [number, number] = [ | ||||||
|         code.indexOf(lineOfInterest) + 1, |         code.indexOf(lineOfInterest) + 1, | ||||||
| @ -711,7 +711,7 @@ describe('Testing removeSingleConstraintInfo', () => { | |||||||
|         pathToNode, |         pathToNode, | ||||||
|         argPosition, |         argPosition, | ||||||
|         ast, |         ast, | ||||||
|         programMemory |         execState.memory | ||||||
|       ) |       ) | ||||||
|       if (!mod) return new Error('mod is undefined') |       if (!mod) return new Error('mod is undefined') | ||||||
|       const recastCode = recast(mod.modifiedAst) |       const recastCode = recast(mod.modifiedAst) | ||||||
| @ -882,7 +882,7 @@ const sketch002 = startSketchOn({ | |||||||
|       // const lineOfInterest = 'line([-2.94, 2.7], %)' |       // const lineOfInterest = 'line([-2.94, 2.7], %)' | ||||||
|       const ast = parse(codeBefore) |       const ast = parse(codeBefore) | ||||||
|       if (err(ast)) throw ast |       if (err(ast)) throw ast | ||||||
|       const programMemory = await enginelessExecutor(ast) |       const execState = await enginelessExecutor(ast) | ||||||
|  |  | ||||||
|       // deleteFromSelection |       // deleteFromSelection | ||||||
|       const range: [number, number] = [ |       const range: [number, number] = [ | ||||||
| @ -895,7 +895,7 @@ const sketch002 = startSketchOn({ | |||||||
|           range, |           range, | ||||||
|           type, |           type, | ||||||
|         }, |         }, | ||||||
|         programMemory, |         execState.memory, | ||||||
|         async () => { |         async () => { | ||||||
|           await new Promise((resolve) => setTimeout(resolve, 100)) |           await new Promise((resolve) => setTimeout(resolve, 100)) | ||||||
|           return { |           return { | ||||||
|  | |||||||
| @ -18,6 +18,7 @@ import { | |||||||
|   ProgramMemory, |   ProgramMemory, | ||||||
|   SourceRange, |   SourceRange, | ||||||
|   sketchGroupFromKclValue, |   sketchGroupFromKclValue, | ||||||
|  |   ExpressionStatement, | ||||||
| } from './wasm' | } from './wasm' | ||||||
| import { | import { | ||||||
|   isNodeSafeToReplacePath, |   isNodeSafeToReplacePath, | ||||||
| @ -80,18 +81,24 @@ export function addStartProfileAt( | |||||||
|   pathToNode: PathToNode, |   pathToNode: PathToNode, | ||||||
|   at: [number, number] |   at: [number, number] | ||||||
| ): { modifiedAst: Program; pathToNode: PathToNode } | Error { | ): { modifiedAst: Program; pathToNode: PathToNode } | Error { | ||||||
|   const _node1 = getNodeFromPath<VariableDeclaration>( |   const _node1 = getNodeFromPath<VariableDeclaration>(node, pathToNode, [ | ||||||
|     node, |     'VariableDeclaration', | ||||||
|     pathToNode, |     'ExpressionStatement', | ||||||
|     'VariableDeclaration' |   ]) as { node: { type: string } } | Error | ||||||
|   ) |  | ||||||
|   if (err(_node1)) return _node1 |   if (err(_node1)) return _node1 | ||||||
|   const variableDeclaration = _node1.node |  | ||||||
|   if (variableDeclaration.type !== 'VariableDeclaration') { |  | ||||||
|     return new Error('variableDeclaration.init.type !== PipeExpression') |  | ||||||
|   } |  | ||||||
|   const _node = { ...node } |   const _node = { ...node } | ||||||
|   const init = variableDeclaration.declarations[0].init |   let expr: Expr | ||||||
|  |   let variableDeclaration: VariableDeclaration | undefined | ||||||
|  |   if (_node1.node.type === 'VariableDeclaration') { | ||||||
|  |     const node: VariableDeclaration = _node1.node as VariableDeclaration | ||||||
|  |     variableDeclaration = node | ||||||
|  |     expr = node.declarations[0].init | ||||||
|  |   } else if (_node1.node.type === 'ExpressionStatement') { | ||||||
|  |     const node: ExpressionStatement = _node1.node as ExpressionStatement | ||||||
|  |     expr = node.expression | ||||||
|  |   } else { | ||||||
|  |     return new Error(`Unrecognized node type ${_node1.node.type}`) | ||||||
|  |   } | ||||||
|   const startProfileAt = createCallExpressionStdLib('startProfileAt', [ |   const startProfileAt = createCallExpressionStdLib('startProfileAt', [ | ||||||
|     createArrayExpression([ |     createArrayExpression([ | ||||||
|       createLiteral(roundOff(at[0])), |       createLiteral(roundOff(at[0])), | ||||||
| @ -99,11 +106,11 @@ export function addStartProfileAt( | |||||||
|     ]), |     ]), | ||||||
|     createPipeSubstitution(), |     createPipeSubstitution(), | ||||||
|   ]) |   ]) | ||||||
|   if (init.type === 'PipeExpression') { |   if (expr.type === 'PipeExpression') { | ||||||
|     init.body.splice(1, 0, startProfileAt) |     expr.body.splice(1, 0, startProfileAt) | ||||||
|   } else { |   } else if (variableDeclaration) { | ||||||
|     variableDeclaration.declarations[0].init = createPipeExpression([ |     variableDeclaration.declarations[0].init = createPipeExpression([ | ||||||
|       init, |       expr, | ||||||
|       startProfileAt, |       startProfileAt, | ||||||
|     ]) |     ]) | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -45,11 +45,11 @@ const variableBelowShouldNotBeIncluded = 3 | |||||||
|     const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 |     const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7 | ||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|  |  | ||||||
|     const { variables, bodyPath, insertIndex } = findAllPreviousVariables( |     const { variables, bodyPath, insertIndex } = findAllPreviousVariables( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       [rangeStart, rangeStart] |       [rangeStart, rangeStart] | ||||||
|     ) |     ) | ||||||
|     expect(variables).toEqual([ |     expect(variables).toEqual([ | ||||||
| @ -351,11 +351,11 @@ const part001 = startSketchAt([-1.41, 3.46]) | |||||||
|     const ast = parse(exampleCode) |     const ast = parse(exampleCode) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const result = hasExtrudeSketchGroup({ |     const result = hasExtrudeSketchGroup({ | ||||||
|       ast, |       ast, | ||||||
|       selection: { type: 'default', range: [100, 101] }, |       selection: { type: 'default', range: [100, 101] }, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|     }) |     }) | ||||||
|     expect(result).toEqual(true) |     expect(result).toEqual(true) | ||||||
|   }) |   }) | ||||||
| @ -370,11 +370,11 @@ const part001 = startSketchAt([-1.41, 3.46]) | |||||||
|     const ast = parse(exampleCode) |     const ast = parse(exampleCode) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const result = hasExtrudeSketchGroup({ |     const result = hasExtrudeSketchGroup({ | ||||||
|       ast, |       ast, | ||||||
|       selection: { type: 'default', range: [100, 101] }, |       selection: { type: 'default', range: [100, 101] }, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|     }) |     }) | ||||||
|     expect(result).toEqual(true) |     expect(result).toEqual(true) | ||||||
|   }) |   }) | ||||||
| @ -383,11 +383,11 @@ const part001 = startSketchAt([-1.41, 3.46]) | |||||||
|     const ast = parse(exampleCode) |     const ast = parse(exampleCode) | ||||||
|     if (err(ast)) throw ast |     if (err(ast)) throw ast | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const result = hasExtrudeSketchGroup({ |     const result = hasExtrudeSketchGroup({ | ||||||
|       ast, |       ast, | ||||||
|       selection: { type: 'default', range: [10, 11] }, |       selection: { type: 'default', range: [10, 11] }, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|     }) |     }) | ||||||
|     expect(result).toEqual(false) |     expect(result).toEqual(false) | ||||||
|   }) |   }) | ||||||
|  | |||||||
| @ -795,7 +795,7 @@ export function isSingleCursorInPipe( | |||||||
|   const pathToNode = getNodePathFromSourceRange(ast, selection.range) |   const pathToNode = getNodePathFromSourceRange(ast, selection.range) | ||||||
|   const nodeTypes = pathToNode.map(([, type]) => type) |   const nodeTypes = pathToNode.map(([, type]) => type) | ||||||
|   if (nodeTypes.includes('FunctionExpression')) return false |   if (nodeTypes.includes('FunctionExpression')) return false | ||||||
|   if (!nodeTypes.includes('VariableDeclaration')) return false |   // if (!nodeTypes.includes('VariableDeclaration')) return false | ||||||
|   if (nodeTypes.includes('PipeExpression')) return true |   if (nodeTypes.includes('PipeExpression')) return true | ||||||
|   return false |   return false | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,17 +12,20 @@ interface CommonCommandProperties { | |||||||
|  |  | ||||||
| export interface PlaneArtifact { | export interface PlaneArtifact { | ||||||
|   type: 'plane' |   type: 'plane' | ||||||
|  |   id: ArtifactId | ||||||
|   pathIds: Array<ArtifactId> |   pathIds: Array<ArtifactId> | ||||||
|   codeRef: CommonCommandProperties |   codeRef: CommonCommandProperties | ||||||
| } | } | ||||||
| export interface PlaneArtifactRich { | export interface PlaneArtifactRich { | ||||||
|   type: 'plane' |   type: 'plane' | ||||||
|  |   id: ArtifactId | ||||||
|   paths: Array<PathArtifact> |   paths: Array<PathArtifact> | ||||||
|   codeRef: CommonCommandProperties |   codeRef: CommonCommandProperties | ||||||
| } | } | ||||||
|  |  | ||||||
| export interface PathArtifact { | export interface PathArtifact { | ||||||
|   type: 'path' |   type: 'path' | ||||||
|  |   id: ArtifactId | ||||||
|   planeId: ArtifactId |   planeId: ArtifactId | ||||||
|   segIds: Array<ArtifactId> |   segIds: Array<ArtifactId> | ||||||
|   sweepId: ArtifactId |   sweepId: ArtifactId | ||||||
| @ -32,10 +35,12 @@ export interface PathArtifact { | |||||||
|  |  | ||||||
| interface solid2D { | interface solid2D { | ||||||
|   type: 'solid2D' |   type: 'solid2D' | ||||||
|  |   id: ArtifactId | ||||||
|   pathId: ArtifactId |   pathId: ArtifactId | ||||||
| } | } | ||||||
| export interface PathArtifactRich { | export interface PathArtifactRich { | ||||||
|   type: 'path' |   type: 'path' | ||||||
|  |   id: ArtifactId | ||||||
|   plane: PlaneArtifact | WallArtifact |   plane: PlaneArtifact | WallArtifact | ||||||
|   segments: Array<SegmentArtifact> |   segments: Array<SegmentArtifact> | ||||||
|   sweep: SweepArtifact |   sweep: SweepArtifact | ||||||
| @ -44,6 +49,7 @@ export interface PathArtifactRich { | |||||||
|  |  | ||||||
| export interface SegmentArtifact { | export interface SegmentArtifact { | ||||||
|   type: 'segment' |   type: 'segment' | ||||||
|  |   id: ArtifactId | ||||||
|   pathId: ArtifactId |   pathId: ArtifactId | ||||||
|   surfaceId: ArtifactId |   surfaceId: ArtifactId | ||||||
|   edgeIds: Array<ArtifactId> |   edgeIds: Array<ArtifactId> | ||||||
| @ -52,6 +58,7 @@ export interface SegmentArtifact { | |||||||
| } | } | ||||||
| interface SegmentArtifactRich { | interface SegmentArtifactRich { | ||||||
|   type: 'segment' |   type: 'segment' | ||||||
|  |   id: ArtifactId | ||||||
|   path: PathArtifact |   path: PathArtifact | ||||||
|   surf: WallArtifact |   surf: WallArtifact | ||||||
|   edges: Array<SweepEdge> |   edges: Array<SweepEdge> | ||||||
| @ -63,14 +70,16 @@ interface SegmentArtifactRich { | |||||||
| interface SweepArtifact { | interface SweepArtifact { | ||||||
|   type: 'sweep' |   type: 'sweep' | ||||||
|   subType: 'extrusion' | 'revolve' |   subType: 'extrusion' | 'revolve' | ||||||
|  |   id: ArtifactId | ||||||
|   pathId: string |   pathId: string | ||||||
|   surfaceIds: Array<string> |   surfaceIds: Array<ArtifactId> | ||||||
|   edgeIds: Array<string> |   edgeIds: Array<ArtifactId> | ||||||
|   codeRef: CommonCommandProperties |   codeRef: CommonCommandProperties | ||||||
| } | } | ||||||
| interface SweepArtifactRich { | interface SweepArtifactRich { | ||||||
|   type: 'sweep' |   type: 'sweep' | ||||||
|   subType: 'extrusion' | 'revolve' |   subType: 'extrusion' | 'revolve' | ||||||
|  |   id: ArtifactId | ||||||
|   path: PathArtifact |   path: PathArtifact | ||||||
|   surfaces: Array<WallArtifact | CapArtifact> |   surfaces: Array<WallArtifact | CapArtifact> | ||||||
|   edges: Array<SweepEdge> |   edges: Array<SweepEdge> | ||||||
| @ -79,6 +88,7 @@ interface SweepArtifactRich { | |||||||
|  |  | ||||||
| interface WallArtifact { | interface WallArtifact { | ||||||
|   type: 'wall' |   type: 'wall' | ||||||
|  |   id: ArtifactId | ||||||
|   segId: ArtifactId |   segId: ArtifactId | ||||||
|   edgeCutEdgeIds: Array<ArtifactId> |   edgeCutEdgeIds: Array<ArtifactId> | ||||||
|   sweepId: ArtifactId |   sweepId: ArtifactId | ||||||
| @ -86,6 +96,7 @@ interface WallArtifact { | |||||||
| } | } | ||||||
| interface CapArtifact { | interface CapArtifact { | ||||||
|   type: 'cap' |   type: 'cap' | ||||||
|  |   id: ArtifactId | ||||||
|   subType: 'start' | 'end' |   subType: 'start' | 'end' | ||||||
|   edgeCutEdgeIds: Array<ArtifactId> |   edgeCutEdgeIds: Array<ArtifactId> | ||||||
|   sweepId: ArtifactId |   sweepId: ArtifactId | ||||||
| @ -94,6 +105,7 @@ interface CapArtifact { | |||||||
|  |  | ||||||
| interface SweepEdge { | interface SweepEdge { | ||||||
|   type: 'sweepEdge' |   type: 'sweepEdge' | ||||||
|  |   id: ArtifactId | ||||||
|   segId: ArtifactId |   segId: ArtifactId | ||||||
|   sweepId: ArtifactId |   sweepId: ArtifactId | ||||||
|   subType: 'opposite' | 'adjacent' |   subType: 'opposite' | 'adjacent' | ||||||
| @ -102,6 +114,7 @@ interface SweepEdge { | |||||||
| /** A edgeCut is a more generic term for both fillet or chamfer */ | /** A edgeCut is a more generic term for both fillet or chamfer */ | ||||||
| interface EdgeCut { | interface EdgeCut { | ||||||
|   type: 'edgeCut' |   type: 'edgeCut' | ||||||
|  |   id: ArtifactId | ||||||
|   subType: 'fillet' | 'chamfer' |   subType: 'fillet' | 'chamfer' | ||||||
|   consumedEdgeId: ArtifactId |   consumedEdgeId: ArtifactId | ||||||
|   edgeIds: Array<ArtifactId> |   edgeIds: Array<ArtifactId> | ||||||
| @ -111,6 +124,7 @@ interface EdgeCut { | |||||||
|  |  | ||||||
| interface EdgeCutEdge { | interface EdgeCutEdge { | ||||||
|   type: 'edgeCutEdge' |   type: 'edgeCutEdge' | ||||||
|  |   id: ArtifactId | ||||||
|   edgeCutId: ArtifactId |   edgeCutId: ArtifactId | ||||||
|   surfaceId: ArtifactId |   surfaceId: ArtifactId | ||||||
| } | } | ||||||
| @ -215,7 +229,7 @@ function mergeArtifacts( | |||||||
|  * It does not mutate the map directly, but returns an array of artifacts to update |  * It does not mutate the map directly, but returns an array of artifacts to update | ||||||
|  * |  * | ||||||
|  * @param currentPlaneId is only needed for `start_path` commands because this command does not have a pathId |  * @param currentPlaneId is only needed for `start_path` commands because this command does not have a pathId | ||||||
|  * instead it relies on the id used with the `enable_sketch_mode` command, so this much be kept track of |  * instead it relies on the id used with the `enable_sketch_mode` command, so this must be kept track of | ||||||
|  * outside of this function. It would be good to update the `start_path` command to include the planeId so we |  * outside of this function. It would be good to update the `start_path` command to include the planeId so we | ||||||
|  * can remove this. |  * can remove this. | ||||||
|  */ |  */ | ||||||
| @ -258,6 +272,7 @@ export function getArtifactsToUpdate({ | |||||||
|           id: currentPlaneId, |           id: currentPlaneId, | ||||||
|           artifact: { |           artifact: { | ||||||
|             type: 'wall', |             type: 'wall', | ||||||
|  |             id: currentPlaneId, | ||||||
|             segId: existingPlane.segId, |             segId: existingPlane.segId, | ||||||
|             edgeCutEdgeIds: existingPlane.edgeCutEdgeIds, |             edgeCutEdgeIds: existingPlane.edgeCutEdgeIds, | ||||||
|             sweepId: existingPlane.sweepId, |             sweepId: existingPlane.sweepId, | ||||||
| @ -267,7 +282,10 @@ export function getArtifactsToUpdate({ | |||||||
|       ] |       ] | ||||||
|     } else { |     } else { | ||||||
|       return [ |       return [ | ||||||
|         { id: currentPlaneId, artifact: { type: 'plane', pathIds, codeRef } }, |         { | ||||||
|  |           id: currentPlaneId, | ||||||
|  |           artifact: { type: 'plane', id: currentPlaneId, pathIds, codeRef }, | ||||||
|  |         }, | ||||||
|       ] |       ] | ||||||
|     } |     } | ||||||
|   } else if (cmd.type === 'start_path') { |   } else if (cmd.type === 'start_path') { | ||||||
| @ -275,6 +293,7 @@ export function getArtifactsToUpdate({ | |||||||
|       id, |       id, | ||||||
|       artifact: { |       artifact: { | ||||||
|         type: 'path', |         type: 'path', | ||||||
|  |         id, | ||||||
|         segIds: [], |         segIds: [], | ||||||
|         planeId: currentPlaneId, |         planeId: currentPlaneId, | ||||||
|         sweepId: '', |         sweepId: '', | ||||||
| @ -287,7 +306,7 @@ export function getArtifactsToUpdate({ | |||||||
|     if (plane?.type === 'plane') { |     if (plane?.type === 'plane') { | ||||||
|       returnArr.push({ |       returnArr.push({ | ||||||
|         id: currentPlaneId, |         id: currentPlaneId, | ||||||
|         artifact: { type: 'plane', pathIds: [id], codeRef }, |         artifact: { type: 'plane', id: currentPlaneId, pathIds: [id], codeRef }, | ||||||
|       }) |       }) | ||||||
|     } |     } | ||||||
|     if (plane?.type === 'wall') { |     if (plane?.type === 'wall') { | ||||||
| @ -295,6 +314,7 @@ export function getArtifactsToUpdate({ | |||||||
|         id: currentPlaneId, |         id: currentPlaneId, | ||||||
|         artifact: { |         artifact: { | ||||||
|           type: 'wall', |           type: 'wall', | ||||||
|  |           id: currentPlaneId, | ||||||
|           segId: plane.segId, |           segId: plane.segId, | ||||||
|           edgeCutEdgeIds: plane.edgeCutEdgeIds, |           edgeCutEdgeIds: plane.edgeCutEdgeIds, | ||||||
|           sweepId: plane.sweepId, |           sweepId: plane.sweepId, | ||||||
| @ -309,6 +329,7 @@ export function getArtifactsToUpdate({ | |||||||
|       id, |       id, | ||||||
|       artifact: { |       artifact: { | ||||||
|         type: 'segment', |         type: 'segment', | ||||||
|  |         id, | ||||||
|         pathId, |         pathId, | ||||||
|         surfaceId: '', |         surfaceId: '', | ||||||
|         edgeIds: [], |         edgeIds: [], | ||||||
| @ -318,21 +339,22 @@ export function getArtifactsToUpdate({ | |||||||
|     const path = getArtifact(pathId) |     const path = getArtifact(pathId) | ||||||
|     if (path?.type === 'path') |     if (path?.type === 'path') | ||||||
|       returnArr.push({ |       returnArr.push({ | ||||||
|         id: pathId, |         id: path.id, | ||||||
|         artifact: { ...path, segIds: [id] }, |         artifact: { ...path, segIds: [id] }, | ||||||
|       }) |       }) | ||||||
|     if ( |     if ( | ||||||
|       response?.type === 'modeling' && |       response?.type === 'modeling' && | ||||||
|       response.data.modeling_response.type === 'close_path' |       response.data.modeling_response.type === 'close_path' | ||||||
|     ) { |     ) { | ||||||
|  |       const id = response.data.modeling_response.data.face_id | ||||||
|       returnArr.push({ |       returnArr.push({ | ||||||
|         id: response.data.modeling_response.data.face_id, |         id, | ||||||
|         artifact: { type: 'solid2D', pathId }, |         artifact: { type: 'solid2D', id, pathId }, | ||||||
|       }) |       }) | ||||||
|       const path = getArtifact(pathId) |       const path = getArtifact(pathId) | ||||||
|       if (path?.type === 'path') |       if (path?.type === 'path') | ||||||
|         returnArr.push({ |         returnArr.push({ | ||||||
|           id: pathId, |           id: path.id, | ||||||
|           artifact: { |           artifact: { | ||||||
|             ...path, |             ...path, | ||||||
|             solid2dId: response.data.modeling_response.data.face_id, |             solid2dId: response.data.modeling_response.data.face_id, | ||||||
| @ -347,6 +369,7 @@ export function getArtifactsToUpdate({ | |||||||
|       artifact: { |       artifact: { | ||||||
|         type: 'sweep', |         type: 'sweep', | ||||||
|         subType: subType, |         subType: subType, | ||||||
|  |         id, | ||||||
|         pathId: cmd.target, |         pathId: cmd.target, | ||||||
|         surfaceIds: [], |         surfaceIds: [], | ||||||
|         edgeIds: [], |         edgeIds: [], | ||||||
| @ -356,7 +379,7 @@ export function getArtifactsToUpdate({ | |||||||
|     const path = getArtifact(cmd.target) |     const path = getArtifact(cmd.target) | ||||||
|     if (path?.type === 'path') |     if (path?.type === 'path') | ||||||
|       returnArr.push({ |       returnArr.push({ | ||||||
|         id: cmd.target, |         id: path.id, | ||||||
|         artifact: { ...path, sweepId: id }, |         artifact: { ...path, sweepId: id }, | ||||||
|       }) |       }) | ||||||
|     return returnArr |     return returnArr | ||||||
| @ -378,6 +401,7 @@ export function getArtifactsToUpdate({ | |||||||
|               id: face_id, |               id: face_id, | ||||||
|               artifact: { |               artifact: { | ||||||
|                 type: 'wall', |                 type: 'wall', | ||||||
|  |                 id: face_id, | ||||||
|                 segId: curve_id, |                 segId: curve_id, | ||||||
|                 edgeCutEdgeIds: [], |                 edgeCutEdgeIds: [], | ||||||
|                 sweepId: path.sweepId, |                 sweepId: path.sweepId, | ||||||
| @ -385,7 +409,7 @@ export function getArtifactsToUpdate({ | |||||||
|               }, |               }, | ||||||
|             }) |             }) | ||||||
|             returnArr.push({ |             returnArr.push({ | ||||||
|               id: curve_id, |               id: seg.id, | ||||||
|               artifact: { ...seg, surfaceId: face_id }, |               artifact: { ...seg, surfaceId: face_id }, | ||||||
|             }) |             }) | ||||||
|             const sweep = getArtifact(path.sweepId) |             const sweep = getArtifact(path.sweepId) | ||||||
| @ -394,6 +418,7 @@ export function getArtifactsToUpdate({ | |||||||
|                 id: path.sweepId, |                 id: path.sweepId, | ||||||
|                 artifact: { |                 artifact: { | ||||||
|                   ...sweep, |                   ...sweep, | ||||||
|  |                   id: path.sweepId, | ||||||
|                   surfaceIds: [face_id], |                   surfaceIds: [face_id], | ||||||
|                 }, |                 }, | ||||||
|               }) |               }) | ||||||
| @ -410,6 +435,7 @@ export function getArtifactsToUpdate({ | |||||||
|             id: face_id, |             id: face_id, | ||||||
|             artifact: { |             artifact: { | ||||||
|               type: 'cap', |               type: 'cap', | ||||||
|  |               id: face_id, | ||||||
|               subType: cap === 'bottom' ? 'start' : 'end', |               subType: cap === 'bottom' ? 'start' : 'end', | ||||||
|               edgeCutEdgeIds: [], |               edgeCutEdgeIds: [], | ||||||
|               sweepId: path.sweepId, |               sweepId: path.sweepId, | ||||||
| @ -419,7 +445,7 @@ export function getArtifactsToUpdate({ | |||||||
|           const sweep = getArtifact(path.sweepId) |           const sweep = getArtifact(path.sweepId) | ||||||
|           if (sweep?.type !== 'sweep') return |           if (sweep?.type !== 'sweep') return | ||||||
|           returnArr.push({ |           returnArr.push({ | ||||||
|             id: path.sweepId, |             id: sweep.id, | ||||||
|             artifact: { |             artifact: { | ||||||
|               ...sweep, |               ...sweep, | ||||||
|               surfaceIds: [face_id], |               surfaceIds: [face_id], | ||||||
| @ -460,6 +486,7 @@ export function getArtifactsToUpdate({ | |||||||
|             cmd.type === 'solid3d_get_next_adjacent_edge' |             cmd.type === 'solid3d_get_next_adjacent_edge' | ||||||
|               ? 'adjacent' |               ? 'adjacent' | ||||||
|               : 'opposite', |               : 'opposite', | ||||||
|  |           id: response.data.modeling_response.data.edge, | ||||||
|           segId: cmd.edge_id, |           segId: cmd.edge_id, | ||||||
|           sweepId: path.sweepId, |           sweepId: path.sweepId, | ||||||
|         }, |         }, | ||||||
| @ -468,6 +495,7 @@ export function getArtifactsToUpdate({ | |||||||
|         id: cmd.edge_id, |         id: cmd.edge_id, | ||||||
|         artifact: { |         artifact: { | ||||||
|           ...segment, |           ...segment, | ||||||
|  |           id: cmd.edge_id, | ||||||
|           edgeIds: [response.data.modeling_response.data.edge], |           edgeIds: [response.data.modeling_response.data.edge], | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| @ -475,6 +503,7 @@ export function getArtifactsToUpdate({ | |||||||
|         id: path.sweepId, |         id: path.sweepId, | ||||||
|         artifact: { |         artifact: { | ||||||
|           ...sweep, |           ...sweep, | ||||||
|  |           id: path.sweepId, | ||||||
|           edgeIds: [response.data.modeling_response.data.edge], |           edgeIds: [response.data.modeling_response.data.edge], | ||||||
|         }, |         }, | ||||||
|       }, |       }, | ||||||
| @ -484,6 +513,7 @@ export function getArtifactsToUpdate({ | |||||||
|       id, |       id, | ||||||
|       artifact: { |       artifact: { | ||||||
|         type: 'edgeCut', |         type: 'edgeCut', | ||||||
|  |         id, | ||||||
|         subType: cmd.cut_type, |         subType: cmd.cut_type, | ||||||
|         consumedEdgeId: cmd.edge_id, |         consumedEdgeId: cmd.edge_id, | ||||||
|         edgeIds: [], |         edgeIds: [], | ||||||
| @ -494,7 +524,7 @@ export function getArtifactsToUpdate({ | |||||||
|     const consumedEdge = getArtifact(cmd.edge_id) |     const consumedEdge = getArtifact(cmd.edge_id) | ||||||
|     if (consumedEdge?.type === 'segment') { |     if (consumedEdge?.type === 'segment') { | ||||||
|       returnArr.push({ |       returnArr.push({ | ||||||
|         id: cmd.edge_id, |         id: consumedEdge.id, | ||||||
|         artifact: { ...consumedEdge, edgeCutId: id }, |         artifact: { ...consumedEdge, edgeCutId: id }, | ||||||
|       }) |       }) | ||||||
|     } |     } | ||||||
| @ -574,6 +604,7 @@ export function expandPlane( | |||||||
|   ) |   ) | ||||||
|   return { |   return { | ||||||
|     type: 'plane', |     type: 'plane', | ||||||
|  |     id: plane.id, | ||||||
|     paths: Array.from(paths.values()), |     paths: Array.from(paths.values()), | ||||||
|     codeRef: plane.codeRef, |     codeRef: plane.codeRef, | ||||||
|   } |   } | ||||||
| @ -602,6 +633,7 @@ export function expandPath( | |||||||
|   if (err(plane)) return plane |   if (err(plane)) return plane | ||||||
|   return { |   return { | ||||||
|     type: 'path', |     type: 'path', | ||||||
|  |     id: path.id, | ||||||
|     segments: Array.from(segs.values()), |     segments: Array.from(segs.values()), | ||||||
|     sweep, |     sweep, | ||||||
|     plane, |     plane, | ||||||
| @ -629,6 +661,7 @@ export function expandSweep( | |||||||
|   return { |   return { | ||||||
|     type: 'sweep', |     type: 'sweep', | ||||||
|     subType: 'extrusion', |     subType: 'extrusion', | ||||||
|  |     id: sweep.id, | ||||||
|     surfaces: Array.from(surfs.values()), |     surfaces: Array.from(surfs.values()), | ||||||
|     edges: Array.from(edges.values()), |     edges: Array.from(edges.values()), | ||||||
|     path, |     path, | ||||||
| @ -664,6 +697,7 @@ export function expandSegment( | |||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     type: 'segment', |     type: 'segment', | ||||||
|  |     id: segment.id, | ||||||
|     path, |     path, | ||||||
|     surf, |     surf, | ||||||
|     edges: Array.from(edges.values()), |     edges: Array.from(edges.values()), | ||||||
| @ -785,3 +819,56 @@ export function getSweepFromSuspectedPath( | |||||||
|     artifactGraph |     artifactGraph | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Get the plane or face from a selection. | ||||||
|  |  * | ||||||
|  |  * TODO: Handle sketch on face. | ||||||
|  |  */ | ||||||
|  | export function getPlaneOrFaceFromSelection( | ||||||
|  |   id: ArtifactId, | ||||||
|  |   artifactGraph: ArtifactGraph | ||||||
|  | ): PlaneArtifactRich | null { | ||||||
|  |   const selection = artifactGraph.get(id) | ||||||
|  |   if (!selection) return null | ||||||
|  |   if (selection.type === 'solid2D') { | ||||||
|  |     const path = artifactGraph.get(selection.pathId) | ||||||
|  |     if (path?.type !== 'path') return null | ||||||
|  |     const plane = artifactGraph.get(path.planeId) | ||||||
|  |     if (plane?.type !== 'plane') return null | ||||||
|  |     return expandPlane(plane, artifactGraph) | ||||||
|  |   } else if (selection.type === 'wall' || selection.type === 'cap') { | ||||||
|  |     const sweep = artifactGraph.get(selection.sweepId) | ||||||
|  |     if (sweep?.type !== 'sweep') return null | ||||||
|  |     const path = artifactGraph.get(sweep.pathId) | ||||||
|  |     if (path?.type !== 'path') return null | ||||||
|  |     const plane = artifactGraph.get(path.planeId) | ||||||
|  |     // TODO: For sketch on face, this won't be a plane. | ||||||
|  |     if (plane?.type !== 'plane') return null | ||||||
|  |     return expandPlane(plane, artifactGraph) | ||||||
|  |   } | ||||||
|  |   return null | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Get the path from a selection. | ||||||
|  |  */ | ||||||
|  | export function getPathFromSelection( | ||||||
|  |   id: ArtifactId, | ||||||
|  |   artifactGraph: ArtifactGraph | ||||||
|  | ): PathArtifact | null { | ||||||
|  |   const selection = artifactGraph.get(id) | ||||||
|  |   if (!selection) return null | ||||||
|  |   if (selection.type === 'solid2D') { | ||||||
|  |     const path = artifactGraph.get(selection.pathId) | ||||||
|  |     if (path?.type !== 'path') return null | ||||||
|  |     return path | ||||||
|  |   } else if (selection.type === 'wall' || selection.type === 'cap') { | ||||||
|  |     const sweep = artifactGraph.get(selection.sweepId) | ||||||
|  |     if (sweep?.type !== 'sweep') return null | ||||||
|  |     const path = artifactGraph.get(sweep.pathId) | ||||||
|  |     if (path?.type !== 'path') return null | ||||||
|  |     return path | ||||||
|  |   } | ||||||
|  |   return null | ||||||
|  | } | ||||||
|  | |||||||
| @ -117,11 +117,11 @@ describe('testing changeSketchArguments', () => { | |||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) return ast |     if (err(ast)) return ast | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const sourceStart = code.indexOf(lineToChange) |     const sourceStart = code.indexOf(lineToChange) | ||||||
|     const changeSketchArgsRetVal = changeSketchArguments( |     const changeSketchArgsRetVal = changeSketchArguments( | ||||||
|       ast, |       ast, | ||||||
|       programMemory, |       execState.memory, | ||||||
|       { |       { | ||||||
|         type: 'sourceRange', |         type: 'sourceRange', | ||||||
|         sourceRange: [sourceStart, sourceStart + lineToChange.length], |         sourceRange: [sourceStart, sourceStart + lineToChange.length], | ||||||
| @ -150,12 +150,12 @@ const mySketch001 = startSketchOn('XY') | |||||||
|     const ast = parse(code) |     const ast = parse(code) | ||||||
|     if (err(ast)) return ast |     if (err(ast)) return ast | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const sourceStart = code.indexOf(lineToChange) |     const sourceStart = code.indexOf(lineToChange) | ||||||
|     expect(sourceStart).toBe(95) |     expect(sourceStart).toBe(95) | ||||||
|     const newSketchLnRetVal = addNewSketchLn({ |     const newSketchLnRetVal = addNewSketchLn({ | ||||||
|       node: ast, |       node: ast, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|       input: { |       input: { | ||||||
|         type: 'straight-segment', |         type: 'straight-segment', | ||||||
|         from: [0, 0], |         from: [0, 0], | ||||||
| @ -186,7 +186,7 @@ const mySketch001 = startSketchOn('XY') | |||||||
|  |  | ||||||
|     const modifiedAst2 = addCloseToPipe({ |     const modifiedAst2 = addCloseToPipe({ | ||||||
|       node: ast, |       node: ast, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|       pathToNode: [ |       pathToNode: [ | ||||||
|         ['body', ''], |         ['body', ''], | ||||||
|         [0, 'index'], |         [0, 'index'], | ||||||
| @ -230,7 +230,7 @@ describe('testing addTagForSketchOnFace', () => { | |||||||
|     const pathToNode = getNodePathFromSourceRange(ast, sourceRange) |     const pathToNode = getNodePathFromSourceRange(ast, sourceRange) | ||||||
|     const sketchOnFaceRetVal = addTagForSketchOnFace( |     const sketchOnFaceRetVal = addTagForSketchOnFace( | ||||||
|       { |       { | ||||||
|         // previousProgramMemory: programMemory, // redundant? |         // previousProgramMemory: execState.memory, // redundant? | ||||||
|         pathToNode, |         pathToNode, | ||||||
|         node: ast, |         node: ast, | ||||||
|       }, |       }, | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ async function testingSwapSketchFnCall({ | |||||||
|   const ast = parse(inputCode) |   const ast = parse(inputCode) | ||||||
|   if (err(ast)) return Promise.reject(ast) |   if (err(ast)) return Promise.reject(ast) | ||||||
|  |  | ||||||
|   const programMemory = await enginelessExecutor(ast) |   const execState = await enginelessExecutor(ast) | ||||||
|   const selections = { |   const selections = { | ||||||
|     codeBasedSelections: [range], |     codeBasedSelections: [range], | ||||||
|     otherSelections: [], |     otherSelections: [], | ||||||
| @ -51,7 +51,7 @@ async function testingSwapSketchFnCall({ | |||||||
|     return Promise.reject(new Error('transformInfos undefined')) |     return Promise.reject(new Error('transformInfos undefined')) | ||||||
|   const ast2 = transformAstSketchLines({ |   const ast2 = transformAstSketchLines({ | ||||||
|     ast, |     ast, | ||||||
|     programMemory, |     programMemory: execState.memory, | ||||||
|     selectionRanges: selections, |     selectionRanges: selections, | ||||||
|     transformInfos, |     transformInfos, | ||||||
|     referenceSegName: '', |     referenceSegName: '', | ||||||
| @ -366,10 +366,10 @@ const part001 = startSketchOn('XY') | |||||||
|   |> line([2.14, 1.35], %) // normal-segment |   |> line([2.14, 1.35], %) // normal-segment | ||||||
|   |> xLine(3.54, %)` |   |> xLine(3.54, %)` | ||||||
|   it('normal case works', async () => { |   it('normal case works', async () => { | ||||||
|     const programMemory = await enginelessExecutor(parse(code)) |     const execState = await enginelessExecutor(parse(code)) | ||||||
|     const index = code.indexOf('// normal-segment') - 7 |     const index = code.indexOf('// normal-segment') - 7 | ||||||
|     const sg = sketchGroupFromKclValue( |     const sg = sketchGroupFromKclValue( | ||||||
|       programMemory.get('part001'), |       execState.memory.get('part001'), | ||||||
|       'part001' |       'part001' | ||||||
|     ) as SketchGroup |     ) as SketchGroup | ||||||
|     const _segment = getSketchSegmentFromSourceRange(sg, [index, index]) |     const _segment = getSketchSegmentFromSourceRange(sg, [index, index]) | ||||||
| @ -383,11 +383,11 @@ const part001 = startSketchOn('XY') | |||||||
|     }) |     }) | ||||||
|   }) |   }) | ||||||
|   it('verify it works when the segment is in the `start` property', async () => { |   it('verify it works when the segment is in the `start` property', async () => { | ||||||
|     const programMemory = await enginelessExecutor(parse(code)) |     const execState = await enginelessExecutor(parse(code)) | ||||||
|     const index = code.indexOf('// segment-in-start') - 7 |     const index = code.indexOf('// segment-in-start') - 7 | ||||||
|     const _segment = getSketchSegmentFromSourceRange( |     const _segment = getSketchSegmentFromSourceRange( | ||||||
|       sketchGroupFromKclValue( |       sketchGroupFromKclValue( | ||||||
|         programMemory.get('part001'), |         execState.memory.get('part001'), | ||||||
|         'part001' |         'part001' | ||||||
|       ) as SketchGroup, |       ) as SketchGroup, | ||||||
|       [index, index] |       [index, index] | ||||||
|  | |||||||
| @ -220,7 +220,7 @@ const part001 = startSketchOn('XY') | |||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const transformInfos = getTransformInfos( |     const transformInfos = getTransformInfos( | ||||||
|       makeSelections(selectionRanges.slice(1)), |       makeSelections(selectionRanges.slice(1)), | ||||||
|       ast, |       ast, | ||||||
| @ -231,7 +231,7 @@ const part001 = startSketchOn('XY') | |||||||
|       ast, |       ast, | ||||||
|       selectionRanges: makeSelections(selectionRanges), |       selectionRanges: makeSelections(selectionRanges), | ||||||
|       transformInfos, |       transformInfos, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|     }) |     }) | ||||||
|     if (err(newAst)) return Promise.reject(newAst) |     if (err(newAst)) return Promise.reject(newAst) | ||||||
|  |  | ||||||
| @ -311,7 +311,7 @@ const part001 = startSketchOn('XY') | |||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const transformInfos = getTransformInfos( |     const transformInfos = getTransformInfos( | ||||||
|       makeSelections(selectionRanges), |       makeSelections(selectionRanges), | ||||||
|       ast, |       ast, | ||||||
| @ -322,7 +322,7 @@ const part001 = startSketchOn('XY') | |||||||
|       ast, |       ast, | ||||||
|       selectionRanges: makeSelections(selectionRanges), |       selectionRanges: makeSelections(selectionRanges), | ||||||
|       transformInfos, |       transformInfos, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|       referenceSegName: '', |       referenceSegName: '', | ||||||
|     }) |     }) | ||||||
|     if (err(newAst)) return Promise.reject(newAst) |     if (err(newAst)) return Promise.reject(newAst) | ||||||
| @ -373,7 +373,7 @@ const part001 = startSketchOn('XY') | |||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|  |  | ||||||
|     const programMemory = await enginelessExecutor(ast) |     const execState = await enginelessExecutor(ast) | ||||||
|     const transformInfos = getTransformInfos( |     const transformInfos = getTransformInfos( | ||||||
|       makeSelections(selectionRanges), |       makeSelections(selectionRanges), | ||||||
|       ast, |       ast, | ||||||
| @ -384,7 +384,7 @@ const part001 = startSketchOn('XY') | |||||||
|       ast, |       ast, | ||||||
|       selectionRanges: makeSelections(selectionRanges), |       selectionRanges: makeSelections(selectionRanges), | ||||||
|       transformInfos, |       transformInfos, | ||||||
|       programMemory, |       programMemory: execState.memory, | ||||||
|       referenceSegName: '', |       referenceSegName: '', | ||||||
|     }) |     }) | ||||||
|     if (err(newAst)) return Promise.reject(newAst) |     if (err(newAst)) return Promise.reject(newAst) | ||||||
| @ -470,7 +470,7 @@ async function helperThing( | |||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|  |  | ||||||
|   const programMemory = await enginelessExecutor(ast) |   const execState = await enginelessExecutor(ast) | ||||||
|   const transformInfos = getTransformInfos( |   const transformInfos = getTransformInfos( | ||||||
|     makeSelections(selectionRanges.slice(1)), |     makeSelections(selectionRanges.slice(1)), | ||||||
|     ast, |     ast, | ||||||
| @ -481,7 +481,7 @@ async function helperThing( | |||||||
|     ast, |     ast, | ||||||
|     selectionRanges: makeSelections(selectionRanges), |     selectionRanges: makeSelections(selectionRanges), | ||||||
|     transformInfos, |     transformInfos, | ||||||
|     programMemory, |     programMemory: execState.memory, | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   if (err(newAst)) return Promise.reject(newAst) |   if (err(newAst)) return Promise.reject(newAst) | ||||||
|  | |||||||
| @ -17,9 +17,9 @@ describe('testing angledLineThatIntersects', () => { | |||||||
|   offset: ${offset}, |   offset: ${offset}, | ||||||
| }, %, $yo2) | }, %, $yo2) | ||||||
| const intersect = segEndX(yo2)` | const intersect = segEndX(yo2)` | ||||||
|     const mem = await enginelessExecutor(parse(code('-1'))) |     const execState = await enginelessExecutor(parse(code('-1'))) | ||||||
|     expect(mem.get('intersect')?.value).toBe(1 + Math.sqrt(2)) |     expect(execState.memory.get('intersect')?.value).toBe(1 + Math.sqrt(2)) | ||||||
|     const noOffset = await enginelessExecutor(parse(code('0'))) |     const noOffset = await enginelessExecutor(parse(code('0'))) | ||||||
|     expect(noOffset.get('intersect')?.value).toBeCloseTo(1) |     expect(noOffset.memory.get('intersect')?.value).toBeCloseTo(1) | ||||||
|   }) |   }) | ||||||
| }) | }) | ||||||
|  | |||||||
| @ -37,6 +37,8 @@ import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' | |||||||
| import { DeepPartial } from 'lib/types' | import { DeepPartial } from 'lib/types' | ||||||
| import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration' | import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration' | ||||||
| import { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup' | import { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup' | ||||||
|  | import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId' | ||||||
|  | import { Artifact } from 'wasm-lib/kcl/bindings/Artifact' | ||||||
|  |  | ||||||
| export type { Program } from '../wasm-lib/kcl/bindings/Program' | export type { Program } from '../wasm-lib/kcl/bindings/Program' | ||||||
| export type { Expr } from '../wasm-lib/kcl/bindings/Expr' | export type { Expr } from '../wasm-lib/kcl/bindings/Expr' | ||||||
| @ -136,6 +138,34 @@ export const parse = (code: string | Error): Program | Error => { | |||||||
|  |  | ||||||
| export type PathToNode = [string | number, string][] | export type PathToNode = [string | number, string][] | ||||||
|  |  | ||||||
|  | interface RawExecState { | ||||||
|  |   memory: RawProgramMemory | ||||||
|  |   artifacts: { [key: ArtifactId]: Artifact } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export interface ExecState { | ||||||
|  |   memory: ProgramMemory | ||||||
|  |   artifacts: { [key: ArtifactId]: Artifact } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Create an empty ExecState.  This is useful on init to prevent needing an | ||||||
|  |  * Option. | ||||||
|  |  */ | ||||||
|  | export function emptyExecState(): ExecState { | ||||||
|  |   return { | ||||||
|  |     memory: ProgramMemory.empty(), | ||||||
|  |     artifacts: {}, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | function execStateFromRaw(raw: RawExecState): ExecState { | ||||||
|  |   return { | ||||||
|  |     memory: ProgramMemory.fromRaw(raw.memory), | ||||||
|  |     artifacts: raw.artifacts, | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| interface Memory { | interface Memory { | ||||||
|   [key: string]: KclValue |   [key: string]: KclValue | ||||||
| } | } | ||||||
| @ -353,12 +383,49 @@ export function sketchGroupFromKclValue( | |||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | export function optionalSketchGroupFromKclValue( | ||||||
|  |   value: KclValue | ||||||
|  | ): SketchGroup | null { | ||||||
|  |   if (value.type === 'UserVal' && value.value.type === 'SketchGroup') | ||||||
|  |     return value.value | ||||||
|  |   return null | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function kclValueFromArtifactId( | ||||||
|  |   execState: ExecState, | ||||||
|  |   id: ArtifactId | ||||||
|  | ): KclValue | null { | ||||||
|  |   const artifact = execState.artifacts[id] | ||||||
|  |   if (!artifact) { | ||||||
|  |     console.warn('kclValueFromArtifactId id not found', id, execState) | ||||||
|  |   } | ||||||
|  |   if (!artifact) return null | ||||||
|  |   return artifact.value | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function sketchGroupFromArtifactId( | ||||||
|  |   execState: ExecState, | ||||||
|  |   id: ArtifactId | ||||||
|  | ): SketchGroup | null { | ||||||
|  |   const kclValue = kclValueFromArtifactId(execState, id) | ||||||
|  |   if (!kclValue) { | ||||||
|  |     console.warn('sketchGroupFromArtifactId id not found', id) | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  |   const sketch = optionalSketchGroupFromKclValue(kclValue) | ||||||
|  |   if (!sketch) { | ||||||
|  |     console.warn('sketchGroupFromArtifactId not a SketchGroup', kclValue) | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  |   return sketch | ||||||
|  | } | ||||||
|  |  | ||||||
| export const executor = async ( | export const executor = async ( | ||||||
|   node: Program, |   node: Program, | ||||||
|   programMemory: ProgramMemory | Error = ProgramMemory.empty(), |   programMemory: ProgramMemory | Error = ProgramMemory.empty(), | ||||||
|   engineCommandManager: EngineCommandManager, |   engineCommandManager: EngineCommandManager, | ||||||
|   isMock: boolean = false |   isMock: boolean = false | ||||||
| ): Promise<ProgramMemory> => { | ): Promise<ExecState> => { | ||||||
|   if (err(programMemory)) return Promise.reject(programMemory) |   if (err(programMemory)) return Promise.reject(programMemory) | ||||||
|  |  | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-floating-promises |   // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||||||
| @ -380,7 +447,7 @@ export const _executor = async ( | |||||||
|   programMemory: ProgramMemory | Error = ProgramMemory.empty(), |   programMemory: ProgramMemory | Error = ProgramMemory.empty(), | ||||||
|   engineCommandManager: EngineCommandManager, |   engineCommandManager: EngineCommandManager, | ||||||
|   isMock: boolean |   isMock: boolean | ||||||
| ): Promise<ProgramMemory> => { | ): Promise<ExecState> => { | ||||||
|   if (err(programMemory)) return Promise.reject(programMemory) |   if (err(programMemory)) return Promise.reject(programMemory) | ||||||
|  |  | ||||||
|   try { |   try { | ||||||
| @ -392,7 +459,7 @@ export const _executor = async ( | |||||||
|       baseUnit = |       baseUnit = | ||||||
|         (await getSettingsState)()?.modeling.defaultUnit.current || 'mm' |         (await getSettingsState)()?.modeling.defaultUnit.current || 'mm' | ||||||
|     } |     } | ||||||
|     const memory: RawProgramMemory = await execute_wasm( |     const execState: RawExecState = await execute_wasm( | ||||||
|       JSON.stringify(node), |       JSON.stringify(node), | ||||||
|       JSON.stringify(programMemory.toRaw()), |       JSON.stringify(programMemory.toRaw()), | ||||||
|       baseUnit, |       baseUnit, | ||||||
| @ -400,7 +467,7 @@ export const _executor = async ( | |||||||
|       fileSystemManager, |       fileSystemManager, | ||||||
|       isMock |       isMock | ||||||
|     ) |     ) | ||||||
|     return ProgramMemory.fromRaw(memory) |     return execStateFromRaw(execState) | ||||||
|   } catch (e: any) { |   } catch (e: any) { | ||||||
|     console.log(e) |     console.log(e) | ||||||
|     const parsed: RustKclError = JSON.parse(e.toString()) |     const parsed: RustKclError = JSON.parse(e.toString()) | ||||||
|  | |||||||
| @ -58,10 +58,12 @@ export type Selection = | |||||||
|         | 'line' |         | 'line' | ||||||
|         | 'arc' |         | 'arc' | ||||||
|         | 'all' |         | 'all' | ||||||
|  |       artifactId?: ArtifactId | ||||||
|       range: SourceRange |       range: SourceRange | ||||||
|     } |     } | ||||||
|   | { |   | { | ||||||
|       type: 'opposite-edgeCut' | 'adjacent-edgeCut' | 'base-edgeCut' |       type: 'opposite-edgeCut' | 'adjacent-edgeCut' | 'base-edgeCut' | ||||||
|  |       artifactId?: ArtifactId | ||||||
|       range: SourceRange |       range: SourceRange | ||||||
|       // TODO this is a temporary measure that well be made redundant with: https://github.com/KittyCAD/modeling-app/pull/3836 |       // TODO this is a temporary measure that well be made redundant with: https://github.com/KittyCAD/modeling-app/pull/3836 | ||||||
|       secondaryRange: SourceRange |       secondaryRange: SourceRange | ||||||
| @ -108,7 +110,11 @@ export async function getEventForSelectWithPoint({ | |||||||
|       type: 'Set selection', |       type: 'Set selection', | ||||||
|       data: { |       data: { | ||||||
|         selectionType: 'singleCodeCursor', |         selectionType: 'singleCodeCursor', | ||||||
|         selection: { range: codeRef.range, type: 'solid2D' }, |         selection: { | ||||||
|  |           artifactId: data.entity_id, | ||||||
|  |           range: codeRef.range, | ||||||
|  |           type: 'solid2D', | ||||||
|  |         }, | ||||||
|       }, |       }, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -120,6 +126,7 @@ export async function getEventForSelectWithPoint({ | |||||||
|       data: { |       data: { | ||||||
|         selectionType: 'singleCodeCursor', |         selectionType: 'singleCodeCursor', | ||||||
|         selection: { |         selection: { | ||||||
|  |           artifactId: data.entity_id, | ||||||
|           range: codeRef.range, |           range: codeRef.range, | ||||||
|           type: _artifact?.subType === 'end' ? 'end-cap' : 'start-cap', |           type: _artifact?.subType === 'end' ? 'end-cap' : 'start-cap', | ||||||
|         }, |         }, | ||||||
| @ -136,7 +143,11 @@ export async function getEventForSelectWithPoint({ | |||||||
|       type: 'Set selection', |       type: 'Set selection', | ||||||
|       data: { |       data: { | ||||||
|         selectionType: 'singleCodeCursor', |         selectionType: 'singleCodeCursor', | ||||||
|         selection: { range: codeRef.range, type: 'extrude-wall' }, |         selection: { | ||||||
|  |           artifactId: data.entity_id, | ||||||
|  |           range: codeRef.range, | ||||||
|  |           type: 'extrude-wall', | ||||||
|  |         }, | ||||||
|       }, |       }, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -145,7 +156,11 @@ export async function getEventForSelectWithPoint({ | |||||||
|       type: 'Set selection', |       type: 'Set selection', | ||||||
|       data: { |       data: { | ||||||
|         selectionType: 'singleCodeCursor', |         selectionType: 'singleCodeCursor', | ||||||
|         selection: { range: _artifact.codeRef.range, type: 'default' }, |         selection: { | ||||||
|  |           artifactId: data.entity_id, | ||||||
|  |           range: _artifact.codeRef.range, | ||||||
|  |           type: 'default', | ||||||
|  |         }, | ||||||
|       }, |       }, | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -807,16 +822,18 @@ export function updateSelections( | |||||||
|         selection?.type === 'opposite-edgeCut' |         selection?.type === 'opposite-edgeCut' | ||||||
|       ) |       ) | ||||||
|         return { |         return { | ||||||
|  |           artifactId: selection?.artifactId, | ||||||
|           range: [node.start, node.end], |           range: [node.start, node.end], | ||||||
|           type: selection?.type, |           type: selection?.type, | ||||||
|           secondaryRange: selection?.secondaryRange, |           secondaryRange: selection?.secondaryRange, | ||||||
|         } |         } | ||||||
|       return { |       return { | ||||||
|  |         artifactId: selection?.artifactId, | ||||||
|         range: [node.start, node.end], |         range: [node.start, node.end], | ||||||
|         type: selection?.type, |         type: selection?.type, | ||||||
|       } |       } | ||||||
|     }) |     }) | ||||||
|     .filter((x?: Selection) => x !== undefined) as Selection[] |     .filter((x?: Selection) => x !== undefined) | ||||||
|  |  | ||||||
|   return { |   return { | ||||||
|     codeBasedSelections: |     codeBasedSelections: | ||||||
|  | |||||||
| @ -1,4 +1,10 @@ | |||||||
| import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm' | import { | ||||||
|  |   Program, | ||||||
|  |   ProgramMemory, | ||||||
|  |   _executor, | ||||||
|  |   SourceRange, | ||||||
|  |   ExecState, | ||||||
|  | } from '../lang/wasm' | ||||||
| import { | import { | ||||||
|   EngineCommandManager, |   EngineCommandManager, | ||||||
|   EngineCommandManagerEvents, |   EngineCommandManagerEvents, | ||||||
| @ -78,7 +84,7 @@ class MockEngineCommandManager { | |||||||
| export async function enginelessExecutor( | export async function enginelessExecutor( | ||||||
|   ast: Program | Error, |   ast: Program | Error, | ||||||
|   pm: ProgramMemory | Error = ProgramMemory.empty() |   pm: ProgramMemory | Error = ProgramMemory.empty() | ||||||
| ): Promise<ProgramMemory> { | ): Promise<ExecState> { | ||||||
|   if (err(ast)) return Promise.reject(ast) |   if (err(ast)) return Promise.reject(ast) | ||||||
|   if (err(pm)) return Promise.reject(pm) |   if (err(pm)) return Promise.reject(pm) | ||||||
|  |  | ||||||
| @ -88,15 +94,15 @@ export async function enginelessExecutor( | |||||||
|   }) as any as EngineCommandManager |   }) as any as EngineCommandManager | ||||||
|   // eslint-disable-next-line @typescript-eslint/no-floating-promises |   // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||||||
|   mockEngineCommandManager.startNewSession() |   mockEngineCommandManager.startNewSession() | ||||||
|   const programMemory = await _executor(ast, pm, mockEngineCommandManager, true) |   const execState = await _executor(ast, pm, mockEngineCommandManager, true) | ||||||
|   await mockEngineCommandManager.waitForAllCommands() |   await mockEngineCommandManager.waitForAllCommands() | ||||||
|   return programMemory |   return execState | ||||||
| } | } | ||||||
|  |  | ||||||
| export async function executor( | export async function executor( | ||||||
|   ast: Program, |   ast: Program, | ||||||
|   pm: ProgramMemory = ProgramMemory.empty() |   pm: ProgramMemory = ProgramMemory.empty() | ||||||
| ): Promise<ProgramMemory> { | ): Promise<ExecState> { | ||||||
|   const engineCommandManager = new EngineCommandManager() |   const engineCommandManager = new EngineCommandManager() | ||||||
|   engineCommandManager.start({ |   engineCommandManager.start({ | ||||||
|     setIsStreamReady: () => {}, |     setIsStreamReady: () => {}, | ||||||
| @ -117,14 +123,9 @@ export async function executor( | |||||||
|       toSync(async () => { |       toSync(async () => { | ||||||
|         // eslint-disable-next-line @typescript-eslint/no-floating-promises |         // eslint-disable-next-line @typescript-eslint/no-floating-promises | ||||||
|         engineCommandManager.startNewSession() |         engineCommandManager.startNewSession() | ||||||
|         const programMemory = await _executor( |         const execState = await _executor(ast, pm, engineCommandManager, false) | ||||||
|           ast, |  | ||||||
|           pm, |  | ||||||
|           engineCommandManager, |  | ||||||
|           false |  | ||||||
|         ) |  | ||||||
|         await engineCommandManager.waitForAllCommands() |         await engineCommandManager.waitForAllCommands() | ||||||
|         resolve(programMemory) |         resolve(execState) | ||||||
|       }, reportRejection) |       }, reportRejection) | ||||||
|     ) |     ) | ||||||
|   }) |   }) | ||||||
|  | |||||||
| @ -97,7 +97,7 @@ export function useCalculateKclExpression({ | |||||||
|         }) |         }) | ||||||
|         if (trap(error, { suppress: true })) return |         if (trap(error, { suppress: true })) return | ||||||
|       } |       } | ||||||
|       const { programMemory } = await executeAst({ |       const { execState } = await executeAst({ | ||||||
|         ast, |         ast, | ||||||
|         engineCommandManager, |         engineCommandManager, | ||||||
|         useFakeExecutor: true, |         useFakeExecutor: true, | ||||||
| @ -111,7 +111,7 @@ export function useCalculateKclExpression({ | |||||||
|       const init = |       const init = | ||||||
|         resultDeclaration?.type === 'VariableDeclaration' && |         resultDeclaration?.type === 'VariableDeclaration' && | ||||||
|         resultDeclaration?.declarations?.[0]?.init |         resultDeclaration?.declarations?.[0]?.init | ||||||
|       const result = programMemory?.get('__result__')?.value |       const result = execState.memory?.get('__result__')?.value | ||||||
|       setCalcResult(typeof result === 'number' ? String(result) : 'NAN') |       setCalcResult(typeof result === 'number' ? String(result) : 'NAN') | ||||||
|       init && setValueNode(init) |       init && setValueNode(init) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,5 +1,8 @@ | |||||||
| import { | import { | ||||||
|  |   Expr, | ||||||
|  |   ExpressionStatement, | ||||||
|   PathToNode, |   PathToNode, | ||||||
|  |   PipeExpression, | ||||||
|   VariableDeclaration, |   VariableDeclaration, | ||||||
|   VariableDeclarator, |   VariableDeclarator, | ||||||
|   parse, |   parse, | ||||||
| @ -2116,15 +2119,24 @@ export function isEditingExistingSketch({ | |||||||
|   // should check that the variable declaration is a pipeExpression |   // should check that the variable declaration is a pipeExpression | ||||||
|   // and that the pipeExpression contains a "startProfileAt" callExpression |   // and that the pipeExpression contains a "startProfileAt" callExpression | ||||||
|   if (!sketchDetails?.sketchPathToNode) return false |   if (!sketchDetails?.sketchPathToNode) return false | ||||||
|   const variableDeclaration = getNodeFromPath<VariableDeclarator>( |   const _node1 = getNodeFromPath<VariableDeclaration | ExpressionStatement>( | ||||||
|     kclManager.ast, |     kclManager.ast, | ||||||
|     sketchDetails.sketchPathToNode, |     sketchDetails.sketchPathToNode, | ||||||
|     'VariableDeclarator' |     ['VariableDeclaration', 'ExpressionStatement'] | ||||||
|   ) |   ) as { node: { type: string } } | Error | ||||||
|   if (err(variableDeclaration)) return false |   if (err(_node1)) return false | ||||||
|   if (variableDeclaration.node.type !== 'VariableDeclarator') return false |   let expr: Expr | ||||||
|   const pipeExpression = variableDeclaration.node.init |   if (_node1.node.type === 'VariableDeclaration') { | ||||||
|   if (pipeExpression.type !== 'PipeExpression') return false |     const varDecl = _node1.node as VariableDeclaration | ||||||
|  |     expr = varDecl.declarations[0].init | ||||||
|  |   } else if (_node1.node.type === 'ExpressionStatement') { | ||||||
|  |     const exprStatement = _node1.node as ExpressionStatement | ||||||
|  |     expr = exprStatement.expression | ||||||
|  |   } else { | ||||||
|  |     return false | ||||||
|  |   } | ||||||
|  |   if (expr.type !== 'PipeExpression') return false | ||||||
|  |   const pipeExpression: PipeExpression = expr | ||||||
|   const hasStartProfileAt = pipeExpression.body.some( |   const hasStartProfileAt = pipeExpression.body.some( | ||||||
|     (item) => |     (item) => | ||||||
|       item.type === 'CallExpression' && item.callee.name === 'startProfileAt' |       item.type === 'CallExpression' && item.callee.name === 'startProfileAt' | ||||||
|  | |||||||
							
								
								
									
										1
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										1
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							| @ -1430,6 +1430,7 @@ dependencies = [ | |||||||
|  "databake", |  "databake", | ||||||
|  "derive-docs", |  "derive-docs", | ||||||
|  "expectorate", |  "expectorate", | ||||||
|  |  "fnv", | ||||||
|  "form_urlencoded", |  "form_urlencoded", | ||||||
|  "futures", |  "futures", | ||||||
|  "git_rev", |  "git_rev", | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ convert_case = "0.6.0" | |||||||
| dashmap = "6.1.0" | dashmap = "6.1.0" | ||||||
| databake = { version = "0.1.8", features = ["derive"] } | databake = { version = "0.1.8", features = ["derive"] } | ||||||
| derive-docs = { version = "0.1.28", path = "../derive-docs" } | derive-docs = { version = "0.1.28", path = "../derive-docs" } | ||||||
|  | fnv = "1.0.7" | ||||||
| form_urlencoded = "1.2.1" | form_urlencoded = "1.2.1" | ||||||
| futures = { version = "0.3.30" } | futures = { version = "0.3.30" } | ||||||
| git_rev = "0.1.0" | git_rev = "0.1.0" | ||||||
|  | |||||||
| @ -4,6 +4,7 @@ use std::{collections::HashMap, sync::Arc}; | |||||||
|  |  | ||||||
| use anyhow::Result; | use anyhow::Result; | ||||||
| use async_recursion::async_recursion; | use async_recursion::async_recursion; | ||||||
|  | use fnv::FnvHashMap; | ||||||
| use kcmc::{ | use kcmc::{ | ||||||
|     each_cmd as mcmd, |     each_cmd as mcmd, | ||||||
|     ok_response::{output::TakeSnapshot, OkModelingCmdResponse}, |     ok_response::{output::TakeSnapshot, OkModelingCmdResponse}, | ||||||
| @ -45,6 +46,16 @@ pub struct ExecState { | |||||||
|     /// The current value of the pipe operator returned from the previous |     /// The current value of the pipe operator returned from the previous | ||||||
|     /// expression.  If we're not currently in a pipeline, this will be None. |     /// expression.  If we're not currently in a pipeline, this will be None. | ||||||
|     pub pipe_value: Option<KclValue>, |     pub pipe_value: Option<KclValue>, | ||||||
|  |     /// Artifacts created by the program.  It's safe to use a faster hash | ||||||
|  |     /// algorithm since all keys are UUIDs that we generate. | ||||||
|  |     pub artifacts: FnvHashMap<ArtifactId, Artifact>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl ExecState { | ||||||
|  |     /// Insert or update artifact by ID. | ||||||
|  |     pub(crate) fn put_artifact(&mut self, id: ArtifactId, value: KclValue) { | ||||||
|  |         self.artifacts.insert(id, Artifact { id, value }); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | ||||||
| @ -396,6 +407,21 @@ impl From<Vec<Box<ExtrudeGroup>>> for KclValue { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema)] | ||||||
|  | pub struct ArtifactId(uuid::Uuid); | ||||||
|  |  | ||||||
|  | impl ArtifactId { | ||||||
|  |     pub(crate) fn new(id: uuid::Uuid) -> Self { | ||||||
|  |         Self(id) | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] | ||||||
|  | pub struct Artifact { | ||||||
|  |     pub id: ArtifactId, | ||||||
|  |     pub value: KclValue, | ||||||
|  | } | ||||||
|  |  | ||||||
| /// A geometry. | /// A geometry. | ||||||
| #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] | ||||||
| #[ts(export)] | #[ts(export)] | ||||||
|  | |||||||
| @ -15,17 +15,17 @@ use uuid::Uuid; | |||||||
| use crate::{ | use crate::{ | ||||||
|     errors::{KclError, KclErrorDetails}, |     errors::{KclError, KclErrorDetails}, | ||||||
|     executor::{ |     executor::{ | ||||||
|         ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet, |         ArtifactId, ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, | ||||||
|         SketchSurface, |         SketchGroupSet, SketchSurface, | ||||||
|     }, |     }, | ||||||
|     std::Args, |     std::Args, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| /// Extrudes by a given amount. | /// Extrudes by a given amount. | ||||||
| pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||||
|     let (length, sketch_group_set) = args.get_number_sketch_group_set()?; |     let (length, sketch_group_set) = args.get_number_sketch_group_set()?; | ||||||
|  |  | ||||||
|     let result = inner_extrude(length, sketch_group_set, args).await?; |     let result = inner_extrude(length, sketch_group_set, exec_state, args).await?; | ||||||
|  |  | ||||||
|     Ok(result.into()) |     Ok(result.into()) | ||||||
| } | } | ||||||
| @ -79,7 +79,12 @@ pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result<KclValue | |||||||
| #[stdlib { | #[stdlib { | ||||||
|     name = "extrude" |     name = "extrude" | ||||||
| }] | }] | ||||||
| async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args) -> Result<ExtrudeGroupSet, KclError> { | async fn inner_extrude( | ||||||
|  |     length: f64, | ||||||
|  |     sketch_group_set: SketchGroupSet, | ||||||
|  |     exec_state: &mut ExecState, | ||||||
|  |     args: Args, | ||||||
|  | ) -> Result<ExtrudeGroupSet, KclError> { | ||||||
|     let id = uuid::Uuid::new_v4(); |     let id = uuid::Uuid::new_v4(); | ||||||
|  |  | ||||||
|     // Extrude the element(s). |     // Extrude the element(s). | ||||||
| @ -120,7 +125,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args | |||||||
|             ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), |             ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), | ||||||
|         ) |         ) | ||||||
|         .await?; |         .await?; | ||||||
|         extrude_groups.push(do_post_extrude(sketch_group.clone(), length, args.clone()).await?); |         extrude_groups.push(do_post_extrude(sketch_group.clone(), length, exec_state, args.clone()).await?); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     Ok(extrude_groups.into()) |     Ok(extrude_groups.into()) | ||||||
| @ -129,6 +134,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args | |||||||
| pub(crate) async fn do_post_extrude( | pub(crate) async fn do_post_extrude( | ||||||
|     sketch_group: SketchGroup, |     sketch_group: SketchGroup, | ||||||
|     length: f64, |     length: f64, | ||||||
|  |     exec_state: &mut ExecState, | ||||||
|     args: Args, |     args: Args, | ||||||
| ) -> Result<Box<ExtrudeGroup>, KclError> { | ) -> Result<Box<ExtrudeGroup>, KclError> { | ||||||
|     // Bring the object to the front of the scene. |     // Bring the object to the front of the scene. | ||||||
| @ -276,7 +282,7 @@ pub(crate) async fn do_post_extrude( | |||||||
|         }) |         }) | ||||||
|         .collect(); |         .collect(); | ||||||
|  |  | ||||||
|     Ok(Box::new(ExtrudeGroup { |     let extrude_group = Box::new(ExtrudeGroup { | ||||||
|         // Ok so you would think that the id would be the id of the extrude group, |         // Ok so you would think that the id would be the id of the extrude group, | ||||||
|         // that we passed in to the function, but it's actually the id of the |         // that we passed in to the function, but it's actually the id of the | ||||||
|         // sketch group. |         // sketch group. | ||||||
| @ -288,7 +294,13 @@ pub(crate) async fn do_post_extrude( | |||||||
|         start_cap_id, |         start_cap_id, | ||||||
|         end_cap_id, |         end_cap_id, | ||||||
|         edge_cuts: vec![], |         edge_cuts: vec![], | ||||||
|     })) |     }); | ||||||
|  |     exec_state.put_artifact( | ||||||
|  |         ArtifactId::new(extrude_group.id), | ||||||
|  |         KclValue::ExtrudeGroup(extrude_group.clone()), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|  |     Ok(extrude_group) | ||||||
| } | } | ||||||
|  |  | ||||||
| #[derive(Default)] | #[derive(Default)] | ||||||
|  | |||||||
| @ -50,10 +50,10 @@ impl Default for LoftData { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Create a 3D surface or solid by interpolating between two or more sketches. | /// Create a 3D surface or solid by interpolating between two or more sketches. | ||||||
| pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||||
|     let (sketch_groups, data): (Vec<SketchGroup>, Option<LoftData>) = args.get_sketch_groups_and_data()?; |     let (sketch_groups, data): (Vec<SketchGroup>, Option<LoftData>) = args.get_sketch_groups_and_data()?; | ||||||
|  |  | ||||||
|     let extrude_group = inner_loft(sketch_groups, data, args).await?; |     let extrude_group = inner_loft(sketch_groups, data, exec_state, args).await?; | ||||||
|     Ok(KclValue::ExtrudeGroup(extrude_group)) |     Ok(KclValue::ExtrudeGroup(extrude_group)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -138,6 +138,7 @@ pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K | |||||||
| async fn inner_loft( | async fn inner_loft( | ||||||
|     sketch_groups: Vec<SketchGroup>, |     sketch_groups: Vec<SketchGroup>, | ||||||
|     data: Option<LoftData>, |     data: Option<LoftData>, | ||||||
|  |     exec_state: &mut ExecState, | ||||||
|     args: Args, |     args: Args, | ||||||
| ) -> Result<Box<ExtrudeGroup>, KclError> { | ) -> Result<Box<ExtrudeGroup>, KclError> { | ||||||
|     // Make sure we have at least two sketches. |     // Make sure we have at least two sketches. | ||||||
| @ -170,5 +171,5 @@ async fn inner_loft( | |||||||
|     .await?; |     .await?; | ||||||
|  |  | ||||||
|     // Using the first sketch as the base curve, idk we might want to change this later. |     // Using the first sketch as the base curve, idk we might want to change this later. | ||||||
|     do_post_extrude(sketch_groups[0].clone(), 0.0, args).await |     do_post_extrude(sketch_groups[0].clone(), 0.0, exec_state, args).await | ||||||
| } | } | ||||||
|  | |||||||
| @ -295,7 +295,7 @@ async fn inner_revolve( | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     do_post_extrude(sketch_group, 0.0, args).await |     do_post_extrude(sketch_group, 0.0, exec_state, args).await | ||||||
| } | } | ||||||
|  |  | ||||||
| #[cfg(test)] | #[cfg(test)] | ||||||
|  | |||||||
| @ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; | |||||||
| use crate::{ | use crate::{ | ||||||
|     ast::types::TagDeclarator, |     ast::types::TagDeclarator, | ||||||
|     errors::KclError, |     errors::KclError, | ||||||
|     executor::{BasePath, ExecState, GeoMeta, KclValue, Path, SketchGroup, SketchSurface}, |     executor::{ArtifactId, BasePath, ExecState, GeoMeta, KclValue, Path, SketchGroup, SketchSurface}, | ||||||
|     std::Args, |     std::Args, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| @ -46,6 +46,11 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, | |||||||
|         args.get_circle_args()?; |         args.get_circle_args()?; | ||||||
|  |  | ||||||
|     let sketch_group = inner_circle(data, sketch_surface_or_group, tag, exec_state, args).await?; |     let sketch_group = inner_circle(data, sketch_surface_or_group, tag, exec_state, args).await?; | ||||||
|  |     exec_state.put_artifact( | ||||||
|  |         ArtifactId::new(sketch_group.id), | ||||||
|  |         KclValue::new_user_val(sketch_group.meta.clone(), sketch_group.clone()), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) |     Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | |||||||
| @ -16,8 +16,8 @@ use crate::{ | |||||||
|     ast::types::TagDeclarator, |     ast::types::TagDeclarator, | ||||||
|     errors::{KclError, KclErrorDetails}, |     errors::{KclError, KclErrorDetails}, | ||||||
|     executor::{ |     executor::{ | ||||||
|         BasePath, ExecState, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, Point3d, |         ArtifactId, BasePath, ExecState, ExtrudeGroup, Face, GeoMeta, KclValue, Path, Plane, PlaneType, Point2d, | ||||||
|         SketchGroup, SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal, |         Point3d, SketchGroup, SketchGroupSet, SketchSurface, TagEngineInfo, TagIdentifier, UserVal, | ||||||
|     }, |     }, | ||||||
|     std::{ |     std::{ | ||||||
|         utils::{ |         utils::{ | ||||||
| @ -93,11 +93,16 @@ pub enum StartOrEnd { | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Draw a line to a point. | /// Draw a line to a point. | ||||||
| pub async fn line_to(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | pub async fn line_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||||
|     let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) = |     let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) = | ||||||
|         args.get_data_and_sketch_group_and_tag()?; |         args.get_data_and_sketch_group_and_tag()?; | ||||||
|  |  | ||||||
|     let new_sketch_group = inner_line_to(to, sketch_group, tag, args).await?; |     let new_sketch_group = inner_line_to(to, sketch_group, tag, args).await?; | ||||||
|  |     exec_state.put_artifact( | ||||||
|  |         ArtifactId::new(new_sketch_group.id), | ||||||
|  |         KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group.clone()), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) |     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -248,11 +253,16 @@ async fn inner_y_line_to( | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Draw a line. | /// Draw a line. | ||||||
| pub async fn line(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||||
|     let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) = |     let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option<TagDeclarator>) = | ||||||
|         args.get_data_and_sketch_group_and_tag()?; |         args.get_data_and_sketch_group_and_tag()?; | ||||||
|  |  | ||||||
|     let new_sketch_group = inner_line(delta, sketch_group, tag, args).await?; |     let new_sketch_group = inner_line(delta, sketch_group, tag, args).await?; | ||||||
|  |     exec_state.put_artifact( | ||||||
|  |         ArtifactId::new(new_sketch_group.id), | ||||||
|  |         KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group.clone()), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) |     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -1410,10 +1420,14 @@ pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2], | |||||||
| } | } | ||||||
|  |  | ||||||
| /// Close the current sketch. | /// Close the current sketch. | ||||||
| pub async fn close(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> { | ||||||
|     let (sketch_group, tag): (SketchGroup, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?; |     let (sketch_group, tag): (SketchGroup, Option<TagDeclarator>) = args.get_sketch_group_and_optional_tag()?; | ||||||
|  |  | ||||||
|     let new_sketch_group = inner_close(sketch_group, tag, args).await?; |     let new_sketch_group = inner_close(sketch_group, tag, args).await?; | ||||||
|  |     exec_state.put_artifact( | ||||||
|  |         ArtifactId::new(new_sketch_group.id), | ||||||
|  |         KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group.clone()), | ||||||
|  |     ); | ||||||
|  |  | ||||||
|     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) |     Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) | ||||||
| } | } | ||||||
|  | |||||||
| @ -59,7 +59,7 @@ pub async fn execute_wasm( | |||||||
|     // gloo-serialize crate instead. |     // gloo-serialize crate instead. | ||||||
|     // DO NOT USE serde_wasm_bindgen::to_value(&memory).map_err(|e| e.to_string()) |     // DO NOT USE serde_wasm_bindgen::to_value(&memory).map_err(|e| e.to_string()) | ||||||
|     // it will break the frontend. |     // it will break the frontend. | ||||||
|     JsValue::from_serde(&exec_state.memory).map_err(|e| e.to_string()) |     JsValue::from_serde(&exec_state).map_err(|e| e.to_string()) | ||||||
| } | } | ||||||
|  |  | ||||||
| // wasm_bindgen wrapper for execute | // wasm_bindgen wrapper for execute | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	