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,
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 sg
}
function colorSegment(object: any, color: number) {

View File

@ -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') {

View File

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

View File

@ -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,6 +152,8 @@ const newVar = myVar + 1`
].join('\n')
const mem = await exe(code)
expect(mem.get('mySk1')).toEqual({
type: 'UserVal',
value: {
type: 'SketchGroup',
on: expect.any(Object),
start: {
@ -209,6 +217,8 @@ const newVar = myVar + 1`
],
id: expect.any(String),
__meta: [{ sourceRange: [39, 63] }],
},
__meta: [{ sourceRange: [39, 63] }],
})
})
it('execute array expression', async () => {
@ -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([

View File

@ -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

View File

@ -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(

View File

@ -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
)

View File

@ -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

View File

@ -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,

View File

@ -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(),

View File

@ -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

View File

@ -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) => {
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 {

View File

@ -203,13 +203,22 @@ impl Environment {
}
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 {
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<TagDeclarator>),
Plane(Box<Plane>),
Face(Box<Face>),
SketchGroup(Box<SketchGroup>),
SketchGroups {
value: Vec<Box<SketchGroup>>,
},
ExtrudeGroup(Box<ExtrudeGroup>),
ExtrudeGroups {
value: Vec<Box<ExtrudeGroup>>,
@ -289,27 +295,8 @@ pub enum KclValue {
}
impl KclValue {
pub(crate) fn get_sketch_group_set(&self) -> Result<SketchGroupSet> {
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::<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 new_user_val<T: Serialize>(meta: Vec<Metadata>, val: T) -> Self {
Self::UserVal(UserVal::set(meta, val))
}
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::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<SketchGroupSet> 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<Vec<Box<SketchGroup>>> for KclValue {
fn from(sg: Vec<Box<SketchGroup>>) -> 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<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 {
fn from(sg: SketchGroup) -> Self {
SketchGroupSet::SketchGroup(Box::new(sg))
@ -641,6 +638,43 @@ pub struct UserVal {
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)]
#[ts(export)]
#[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::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

View File

@ -251,11 +251,11 @@ impl Args {
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)
}
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)
}
@ -270,9 +270,7 @@ impl Args {
FromArgs::from_args(self, 0)
}
pub(crate) fn get_sketch_group_and_optional_tag(
&self,
) -> Result<(Box<SketchGroup>, Option<TagDeclarator>), KclError> {
pub(crate) fn get_sketch_group_and_optional_tag(&self) -> Result<(SketchGroup, Option<TagDeclarator>), 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<SketchGroup>), 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<SketchGroup>, Option<TagDeclarator>), KclError>
) -> Result<(T, SketchGroup, Option<TagDeclarator>), 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<SketchGroup>), 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<Self> {
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<SketchGroup> {
impl<'a> FromKclValue<'a> for SketchGroupSet {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
let KclValue::SketchGroup(s) = arg else {
let KclValue::UserVal(uv) = arg else {
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()
}
}
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 {
fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
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,

View File

@ -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<Box<SketchGroup>> = sketch_group_set.into();
let sketch_groups: Vec<SketchGroup> = 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<SketchGroup>,
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 {

View File

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

View File

@ -108,7 +108,7 @@ pub async fn last_segment_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
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
.value
.last()
@ -151,7 +151,7 @@ pub async fn last_segment_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
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
.value
.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(
tag: &TagIdentifier,
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<f64, KclError> {
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(
tag: &TagIdentifier,
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<f64, KclError> {
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()?;
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<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
let sketch_surface = match sketch_surface_or_group {
SketchSurfaceOrGroup::SketchSurface(surface) => surface,
SketchSurfaceOrGroup::SketchGroup(group) => group.on,

View File

@ -90,11 +90,11 @@ pub enum StartOrEnd {
/// Draw a line to a point.
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()?;
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<KclValue, KclError> {
}]
async fn inner_line_to(
to: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_x_line_to(
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_y_line_to(
to: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_line(
delta: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_x_line(
length: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_y_line(
length: f64,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_angled_line(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_angled_line_of_x_length(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_angled_line_to_x(
data: AngledLineToData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_angled_line_of_y_length(
data: AngledLineData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_angled_line_to_y(
data: AngledLineToData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclErro
}]
async fn inner_angled_line_that_intersects(
data: AngledLineThatIntersectsData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<KclValue, KclError> {
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<KclValue, KclError> {
#[stdlib {
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 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<KclValue, KclError> {
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<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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)?;
args.make_user_val_from_f64(x)
}
@ -1347,13 +1347,13 @@ pub async fn profile_start_x(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
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])
}
/// Returns the Y component of the sketch profile start point.
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)?;
args.make_user_val_from_f64(x)
}
@ -1370,13 +1370,13 @@ pub async fn profile_start_y(args: Args) -> Result<KclValue, KclError> {
#[stdlib {
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])
}
/// Returns the sketch profile start point.
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)?;
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<KclValue, KclError> {
#[stdlib {
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)
}
/// Close the current sketch.
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?;
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<KclValue, KclError> {
name = "close",
}]
pub(crate) async fn inner_close(
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
pub(crate) async fn inner_arc(
data: ArcData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_tangential_arc(
data: TangentialArcData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<KclValue, KclError> {
// 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<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) {
memory_item.get_json_opt()?
} 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?;
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<KclValue, KclError> {
}]
async fn inner_tangential_arc_to(
to: [f64; 2],
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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()?;
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<KclValue, KclError> {
}]
async fn inner_bezier_curve(
data: BezierData,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
tag: Option<TagDeclarator>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
) -> Result<SketchGroup, KclError> {
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<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?;
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<KclValue, KclError> {
}]
async fn inner_hole(
hole_sketch_group: SketchGroupSet,
sketch_group: Box<SketchGroup>,
sketch_group: SketchGroup,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
let hole_sketch_groups: Vec<Box<SketchGroup>> = hole_sketch_group.into();
) -> Result<SketchGroup, KclError> {
let hole_sketch_groups: Vec<SketchGroup> = hole_sketch_group.into();
for hole_sketch_group in hole_sketch_groups {
args.batch_modeling_cmd(
uuid::Uuid::new_v4(),

View File

@ -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::<SketchGroup>() else {
anyhow::bail!("part001 was not a SketchGroup");
};
let sketch_id = sketch_group.id;
let plane_id = uuid::Uuid::new_v4();