Remove KclValue::SketchGroup variant (#3446)

We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.

Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.

Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.

My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
This commit is contained in:
Adam Chalmers
2024-08-21 11:06:48 -05:00
committed by GitHub
parent 682590deea
commit d656a389f8
20 changed files with 473 additions and 326 deletions

View File

@ -50,6 +50,7 @@ import {
ExtrudeGroup, ExtrudeGroup,
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
sketchGroupFromKclValue,
} from 'lang/wasm' } from 'lang/wasm'
import { import {
engineCommandManager, engineCommandManager,
@ -588,10 +589,12 @@ export class SceneEntities {
const variableDeclarationName = const variableDeclarationName =
_node1.node?.declarations?.[0]?.id?.name || '' _node1.node?.declarations?.[0]?.id?.name || ''
const sg = kclManager.programMemory.get( const sg = sketchGroupFromKclValue(
kclManager.programMemory.get(variableDeclarationName),
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` 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, programMemoryOverride,
}) })
this.sceneProgramMemory = programMemory this.sceneProgramMemory = programMemory
const sketchGroup = programMemory.get( const sketchGroup = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName variableDeclarationName
) as SketchGroup )
if (err(sketchGroup)) return sketchGroup
const sgPaths = sketchGroup.value const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -840,9 +845,11 @@ export class SceneEntities {
// Prepare to update the THREEjs scene // Prepare to update the THREEjs scene
this.sceneProgramMemory = programMemory this.sceneProgramMemory = programMemory
const sketchGroup = programMemory.get( const sketchGroup = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName),
variableDeclarationName variableDeclarationName
) as SketchGroup )
if (err(sketchGroup)) return sketchGroup
const sgPaths = sketchGroup.value const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -1111,8 +1118,12 @@ export class SceneEntities {
const maybeSketchGroup = programMemory.get(variableDeclarationName) const maybeSketchGroup = programMemory.get(variableDeclarationName)
let sketchGroup = undefined let sketchGroup = undefined
if (maybeSketchGroup?.type === 'SketchGroup') { const sg = sketchGroupFromKclValue(
sketchGroup = maybeSketchGroup maybeSketchGroup,
variableDeclarationName
)
if (!err(sg)) {
sketchGroup = sg
} else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) { } else if ((maybeSketchGroup as ExtrudeGroup).sketchGroup) {
sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup sketchGroup = (maybeSketchGroup as ExtrudeGroup).sketchGroup
} }
@ -1656,9 +1667,12 @@ function prepareTruncatedMemoryAndAst(
) )
if (err(_node)) return _node if (err(_node)) return _node
const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || '' const variableDeclarationName = _node.node?.declarations?.[0]?.id?.name || ''
const lastSeg = ( const sg = sketchGroupFromKclValue(
programMemory.get(variableDeclarationName) as SketchGroup programMemory.get(variableDeclarationName),
).value.slice(-1)[0] variableDeclarationName
)
if (err(sg)) return sg
const lastSeg = sg?.value.slice(-1)[0]
if (draftSegment) { if (draftSegment) {
// truncatedAst needs to setup with another segment at the end // truncatedAst needs to setup with another segment at the end
let newSegment let newSegment
@ -1782,10 +1796,11 @@ export function sketchGroupFromPathToNode({
if (result?.type === 'ExtrudeGroup') { if (result?.type === 'ExtrudeGroup') {
return result.sketchGroup return result.sketchGroup
} }
if (result?.type === 'SketchGroup') { const sg = sketchGroupFromKclValue(result, varDec?.id?.name)
return result if (err(sg)) {
}
return null return null
}
return sg
} }
function colorSegment(object: any, color: number) { function colorSegment(object: any, color: number) {

View File

@ -1,11 +1,16 @@
import toast from 'react-hot-toast' import toast from 'react-hot-toast'
import ReactJson from 'react-json-view' import ReactJson from 'react-json-view'
import { useMemo } from 'react' 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 { useKclContext } from 'lang/KclProvider'
import { useResolvedTheme } from 'hooks/useResolvedTheme' import { useResolvedTheme } from 'hooks/useResolvedTheme'
import { ActionButton } from 'components/ActionButton' import { ActionButton } from 'components/ActionButton'
import { trap } from 'lib/trap' import { err, trap } from 'lib/trap'
import Tooltip from 'components/Tooltip' import Tooltip from 'components/Tooltip'
import { useModelingContext } from 'hooks/useModelingContext' import { useModelingContext } from 'hooks/useModelingContext'
@ -84,8 +89,9 @@ export const processMemory = (programMemory: ProgramMemory) => {
const processedMemory: any = {} const processedMemory: any = {}
for (const [key, val] of programMemory?.visibleEntries()) { for (const [key, val] of programMemory?.visibleEntries()) {
if (typeof val.value !== 'function') { if (typeof val.value !== 'function') {
if (val.type === 'SketchGroup') { const sg = sketchGroupFromKclValue(val, null)
processedMemory[key] = val.value.map(({ __geoMeta, ...rest }: Path) => { if (!err(sg)) {
processedMemory[key] = sg.value.map(({ __geoMeta, ...rest }: Path) => {
return rest return rest
}) })
} else if (val.type === 'ExtrudeGroup') { } else if (val.type === 'ExtrudeGroup') {

View File

@ -18,6 +18,9 @@ const mySketch001 = startSketchOn('XY')
// @ts-ignore // @ts-ignore
const sketch001 = programMemory?.get('mySketch001') const sketch001 = programMemory?.get('mySketch001')
expect(sketch001).toEqual({ expect(sketch001).toEqual({
type: 'UserVal',
__meta: [{ sourceRange: [46, 71] }],
value: {
type: 'SketchGroup', type: 'SketchGroup',
on: expect.any(Object), on: expect.any(Object),
start: { start: {
@ -53,6 +56,7 @@ const mySketch001 = startSketchOn('XY')
], ],
id: expect.any(String), id: expect.any(String),
__meta: [{ sourceRange: [46, 71] }], __meta: [{ sourceRange: [46, 71] }],
},
}) })
}) })
test('extrude artifacts', async () => { test('extrude artifacts', async () => {

View File

@ -1,6 +1,12 @@
import fs from 'node:fs' 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 { enginelessExecutor } from '../lib/testHelpers'
import { KCLError } from './errors' import { KCLError } from './errors'
@ -52,7 +58,7 @@ const newVar = myVar + 1`
` `
const mem = await exe(code) const mem = await exe(code)
// geo is three js buffer geometry and is very bloated to have in tests // 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([ expect(minusGeo).toEqual([
{ {
type: 'ToPoint', type: 'ToPoint',
@ -146,6 +152,8 @@ const newVar = myVar + 1`
].join('\n') ].join('\n')
const mem = await exe(code) const mem = await exe(code)
expect(mem.get('mySk1')).toEqual({ expect(mem.get('mySk1')).toEqual({
type: 'UserVal',
value: {
type: 'SketchGroup', type: 'SketchGroup',
on: expect.any(Object), on: expect.any(Object),
start: { start: {
@ -209,6 +217,8 @@ const newVar = myVar + 1`
], ],
id: expect.any(String), id: expect.any(String),
__meta: [{ sourceRange: [39, 63] }], __meta: [{ sourceRange: [39, 63] }],
},
__meta: [{ sourceRange: [39, 63] }],
}) })
}) })
it('execute array expression', async () => { it('execute array expression', async () => {
@ -358,7 +368,7 @@ describe('testing math operators', () => {
'|> line([-2.21, -legLen(5, min(3, 999))], %)', '|> line([-2.21, -legLen(5, min(3, 999))], %)',
].join('\n') ].join('\n')
const mem = await exe(code) 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 // result of `-legLen(5, min(3, 999))` should be -4
const yVal = (sketch as SketchGroup).value?.[0]?.to?.[1] const yVal = (sketch as SketchGroup).value?.[0]?.to?.[1]
expect(yVal).toBe(-4) expect(yVal).toBe(-4)
@ -376,7 +386,7 @@ describe('testing math operators', () => {
``, ``,
].join('\n') ].join('\n')
const mem = await exe(code) 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 -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]?.from).toEqual([3, 4])
expect((sketch as SketchGroup).value?.[1]?.to).toEqual([6, 0]) expect((sketch as SketchGroup).value?.[1]?.to).toEqual([6, 0])
@ -385,7 +395,10 @@ describe('testing math operators', () => {
`legLen(segLen(seg01), myVar)` `legLen(segLen(seg01), myVar)`
) )
const removedUnaryExpMem = await exe(removedUnaryExp) 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 // without the minus sign, the y value should be 8
expect((removedUnaryExpMemSketch as SketchGroup).value?.[1]?.to).toEqual([ expect((removedUnaryExpMemSketch as SketchGroup).value?.[1]?.to).toEqual([

View File

@ -17,7 +17,7 @@ import {
PathToNode, PathToNode,
ProgramMemory, ProgramMemory,
SourceRange, SourceRange,
SketchGroup, sketchGroupFromKclValue,
} from './wasm' } from './wasm'
import { import {
isNodeSafeToReplacePath, isNodeSafeToReplacePath,
@ -981,7 +981,11 @@ export async function deleteFromSelection(
if (err(parent)) { if (err(parent)) {
return return
} }
const sketchToPreserve = programMemory.get(sketchName) as SketchGroup const sketchToPreserve = sketchGroupFromKclValue(
programMemory.get(sketchName),
sketchName
)
if (err(sketchToPreserve)) return sketchToPreserve
console.log('sketchName', sketchName) console.log('sketchName', sketchName)
// Can't kick off multiple requests at once as getFaceDetails // Can't kick off multiple requests at once as getFaceDetails
// is three engine calls in one and they conflict // is three engine calls in one and they conflict

View File

@ -10,12 +10,12 @@ import {
Program, Program,
ProgramMemory, ProgramMemory,
ReturnStatement, ReturnStatement,
SketchGroup,
SourceRange, SourceRange,
SyntaxType, SyntaxType,
Expr, Expr,
VariableDeclaration, VariableDeclaration,
VariableDeclarator, VariableDeclarator,
sketchGroupFromKclValue,
} from './wasm' } from './wasm'
import { createIdentifier, splitPathAtLastIndex } from './modifyAst' import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
import { getSketchSegmentFromSourceRange } from './std/sketchConstraints' import { getSketchSegmentFromSourceRange } from './std/sketchConstraints'
@ -661,15 +661,16 @@ export function isLinesParallelAndConstrained(
if (err(_varDec)) return _varDec if (err(_varDec)) return _varDec
const varDec = _varDec.node const varDec = _varDec.node
const varName = (varDec as VariableDeclaration)?.declarations[0]?.id?.name 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( const _primarySegment = getSketchSegmentFromSourceRange(
path, sg,
primaryLine.range primaryLine.range
) )
if (err(_primarySegment)) return _primarySegment if (err(_primarySegment)) return _primarySegment
const primarySegment = _primarySegment.segment const primarySegment = _primarySegment.segment
const _segment = getSketchSegmentFromSourceRange(path, secondaryLine.range) const _segment = getSketchSegmentFromSourceRange(sg, secondaryLine.range)
if (err(_segment)) return _segment if (err(_segment)) return _segment
const { segment: secondarySegment, index: secondaryIndex } = _segment const { segment: secondarySegment, index: secondaryIndex } = _segment
const primaryAngle = getAngle(primarySegment.from, primarySegment.to) const primaryAngle = getAngle(primarySegment.from, primarySegment.to)
@ -708,9 +709,7 @@ export function isLinesParallelAndConstrained(
constraintType === 'angle' || constraintLevel === 'full' constraintType === 'angle' || constraintLevel === 'full'
// get the previous segment // get the previous segment
const prevSegment = (programMemory.get(varName) as SketchGroup).value[ const prevSegment = sg.value[secondaryIndex - 1]
secondaryIndex - 1
]
const prevSourceRange = prevSegment.__geoMeta.sourceRange const prevSourceRange = prevSegment.__geoMeta.sourceRange
const isParallelAndConstrained = const isParallelAndConstrained =
@ -779,7 +778,10 @@ export function hasExtrudeSketchGroup({
if (varDec.type !== 'VariableDeclaration') return false if (varDec.type !== 'VariableDeclaration') return false
const varName = varDec.declarations[0].id.name const varName = varDec.declarations[0].id.name
const varValue = programMemory?.get(varName) const varValue = programMemory?.get(varName)
return varValue?.type === 'ExtrudeGroup' || varValue?.type === 'SketchGroup' return (
varValue?.type === 'ExtrudeGroup' ||
!err(sketchGroupFromKclValue(varValue, varName))
)
} }
export function isSingleCursorInPipe( export function isSingleCursorInPipe(

View File

@ -12,6 +12,7 @@ import {
Literal, Literal,
VariableDeclaration, VariableDeclaration,
Identifier, Identifier,
sketchGroupFromKclValue,
} from 'lang/wasm' } from 'lang/wasm'
import { import {
getNodeFromPath, getNodeFromPath,
@ -1009,9 +1010,12 @@ export const angledLineOfXLength: SketchLineHelper = {
const { node: varDec } = nodeMeta2 const { node: varDec } = nodeMeta2
const variableName = varDec.id.name const variableName = varDec.id.name
const sketch = previousProgramMemory?.get(variableName) const sketch = sketchGroupFromKclValue(
if (!sketch || sketch.type !== 'SketchGroup') { previousProgramMemory?.get(variableName),
return new Error('not a SketchGroup') variableName
)
if (err(sketch)) {
return sketch
} }
const angle = createLiteral(roundOff(getAngle(from, to), 0)) const angle = createLiteral(roundOff(getAngle(from, to), 0))
const xLength = createLiteral(roundOff(Math.abs(from[0] - to[0]), 2) || 0.1) 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 if (err(nodeMeta2)) return nodeMeta2
const { node: varDec } = nodeMeta2 const { node: varDec } = nodeMeta2
const variableName = varDec.id.name const variableName = varDec.id.name
const sketch = previousProgramMemory?.get(variableName) const sketch = sketchGroupFromKclValue(
if (!sketch || sketch.type !== 'SketchGroup') { previousProgramMemory?.get(variableName),
return new Error('not a SketchGroup') variableName
} )
if (err(sketch)) return sketch
const angle = createLiteral(roundOff(getAngle(from, to), 0)) const angle = createLiteral(roundOff(getAngle(from, to), 0))
const yLength = createLiteral(roundOff(Math.abs(from[1] - to[1]), 2) || 0.1) 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 { node: varDec } = nodeMeta2
const varName = varDec.declarations[0].id.name 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( const intersectPath = sketchGroup.value.find(
({ tag }: Path) => tag && tag.value === intersectTagName ({ tag }: Path) => tag && tag.value === intersectTagName
) )

View File

@ -1,4 +1,10 @@
import { parse, SketchGroup, recast, initPromise } from '../wasm' import {
parse,
SketchGroup,
recast,
initPromise,
sketchGroupFromKclValue,
} from '../wasm'
import { import {
ConstraintType, ConstraintType,
getTransformInfos, getTransformInfos,
@ -362,10 +368,11 @@ const part001 = startSketchOn('XY')
it('normal case works', async () => { it('normal case works', async () => {
const programMemory = await enginelessExecutor(parse(code)) const programMemory = await enginelessExecutor(parse(code))
const index = code.indexOf('// normal-segment') - 7 const index = code.indexOf('// normal-segment') - 7
const _segment = getSketchSegmentFromSourceRange( const sg = sketchGroupFromKclValue(
programMemory.get('part001') as SketchGroup, programMemory.get('part001'),
[index, index] 'part001'
) ) as SketchGroup
const _segment = getSketchSegmentFromSourceRange(sg, [index, index])
if (err(_segment)) throw _segment if (err(_segment)) throw _segment
const { __geoMeta, ...segment } = _segment.segment const { __geoMeta, ...segment } = _segment.segment
expect(segment).toEqual({ expect(segment).toEqual({
@ -379,7 +386,10 @@ const part001 = startSketchOn('XY')
const programMemory = await enginelessExecutor(parse(code)) const programMemory = 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(
programMemory.get('part001') as SketchGroup, sketchGroupFromKclValue(
programMemory.get('part001'),
'part001'
) as SketchGroup,
[index, index] [index, index]
) )
if (err(_segment)) throw _segment if (err(_segment)) throw _segment

View File

@ -10,6 +10,7 @@ import {
VariableDeclarator, VariableDeclarator,
PathToNode, PathToNode,
ProgramMemory, ProgramMemory,
sketchGroupFromKclValue,
} from '../wasm' } from '../wasm'
import { import {
getNodeFromPath, getNodeFromPath,
@ -1636,12 +1637,16 @@ export function transformAstSketchLines({
}) })
const varName = varDec.node.id.name const varName = varDec.node.id.name
let sketchGroup = programMemory.get(varName) let kclVal = programMemory.get(varName)
if (sketchGroup?.type === 'ExtrudeGroup') { let sketchGroup
sketchGroup = sketchGroup.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( const segMeta = getSketchSegmentFromPathToNode(
sketchGroup, sketchGroup,
ast, ast,

View File

@ -38,6 +38,7 @@ import { err } from 'lib/trap'
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration' 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'
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'
@ -126,6 +127,7 @@ export const parse = (code: string | Error): Program | Error => {
const program: Program = parse_wasm(code) const program: Program = parse_wasm(code)
return program return program
} catch (e: any) { } catch (e: any) {
// throw e
const parsed: RustKclError = JSON.parse(e.toString()) const parsed: RustKclError = JSON.parse(e.toString())
return new KCLError( return new KCLError(
parsed.kind, parsed.kind,
@ -312,7 +314,7 @@ export class ProgramMemory {
*/ */
hasSketchOrExtrudeGroup(): boolean { hasSketchOrExtrudeGroup(): boolean {
for (const node of this.visibleEntries().values()) { for (const node of this.visibleEntries().values()) {
if (node.type === 'ExtrudeGroup' || node.type === 'SketchGroup') { if (node.type === 'ExtrudeGroup' || node.value?.type === 'SketchGroup') {
return true 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 ( export const executor = async (
node: Program, node: Program,
programMemory: ProgramMemory | Error = ProgramMemory.empty(), programMemory: ProgramMemory | Error = ProgramMemory.empty(),

View File

@ -4,6 +4,7 @@ import {
VariableDeclarator, VariableDeclarator,
parse, parse,
recast, recast,
sketchGroupFromKclValue,
} from 'lang/wasm' } from 'lang/wasm'
import { Axis, Selection, Selections, updateSelections } from 'lib/selections' import { Axis, Selection, Selections, updateSelections } from 'lib/selections'
import { assign, createMachine } from 'xstate' import { assign, createMachine } from 'xstate'
@ -1187,8 +1188,11 @@ export const modelingMachine = createMachine(
) )
if (err(varDecNode)) return if (err(varDecNode)) return
const sketchVar = varDecNode.node.declarations[0].id.name const sketchVar = varDecNode.node.declarations[0].id.name
const sketchGroup = kclManager.programMemory.get(sketchVar) const sketchGroup = sketchGroupFromKclValue(
if (sketchGroup?.type !== 'SketchGroup') return kclManager.programMemory.get(sketchVar),
sketchVar
)
if (trap(sketchGroup)) return
const extrusion = getExtrusionFromSuspectedPath( const extrusion = getExtrusionFromSuspectedPath(
sketchGroup.id, sketchGroup.id,
engineCommandManager.artifactGraph engineCommandManager.artifactGraph

View File

@ -23,7 +23,7 @@ use crate::{
docs::StdLibFn, docs::StdLibFn,
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{ executor::{
BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SourceRange, BodyType, DynamicState, ExecutorContext, KclValue, Metadata, PipeInfo, ProgramMemory, SketchGroup, SourceRange,
StatementKind, TagEngineInfo, TagIdentifier, UserVal, StatementKind, TagEngineInfo, TagIdentifier, UserVal,
}, },
parser::PIPE_OPERATOR, 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 // TODO: This could probably be done in a better way, but as of now this was my only idea
// and it works. // and it works.
match result { match result {
KclValue::SketchGroup(ref sketch_group) => { KclValue::UserVal(ref mut uval) => {
uval.mutate(|sketch_group: &mut SketchGroup| {
for (_, tag) in sketch_group.tags.iter() { for (_, tag) in sketch_group.tags.iter() {
memory.update_tag(&tag.value, tag.clone())?; memory.update_tag(&tag.value, tag.clone())?;
} }
Ok::<_, KclError>(())
})?;
} }
KclValue::ExtrudeGroup(ref mut extrude_group) => { KclValue::ExtrudeGroup(ref mut extrude_group) => {
for value in &extrude_group.value { for value in &extrude_group.value {

View File

@ -203,13 +203,22 @@ impl Environment {
} }
for (_, val) in self.bindings.iter_mut() { for (_, val) in self.bindings.iter_mut() {
if let KclValue::SketchGroup(ref mut sketch_group) = val { let KclValue::UserVal(v) = val else { continue };
let meta = v.meta.clone();
let maybe_sg: Result<SketchGroup, _> = serde_json::from_value(v.value.clone());
let Ok(mut sketch_group) = maybe_sg else {
continue;
};
if sketch_group.original_id == sg.original_id { if sketch_group.original_id == sg.original_id {
for tag in sg.tags.iter() { for tag in sg.tags.iter() {
sketch_group.tags.insert(tag.0.clone(), tag.1.clone()); 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<TagDeclarator>), TagDeclarator(Box<TagDeclarator>),
Plane(Box<Plane>), Plane(Box<Plane>),
Face(Box<Face>), Face(Box<Face>),
SketchGroup(Box<SketchGroup>),
SketchGroups {
value: Vec<Box<SketchGroup>>,
},
ExtrudeGroup(Box<ExtrudeGroup>), ExtrudeGroup(Box<ExtrudeGroup>),
ExtrudeGroups { ExtrudeGroups {
value: Vec<Box<ExtrudeGroup>>, value: Vec<Box<ExtrudeGroup>>,
@ -289,27 +295,8 @@ pub enum KclValue {
} }
impl KclValue { impl KclValue {
pub(crate) fn get_sketch_group_set(&self) -> Result<SketchGroupSet> { pub(crate) fn new_user_val<T: Serialize>(meta: Vec<Metadata>, val: T) -> Self {
match self { Self::UserVal(UserVal::set(meta, val))
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::<Vec<Box<SketchGroup>>>(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::<Box<SketchGroup>>(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 get_extrude_group_set(&self) -> Result<ExtrudeGroupSet> { pub(crate) fn get_extrude_group_set(&self) -> Result<ExtrudeGroupSet> {
@ -342,8 +329,6 @@ impl KclValue {
KclValue::UserVal(u) => human_friendly_type(&u.value), KclValue::UserVal(u) => human_friendly_type(&u.value),
KclValue::TagDeclarator(_) => "TagDeclarator", KclValue::TagDeclarator(_) => "TagDeclarator",
KclValue::TagIdentifier(_) => "TagIdentifier", KclValue::TagIdentifier(_) => "TagIdentifier",
KclValue::SketchGroup(_) => "SketchGroup",
KclValue::SketchGroups { .. } => "SketchGroups",
KclValue::ExtrudeGroup(_) => "ExtrudeGroup", KclValue::ExtrudeGroup(_) => "ExtrudeGroup",
KclValue::ExtrudeGroups { .. } => "ExtrudeGroups", KclValue::ExtrudeGroups { .. } => "ExtrudeGroups",
KclValue::ImportedGeometry(_) => "ImportedGeometry", KclValue::ImportedGeometry(_) => "ImportedGeometry",
@ -356,20 +341,14 @@ impl KclValue {
impl From<SketchGroupSet> for KclValue { impl From<SketchGroupSet> for KclValue {
fn from(sg: SketchGroupSet) -> Self { fn from(sg: SketchGroupSet) -> Self {
match sg { KclValue::UserVal(UserVal::set(sg.meta(), sg))
SketchGroupSet::SketchGroup(sg) => KclValue::SketchGroup(sg),
SketchGroupSet::SketchGroups(sgs) => KclValue::SketchGroups { value: sgs },
}
} }
} }
impl From<Vec<Box<SketchGroup>>> for KclValue { impl From<Vec<Box<SketchGroup>>> for KclValue {
fn from(sg: Vec<Box<SketchGroup>>) -> Self { fn from(sg: Vec<Box<SketchGroup>>) -> Self {
if sg.len() == 1 { let meta = sg.iter().flat_map(|sg| sg.meta.clone()).collect();
KclValue::SketchGroup(sg[0].clone()) KclValue::UserVal(UserVal::set(meta, sg))
} else {
KclValue::SketchGroups { value: sg }
}
} }
} }
@ -428,6 +407,24 @@ pub enum SketchGroupSet {
SketchGroups(Vec<Box<SketchGroup>>), SketchGroups(Vec<Box<SketchGroup>>),
} }
impl SketchGroupSet {
pub fn meta(&self) -> Vec<Metadata> {
match self {
SketchGroupSet::SketchGroup(sg) => sg.meta.clone(),
SketchGroupSet::SketchGroups(sg) => sg.iter().flat_map(|sg| sg.meta.clone()).collect(),
}
}
}
impl From<SketchGroupSet> for Vec<SketchGroup> {
fn from(value: SketchGroupSet) -> Self {
match value {
SketchGroupSet::SketchGroup(sg) => vec![*sg],
SketchGroupSet::SketchGroups(sgs) => sgs.into_iter().map(|sg| *sg).collect(),
}
}
}
impl From<SketchGroup> for SketchGroupSet { impl From<SketchGroup> for SketchGroupSet {
fn from(sg: SketchGroup) -> Self { fn from(sg: SketchGroup) -> Self {
SketchGroupSet::SketchGroup(Box::new(sg)) SketchGroupSet::SketchGroup(Box::new(sg))
@ -641,6 +638,43 @@ pub struct UserVal {
pub meta: Vec<Metadata>, pub meta: Vec<Metadata>,
} }
impl UserVal {
/// If the UserVal matches the type `T`, return it.
pub fn get<T: serde::de::DeserializeOwned>(&self) -> Option<(T, Vec<Metadata>)> {
let meta = self.meta.clone();
// TODO: This clone might cause performance problems, it'll happen a lot.
let res: Result<T, _> = 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<T, F, E>(&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::<T>() else {
return Ok(());
};
mutate(&mut val)?;
*self = Self::set(meta, val);
Ok(())
}
/// Put the given value into this UserVal.
pub fn set<T: serde::Serialize>(meta: Vec<Metadata>, 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)] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")] #[serde(tag = "type", rename_all = "camelCase")]
@ -720,11 +754,6 @@ impl From<KclValue> for Vec<SourceRange> {
KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(), KclValue::UserVal(u) => u.meta.iter().map(|m| m.source_range).collect(),
KclValue::TagDeclarator(t) => t.into(), KclValue::TagDeclarator(t) => t.into(),
KclValue::TagIdentifier(t) => t.meta.iter().map(|m| m.source_range).collect(), 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::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
KclValue::ExtrudeGroups { value } => value KclValue::ExtrudeGroups { value } => value
.iter() .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)] #[ts(export)]
pub struct Point3d { pub struct Point3d {
pub x: f64, pub x: f64,
@ -2085,6 +2114,39 @@ mod tests {
.unwrap() .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")] #[tokio::test(flavor = "multi_thread")]
async fn test_execute_assign_two_variables() { async fn test_execute_assign_two_variables() {
let ast = r#"const myVar = 5 let ast = r#"const myVar = 5

View File

@ -251,11 +251,11 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, Box<SketchGroup>), KclError> { pub(crate) fn get_sketch_groups(&self) -> Result<(SketchGroupSet, SketchGroup), KclError> {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_sketch_group(&self) -> Result<Box<SketchGroup>, KclError> { pub(crate) fn get_sketch_group(&self) -> Result<SketchGroup, KclError> {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
@ -270,9 +270,7 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_sketch_group_and_optional_tag( pub(crate) fn get_sketch_group_and_optional_tag(&self) -> Result<(SketchGroup, Option<TagDeclarator>), KclError> {
&self,
) -> Result<(Box<SketchGroup>, Option<TagDeclarator>), KclError> {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
@ -283,7 +281,7 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, Box<SketchGroup>), KclError> pub(crate) fn get_data_and_sketch_group<'a, T>(&'a self) -> Result<(T, SketchGroup), KclError>
where where
T: serde::de::DeserializeOwned + FromArgs<'a>, T: serde::de::DeserializeOwned + FromArgs<'a>,
{ {
@ -299,7 +297,7 @@ impl Args {
pub(crate) fn get_data_and_sketch_group_and_tag<'a, T>( pub(crate) fn get_data_and_sketch_group_and_tag<'a, T>(
&'a self, &'a self,
) -> Result<(T, Box<SketchGroup>, Option<TagDeclarator>), KclError> ) -> Result<(T, SketchGroup, Option<TagDeclarator>), KclError>
where where
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized, T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
{ {
@ -338,7 +336,7 @@ impl Args {
FromArgs::from_args(self, 0) FromArgs::from_args(self, 0)
} }
pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, Box<SketchGroup>), KclError> { pub(crate) fn get_tag_to_number_sketch_group(&self) -> Result<(TagIdentifier, f64, SketchGroup), KclError> {
FromArgs::from_args(self, 0) 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<Self> {
let KclValue::SketchGroup(s) = arg else {
return None;
};
Some(s.as_ref())
}
}
macro_rules! impl_from_arg_via_json { macro_rules! impl_from_arg_via_json {
($typ:path) => { ($typ:path) => {
impl<'a> FromKclValue<'a> for $typ { 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!(super::sketch::SketchData);
impl_from_arg_via_json!(crate::std::import::ImportFormat); impl_from_arg_via_json!(crate::std::import::ImportFormat);
impl_from_arg_via_json!(crate::std::polar::PolarCoordsData); 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!(FaceTag);
impl_from_arg_via_json!(String); impl_from_arg_via_json!(String);
impl_from_arg_via_json!(u32); 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!(2);
impl_from_arg_for_array!(3); impl_from_arg_for_array!(3);
impl<'a> FromKclValue<'a> for &'a Box<SketchGroup> { impl<'a> FromKclValue<'a> for SketchGroupSet {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> { fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else { let KclValue::UserVal(uv) = arg else {
return None; return None;
}; };
Some(s) if let Some((x, _meta)) = uv.get::<SketchGroup>() {
Some(SketchGroupSet::from(x))
} else {
uv.get::<Vec<SketchGroup>>().map(|x| x.0).map(SketchGroupSet::from)
} }
}
impl<'a> FromKclValue<'a> for Box<SketchGroup> {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else {
return None;
};
Some(s.to_owned())
} }
} }
@ -656,15 +641,16 @@ impl<'a> FromKclValue<'a> for ExtrudeGroupSet {
arg.get_extrude_group_set().ok() arg.get_extrude_group_set().ok()
} }
} }
impl<'a> FromKclValue<'a> for SketchGroupSet {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
arg.get_sketch_group_set().ok()
}
}
impl<'a> FromKclValue<'a> for SketchSurfaceOrGroup { impl<'a> FromKclValue<'a> for SketchSurfaceOrGroup {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> { fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
match arg { 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::Plane(sg) => Some(Self::SketchSurface(SketchSurface::Plane(sg.clone()))),
KclValue::Face(sg) => Some(Self::SketchSurface(SketchSurface::Face(sg.clone()))), KclValue::Face(sg) => Some(Self::SketchSurface(SketchSurface::Face(sg.clone()))),
_ => None, _ => None,

View File

@ -76,7 +76,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args
let id = uuid::Uuid::new_v4(); let id = uuid::Uuid::new_v4();
// Extrude the element(s). // Extrude the element(s).
let sketch_groups: Vec<Box<SketchGroup>> = sketch_group_set.into(); let sketch_groups: Vec<SketchGroup> = sketch_group_set.into();
let mut extrude_groups = Vec::new(); let mut extrude_groups = Vec::new();
for sketch_group in &sketch_groups { for sketch_group in &sketch_groups {
// Before we extrude, we need to enable the sketch mode. // 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( pub(crate) async fn do_post_extrude(
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
length: f64, length: f64,
id: Uuid, id: Uuid,
args: Args, 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 we were sketching on a face, we need the original face id.
if let SketchSurface::Face(ref face) = sketch_group.on { if let SketchSurface::Face(ref face) = sketch_group.on {

View File

@ -102,7 +102,7 @@ impl RevolveAxisAndOrigin {
/// Revolve a sketch around an axis. /// Revolve a sketch around an axis.
pub async fn revolve(args: Args) -> Result<KclValue, KclError> { pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group): (RevolveData, Box<SketchGroup>) = 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?; let extrude_group = inner_revolve(data, sketch_group, args).await?;
Ok(KclValue::ExtrudeGroup(extrude_group)) Ok(KclValue::ExtrudeGroup(extrude_group))
@ -249,7 +249,7 @@ pub async fn revolve(args: Args) -> Result<KclValue, KclError> {
}] }]
async fn inner_revolve( async fn inner_revolve(
data: RevolveData, data: RevolveData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
args: Args, args: Args,
) -> Result<Box<ExtrudeGroup>, KclError> { ) -> Result<Box<ExtrudeGroup>, KclError> {
if let Some(angle) = data.angle { if let Some(angle) = data.angle {

View File

@ -108,7 +108,7 @@ pub async fn last_segment_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "lastSegX", name = "lastSegX",
}] }]
fn inner_last_segment_x(sketch_group: Box<SketchGroup>, args: Args) -> Result<f64, KclError> { fn inner_last_segment_x(sketch_group: SketchGroup, args: Args) -> Result<f64, KclError> {
let last_line = sketch_group let last_line = sketch_group
.value .value
.last() .last()
@ -151,7 +151,7 @@ pub async fn last_segment_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "lastSegY", name = "lastSegY",
}] }]
fn inner_last_segment_y(sketch_group: Box<SketchGroup>, args: Args) -> Result<f64, KclError> { fn inner_last_segment_y(sketch_group: SketchGroup, args: Args) -> Result<f64, KclError> {
let last_line = sketch_group let last_line = sketch_group
.value .value
.last() .last()
@ -281,7 +281,7 @@ pub async fn angle_to_match_length_x(args: Args) -> Result<KclValue, KclError> {
fn inner_angle_to_match_length_x( fn inner_angle_to_match_length_x(
tag: &TagIdentifier, tag: &TagIdentifier,
to: f64, to: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
args: Args, args: Args,
) -> Result<f64, KclError> { ) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?; let line = args.get_tag_engine_info(tag)?;
@ -347,7 +347,7 @@ pub async fn angle_to_match_length_y(args: Args) -> Result<KclValue, KclError> {
fn inner_angle_to_match_length_y( fn inner_angle_to_match_length_y(
tag: &TagIdentifier, tag: &TagIdentifier,
to: f64, to: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
args: Args, args: Args,
) -> Result<f64, KclError> { ) -> Result<f64, KclError> {
let line = args.get_tag_engine_info(tag)?; let line = args.get_tag_engine_info(tag)?;

View File

@ -27,7 +27,7 @@ pub async fn circle(args: Args) -> Result<KclValue, KclError> {
args.get_circle_args()?; args.get_circle_args()?;
let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?; 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 /// Construct a 2-dimensional circle, of the specified radius, centered at
@ -60,7 +60,7 @@ async fn inner_circle(
sketch_surface_or_group: SketchSurfaceOrGroup, sketch_surface_or_group: SketchSurfaceOrGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let sketch_surface = match sketch_surface_or_group { let sketch_surface = match sketch_surface_or_group {
SketchSurfaceOrGroup::SketchSurface(surface) => surface, SketchSurfaceOrGroup::SketchSurface(surface) => surface,
SketchSurfaceOrGroup::SketchGroup(group) => group.on, SketchSurfaceOrGroup::SketchGroup(group) => group.on,

View File

@ -90,11 +90,11 @@ pub enum StartOrEnd {
/// Draw a line to a point. /// Draw a line to a point.
pub async fn line_to(args: Args) -> Result<KclValue, KclError> { pub async fn line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): ([f64; 2], Box<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?;
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. /// 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<KclValue, KclError> {
}] }]
async fn inner_line_to( async fn inner_line_to(
to: [f64; 2], to: [f64; 2],
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let id = uuid::Uuid::new_v4(); 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. /// Draw a line to a point on the x-axis.
pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> { pub async fn x_line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) = let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_x_line_to(to, sketch_group, tag, args).await?; 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. /// 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<KclValue, KclError> {
}] }]
async fn inner_x_line_to( async fn inner_x_line_to(
to: f64, to: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let new_sketch_group = inner_line_to([to, from.y], sketch_group, tag, args).await?; 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. /// Draw a line to a point on the y-axis.
pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> { pub async fn y_line_to(args: Args) -> Result<KclValue, KclError> {
let (to, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) = let (to, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_y_line_to(to, sketch_group, tag, args).await?; 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. /// 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<KclValue, KclError> {
}] }]
async fn inner_y_line_to( async fn inner_y_line_to(
to: f64, to: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let new_sketch_group = inner_line_to([from.x, to], sketch_group, tag, args).await?; 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. /// Draw a line.
pub async fn line(args: Args) -> Result<KclValue, KclError> { pub async fn line(args: Args) -> Result<KclValue, KclError> {
let (delta, sketch_group, tag): ([f64; 2], Box<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?;
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 /// 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<KclValue, KclError> {
}] }]
async fn inner_line( async fn inner_line(
delta: [f64; 2], delta: [f64; 2],
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let to = [from.x + delta[0], from.y + delta[1]]; 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. /// Draw a line on the x-axis.
pub async fn x_line(args: Args) -> Result<KclValue, KclError> { pub async fn x_line(args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) = let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_x_line(length, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_x_line( async fn inner_x_line(
length: f64, length: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
inner_line([length, 0.0], sketch_group, tag, args).await inner_line([length, 0.0], sketch_group, tag, args).await
} }
/// Draw a line on the y-axis. /// Draw a line on the y-axis.
pub async fn y_line(args: Args) -> Result<KclValue, KclError> { pub async fn y_line(args: Args) -> Result<KclValue, KclError> {
let (length, sketch_group, tag): (f64, Box<SketchGroup>, Option<TagDeclarator>) = let (length, sketch_group, tag): (f64, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_y_line(length, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_y_line( async fn inner_y_line(
length: f64, length: f64,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
inner_line([0.0, length], sketch_group, tag, args).await inner_line([0.0, length], sketch_group, tag, args).await
} }
@ -431,11 +431,11 @@ pub enum AngledLineData {
/// Draw an angled line. /// Draw an angled line.
pub async fn angled_line(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line(data, sketch_group, tag, args).await?; 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 /// Draw a line segment relative to the current origin using the polar
@ -460,10 +460,10 @@ pub async fn angled_line(args: Args) -> Result<KclValue, KclError> {
}] }]
async fn inner_angled_line( async fn inner_angled_line(
data: AngledLineData, data: AngledLineData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let (angle, length) = match data { let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
@ -520,11 +520,11 @@ async fn inner_angled_line(
/// Draw an angled line of a given x length. /// Draw an angled line of a given x length.
pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line_of_x_length(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_of_x_length(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_angled_line_of_x_length( async fn inner_angled_line_of_x_length(
data: AngledLineData, data: AngledLineData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let (angle, length) = match data { let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]), AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]),
@ -588,11 +588,11 @@ pub struct AngledLineToData {
/// Draw an angled line to a given x coordinate. /// Draw an angled line to a given x coordinate.
pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line_to_x(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_to_x(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_angled_line_to_x( async fn inner_angled_line_to_x(
data: AngledLineToData, data: AngledLineToData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let AngledLineToData { angle, to: x_to } = data; 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. /// Draw an angled line of a given y length.
pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line_of_y_length(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_of_y_length(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_angled_line_of_y_length( async fn inner_angled_line_of_y_length(
data: AngledLineData, data: AngledLineData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let (angle, length) = match data { let (angle, length) = match data {
AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length), AngledLineData::AngleAndLengthNamed { angle, length } => (angle, length),
AngledLineData::AngleAndLengthPair(pair) => (pair[0], pair[1]), 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. /// Draw an angled line to a given y coordinate.
pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line_to_y(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineToData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineToData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_to_y(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_angled_line_to_y( async fn inner_angled_line_to_y(
data: AngledLineToData, data: AngledLineToData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let AngledLineToData { angle, to: y_to } = data; let AngledLineToData { angle, to: y_to } = data;
@ -776,10 +776,10 @@ pub struct AngledLineThatIntersectsData {
/// Draw an angled line that intersects with a given line. /// Draw an angled line that intersects with a given line.
pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclError> { pub async fn angled_line_that_intersects(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (AngledLineThatIntersectsData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (AngledLineThatIntersectsData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_angled_line_that_intersects(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclErro
}] }]
async fn inner_angled_line_that_intersects( async fn inner_angled_line_that_intersects(
data: AngledLineThatIntersectsData, data: AngledLineThatIntersectsData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?; let intersect_path = args.get_tag_engine_info(&data.intersect_tag)?;
let path = intersect_path.path.clone().ok_or_else(|| { let path = intersect_path.path.clone().ok_or_else(|| {
KclError::Type(KclErrorDetails { KclError::Type(KclErrorDetails {
@ -835,7 +835,7 @@ pub async fn start_sketch_at(args: Args) -> Result<KclValue, KclError> {
let data: [f64; 2] = args.get_data()?; let data: [f64; 2] = args.get_data()?;
let sketch_group = inner_start_sketch_at(data, args).await?; 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. /// 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<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "startSketchAt", name = "startSketchAt",
}] }]
async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<Box<SketchGroup>, KclError> { async fn inner_start_sketch_at(data: [f64; 2], args: Args) -> Result<SketchGroup, KclError> {
// Let's assume it's the XY plane for now, this is just for backwards compatibility. // Let's assume it's the XY plane for now, this is just for backwards compatibility.
let xy_plane = PlaneData::XY; let xy_plane = PlaneData::XY;
let sketch_surface = inner_start_sketch_on(SketchData::Plane(xy_plane), None, args.clone()).await?; 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<KclValue, KclError> {
args.get_data_and_sketch_surface()?; args.get_data_and_sketch_surface()?;
let sketch_group = inner_start_profile_at(start, sketch_surface, tag, args).await?; 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. /// Start a new profile at a given point.
@ -1249,7 +1249,7 @@ pub(crate) async fn inner_start_profile_at(
sketch_surface: SketchSurface, sketch_surface: SketchSurface,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
if let SketchSurface::Face(face) = &sketch_surface { if let SketchSurface::Face(face) = &sketch_surface {
// Flush the batch for our fillets/chamfers if there are any. // 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. // 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, start: current_path,
}; };
Ok(Box::new(sketch_group)) Ok(sketch_group)
} }
/// Returns the X component of the sketch profile start point. /// Returns the X component of the sketch profile start point.
pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> { pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?; let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_x(sketch_group)?; let x = inner_profile_start_x(sketch_group)?;
args.make_user_val_from_f64(x) args.make_user_val_from_f64(x)
} }
@ -1347,13 +1347,13 @@ pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "profileStartX" name = "profileStartX"
}] }]
pub(crate) fn inner_profile_start_x(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> { pub(crate) fn inner_profile_start_x(sketch_group: SketchGroup) -> Result<f64, KclError> {
Ok(sketch_group.start.to[0]) Ok(sketch_group.start.to[0])
} }
/// Returns the Y component of the sketch profile start point. /// Returns the Y component of the sketch profile start point.
pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> { pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?; let sketch_group: SketchGroup = args.get_sketch_group()?;
let x = inner_profile_start_y(sketch_group)?; let x = inner_profile_start_y(sketch_group)?;
args.make_user_val_from_f64(x) args.make_user_val_from_f64(x)
} }
@ -1370,13 +1370,13 @@ pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "profileStartY" name = "profileStartY"
}] }]
pub(crate) fn inner_profile_start_y(sketch_group: Box<SketchGroup>) -> Result<f64, KclError> { pub(crate) fn inner_profile_start_y(sketch_group: SketchGroup) -> Result<f64, KclError> {
Ok(sketch_group.start.to[1]) Ok(sketch_group.start.to[1])
} }
/// Returns the sketch profile start point. /// Returns the sketch profile start point.
pub async fn profile_start(args: Args) -> Result<KclValue, KclError> { pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
let sketch_group: Box<SketchGroup> = args.get_sketch_group()?; let sketch_group: SketchGroup = args.get_sketch_group()?;
let point = inner_profile_start(sketch_group)?; let point = inner_profile_start(sketch_group)?;
Ok(KclValue::UserVal(UserVal { Ok(KclValue::UserVal(UserVal {
value: serde_json::to_value(point).map_err(|e| { value: serde_json::to_value(point).map_err(|e| {
@ -1404,17 +1404,17 @@ pub async fn profile_start(args: Args) -> Result<KclValue, KclError> {
#[stdlib { #[stdlib {
name = "profileStart" name = "profileStart"
}] }]
pub(crate) fn inner_profile_start(sketch_group: Box<SketchGroup>) -> Result<[f64; 2], KclError> { pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2], KclError> {
Ok(sketch_group.start.to) Ok(sketch_group.start.to)
} }
/// Close the current sketch. /// Close the current sketch.
pub async fn close(args: Args) -> Result<KclValue, KclError> { pub async fn close(args: Args) -> Result<KclValue, KclError> {
let (sketch_group, tag): (Box<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?;
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 /// Construct a line segment from the current origin back to the profile's
@ -1442,10 +1442,10 @@ pub async fn close(args: Args) -> Result<KclValue, KclError> {
name = "close", name = "close",
}] }]
pub(crate) async fn inner_close( pub(crate) async fn inner_close(
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let to: Point2d = sketch_group.start.from.into(); let to: Point2d = sketch_group.start.from.into();
@ -1517,11 +1517,11 @@ pub enum ArcData {
/// Draw an arc. /// Draw an arc.
pub async fn arc(args: Args) -> Result<KclValue, KclError> { pub async fn arc(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (ArcData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (ArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_arc(data, sketch_group, tag, args).await?; 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 /// Starting at the current sketch's origin, draw a curved line segment along
@ -1553,10 +1553,10 @@ pub async fn arc(args: Args) -> Result<KclValue, KclError> {
}] }]
pub(crate) async fn inner_arc( pub(crate) async fn inner_arc(
data: ArcData, data: ArcData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?; let from: Point2d = sketch_group.current_pen_position()?;
let (center, angle_start, angle_end, radius, end) = match &data { let (center, angle_start, angle_end, radius, end) = match &data {
@ -1640,11 +1640,11 @@ pub enum TangentialArcData {
/// Draw a tangential arc. /// Draw a tangential arc.
pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> { pub async fn tangential_arc(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (TangentialArcData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (TangentialArcData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_tangential_arc(data, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_tangential_arc( async fn inner_tangential_arc(
data: TangentialArcData, data: TangentialArcData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?; let from: Point2d = sketch_group.current_pen_position()?;
// next set of lines is some undocumented voodoo from get_tangential_arc_to_info // 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 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<KclValue, KclError> {
// Get arguments to function call // Get arguments to function call
let mut it = args.args.iter(); let mut it = args.args.iter();
let to: [f64; 2] = get_arg(&mut it, src)?.get_json()?; let to: [f64; 2] = get_arg(&mut it, src)?.get_json()?;
let sketch_group: Box<SketchGroup> = 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) { let tag = if let Ok(memory_item) = get_arg(&mut it, src) {
memory_item.get_json_opt()? memory_item.get_json_opt()?
} else { } else {
@ -1801,7 +1801,7 @@ pub async fn tangential_arc_to(args: Args) -> Result<KclValue, KclError> {
}; };
let new_sketch_group = inner_tangential_arc_to(to, sketch_group, tag, args).await?; 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 /// 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<KclValue, KclError> {
}] }]
async fn inner_tangential_arc_to( async fn inner_tangential_arc_to(
to: [f64; 2], to: [f64; 2],
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from: Point2d = sketch_group.current_pen_position()?; let from: Point2d = sketch_group.current_pen_position()?;
let tangent_info = sketch_group.get_tangential_info_from_paths(); let tangent_info = sketch_group.get_tangential_info_from_paths();
let tan_previous_point = if tangent_info.is_center { let tan_previous_point = if tangent_info.is_center {
@ -1888,11 +1888,11 @@ pub struct BezierData {
/// Draw a bezier curve. /// Draw a bezier curve.
pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> { pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
let (data, sketch_group, tag): (BezierData, Box<SketchGroup>, Option<TagDeclarator>) = let (data, sketch_group, tag): (BezierData, SketchGroup, Option<TagDeclarator>) =
args.get_data_and_sketch_group_and_tag()?; args.get_data_and_sketch_group_and_tag()?;
let new_sketch_group = inner_bezier_curve(data, sketch_group, tag, args).await?; 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 /// Draw a smooth, continuous, curved line segment from the current origin to
@ -1918,10 +1918,10 @@ pub async fn bezier_curve(args: Args) -> Result<KclValue, KclError> {
}] }]
async fn inner_bezier_curve( async fn inner_bezier_curve(
data: BezierData, data: BezierData,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
tag: Option<TagDeclarator>, tag: Option<TagDeclarator>,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let from = sketch_group.current_pen_position()?; let from = sketch_group.current_pen_position()?;
let relative = true; let relative = true;
@ -1980,10 +1980,10 @@ async fn inner_bezier_curve(
/// Use a sketch to cut a hole in another sketch. /// Use a sketch to cut a hole in another sketch.
pub async fn hole(args: Args) -> Result<KclValue, KclError> { pub async fn hole(args: Args) -> Result<KclValue, KclError> {
let (hole_sketch_group, sketch_group): (SketchGroupSet, Box<SketchGroup>) = 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?; 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. /// 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<KclValue, KclError> {
}] }]
async fn inner_hole( async fn inner_hole(
hole_sketch_group: SketchGroupSet, hole_sketch_group: SketchGroupSet,
sketch_group: Box<SketchGroup>, sketch_group: SketchGroup,
args: Args, args: Args,
) -> Result<Box<SketchGroup>, KclError> { ) -> Result<SketchGroup, KclError> {
let hole_sketch_groups: Vec<Box<SketchGroup>> = hole_sketch_group.into(); let hole_sketch_groups: Vec<SketchGroup> = hole_sketch_group.into();
for hole_sketch_group in hole_sketch_groups { for hole_sketch_group in hole_sketch_groups {
args.batch_modeling_cmd( args.batch_modeling_cmd(
uuid::Uuid::new_v4(), uuid::Uuid::new_v4(),

View File

@ -1,7 +1,7 @@
use anyhow::Result; use anyhow::Result;
use kcl_lib::{ use kcl_lib::{
ast::{modify::modify_ast_for_sketch, types::Program}, 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 kittycad::types::{ModelingCmd, Point3D};
use pretty_assertions::assert_eq; 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. // We need to get the sketch ID.
// Get the sketch group ID from memory. // 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); anyhow::bail!("part001 not found in memory: {:?}", memory);
}; };
let Some((sketch_group, _meta)) = user_val.get::<SketchGroup>() else {
anyhow::bail!("part001 was not a SketchGroup");
};
let sketch_id = sketch_group.id; let sketch_id = sketch_group.id;
let plane_id = uuid::Uuid::new_v4(); let plane_id = uuid::Uuid::new_v4();