diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index 321342805..f6260ceb3 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -4,6 +4,7 @@ use std::{collections::HashMap, sync::Arc}; use anyhow::Result; use async_recursion::async_recursion; +use fnv::FnvHashMap; use kcmc::{ each_cmd as mcmd, ok_response::{output::TakeSnapshot, OkModelingCmdResponse}, @@ -45,6 +46,16 @@ pub struct ExecState { /// The current value of the pipe operator returned from the previous /// expression. If we're not currently in a pipeline, this will be None. pub pipe_value: Option, + /// Artifacts created by the program. It's safe to use a faster hash + /// algorithm since all keys are UUIDs that we generate. + pub artifacts: FnvHashMap, +} + +impl ExecState { + /// Insert or update artifact by ID. + pub(crate) fn put_artifact(&mut self, id: ArtifactId, value: KclValue) { + self.artifacts.insert(id, Artifact { id, value }); + } } #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] @@ -396,6 +407,21 @@ impl From>> for KclValue { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema)] +pub struct ArtifactId(uuid::Uuid); + +impl ArtifactId { + pub(crate) fn new(id: uuid::Uuid) -> Self { + Self(id) + } +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)] +pub struct Artifact { + pub id: ArtifactId, + pub value: KclValue, +} + /// A geometry. #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[ts(export)] diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index ef897383f..7513c6212 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -15,17 +15,17 @@ use uuid::Uuid; use crate::{ errors::{KclError, KclErrorDetails}, executor::{ - ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, SketchGroupSet, - SketchSurface, + ArtifactId, ExecState, ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, GeoMeta, KclValue, Path, SketchGroup, + SketchGroupSet, SketchSurface, }, std::Args, }; /// Extrudes by a given amount. -pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result { +pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result { let (length, sketch_group_set) = args.get_number_sketch_group_set()?; - let result = inner_extrude(length, sketch_group_set, args).await?; + let result = inner_extrude(length, sketch_group_set, exec_state, args).await?; Ok(result.into()) } @@ -79,7 +79,12 @@ pub async fn extrude(_exec_state: &mut ExecState, args: Args) -> Result Result { +async fn inner_extrude( + length: f64, + sketch_group_set: SketchGroupSet, + exec_state: &mut ExecState, + args: Args, +) -> Result { let id = uuid::Uuid::new_v4(); // Extrude the element(s). @@ -120,7 +125,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}), ) .await?; - extrude_groups.push(do_post_extrude(sketch_group.clone(), length, args.clone()).await?); + extrude_groups.push(do_post_extrude(sketch_group.clone(), length, exec_state, args.clone()).await?); } Ok(extrude_groups.into()) @@ -129,6 +134,7 @@ async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args pub(crate) async fn do_post_extrude( sketch_group: SketchGroup, length: f64, + exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { // Bring the object to the front of the scene. @@ -276,7 +282,7 @@ pub(crate) async fn do_post_extrude( }) .collect(); - Ok(Box::new(ExtrudeGroup { + let extrude_group = Box::new(ExtrudeGroup { // Ok so you would think that the id would be the id of the extrude group, // that we passed in to the function, but it's actually the id of the // sketch group. @@ -288,7 +294,13 @@ pub(crate) async fn do_post_extrude( start_cap_id, end_cap_id, edge_cuts: vec![], - })) + }); + exec_state.put_artifact( + ArtifactId::new(extrude_group.id), + KclValue::ExtrudeGroup(extrude_group.clone()), + ); + + Ok(extrude_group) } #[derive(Default)] diff --git a/src/wasm-lib/kcl/src/std/loft.rs b/src/wasm-lib/kcl/src/std/loft.rs index 4920d38fa..ad0941839 100644 --- a/src/wasm-lib/kcl/src/std/loft.rs +++ b/src/wasm-lib/kcl/src/std/loft.rs @@ -50,10 +50,10 @@ impl Default for LoftData { } /// Create a 3D surface or solid by interpolating between two or more sketches. -pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result { +pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result { let (sketch_groups, data): (Vec, Option) = args.get_sketch_groups_and_data()?; - let extrude_group = inner_loft(sketch_groups, data, args).await?; + let extrude_group = inner_loft(sketch_groups, data, exec_state, args).await?; Ok(KclValue::ExtrudeGroup(extrude_group)) } @@ -138,6 +138,7 @@ pub async fn loft(_exec_state: &mut ExecState, args: Args) -> Result, data: Option, + exec_state: &mut ExecState, args: Args, ) -> Result, KclError> { // Make sure we have at least two sketches. @@ -170,5 +171,5 @@ async fn inner_loft( .await?; // Using the first sketch as the base curve, idk we might want to change this later. - do_post_extrude(sketch_groups[0].clone(), 0.0, args).await + do_post_extrude(sketch_groups[0].clone(), 0.0, exec_state, args).await } diff --git a/src/wasm-lib/kcl/src/std/revolve.rs b/src/wasm-lib/kcl/src/std/revolve.rs index 8edc22098..ace0a1ee5 100644 --- a/src/wasm-lib/kcl/src/std/revolve.rs +++ b/src/wasm-lib/kcl/src/std/revolve.rs @@ -295,7 +295,7 @@ async fn inner_revolve( } } - do_post_extrude(sketch_group, 0.0, args).await + do_post_extrude(sketch_group, 0.0, exec_state, args).await } #[cfg(test)] diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 50d0233a9..26ee8003d 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; use crate::{ ast::types::TagDeclarator, errors::KclError, - executor::{BasePath, ExecState, GeoMeta, KclValue, Path, SketchGroup, SketchSurface}, + executor::{ArtifactId, BasePath, ExecState, GeoMeta, KclValue, Path, SketchGroup, SketchSurface}, std::Args, }; @@ -46,6 +46,11 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result Result { +pub async fn line_to(exec_state: &mut ExecState, args: Args) -> Result { let (to, sketch_group, tag): ([f64; 2], SketchGroup, Option) = args.get_data_and_sketch_group_and_tag()?; let new_sketch_group = inner_line_to(to, sketch_group, tag, args).await?; + exec_state.put_artifact( + ArtifactId::new(new_sketch_group.id), + KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group.clone()), + ); + Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) } @@ -1410,10 +1415,14 @@ pub(crate) fn inner_profile_start(sketch_group: SketchGroup) -> Result<[f64; 2], } /// Close the current sketch. -pub async fn close(_exec_state: &mut ExecState, args: Args) -> Result { +pub async fn close(exec_state: &mut ExecState, args: Args) -> Result { let (sketch_group, tag): (SketchGroup, Option) = args.get_sketch_group_and_optional_tag()?; let new_sketch_group = inner_close(sketch_group, tag, args).await?; + exec_state.put_artifact( + ArtifactId::new(new_sketch_group.id), + KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group.clone()), + ); Ok(KclValue::new_user_val(new_sketch_group.meta.clone(), new_sketch_group)) }