Compare commits
4 Commits
v0.24.5
...
lee/native
Author | SHA1 | Date | |
---|---|---|---|
8b0510a244 | |||
5c6483ae67 | |||
7700d01403 | |||
b212ff4470 |
938
src/wasm-lib/Cargo.lock
generated
938
src/wasm-lib/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
BIN
src/wasm-lib/grackle/samples/cube.stl
Normal file
BIN
src/wasm-lib/grackle/samples/cube.stl
Normal file
Binary file not shown.
0
src/wasm-lib/grackle/samples/empty-file.stl
Normal file
0
src/wasm-lib/grackle/samples/empty-file.stl
Normal file
@ -121,6 +121,10 @@ impl BindingScope {
|
||||
"close".into(),
|
||||
EpBinding::from(KclFunction::Close(native_functions::sketch::Close)),
|
||||
),
|
||||
(
|
||||
"import".into(),
|
||||
EpBinding::from(KclFunction::ImportFile(native_functions::import_files::ImportFiles)),
|
||||
),
|
||||
]),
|
||||
parent: None,
|
||||
}
|
||||
|
@ -264,6 +264,7 @@ impl Planner {
|
||||
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Add(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Close(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::ImportFile(f) => f.call(&mut self.next_addr, args)?,
|
||||
KclFunction::UserDefined(f) => {
|
||||
let UserDefinedFunction {
|
||||
params_optional,
|
||||
@ -631,6 +632,7 @@ enum KclFunction {
|
||||
Id(native_functions::Id),
|
||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||
LineTo(native_functions::sketch::LineTo),
|
||||
ImportFile(native_functions::import_files::ImportFiles),
|
||||
Add(native_functions::Add),
|
||||
UserDefined(UserDefinedFunction),
|
||||
Extrude(native_functions::sketch::Extrude),
|
||||
|
@ -7,6 +7,7 @@ use kittycad_execution_plan_traits::Address;
|
||||
|
||||
use crate::{CompileError, EpBinding, EvalPlan};
|
||||
|
||||
pub mod import_files;
|
||||
pub mod sketch;
|
||||
|
||||
/// The identity function. Always returns its first input.
|
||||
|
116
src/wasm-lib/grackle/src/native_functions/import_files.rs
Normal file
116
src/wasm-lib/grackle/src/native_functions/import_files.rs
Normal file
@ -0,0 +1,116 @@
|
||||
//! Standard library functions involved in importing files.
|
||||
|
||||
use kittycad_execution_plan::{api_request::ApiRequest, import_files, Instruction};
|
||||
use kittycad_execution_plan_traits::{Address, InMemory, MemoryArea, Primitive};
|
||||
use kittycad_modeling_cmds::ModelingCmdEndpoint;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{binding_scope::EpBinding, error::CompileError, EvalPlan};
|
||||
|
||||
use super::Callable;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct ImportFiles;
|
||||
|
||||
/// Import a CAD file.
|
||||
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
|
||||
/// Otherwise you can specify the unit by passing in the options parameter.
|
||||
/// If you import a gltf file, we will try to find the bin file and import it as well.
|
||||
impl Callable for ImportFiles {
|
||||
fn call(&self, addr_retval: &mut Address, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||
let mut instructions: Vec<Instruction> = vec![];
|
||||
|
||||
let fn_name = "import";
|
||||
let required_args = 1;
|
||||
|
||||
let args_len = args.len();
|
||||
let mut args_iter = args.into_iter();
|
||||
|
||||
let EpBinding::Single(file_path) = args_iter.next().ok_or_else(|| CompileError::NotEnoughArgs {
|
||||
fn_name: fn_name.into(),
|
||||
required: required_args,
|
||||
actual: args_len,
|
||||
})?
|
||||
else {
|
||||
panic!("file path must be a single value.")
|
||||
};
|
||||
|
||||
let file_format = if let Some(EpBinding::Map {
|
||||
length_at: e,
|
||||
properties: format_props,
|
||||
}) = args_iter.next()
|
||||
{
|
||||
let mut input_format_vals: Vec<InMemory> = vec![];
|
||||
if let Some(EpBinding::Single(addr_type)) = format_props.get("type") {
|
||||
input_format_vals.push((*addr_type).into());
|
||||
}
|
||||
if let Some(EpBinding::Single(addr_units)) = format_props.get("units") {
|
||||
input_format_vals.push((*addr_units).into());
|
||||
}
|
||||
if let Some(EpBinding::Map {
|
||||
length_at: _,
|
||||
properties: coords_props,
|
||||
}) = format_props.get("coords")
|
||||
{
|
||||
if let Some(EpBinding::Map {
|
||||
length_at: _,
|
||||
properties: forward_props,
|
||||
}) = coords_props.get("forward")
|
||||
{
|
||||
if let Some(EpBinding::Single(addr_coords_forward_axis)) = forward_props.get("axis") {
|
||||
input_format_vals.push((*addr_coords_forward_axis).into());
|
||||
}
|
||||
if let Some(EpBinding::Single(addr_coords_forward_direction)) = forward_props.get("direction") {
|
||||
input_format_vals.push((*addr_coords_forward_direction).into());
|
||||
}
|
||||
}
|
||||
if let Some(EpBinding::Map {
|
||||
length_at: _,
|
||||
properties: up_props,
|
||||
}) = coords_props.get("up")
|
||||
{
|
||||
if let Some(EpBinding::Single(addr_coords_up_axis)) = up_props.get("axis") {
|
||||
input_format_vals.push((*addr_coords_up_axis).into());
|
||||
}
|
||||
if let Some(EpBinding::Single(addr_coords_up_direction)) = up_props.get("direction") {
|
||||
input_format_vals.push((*addr_coords_up_direction).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
input_format_vals
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
if let Some(_) = args_iter.next() {
|
||||
return Err(CompileError::TooManyArgs {
|
||||
fn_name: fn_name.into(),
|
||||
maximum: 2,
|
||||
actual: args_len,
|
||||
});
|
||||
}
|
||||
|
||||
let mut import_files_arguments = vec![file_path.into()];
|
||||
import_files_arguments.extend(file_format);
|
||||
|
||||
instructions.push(Instruction::ImportFiles(import_files::ImportFiles {
|
||||
store_response: Some(MemoryArea::Stack),
|
||||
arguments: import_files_arguments,
|
||||
}));
|
||||
|
||||
instructions.push(Instruction::ApiRequest(ApiRequest {
|
||||
endpoint: ModelingCmdEndpoint::ImportFiles,
|
||||
store_response: Some(*addr_retval),
|
||||
arguments: vec![InMemory::StackPop],
|
||||
cmd_id: Uuid::new_v4().into(),
|
||||
}));
|
||||
|
||||
let retval_without_enclosing_enum_variant_name = *addr_retval + 1;
|
||||
|
||||
Ok(EvalPlan {
|
||||
instructions,
|
||||
binding: EpBinding::Single(retval_without_enclosing_enum_variant_name),
|
||||
})
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ use std::{collections::HashMap, env};
|
||||
|
||||
use ep::{sketch_types, Destination, UnaryArithmetic};
|
||||
use ept::{ListHeader, ObjectHeader};
|
||||
use kittycad_execution_plan_traits::ReadMemory;
|
||||
use kittycad_modeling_cmds::shared::Point2d;
|
||||
use kittycad_modeling_session::SessionBuilder;
|
||||
use pretty_assertions::assert_eq;
|
||||
@ -1328,3 +1329,143 @@ fn mod_and_pow() {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_file_cube_stl() {
|
||||
let program = "let x = import(\"samples/cube.stl\", { type: \"stl\", units: \"mm\", coords: { forward: { axis: \"Y\", direction: \"negative\" }, up: { axis: \"Z\", direction: \"positive\" } } })";
|
||||
|
||||
let (_plan, scope) = must_plan(program);
|
||||
|
||||
let Some(EpBinding::Single(x)) = scope.get("x") else {
|
||||
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let mut expected_format =
|
||||
kittycad_modeling_cmds::format::InputFormat::Stl(kittycad_modeling_cmds::format::stl::import::Options {
|
||||
coords: kittycad_execution_plan::import_files::ZOO_COORD_SYSTEM,
|
||||
units: kittycad_modeling_cmds::units::UnitLength::Millimeters,
|
||||
});
|
||||
|
||||
let imported_geometry = mem
|
||||
.get_composite::<kittycad_modeling_cmds::ok_response::output::ImportedGeometry>(*x)
|
||||
.unwrap();
|
||||
|
||||
use ept::ReadMemory;
|
||||
assert_eq!(
|
||||
imported_geometry.0,
|
||||
kittycad_modeling_cmds::ok_response::output::ImportedGeometry {
|
||||
id: imported_geometry.0.id,
|
||||
value: vec!["cube.stl".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_file_cube_stl_no_options() {
|
||||
let program = "let x = import(\"samples/cube.stl\")";
|
||||
|
||||
let (_plan, scope) = must_plan(program);
|
||||
|
||||
let Some(EpBinding::Single(x)) = scope.get("x") else {
|
||||
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let mut expected_format =
|
||||
kittycad_modeling_cmds::format::InputFormat::Stl(kittycad_modeling_cmds::format::stl::import::Options {
|
||||
coords: kittycad_execution_plan::import_files::ZOO_COORD_SYSTEM,
|
||||
units: kittycad_modeling_cmds::units::UnitLength::Millimeters,
|
||||
});
|
||||
|
||||
let imported_geometry = mem
|
||||
.get_composite::<kittycad_modeling_cmds::ok_response::output::ImportedGeometry>(*x)
|
||||
.unwrap();
|
||||
|
||||
use ept::ReadMemory;
|
||||
assert_eq!(
|
||||
imported_geometry.0,
|
||||
kittycad_modeling_cmds::ok_response::output::ImportedGeometry {
|
||||
id: imported_geometry.0.id,
|
||||
value: vec!["cube.stl".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_file_cube_stl_options_missing_param() {
|
||||
let program = "let x = import(\"samples/cube.stl\", { type: \"stx\", coords: { forward: { axis: \"Y\", direction: \"negative\" }, up: { axis: \"Z\", direction: \"positive\" } } })";
|
||||
|
||||
let (_plan, scope) = must_plan(program);
|
||||
|
||||
let Some(EpBinding::Single(x)) = scope.get("x") else {
|
||||
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let mut expected_format =
|
||||
kittycad_modeling_cmds::format::InputFormat::Stl(kittycad_modeling_cmds::format::stl::import::Options {
|
||||
coords: kittycad_execution_plan::import_files::ZOO_COORD_SYSTEM,
|
||||
units: kittycad_modeling_cmds::units::UnitLength::Millimeters,
|
||||
});
|
||||
|
||||
let imported_geometry = mem
|
||||
.get_composite::<kittycad_modeling_cmds::ok_response::output::ImportedGeometry>(*x)
|
||||
.unwrap();
|
||||
|
||||
use ept::ReadMemory;
|
||||
assert_eq!(
|
||||
imported_geometry.0,
|
||||
kittycad_modeling_cmds::ok_response::output::ImportedGeometry {
|
||||
id: imported_geometry.0.id,
|
||||
value: vec!["cube.stl".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn import_file_cube_stl_format_mismatch() {
|
||||
let program = "let x = import(\"samples/cube.stl\", { type: \"step\" })";
|
||||
|
||||
let (_plan, scope) = must_plan(program);
|
||||
|
||||
let Some(EpBinding::Single(x)) = scope.get("x") else {
|
||||
panic!("Unexpected binding for variable 'x': {:?}", scope.get("x"));
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
let mut expected_format =
|
||||
kittycad_modeling_cmds::format::InputFormat::Stl(kittycad_modeling_cmds::format::stl::import::Options {
|
||||
coords: kittycad_execution_plan::import_files::ZOO_COORD_SYSTEM,
|
||||
units: kittycad_modeling_cmds::units::UnitLength::Millimeters,
|
||||
});
|
||||
|
||||
let imported_geometry = mem
|
||||
.get_composite::<kittycad_modeling_cmds::ok_response::output::ImportedGeometry>(*x)
|
||||
.unwrap();
|
||||
|
||||
use ept::ReadMemory;
|
||||
assert_eq!(
|
||||
imported_geometry.0,
|
||||
kittycad_modeling_cmds::ok_response::output::ImportedGeometry {
|
||||
id: imported_geometry.0.id,
|
||||
value: vec!["cube.stl".to_string()],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user