From fedffbb3841195875bb903e036ae13048bafb8bb Mon Sep 17 00:00:00 2001 From: Adam Chalmers Date: Sat, 2 Mar 2024 18:39:31 -0600 Subject: [PATCH] Grackle: stdlib LineTo function (#1601) * Bump execution plan * Grackle: lineTo stdlib function (#1605) * Remove test JSON output --- .gitignore | 1 + src/wasm-lib/Cargo.lock | 16 ++-- src/wasm-lib/grackle/src/binding_scope.rs | 4 + src/wasm-lib/grackle/src/lib.rs | 2 + .../grackle/src/native_functions/sketch.rs | 2 +- .../src/native_functions/sketch/helpers.rs | 8 +- .../sketch/stdlib_functions.rs | 78 ++++++++++++++++++- .../src/native_functions/sketch/types.rs | 21 +++-- src/wasm-lib/grackle/src/tests.rs | 25 ++++-- 9 files changed, 133 insertions(+), 24 deletions(-) diff --git a/.gitignore b/.gitignore index 7386145ef..353024d3a 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ src/wasm-lib/bindings src/wasm-lib/kcl/bindings public/wasm_lib_bg.wasm src/wasm-lib/lcov.info +src/wasm-lib/grackle/test_json_output e2e/playwright/playwright-secrets.env e2e/playwright/temp1.png diff --git a/src/wasm-lib/Cargo.lock b/src/wasm-lib/Cargo.lock index af3c94ab3..44c2ffc7d 100644 --- a/src/wasm-lib/Cargo.lock +++ b/src/wasm-lib/Cargo.lock @@ -1990,7 +1990,7 @@ dependencies = [ [[package]] name = "kittycad-execution-plan" version = "0.1.0" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "bytes", "insta", @@ -2009,7 +2009,7 @@ dependencies = [ [[package]] name = "kittycad-execution-plan-macros" version = "0.1.6" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "proc-macro2", "quote", @@ -2019,7 +2019,7 @@ dependencies = [ [[package]] name = "kittycad-execution-plan-traits" version = "0.1.11" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "serde", "thiserror", @@ -2028,8 +2028,8 @@ dependencies = [ [[package]] name = "kittycad-modeling-cmds" -version = "0.1.26" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +version = "0.1.27" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "anyhow", "chrono", @@ -2056,8 +2056,8 @@ dependencies = [ [[package]] name = "kittycad-modeling-cmds-macros" -version = "0.1.1" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +version = "0.1.2" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "proc-macro2", "quote", @@ -2067,7 +2067,7 @@ dependencies = [ [[package]] name = "kittycad-modeling-session" version = "0.1.0" -source = "git+https://github.com/KittyCAD/modeling-api?branch=main#29086e1079adb82b6427639a779dc58eabcd7f78" +source = "git+https://github.com/KittyCAD/modeling-api?branch=main#9c96767289139f03036c2ba40f889f974ca3e976" dependencies = [ "futures", "kittycad", diff --git a/src/wasm-lib/grackle/src/binding_scope.rs b/src/wasm-lib/grackle/src/binding_scope.rs index 10f68f2c2..85fe65b1b 100644 --- a/src/wasm-lib/grackle/src/binding_scope.rs +++ b/src/wasm-lib/grackle/src/binding_scope.rs @@ -105,6 +105,10 @@ impl BindingScope { "startSketchAt".into(), EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)), ), + ( + "lineTo".into(), + EpBinding::from(KclFunction::LineTo(native_functions::sketch::LineTo)), + ), ]), parent: None, } diff --git a/src/wasm-lib/grackle/src/lib.rs b/src/wasm-lib/grackle/src/lib.rs index 6fd5854b6..0e7fe3c06 100644 --- a/src/wasm-lib/grackle/src/lib.rs +++ b/src/wasm-lib/grackle/src/lib.rs @@ -252,6 +252,7 @@ impl Planner { } = match callee { KclFunction::Id(f) => f.call(&mut self.next_addr, args)?, KclFunction::StartSketchAt(f) => f.call(&mut self.next_addr, args)?, + KclFunction::LineTo(f) => f.call(&mut self.next_addr, args)?, KclFunction::Add(f) => f.call(&mut self.next_addr, args)?, KclFunction::UserDefined(f) => { let UserDefinedFunction { @@ -619,6 +620,7 @@ impl Eq for UserDefinedFunction {} enum KclFunction { Id(native_functions::Id), StartSketchAt(native_functions::sketch::StartSketchAt), + LineTo(native_functions::sketch::LineTo), Add(native_functions::Add), UserDefined(UserDefinedFunction), } diff --git a/src/wasm-lib/grackle/src/native_functions/sketch.rs b/src/wasm-lib/grackle/src/native_functions/sketch.rs index 03a717279..116734b9b 100644 --- a/src/wasm-lib/grackle/src/native_functions/sketch.rs +++ b/src/wasm-lib/grackle/src/native_functions/sketch.rs @@ -4,4 +4,4 @@ pub mod helpers; pub mod stdlib_functions; pub mod types; -pub use stdlib_functions::StartSketchAt; +pub use stdlib_functions::{LineTo, StartSketchAt}; diff --git a/src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs b/src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs index d1aa54018..9a2636d53 100644 --- a/src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs +++ b/src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs @@ -1,4 +1,4 @@ -use kittycad_execution_plan::{api_request::ApiRequest, Instruction}; +use kittycad_execution_plan::{api_request::ApiRequest, Destination, Instruction}; use kittycad_execution_plan_traits::{Address, InMemory}; use kittycad_modeling_cmds::{id::ModelingCmdId, ModelingCmdEndpoint}; @@ -120,11 +120,13 @@ pub fn arg_point2d( instructions.extend([ Instruction::Copy { source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?, - destination: start_x, + destination: Destination::Address(start_x), + length: 1, }, Instruction::Copy { source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?, - destination: start_y, + destination: Destination::Address(start_y), + length: 1, }, Instruction::SetPrimitive { address: start_z, diff --git a/src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs b/src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs index f7d614026..06b25c0d9 100644 --- a/src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs +++ b/src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs @@ -1,4 +1,4 @@ -use kittycad_execution_plan::{api_request::ApiRequest, Instruction}; +use kittycad_execution_plan::{api_request::ApiRequest, Destination, Instruction}; use kittycad_execution_plan_traits::{Address, InMemory, Value}; use kittycad_modeling_cmds::{ shared::{Point3d, Point4d}, @@ -12,6 +12,82 @@ use super::{ }; use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan}; +#[derive(Debug, Clone)] +#[cfg_attr(test, derive(Eq, PartialEq))] +pub struct LineTo; + +impl Callable for LineTo { + fn call(&self, next_addr: &mut Address, args: Vec) -> Result { + let mut instructions = Vec::new(); + let fn_name = "lineTo"; + // Get both required params. + let mut args_iter = args.into_iter(); + let Some(to) = args_iter.next() else { + return Err(CompileError::NotEnoughArgs { + fn_name: fn_name.into(), + required: 2, + actual: 0, + }); + }; + let Some(sketch_group) = args_iter.next() else { + return Err(CompileError::NotEnoughArgs { + fn_name: fn_name.into(), + required: 2, + actual: 1, + }); + }; + // Check the type of both required params. + let to = arg_point2d(to, fn_name, &mut instructions, next_addr, 0)?; + let sg = single_binding(sketch_group, fn_name, "sketch group", 1)?; + let id = Uuid::new_v4(); + let start_of_line = next_addr.offset(1); + let length_of_3d_point = Point3d::::default().into_parts().len(); + instructions.extend([ + // Push the `to` 2D point onto the stack. + Instruction::Copy { + source: to, + length: 2, + destination: Destination::StackPush, + }, + // Make it a 3D point. + Instruction::StackExtend { data: vec![0.0.into()] }, + // Append the new path segment to memory. + // First comes its tag. + Instruction::SetPrimitive { + address: start_of_line, + value: "Line".to_owned().into(), + }, + // Then its end + Instruction::StackPop { + destination: Some(start_of_line + 1), + }, + // Then its `relative` field. + Instruction::SetPrimitive { + address: start_of_line + 1 + length_of_3d_point, + value: false.into(), + }, + // Send the ExtendPath request + Instruction::ApiRequest(ApiRequest { + endpoint: ModelingCmdEndpoint::ExtendPath, + store_response: None, + arguments: vec![ + // Path ID + InMemory::Address(sg + SketchGroup::path_id_offset()), + // Segment + InMemory::Address(start_of_line), + ], + cmd_id: id.into(), + }), + ]); + + // TODO: Create a new SketchGroup from the old one + add the new path, then store it. + Ok(EvalPlan { + instructions, + binding: EpBinding::Single(Address::ZERO + 9999), + }) + } +} + #[derive(Debug, Clone)] #[cfg_attr(test, derive(Eq, PartialEq))] pub struct StartSketchAt; diff --git a/src/wasm-lib/grackle/src/native_functions/sketch/types.rs b/src/wasm-lib/grackle/src/native_functions/sketch/types.rs index db715faa9..04c6a9e5d 100644 --- a/src/wasm-lib/grackle/src/native_functions/sketch/types.rs +++ b/src/wasm-lib/grackle/src/native_functions/sketch/types.rs @@ -1,4 +1,4 @@ -use kittycad_execution_plan::Instruction; +use kittycad_execution_plan::{Destination, Instruction}; use kittycad_execution_plan_macros::ExecutionPlanValue; use kittycad_execution_plan_traits::{Address, Value}; use kittycad_modeling_cmds::shared::{Point2d, Point3d, Point4d}; @@ -7,6 +7,8 @@ use uuid::Uuid; /// A sketch group is a collection of paths. #[derive(Clone, ExecutionPlanValue)] pub struct SketchGroup { + // NOTE to developers + // Do NOT reorder these fields without updating the _offset() methods below. /// The id of the sketch group. pub id: Uuid, /// What the sketch is on (can be a plane or a face). @@ -26,6 +28,10 @@ pub struct SketchGroup { } impl SketchGroup { + /// Get the offset for the `id` field. + pub fn path_id_offset() -> usize { + 0 + } pub fn set_base_path(&self, sketch_group: Address, start_point: Address, tag: Option
) -> Vec { let base_path_addr = sketch_group + self.id.into_parts().len() @@ -39,21 +45,24 @@ impl SketchGroup { // Copy over the `from` field. Instruction::Copy { source: start_point, - destination: base_path_addr, + destination: Destination::Address(base_path_addr), + length: 1, }, // Copy over the `to` field. Instruction::Copy { source: start_point, - destination: base_path_addr + self.path_first.from.into_parts().len(), + destination: Destination::Address(base_path_addr + self.path_first.from.into_parts().len()), + length: 1, }, ]; if let Some(tag) = tag { // Copy over the `name` field. out.push(Instruction::Copy { source: tag, - destination: base_path_addr - + self.path_first.from.into_parts().len() - + self.path_first.to.into_parts().len(), + destination: Destination::Address( + base_path_addr + self.path_first.from.into_parts().len() + self.path_first.to.into_parts().len(), + ), + length: 1, }); } out diff --git a/src/wasm-lib/grackle/src/tests.rs b/src/wasm-lib/grackle/src/tests.rs index 9765a6d95..9e3bdcb60 100644 --- a/src/wasm-lib/grackle/src/tests.rs +++ b/src/wasm-lib/grackle/src/tests.rs @@ -1048,15 +1048,30 @@ fn store_object_with_array_property() { #[tokio::test] async fn stdlib_cube_partial() { let program = r#" - let cube = startSketchAt([22.0, 33.0]) + let cube = startSketchAt([0.0, 0.0]) + |> lineTo([4.0, 0.0], %) "#; - let (plan, _scope) = must_plan(program); - std::fs::write("stdlib_cube_partial.json", serde_json::to_string_pretty(&plan).unwrap()).unwrap(); + let (_plan, _scope) = must_plan(program); let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program)) .ast() .unwrap(); - let mem = crate::execute(ast, Some(test_client().await)).await.unwrap(); - dbg!(mem); + let client = test_client().await; + let _mem = crate::execute(ast, Some(client)).await.unwrap(); + // use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat, ModelingCmd}; + // let out = client + // .run_command( + // uuid::Uuid::new_v4().into(), + // each_cmd::TakeSnapshot { + // format: ImageFormat::Png, + // }, + // ) + // .await + // .unwrap(); + // let out = match out { + // OkModelingCmdResponse::TakeSnapshot(b) => b, + // other => panic!("wrong output: {other:?}"), + // }; + // let out: Vec = out.contents.into(); } async fn test_client() -> Session {