Compare commits
4 Commits
v0.42.0
...
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(),
|
"close".into(),
|
||||||
EpBinding::from(KclFunction::Close(native_functions::sketch::Close)),
|
EpBinding::from(KclFunction::Close(native_functions::sketch::Close)),
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"import".into(),
|
||||||
|
EpBinding::from(KclFunction::ImportFile(native_functions::import_files::ImportFiles)),
|
||||||
|
),
|
||||||
]),
|
]),
|
||||||
parent: None,
|
parent: None,
|
||||||
}
|
}
|
||||||
|
@ -264,6 +264,7 @@ impl Planner {
|
|||||||
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
KclFunction::LineTo(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::Add(f) => f.call(&mut ctx, args)?,
|
KclFunction::Add(f) => f.call(&mut ctx, args)?,
|
||||||
KclFunction::Close(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) => {
|
KclFunction::UserDefined(f) => {
|
||||||
let UserDefinedFunction {
|
let UserDefinedFunction {
|
||||||
params_optional,
|
params_optional,
|
||||||
@ -631,6 +632,7 @@ enum KclFunction {
|
|||||||
Id(native_functions::Id),
|
Id(native_functions::Id),
|
||||||
StartSketchAt(native_functions::sketch::StartSketchAt),
|
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||||
LineTo(native_functions::sketch::LineTo),
|
LineTo(native_functions::sketch::LineTo),
|
||||||
|
ImportFile(native_functions::import_files::ImportFiles),
|
||||||
Add(native_functions::Add),
|
Add(native_functions::Add),
|
||||||
UserDefined(UserDefinedFunction),
|
UserDefined(UserDefinedFunction),
|
||||||
Extrude(native_functions::sketch::Extrude),
|
Extrude(native_functions::sketch::Extrude),
|
||||||
|
@ -7,6 +7,7 @@ use kittycad_execution_plan_traits::Address;
|
|||||||
|
|
||||||
use crate::{CompileError, EpBinding, EvalPlan};
|
use crate::{CompileError, EpBinding, EvalPlan};
|
||||||
|
|
||||||
|
pub mod import_files;
|
||||||
pub mod sketch;
|
pub mod sketch;
|
||||||
|
|
||||||
/// The identity function. Always returns its first input.
|
/// 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 ep::{sketch_types, Destination, UnaryArithmetic};
|
||||||
use ept::{ListHeader, ObjectHeader};
|
use ept::{ListHeader, ObjectHeader};
|
||||||
|
use kittycad_execution_plan_traits::ReadMemory;
|
||||||
use kittycad_modeling_cmds::shared::Point2d;
|
use kittycad_modeling_cmds::shared::Point2d;
|
||||||
use kittycad_modeling_session::SessionBuilder;
|
use kittycad_modeling_session::SessionBuilder;
|
||||||
use pretty_assertions::assert_eq;
|
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