diff --git a/src/clientSideScene/sceneEntities.ts b/src/clientSideScene/sceneEntities.ts index e454c32971..957698905e 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -50,6 +50,7 @@ import { ExtrudeGroup, VariableDeclaration, VariableDeclarator, + sketchGroupFromKclValue, } from 'lang/wasm' import { engineCommandManager, @@ -588,10 +589,12 @@ export class SceneEntities { const variableDeclarationName = _node1.node?.declarations?.[0]?.id?.name || '' - const sg = kclManager.programMemory.get( + const sg = sketchGroupFromKclValue( + kclManager.programMemory.get(variableDeclarationName), variableDeclarationName - ) as SketchGroup - const lastSeg = sg?.value?.slice(-1)[0] || sg.start + ) + if (err(sg)) return sg + const lastSeg = sg.value?.slice(-1)[0] || sg.start const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1` @@ -786,9 +789,11 @@ export class SceneEntities { programMemoryOverride, }) this.sceneProgramMemory = programMemory - const sketchGroup = programMemory.get( + const sketchGroup = sketchGroupFromKclValue( + programMemory.get(variableDeclarationName), variableDeclarationName - ) as SketchGroup + ) + if (err(sketchGroup)) return sketchGroup const sgPaths = sketchGroup.value const orthoFactor = orthoScale(sceneInfra.camControls.camera) @@ -840,9 +845,11 @@ export class SceneEntities { // Prepare to update the THREEjs scene this.sceneProgramMemory = programMemory - const sketchGroup = programMemory.get( + const sketchGroup = sketchGroupFromKclValue( + programMemory.get(variableDeclarationName), variableDeclarationName - ) as SketchGroup + ) + if (err(sketchGroup)) return sketchGroup const sgPaths = sketchGroup.value const orthoFactor = orthoScale(sceneInfra.camControls.camera) @@ -1111,8 +1118,12 @@ export class SceneEntities { const maybeSketchGroup = programMemory.get(variableDeclarationName) let sketchGroup = undefined - if (maybeSketchGroup?.type === 'SketchGroup') { - sketchGroup = maybeSketchGroup + const sg = sketchGroupFromKclValue( + maybeSketchGroup, + variableDeclarationName + ) + if (!err(sg)) { + sketchGroup = sg } else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) { sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup } @@ -1656,9 +1667,12 @@ function prepareTruncatedMemoryAndAst( ) if (err(_node)) return _node const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || '' - const lastSeg = ( - programMemory.get(variableDeclarationName) as SketchGroup - ).value.slice(-1)[0] + const sg = sketchGroupFromKclValue( + programMemory.get(variableDeclarationName), + variableDeclarationName + ) + if (err(sg)) return sg + const lastSeg = sg?.value.slice(-1)[0] if (draftSegment) { // truncatedAst needs to setup with another segment at the end let newSegment @@ -1782,10 +1796,11 @@ export function sketchGroupFromPathToNode({ if (result?.type === 'ExtrudeGroup') { return result.sketchGroup } - if (result?.type === 'SketchGroup') { - return result + const sg = sketchGroupFromKclValue(result, varDec?.id?.name) + if (err(sg)) { + return null } - return null + return sg } function colorSegment(object: any, color: number) { diff --git a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx index e4d0e3b1c3..efb52e533b 100644 --- a/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx +++ b/src/components/ModelingSidebar/ModelingPanes/MemoryPane.tsx @@ -1,11 +1,16 @@ import toast from 'react-hot-toast' import ReactJson from 'react-json-view' import { useMemo } from 'react' -import { ProgramMemory, Path, ExtrudeSurface } from 'lang/wasm' +import { + ProgramMemory, + Path, + ExtrudeSurface, + sketchGroupFromKclValue, +} from 'lang/wasm' import { useKclContext } from 'lang/KclProvider' import { useResolvedTheme } from 'hooks/useResolvedTheme' import { ActionButton } from 'components/ActionButton' -import { trap } from 'lib/trap' +import { err, trap } from 'lib/trap' import Tooltip from 'components/Tooltip' import { useModelingContext } from 'hooks/useModelingContext' @@ -84,8 +89,9 @@ export const processMemory = (programMemory: ProgramMemory) => { const processedMemory: any = {} for (const [key, val] of programMemory?.visibleEntries()) { if (typeof val.value !== 'function') { - if (val.type === 'SketchGroup') { - processedMemory[key] = val.value.map(({ __geoMeta, ...rest }: Path) => { + const sg = sketchGroupFromKclValue(val, null) + if (!err(sg)) { + processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => { return rest }) } else if (val.type === 'ExtrudeGroup') { diff --git a/src/lang/artifact.test.ts b/src/lang/artifact.test.ts index 6776988009..e10498323b 100644 --- a/src/lang/artifact.test.ts +++ b/src/lang/artifact.test.ts @@ -18,41 +18,45 @@ const mySketch001 = startSketchOn('XY') // @ts-ignore const sketch001 = programMemory?.get('mySketch001') expect(sketch001).toEqual({ - type: 'SketchGroup', - on: expect.any(Object), - start: { - to: [0, 0], - from: [0, 0], - tag: null, - __geoMeta: { - id: expect.any(String), - sourceRange: [46, 71], - }, - }, - value: [ - { - type: 'ToPoint', - tag: null, - to: [-1.59, -1.54], + type: 'UserVal', + __meta: [{ sourceRange: [46, 71] }], + value: { + type: 'SketchGroup', + on: expect.any(Object), + start: { + to: [0, 0], from: [0, 0], - __geoMeta: { - sourceRange: [77, 102], - id: expect.any(String), - }, - }, - { - type: 'ToPoint', - to: [0.46, -5.82], - from: [-1.59, -1.54], tag: null, __geoMeta: { - sourceRange: [108, 132], id: expect.any(String), + sourceRange: [46, 71], }, }, - ], - id: expect.any(String), - __meta: [{ sourceRange: [46, 71] }], + value: [ + { + type: 'ToPoint', + tag: null, + to: [-1.59, -1.54], + from: [0, 0], + __geoMeta: { + sourceRange: [77, 102], + id: expect.any(String), + }, + }, + { + type: 'ToPoint', + to: [0.46, -5.82], + from: [-1.59, -1.54], + tag: null, + __geoMeta: { + sourceRange: [108, 132], + id: expect.any(String), + }, + }, + ], + id: expect.any(String), + __meta: [{ sourceRange: [46, 71] }], + }, }) }) test('extrude artifacts', async () => { diff --git a/src/lang/executor.test.ts b/src/lang/executor.test.ts index ffd5a3988f..a07c8cb2fa 100644 --- a/src/lang/executor.test.ts +++ b/src/lang/executor.test.ts @@ -1,6 +1,12 @@ import fs from 'node:fs' -import { parse, ProgramMemory, SketchGroup, initPromise } from './wasm' +import { + parse, + ProgramMemory, + SketchGroup, + initPromise, + sketchGroupFromKclValue, +} from './wasm' import { enginelessExecutor } from '../lib/testHelpers' import { KCLError } from './errors' @@ -52,7 +58,7 @@ const newVar = myVar + 1` ` const mem = await exe(code) // geo is three js buffer geometry and is very bloated to have in tests - const minusGeo = mem.get('mySketch')?.value + const minusGeo = mem.get('mySketch')?.value?.value expect(minusGeo).toEqual([ { type: 'ToPoint', @@ -146,68 +152,72 @@ const newVar = myVar + 1` ].join('\n') const mem = await exe(code) expect(mem.get('mySk1')).toEqual({ - type: 'SketchGroup', - on: expect.any(Object), - start: { - to: [0, 0], - from: [0, 0], - tag: null, - __geoMeta: { - id: expect.any(String), - sourceRange: [39, 63], - }, - }, - tags: { - myPath: { - __meta: [ - { - sourceRange: [109, 116], - }, - ], - type: 'TagIdentifier', - value: 'myPath', - info: expect.any(Object), - }, - }, - value: [ - { - type: 'ToPoint', - to: [1, 1], + type: 'UserVal', + value: { + type: 'SketchGroup', + on: expect.any(Object), + start: { + to: [0, 0], from: [0, 0], tag: null, __geoMeta: { - sourceRange: [69, 85], id: expect.any(String), + sourceRange: [39, 63], }, }, - { - type: 'ToPoint', - to: [0, 1], - from: [1, 1], - __geoMeta: { - sourceRange: [91, 117], - id: expect.any(String), - }, - tag: { - end: 116, - start: 109, - type: 'TagDeclarator', + tags: { + myPath: { + __meta: [ + { + sourceRange: [109, 116], + }, + ], + type: 'TagIdentifier', value: 'myPath', - digest: null, + info: expect.any(Object), }, }, - { - type: 'ToPoint', - to: [1, 1], - from: [0, 1], - tag: null, - __geoMeta: { - sourceRange: [123, 139], - id: expect.any(String), + value: [ + { + type: 'ToPoint', + to: [1, 1], + from: [0, 0], + tag: null, + __geoMeta: { + sourceRange: [69, 85], + id: expect.any(String), + }, }, - }, - ], - id: expect.any(String), + { + type: 'ToPoint', + to: [0, 1], + from: [1, 1], + __geoMeta: { + sourceRange: [91, 117], + id: expect.any(String), + }, + tag: { + end: 116, + start: 109, + type: 'TagDeclarator', + value: 'myPath', + digest: null, + }, + }, + { + type: 'ToPoint', + to: [1, 1], + from: [0, 1], + tag: null, + __geoMeta: { + sourceRange: [123, 139], + id: expect.any(String), + }, + }, + ], + id: expect.any(String), + __meta: [{ sourceRange: [39, 63] }], + }, __meta: [{ sourceRange: [39, 63] }], }) }) @@ -358,7 +368,7 @@ describe('testing math operators', () => { '|> line([-2.21, -legLen(5, min(3, 999))], %)', ].join('\n') const mem = await exe(code) - const sketch = mem.get('part001') + const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001') // result of `-legLen(5, min(3, 999))` should be -4 const yVal = (sketch as SketchGroup).value?.[0]?.to?.[1] expect(yVal).toBe(-4) @@ -376,7 +386,7 @@ describe('testing math operators', () => { ``, ].join('\n') const mem = await exe(code) - const sketch = mem.get('part001') + const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001') // expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0 expect((sketch as SketchGroup).value?.[1]?.from).toEqual([3, 4]) expect((sketch as SketchGroup).value?.[1]?.to).toEqual([6, 0]) @@ -385,7 +395,10 @@ describe('testing math operators', () => { `legLen(segLen(seg01), myVar)` ) const removedUnaryExpMem = await exe(removedUnaryExp) - const removedUnaryExpMemSketch = removedUnaryExpMem.get('part001') + const removedUnaryExpMemSketch = sketchGroupFromKclValue( + removedUnaryExpMem.get('part001'), + 'part001' + ) // without the minus sign, the y value should be 8 expect((removedUnaryExpMemSketch as SketchGroup).value?.[1]?.to).toEqual([ diff --git a/src/lang/modifyAst.ts b/src/lang/modifyAst.ts index 5d33b075c4..198493f629 100644 --- a/src/lang/modifyAst.ts +++ b/src/lang/modifyAst.ts @@ -17,7 +17,7 @@ import { PathToNode, ProgramMemory, SourceRange, - SketchGroup, + sketchGroupFromKclValue, } from './wasm' import { isNodeSafeToReplacePath, @@ -981,7 +981,11 @@ export async function deleteFromSelection( if (err(parent)) { return } - const sketchToPreserve = programMemory.get(sketchName) as SketchGroup + const sketchToPreserve = sketchGroupFromKclValue( + programMemory.get(sketchName), + sketchName + ) + if (err(sketchToPreserve)) return sketchToPreserve console.log('sketchName', sketchName) // Can't kick off multiple requests at once as getFaceDetails // is three engine calls in one and they conflict diff --git a/src/lang/queryAst.ts b/src/lang/queryAst.ts index da797ded57..236b21a501 100644 --- a/src/lang/queryAst.ts +++ b/src/lang/queryAst.ts @@ -10,12 +10,12 @@ import { Program, ProgramMemory, ReturnStatement, - SketchGroup, SourceRange, SyntaxType, Expr, VariableDeclaration, VariableDeclarator, + sketchGroupFromKclValue, } from './wasm' import { createIdentifier, splitPathAtLastIndex } from './modifyAst' import { getSketchSegmentFromSourceRange } from './std/sketchConstraints' @@ -661,15 +661,16 @@ export function isLinesParallelAndConstrained( if (err(_varDec)) return _varDec const varDec = _varDec.node const varName = (varDec as VariableDeclaration)?.declarations[0]?.id?.name - const path = programMemory?.get(varName) as SketchGroup + const sg = sketchGroupFromKclValue(programMemory?.get(varName), varName) + if (err(sg)) return sg const _primarySegment = getSketchSegmentFromSourceRange( - path, + sg, primaryLine.range ) if (err(_primarySegment)) return _primarySegment const primarySegment = _primarySegment.segment - const _segment = getSketchSegmentFromSourceRange(path, secondaryLine.range) + const _segment = getSketchSegmentFromSourceRange(sg, secondaryLine.range) if (err(_segment)) return _segment const { segment: secondarySegment, index: secondaryIndex } = _segment const primaryAngle = getAngle(primarySegment.from, primarySegment.to) @@ -708,9 +709,7 @@ export function isLinesParallelAndConstrained( constraintType === 'angle' || constraintLevel === 'full' // get the previous segment - const prevSegment = (programMemory.get(varName) as SketchGroup).value[ - secondaryIndex - 1 - ] + const prevSegment = sg.value[secondaryIndex - 1] const prevSourceRange = prevSegment.__geoMeta.sourceRange const isParallelAndConstrained = @@ -779,7 +778,10 @@ export function hasExtrudeSketchGroup({ if (varDec.type !== 'VariableDeclaration') return false const varName = varDec.declarations[0].id.name const varValue = programMemory?.get(varName) - return varValue?.type === 'ExtrudeGroup' || varValue?.type === 'SketchGroup' + return ( + varValue?.type === 'ExtrudeGroup' || + !err(sketchGroupFromKclValue(varValue, varName)) + ) } export function isSingleCursorInPipe( diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index 9115f6ce9a..068c2fd63a 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -12,6 +12,7 @@ import { Literal, VariableDeclaration, Identifier, + sketchGroupFromKclValue, } from 'lang/wasm' import { getNodeFromPath, @@ -1009,9 +1010,12 @@ export const angledLineOfXLength: SketchLineHelper = { const { node: varDec } = nodeMeta2 const variableName = varDec.id.name - const sketch = previousProgramMemory?.get(variableName) - if (!sketch || sketch.type !== 'SketchGroup') { - return new Error('not a SketchGroup') + const sketch = sketchGroupFromKclValue( + previousProgramMemory?.get(variableName), + variableName + ) + if (err(sketch)) { + return sketch } const angle = createLiteral(roundOff(getAngle(from, to), 0)) const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1) @@ -1105,10 +1109,11 @@ export const angledLineOfYLength: SketchLineHelper = { if (err(nodeMeta2)) return nodeMeta2 const { node: varDec } = nodeMeta2 const variableName = varDec.id.name - const sketch = previousProgramMemory?.get(variableName) - if (!sketch || sketch.type !== 'SketchGroup') { - return new Error('not a SketchGroup') - } + const sketch = sketchGroupFromKclValue( + previousProgramMemory?.get(variableName), + variableName + ) + if (err(sketch)) return sketch const angle = createLiteral(roundOff(getAngle(from, to), 0)) const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1) @@ -1443,7 +1448,11 @@ export const angledLineThatIntersects: SketchLineHelper = { const { node: varDec } = nodeMeta2 const varName = varDec.declarations[0].id.name - const sketchGroup = previousProgramMemory.get(varName) as SketchGroup + const sketchGroup = sketchGroupFromKclValue( + previousProgramMemory.get(varName), + varName + ) + if (err(sketchGroup)) return sketchGroup const intersectPath = sketchGroup.value.find( ({ tag }: Path) => tag && tag.value === intersectTagName ) diff --git a/src/lang/std/sketchConstraints.test.ts b/src/lang/std/sketchConstraints.test.ts index 0fb2099a02..d5694d1f45 100644 --- a/src/lang/std/sketchConstraints.test.ts +++ b/src/lang/std/sketchConstraints.test.ts @@ -1,4 +1,10 @@ -import { parse, SketchGroup, recast, initPromise } from '../wasm' +import { + parse, + SketchGroup, + recast, + initPromise, + sketchGroupFromKclValue, +} from '../wasm' import { ConstraintType, getTransformInfos, @@ -362,10 +368,11 @@ const part001 = startSketchOn('XY') it('normal case works', async () => { const programMemory = await enginelessExecutor(parse(code)) const index = code.indexOf('// normal-segment') - 7 - const _segment = getSketchSegmentFromSourceRange( - programMemory.get('part001') as SketchGroup, - [index, index] - ) + const sg = sketchGroupFromKclValue( + programMemory.get('part001'), + 'part001' + ) as SketchGroup + const _segment = getSketchSegmentFromSourceRange(sg, [index, index]) if (err(_segment)) throw _segment const { __geoMeta, ...segment } = _segment.segment expect(segment).toEqual({ @@ -379,7 +386,10 @@ const part001 = startSketchOn('XY') const programMemory = await enginelessExecutor(parse(code)) const index = code.indexOf('// segment-in-start') - 7 const _segment = getSketchSegmentFromSourceRange( - programMemory.get('part001') as SketchGroup, + sketchGroupFromKclValue( + programMemory.get('part001'), + 'part001' + ) as SketchGroup, [index, index] ) if (err(_segment)) throw _segment diff --git a/src/lang/std/sketchcombos.ts b/src/lang/std/sketchcombos.ts index 8042797891..bfe12a6ff2 100644 --- a/src/lang/std/sketchcombos.ts +++ b/src/lang/std/sketchcombos.ts @@ -10,6 +10,7 @@ import { VariableDeclarator, PathToNode, ProgramMemory, + sketchGroupFromKclValue, } from '../wasm' import { getNodeFromPath, @@ -1636,12 +1637,16 @@ export function transformAstSketchLines({ }) const varName = varDec.node.id.name - let sketchGroup = programMemory.get(varName) - if (sketchGroup?.type === 'ExtrudeGroup') { - sketchGroup = sketchGroup.sketchGroup + let kclVal = programMemory.get(varName) + let sketchGroup + if (kclVal?.type === 'ExtrudeGroup') { + sketchGroup = kclVal.sketchGroup + } else { + sketchGroup = sketchGroupFromKclValue(kclVal, varName) + if (err(sketchGroup)) { + return + } } - if (!sketchGroup || sketchGroup.type !== 'SketchGroup') - return new Error('not a sketch group') const segMeta = getSketchSegmentFromPathToNode( sketchGroup, ast, diff --git a/src/lang/wasm.ts b/src/lang/wasm.ts index 986d68a5c5..609c6c897b 100644 --- a/src/lang/wasm.ts +++ b/src/lang/wasm.ts @@ -38,6 +38,7 @@ import { err } from 'lib/trap' import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' import { DeepPartial } from 'lib/types' import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration' +import { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup' export type { Program } from '../wasm-lib/kcl/bindings/Program' export type { Expr } from '../wasm-lib/kcl/bindings/Expr' @@ -126,6 +127,7 @@ export const parse = (code: string | Error): Program | Error => { const program: Program = parse_wasm(code) return program } catch (e: any) { + // throw e const parsed: RustKclError = JSON.parse(e.toString()) return new KCLError( parsed.kind, @@ -312,7 +314,7 @@ export class ProgramMemory { */ hasSketchOrExtrudeGroup(): boolean { for (const node of this.visibleEntries().values()) { - if (node.type === 'ExtrudeGroup' || node.type === 'SketchGroup') { + if (node.type === 'ExtrudeGroup' || node.value?.type === 'SketchGroup') { return true } } @@ -332,6 +334,25 @@ export class ProgramMemory { } } +// TODO: In the future, make the parameter be a KclValue. +export function sketchGroupFromKclValue( + obj: any, + varName: string | null +): SketchGroup | Error { + if (obj?.value?.type === 'SketchGroup') return obj.value + if (!varName) { + varName = 'a KCL value' + } + const actualType = obj?.value?.type ?? obj?.type + if (actualType) { + return new Error( + `Expected ${varName} to be a sketchGroup, but it was ${actualType} instead.` + ) + } else { + return new Error(`Expected ${varName} to be a sketchGroup, but it wasn't.`) + } +} + export const executor = async ( node: Program, programMemory: ProgramMemory | Error = ProgramMemory.empty(), diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index 6afa9074e1..4e3026dfc3 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -4,6 +4,7 @@ import { VariableDeclarator, parse, recast, + sketchGroupFromKclValue, } from 'lang/wasm' import { Axis, Selection, Selections, updateSelections } from 'lib/selections' import { assign, createMachine } from 'xstate' @@ -1187,8 +1188,11 @@ export const modelingMachine = createMachine( ) if (err(varDecNode)) return const sketchVar = varDecNode.node.declarations[0].id.name - const sketchGroup = kclManager.programMemory.get(sketchVar) - if (sketchGroup?.type !== 'SketchGroup') return + const sketchGroup = sketchGroupFromKclValue( + kclManager.programMemory.get(sketchVar), + sketchVar + ) + if (trap(sketchGroup)) return const extrusion = getExtrusionFromSuspectedPath( sketchGroup.id, engineCommandManager.artifactGraph diff --git a/src/wasm-lib/kcl/src/ast/types.rs b/src/wasm-lib/kcl/src/ast/types.rs index 0ed1afe463..35f4833ea2 100644 --- a/src/wasm-lib/kcl/src/ast/types.rs +++ b/src/wasm-lib/kcl/src/ast/types.rs @@ -23,7 +23,7 @@ use crate::{ docs::StdLibFn, errors::{KclError, KclErrorDetails}, executor::{ - BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SourceRange, + BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SketchGroup, SourceRange, StatementKind, TagEngineInfo, TagIdentifier, UserVal, }, parser::PIPE_OPERATOR, @@ -1377,10 +1377,13 @@ impl CallExpression { // TODO: This could probably be done in a better way, but as of now this was my only idea // and it works. match result { - KclValue::SketchGroup(ref sketch_group) => { - for (_, tag) in sketch_group.tags.iter() { - memory.update_tag(&tag.value, tag.clone())?; - } + KclValue::UserVal(ref mut uval) => { + uval.mutate(|sketch_group: &mut SketchGroup| { + for (_, tag) in sketch_group.tags.iter() { + memory.update_tag(&tag.value, tag.clone())?; + } + Ok::<_, KclError>(()) + })?; } KclValue::ExtrudeGroup(ref mut extrude_group) => { for value in &extrude_group.value { diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 9f44172198..8bacea1779 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -203,13 +203,22 @@ impl Environment { } for (_, val) in self.bindings.iter_mut() { - if let KclValue::SketchGroup(ref mut sketch_group) = val { - if sketch_group.original_id == sg.original_id { - for tag in sg.tags.iter() { - sketch_group.tags.insert(tag.0.clone(), tag.1.clone()); - } + let KclValue::UserVal(v) = val else { continue }; + let meta = v.meta.clone(); + let maybe_sg: Result = serde_json::from_value(v.value.clone()); + let Ok(mut sketch_group) = maybe_sg else { + continue; + }; + + if sketch_group.original_id == sg.original_id { + for tag in sg.tags.iter() { + sketch_group.tags.insert(tag.0.clone(), tag.1.clone()); } } + *val = KclValue::UserVal(UserVal { + meta, + value: serde_json::to_value(sketch_group).expect("can always turn SketchGroup into JSON"), + }); } } } @@ -268,10 +277,7 @@ pub enum KclValue { TagDeclarator(Box), Plane(Box), Face(Box), - SketchGroup(Box), - SketchGroups { - value: Vec>, - }, + ExtrudeGroup(Box), ExtrudeGroups { value: Vec>, @@ -289,27 +295,8 @@ pub enum KclValue { } impl KclValue { - pub(crate) fn get_sketch_group_set(&self) -> Result { - match self { - KclValue::SketchGroup(s) => Ok(SketchGroupSet::SketchGroup(s.clone())), - KclValue::SketchGroups { value } => Ok(SketchGroupSet::SketchGroups(value.clone())), - KclValue::UserVal(value) => { - let value = value.value.clone(); - match value { - JValue::Null | JValue::Bool(_) | JValue::Number(_) | JValue::String(_) => Err(anyhow::anyhow!( - "Failed to deserialize sketch group set from JSON {}", - human_friendly_type(&value) - )), - JValue::Array(_) => serde_json::from_value::>>(value) - .map(SketchGroupSet::from) - .map_err(|e| anyhow::anyhow!("Failed to deserialize array of sketch groups from JSON: {}", e)), - JValue::Object(_) => serde_json::from_value::>(value) - .map(SketchGroupSet::from) - .map_err(|e| anyhow::anyhow!("Failed to deserialize sketch group from JSON: {}", e)), - } - } - _ => anyhow::bail!("Not a sketch group or sketch groups: {:?}", self), - } + pub(crate) fn new_user_val(meta: Vec, val: T) -> Self { + Self::UserVal(UserVal::set(meta, val)) } pub(crate) fn get_extrude_group_set(&self) -> Result { @@ -342,8 +329,6 @@ impl KclValue { KclValue::UserVal(u) => human_friendly_type(&u.value), KclValue::TagDeclarator(_) => "TagDeclarator", KclValue::TagIdentifier(_) => "TagIdentifier", - KclValue::SketchGroup(_) => "SketchGroup", - KclValue::SketchGroups { .. } => "SketchGroups", KclValue::ExtrudeGroup(_) => "ExtrudeGroup", KclValue::ExtrudeGroups { .. } => "ExtrudeGroups", KclValue::ImportedGeometry(_) => "ImportedGeometry", @@ -356,20 +341,14 @@ impl KclValue { impl From for KclValue { fn from(sg: SketchGroupSet) -> Self { - match sg { - SketchGroupSet::SketchGroup(sg) => KclValue::SketchGroup(sg), - SketchGroupSet::SketchGroups(sgs) => KclValue::SketchGroups { value: sgs }, - } + KclValue::UserVal(UserVal::set(sg.meta(), sg)) } } impl From>> for KclValue { fn from(sg: Vec>) -> Self { - if sg.len() == 1 { - KclValue::SketchGroup(sg[0].clone()) - } else { - KclValue::SketchGroups { value: sg } - } + let meta = sg.iter().flat_map(|sg| sg.meta.clone()).collect(); + KclValue::UserVal(UserVal::set(meta, sg)) } } @@ -428,6 +407,24 @@ pub enum SketchGroupSet { SketchGroups(Vec>), } +impl SketchGroupSet { + pub fn meta(&self) -> Vec { + match self { + SketchGroupSet::SketchGroup(sg) => sg.meta.clone(), + SketchGroupSet::SketchGroups(sg) => sg.iter().flat_map(|sg| sg.meta.clone()).collect(), + } + } +} + +impl From for Vec { + fn from(value: SketchGroupSet) -> Self { + match value { + SketchGroupSet::SketchGroup(sg) => vec![*sg], + SketchGroupSet::SketchGroups(sgs) => sgs.into_iter().map(|sg| *sg).collect(), + } + } +} + impl From for SketchGroupSet { fn from(sg: SketchGroup) -> Self { SketchGroupSet::SketchGroup(Box::new(sg)) @@ -641,6 +638,43 @@ pub struct UserVal { pub meta: Vec, } +impl UserVal { + /// If the UserVal matches the type `T`, return it. + pub fn get(&self) -> Option<(T, Vec)> { + let meta = self.meta.clone(); + // TODO: This clone might cause performance problems, it'll happen a lot. + let res: Result = serde_json::from_value(self.value.clone()); + if let Ok(t) = res { + Some((t, meta)) + } else { + None + } + } + + /// If the UserVal matches the type `T`, then mutate it via the given closure. + /// If the closure returns Err, the mutation won't be applied. + pub fn mutate(&mut self, mutate: F) -> Result<(), E> + where + T: serde::de::DeserializeOwned + Serialize, + F: FnOnce(&mut T) -> Result<(), E>, + { + let Some((mut val, meta)) = self.get::() else { + return Ok(()); + }; + mutate(&mut val)?; + *self = Self::set(meta, val); + Ok(()) + } + + /// Put the given value into this UserVal. + pub fn set(meta: Vec, val: T) -> Self { + Self { + meta, + value: serde_json::to_value(val).expect("all KCL values should be compatible with JSON"), + } + } +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] #[serde(tag = "type", rename_all = "camelCase")] @@ -720,11 +754,6 @@ impl From for Vec { KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(), KclValue::TagDeclarator(t) => t.into(), KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(), - KclValue::SketchGroup(s) => s.meta.iter().map(|m| m.source_range).collect(), - KclValue::SketchGroups { value } => value - .iter() - .flat_map(|sg| sg.meta.iter().map(|m| m.source_range)) - .collect(), KclValue::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(), KclValue::ExtrudeGroups { value } => value .iter() @@ -1274,7 +1303,7 @@ impl Point2d { } } -#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, ts_rs::TS, JsonSchema, Default)] #[ts(export)] pub struct Point3d { pub x: f64, @@ -2085,6 +2114,39 @@ mod tests { .unwrap() } + #[test] + fn how_does_sketchgroup_serialize() { + let sg = SketchGroup { + id: uuid::Uuid::new_v4(), + value: vec![], + on: SketchSurface::Plane(Box::new(Plane { + id: uuid::Uuid::new_v4(), + value: PlaneType::XY, + origin: Point3d::default(), + x_axis: Point3d::default(), + y_axis: Point3d::default(), + z_axis: Point3d::default(), + meta: Vec::new(), + })), + start: BasePath { + from: [0.0, 0.0], + to: [0.0, 0.0], + tag: None, + geo_meta: GeoMeta { + id: uuid::Uuid::new_v4(), + metadata: Metadata { + source_range: SourceRange([0, 0]), + }, + }, + }, + tags: HashMap::new(), + original_id: uuid::Uuid::new_v4(), + meta: Vec::new(), + }; + let jstr = serde_json::to_string_pretty(&sg).unwrap(); + println!("{jstr}"); + } + #[tokio::test(flavor = "multi_thread")] async fn test_execute_assign_two_variables() { let ast = r#"const myVar = 5 diff --git a/src/wasm-lib/kcl/src/std/args.rs b/src/wasm-lib/kcl/src/std/args.rs index 1e796a4887..68c0724277 100644 --- a/src/wasm-lib/kcl/src/std/args.rs +++ b/src/wasm-lib/kcl/src/std/args.rs @@ -251,11 +251,11 @@ impl Args { FromArgs::from_args(self, 0) } - pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, Box), KclError> { + pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, SketchGroup), KclError> { FromArgs::from_args(self, 0) } - pub(crate) fn get_sketch_group(&self) -> Result, KclError> { + pub(crate) fn get_sketch_group(&self) -> Result { FromArgs::from_args(self, 0) } @@ -270,9 +270,7 @@ impl Args { FromArgs::from_args(self, 0) } - pub(crate) fn get_sketch_group_and_optional_tag( - &self, - ) -> Result<(Box, Option), KclError> { + pub(crate) fn get_sketch_group_and_optional_tag(&self) -> Result<(SketchGroup, Option), KclError> { FromArgs::from_args(self, 0) } @@ -283,7 +281,7 @@ impl Args { FromArgs::from_args(self, 0) } - pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, Box), KclError> + pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, SketchGroup), KclError> where T: serde::de::DeserializeOwned + FromArgs<'a>, { @@ -299,7 +297,7 @@ impl Args { pub(crate) fn get_data_and_sketch_group_and_tag<'a, T>( &'a self, - ) -> Result<(T, Box, Option), KclError> + ) -> Result<(T, SketchGroup, Option), KclError> where T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized, { @@ -338,7 +336,7 @@ impl Args { FromArgs::from_args(self, 0) } - pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, Box), KclError> { + pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, SketchGroup), KclError> { FromArgs::from_args(self, 0) } @@ -550,15 +548,6 @@ impl<'a> FromKclValue<'a> for TagIdentifier { } } -impl<'a> FromKclValue<'a> for &'a SketchGroup { - fn from_mem_item(arg: &'a KclValue) -> Option { - let KclValue::SketchGroup(s) = arg else { - return None; - }; - Some(s.as_ref()) - } -} - macro_rules! impl_from_arg_via_json { ($typ:path) => { impl<'a> FromKclValue<'a> for $typ { @@ -608,6 +597,7 @@ impl_from_arg_via_json!(super::revolve::RevolveData); impl_from_arg_via_json!(super::sketch::SketchData); impl_from_arg_via_json!(crate::std::import::ImportFormat); impl_from_arg_via_json!(crate::std::polar::PolarCoordsData); +impl_from_arg_via_json!(SketchGroup); impl_from_arg_via_json!(FaceTag); impl_from_arg_via_json!(String); impl_from_arg_via_json!(u32); @@ -618,21 +608,16 @@ impl_from_arg_via_json!(bool); impl_from_arg_for_array!(2); impl_from_arg_for_array!(3); -impl<'a> FromKclValue<'a> for &'a Box { - fn from_mem_item(arg: &'a KclValue) -> Option { - let KclValue::SketchGroup(s) = arg else { - return None; - }; - Some(s) - } -} - -impl<'a> FromKclValue<'a> for Box { +impl<'a> FromKclValue<'a> for SketchGroupSet { fn from_mem_item(arg: &'a KclValue) -> Option { - let KclValue::SketchGroup(s) = arg else { + let KclValue::UserVal(uv) = arg else { return None; }; - Some(s.to_owned()) + if let Some((x, _meta)) = uv.get::() { + Some(SketchGroupSet::from(x)) + } else { + uv.get::>().map(|x| x.0).map(SketchGroupSet::from) + } } } @@ -656,15 +641,16 @@ impl<'a> FromKclValue<'a> for ExtrudeGroupSet { arg.get_extrude_group_set().ok() } } -impl<'a> FromKclValue<'a> for SketchGroupSet { - fn from_mem_item(arg: &'a KclValue) -> Option { - arg.get_sketch_group_set().ok() - } -} impl<'a> FromKclValue<'a> for SketchSurfaceOrGroup { fn from_mem_item(arg: &'a KclValue) -> Option { match arg { - KclValue::SketchGroup(sg) => Some(Self::SketchGroup(sg.clone())), + KclValue::UserVal(uv) => { + if let Some((sg, _meta)) = uv.get() { + Some(Self::SketchGroup(sg)) + } else { + None + } + } KclValue::Plane(sg) => Some(Self::SketchSurface(SketchSurface::Plane(sg.clone()))), KclValue::Face(sg) => Some(Self::SketchSurface(SketchSurface::Face(sg.clone()))), _ => None, diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index 58b741bd5c..0a0081c4a9 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -76,7 +76,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args let id = uuid::Uuid::new_v4(); // Extrude the element(s). - let sketch_groups: Vec> = sketch_group_set.into(); + let sketch_groups: Vec = sketch_group_set.into(); let mut extrude_groups = Vec::new(); for sketch_group in &sketch_groups { // Before we extrude, we need to enable the sketch mode. @@ -118,7 +118,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args } pub(crate) async fn do_post_extrude( - sketch_group: Box, + sketch_group: SketchGroup, length: f64, id: Uuid, args: Args, @@ -155,7 +155,7 @@ pub(crate) async fn do_post_extrude( })); }; - let mut sketch_group = *sketch_group.clone(); + let mut sketch_group = sketch_group.clone(); // If we were sketching on a face, we need the original face id. if let SketchSurface::Face(ref face) = sketch_group.on { diff --git a/src/wasm-lib/kcl/src/std/revolve.rs b/src/wasm-lib/kcl/src/std/revolve.rs index 3b2bf4fd37..b91940a0fd 100644 --- a/src/wasm-lib/kcl/src/std/revolve.rs +++ b/src/wasm-lib/kcl/src/std/revolve.rs @@ -102,7 +102,7 @@ impl RevolveAxisAndOrigin { /// Revolve a sketch around an axis. pub async fn revolve(args: Args) -> Result { - let (data, sketch_group): (RevolveData, Box) = args.get_data_and_sketch_group()?; + let (data, sketch_group): (RevolveData, SketchGroup) = args.get_data_and_sketch_group()?; let extrude_group = inner_revolve(data, sketch_group, args).await?; Ok(KclValue::ExtrudeGroup(extrude_group)) @@ -249,7 +249,7 @@ pub async fn revolve(args: Args) -> Result { }] async fn inner_revolve( data: RevolveData, - sketch_group: Box, + sketch_group: SketchGroup, args: Args, ) -> Result, KclError> { if let Some(angle) = data.angle { diff --git a/src/wasm-lib/kcl/src/std/segment.rs b/src/wasm-lib/kcl/src/std/segment.rs index d67d21b3ca..fed858a555 100644 --- a/src/wasm-lib/kcl/src/std/segment.rs +++ b/src/wasm-lib/kcl/src/std/segment.rs @@ -108,7 +108,7 @@ pub async fn last_segment_x(args: Args) -> Result { #[stdlib { name = "lastSegX", }] -fn inner_last_segment_x(sketch_group: Box, args: Args) -> Result { +fn inner_last_segment_x(sketch_group: SketchGroup, args: Args) -> Result { let last_line = sketch_group .value .last() @@ -151,7 +151,7 @@ pub async fn last_segment_y(args: Args) -> Result { #[stdlib { name = "lastSegY", }] -fn inner_last_segment_y(sketch_group: Box, args: Args) -> Result { +fn inner_last_segment_y(sketch_group: SketchGroup, args: Args) -> Result { let last_line = sketch_group .value .last() @@ -281,7 +281,7 @@ pub async fn angle_to_match_length_x(args: Args) -> Result { fn inner_angle_to_match_length_x( tag: &TagIdentifier, to: f64, - sketch_group: Box, + sketch_group: SketchGroup, args: Args, ) -> Result { let line = args.get_tag_engine_info(tag)?; @@ -347,7 +347,7 @@ pub async fn angle_to_match_length_y(args: Args) -> Result { fn inner_angle_to_match_length_y( tag: &TagIdentifier, to: f64, - sketch_group: Box, + sketch_group: SketchGroup, args: Args, ) -> Result { let line = args.get_tag_engine_info(tag)?; diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 967d38160b..1c80c2d9d4 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -27,7 +27,7 @@ pub async fn circle(args: Args) -> Result { args.get_circle_args()?; let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?; - Ok(KclValue::SketchGroup(sketch_group)) + Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) } /// Construct a 2-dimensional circle, of the specified radius, centered at @@ -60,7 +60,7 @@ async fn inner_circle( sketch_surface_or_group: SketchSurfaceOrGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let sketch_surface = match sketch_surface_or_group { SketchSurfaceOrGroup::SketchSurface(surface) => surface, SketchSurfaceOrGroup::SketchGroup(group) => group.on, diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index 4359170f5c..0f19d060d4 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -90,11 +90,11 @@ pub enum StartOrEnd { /// Draw a line to a point. pub async fn line_to(args: Args) -> Result { - let (to, sketch_group, tag): ([f64; 2], Box, Option) = + let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_line_to(to, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line from the current origin to some absolute (x, y) point. @@ -114,10 +114,10 @@ pub async fn line_to(args: Args) -> Result { }] async fn inner_line_to( to: [f64; 2], - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let id = uuid::Uuid::new_v4(); @@ -161,11 +161,11 @@ async fn inner_line_to( /// Draw a line to a point on the x-axis. pub async fn x_line_to(args: Args) -> Result { - let (to, sketch_group, tag): (f64, Box, Option) = + let (to, sketch_group, tag): (f64, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_x_line_to(to, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line parallel to the X axis, that ends at the given X. @@ -196,10 +196,10 @@ pub async fn x_line_to(args: Args) -> Result { }] async fn inner_x_line_to( to: f64, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let new_sketch_group = inner_line_to([to, from.y], sketch_group, tag, args).await?; @@ -209,11 +209,11 @@ async fn inner_x_line_to( /// Draw a line to a point on the y-axis. pub async fn y_line_to(args: Args) -> Result { - let (to, sketch_group, tag): (f64, Box, Option) = + let (to, sketch_group, tag): (f64, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_y_line_to(to, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line parallel to the Y axis, that ends at the given Y. @@ -237,10 +237,10 @@ pub async fn y_line_to(args: Args) -> Result { }] async fn inner_y_line_to( to: f64, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let new_sketch_group = inner_line_to([from.x, to], sketch_group, tag, args).await?; @@ -249,11 +249,11 @@ async fn inner_y_line_to( /// Draw a line. pub async fn line(args: Args) -> Result { - let (delta, sketch_group, tag): ([f64; 2], Box, Option) = + let (delta, sketch_group, tag): ([f64; 2], SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_line(delta, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line relative to the current origin to a specified (x, y) away @@ -285,10 +285,10 @@ pub async fn line(args: Args) -> Result { }] async fn inner_line( delta: [f64; 2], - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let to = [from.x + delta[0], from.y + delta[1]]; @@ -334,11 +334,11 @@ async fn inner_line( /// Draw a line on the x-axis. pub async fn x_line(args: Args) -> Result { - let (length, sketch_group, tag): (f64, Box, Option) = + let (length, sketch_group, tag): (f64, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_x_line(length, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line relative to the current origin to a specified distance away @@ -368,20 +368,20 @@ pub async fn x_line(args: Args) -> Result { }] async fn inner_x_line( length: f64, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { inner_line([length, 0.0], sketch_group, tag, args).await } /// Draw a line on the y-axis. pub async fn y_line(args: Args) -> Result { - let (length, sketch_group, tag): (f64, Box, Option) = + let (length, sketch_group, tag): (f64, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_y_line(length, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line relative to the current origin to a specified distance away @@ -406,10 +406,10 @@ pub async fn y_line(args: Args) -> Result { }] async fn inner_y_line( length: f64, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { inner_line([0.0, length], sketch_group, tag, args).await } @@ -431,11 +431,11 @@ pub enum AngledLineData { /// Draw an angled line. pub async fn angled_line(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineData, Box, Option) = + let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a line segment relative to the current origin using the polar @@ -460,10 +460,10 @@ pub async fn angled_line(args: Args) -> Result { }] async fn inner_angled_line( data: AngledLineData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let (angle, length) = match data { AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), @@ -520,11 +520,11 @@ async fn inner_angled_line( /// Draw an angled line of a given x length. pub async fn angled_line_of_x_length(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineData, Box, Option) = + let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line_of_x_length(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Create a line segment from the current 2-dimensional sketch origin @@ -545,10 +545,10 @@ pub async fn angled_line_of_x_length(args: Args) -> Result { }] async fn inner_angled_line_of_x_length( data: AngledLineData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let (angle, length) = match data { AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]), @@ -588,11 +588,11 @@ pub struct AngledLineToData { /// Draw an angled line to a given x coordinate. pub async fn angled_line_to_x(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineToData, Box, Option) = + let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line_to_x(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Create a line segment from the current 2-dimensional sketch origin @@ -614,10 +614,10 @@ pub async fn angled_line_to_x(args: Args) -> Result { }] async fn inner_angled_line_to_x( data: AngledLineToData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let AngledLineToData { angle, to: x_to } = data; @@ -645,12 +645,12 @@ async fn inner_angled_line_to_x( /// Draw an angled line of a given y length. pub async fn angled_line_of_y_length(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineData, Box, Option) = + let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line_of_y_length(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Create a line segment from the current 2-dimensional sketch origin @@ -673,10 +673,10 @@ pub async fn angled_line_of_y_length(args: Args) -> Result { }] async fn inner_angled_line_of_y_length( data: AngledLineData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let (angle, length) = match data { AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]), @@ -705,11 +705,11 @@ async fn inner_angled_line_of_y_length( /// Draw an angled line to a given y coordinate. pub async fn angled_line_to_y(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineToData, Box, Option) = + let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line_to_y(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Create a line segment from the current 2-dimensional sketch origin @@ -731,10 +731,10 @@ pub async fn angled_line_to_y(args: Args) -> Result { }] async fn inner_angled_line_to_y( data: AngledLineToData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let AngledLineToData { angle, to: y_to } = data; @@ -776,10 +776,10 @@ pub struct AngledLineThatIntersectsData { /// Draw an angled line that intersects with a given line. pub async fn angled_line_that_intersects(args: Args) -> Result { - let (data, sketch_group, tag): (AngledLineThatIntersectsData, Box, Option) = + let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw an angled line from the current origin, constructing a line segment @@ -806,10 +806,10 @@ pub async fn angled_line_that_intersects(args: Args) -> Result, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?; let path = intersect_path.path.clone().ok_or_else(|| { KclError::Type(KclErrorDetails { @@ -835,7 +835,7 @@ pub async fn start_sketch_at(args: Args) -> Result { let data: [f64; 2] = args.get_data()?; let sketch_group = inner_start_sketch_at(data, args).await?; - Ok(KclValue::SketchGroup(sketch_group)) + Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) } /// Start a new 2-dimensional sketch at a given point on the 'XY' plane. @@ -872,7 +872,7 @@ pub async fn start_sketch_at(args: Args) -> Result { #[stdlib { name = "startSketchAt", }] -async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result, KclError> { +async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result { // Let's assume it's the XY plane for now, this is just for backwards compatibility. let xy_plane = PlaneData::XY; let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?; @@ -1204,7 +1204,7 @@ pub async fn start_profile_at(args: Args) -> Result { args.get_data_and_sketch_surface()?; let sketch_group = inner_start_profile_at(start, sketch_surface, tag, args).await?; - Ok(KclValue::SketchGroup(sketch_group)) + Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) } /// Start a new profile at a given point. @@ -1249,7 +1249,7 @@ pub(crate) async fn inner_start_profile_at( sketch_surface: SketchSurface, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { if let SketchSurface::Face(face) = &sketch_surface { // Flush the batch for our fillets/chamfers if there are any. // If we do not do these for sketch on face, things will fail with face does not exist. @@ -1324,12 +1324,12 @@ pub(crate) async fn inner_start_profile_at( }, start: current_path, }; - Ok(Box::new(sketch_group)) + Ok(sketch_group) } /// Returns the X component of the sketch profile start point. pub async fn profile_start_x(args: Args) -> Result { - let sketch_group: Box = args.get_sketch_group()?; + let sketch_group: SketchGroup = args.get_sketch_group()?; let x = inner_profile_start_x(sketch_group)?; args.make_user_val_from_f64(x) } @@ -1347,13 +1347,13 @@ pub async fn profile_start_x(args: Args) -> Result { #[stdlib { name = "profileStartX" }] -pub(crate) fn inner_profile_start_x(sketch_group: Box) -> Result { +pub(crate) fn inner_profile_start_x(sketch_group: SketchGroup) -> Result { Ok(sketch_group.start.to[0]) } /// Returns the Y component of the sketch profile start point. pub async fn profile_start_y(args: Args) -> Result { - let sketch_group: Box = args.get_sketch_group()?; + let sketch_group: SketchGroup = args.get_sketch_group()?; let x = inner_profile_start_y(sketch_group)?; args.make_user_val_from_f64(x) } @@ -1370,13 +1370,13 @@ pub async fn profile_start_y(args: Args) -> Result { #[stdlib { name = "profileStartY" }] -pub(crate) fn inner_profile_start_y(sketch_group: Box) -> Result { +pub(crate) fn inner_profile_start_y(sketch_group: SketchGroup) -> Result { Ok(sketch_group.start.to[1]) } /// Returns the sketch profile start point. pub async fn profile_start(args: Args) -> Result { - let sketch_group: Box = args.get_sketch_group()?; + let sketch_group: SketchGroup = args.get_sketch_group()?; let point = inner_profile_start(sketch_group)?; Ok(KclValue::UserVal(UserVal { value: serde_json::to_value(point).map_err(|e| { @@ -1404,17 +1404,17 @@ pub async fn profile_start(args: Args) -> Result { #[stdlib { name = "profileStart" }] -pub(crate) fn inner_profile_start(sketch_group: Box) -> Result<[f64; 2], KclError> { +pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2], KclError> { Ok(sketch_group.start.to) } /// Close the current sketch. pub async fn close(args: Args) -> Result { - let (sketch_group, tag): (Box, Option) = args.get_sketch_group_and_optional_tag()?; + let (sketch_group, tag): (SketchGroup, Option) = args.get_sketch_group_and_optional_tag()?; let new_sketch_group = inner_close(sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Construct a line segment from the current origin back to the profile's @@ -1442,10 +1442,10 @@ pub async fn close(args: Args) -> Result { name = "close", }] pub(crate) async fn inner_close( - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let to: Point2d = sketch_group.start.from.into(); @@ -1517,11 +1517,11 @@ pub enum ArcData { /// Draw an arc. pub async fn arc(args: Args) -> Result { - let (data, sketch_group, tag): (ArcData, Box, Option) = + let (data, sketch_group, tag): (ArcData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_arc(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Starting at the current sketch's origin, draw a curved line segment along @@ -1553,10 +1553,10 @@ pub async fn arc(args: Args) -> Result { }] pub(crate) async fn inner_arc( data: ArcData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from: Point2d = sketch_group.current_pen_position()?; let (center, angle_start, angle_end, radius, end) = match &data { @@ -1640,11 +1640,11 @@ pub enum TangentialArcData { /// Draw a tangential arc. pub async fn tangential_arc(args: Args) -> Result { - let (data, sketch_group, tag): (TangentialArcData, Box, Option) = + let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_tangential_arc(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Starting at the current sketch's origin, draw a curved line segment along @@ -1676,10 +1676,10 @@ pub async fn tangential_arc(args: Args) -> Result { }] async fn inner_tangential_arc( data: TangentialArcData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from: Point2d = sketch_group.current_pen_position()?; // next set of lines is some undocumented voodoo from get_tangential_arc_to_info let tangent_info = sketch_group.get_tangential_info_from_paths(); //this function desperately needs some documentation @@ -1793,7 +1793,7 @@ pub async fn tangential_arc_to(args: Args) -> Result { // Get arguments to function call let mut it = args.args.iter(); let to: [f64; 2] = get_arg(&mut it, src)?.get_json()?; - let sketch_group: Box = get_arg(&mut it, src)?.get_json()?; + let sketch_group: SketchGroup = get_arg(&mut it, src)?.get_json()?; let tag = if let Ok(memory_item) = get_arg(&mut it, src) { memory_item.get_json_opt()? } else { @@ -1801,7 +1801,7 @@ pub async fn tangential_arc_to(args: Args) -> Result { }; let new_sketch_group = inner_tangential_arc_to(to, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Starting at the current sketch's origin, draw a curved line segment along @@ -1826,10 +1826,10 @@ pub async fn tangential_arc_to(args: Args) -> Result { }] async fn inner_tangential_arc_to( to: [f64; 2], - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from: Point2d = sketch_group.current_pen_position()?; let tangent_info = sketch_group.get_tangential_info_from_paths(); let tan_previous_point = if tangent_info.is_center { @@ -1888,11 +1888,11 @@ pub struct BezierData { /// Draw a bezier curve. pub async fn bezier_curve(args: Args) -> Result { - let (data, sketch_group, tag): (BezierData, Box, Option) = + let (data, sketch_group, tag): (BezierData, SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_bezier_curve(data, sketch_group, tag, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Draw a smooth, continuous, curved line segment from the current origin to @@ -1918,10 +1918,10 @@ pub async fn bezier_curve(args: Args) -> Result { }] async fn inner_bezier_curve( data: BezierData, - sketch_group: Box, + sketch_group: SketchGroup, tag: Option, args: Args, -) -> Result, KclError> { +) -> Result { let from = sketch_group.current_pen_position()?; let relative = true; @@ -1980,10 +1980,10 @@ async fn inner_bezier_curve( /// Use a sketch to cut a hole in another sketch. pub async fn hole(args: Args) -> Result { - let (hole_sketch_group, sketch_group): (SketchGroupSet, Box) = args.get_sketch_groups()?; + let (hole_sketch_group, sketch_group): (SketchGroupSet, SketchGroup) = args.get_sketch_groups()?; let new_sketch_group = inner_hole(hole_sketch_group, sketch_group, args).await?; - Ok(KclValue::SketchGroup(new_sketch_group)) + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } /// Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch. @@ -2022,10 +2022,10 @@ pub async fn hole(args: Args) -> Result { }] async fn inner_hole( hole_sketch_group: SketchGroupSet, - sketch_group: Box, + sketch_group: SketchGroup, args: Args, -) -> Result, KclError> { - let hole_sketch_groups: Vec> = hole_sketch_group.into(); +) -> Result { + let hole_sketch_groups: Vec = hole_sketch_group.into(); for hole_sketch_group in hole_sketch_groups { args.batch_modeling_cmd( uuid::Uuid::new_v4(), diff --git a/src/wasm-lib/tests/modify/main.rs b/src/wasm-lib/tests/modify/main.rs index fa7c0319c2..b075485ec0 100644 --- a/src/wasm-lib/tests/modify/main.rs +++ b/src/wasm-lib/tests/modify/main.rs @@ -1,7 +1,7 @@ use anyhow::Result; use kcl_lib::{ ast::{modify::modify_ast_for_sketch, types::Program}, - executor::{ExecutorContext, KclValue, PlaneType, SourceRange}, + executor::{ExecutorContext, KclValue, PlaneType, SketchGroup, SourceRange}, }; use kittycad::types::{ModelingCmd, Point3D}; use pretty_assertions::assert_eq; @@ -39,9 +39,12 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid // We need to get the sketch ID. // Get the sketch group ID from memory. - let KclValue::SketchGroup(sketch_group) = memory.get(name, SourceRange::default()).unwrap() else { + let KclValue::UserVal(user_val) = memory.get(name, SourceRange::default()).unwrap() else { anyhow::bail!("part001 not found in memory: {:?}", memory); }; + let Some((sketch_group, _meta)) = user_val.get::() else { + anyhow::bail!("part001 was not a SketchGroup"); + }; let sketch_id = sketch_group.id; let plane_id = uuid::Uuid::new_v4();