Add tangentialArcTo to grackle stdlib (#1731)
* Add tangentialArcTo to grackle stdlib * Clean up test
This commit is contained in:
BIN
src/wasm-lib/grackle/fixtures/cube_tangentialArcTo.png
Normal file
BIN
src/wasm-lib/grackle/fixtures/cube_tangentialArcTo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
@ -190,6 +190,10 @@ impl BindingScope {
|
||||
"yLine".into(),
|
||||
EpBinding::from(KclFunction::YLine(native_functions::sketch::YLine)),
|
||||
),
|
||||
(
|
||||
"tangentialArcTo".into(),
|
||||
EpBinding::from(KclFunction::TangentialArcTo(native_functions::sketch::TangentialArcTo)),
|
||||
),
|
||||
(
|
||||
"extrude".into(),
|
||||
EpBinding::from(KclFunction::Extrude(native_functions::sketch::Extrude)),
|
||||
|
||||
@ -285,6 +285,7 @@ impl Planner {
|
||||
KclFunction::XLine(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::YLineTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::YLine(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::TangentialArcTo(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Add(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::Close(f) => f.call(&mut ctx, args)?,
|
||||
KclFunction::UserDefined(f) => {
|
||||
@ -674,6 +675,7 @@ enum KclFunction {
|
||||
XLine(native_functions::sketch::XLine),
|
||||
YLineTo(native_functions::sketch::YLineTo),
|
||||
YLine(native_functions::sketch::YLine),
|
||||
TangentialArcTo(native_functions::sketch::TangentialArcTo),
|
||||
Add(native_functions::Add),
|
||||
Log(native_functions::Log),
|
||||
Max(native_functions::Max),
|
||||
|
||||
@ -3,4 +3,6 @@
|
||||
pub mod helpers;
|
||||
pub mod stdlib_functions;
|
||||
|
||||
pub use stdlib_functions::{Close, Extrude, Line, LineTo, StartSketchAt, XLine, XLineTo, YLine, YLineTo};
|
||||
pub use stdlib_functions::{
|
||||
Close, Extrude, Line, LineTo, StartSketchAt, TangentialArcTo, XLine, XLineTo, YLine, YLineTo,
|
||||
};
|
||||
|
||||
@ -716,3 +716,134 @@ impl Callable for StartSketchAt {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||
pub struct TangentialArcTo;
|
||||
|
||||
impl Callable for TangentialArcTo {
|
||||
fn call(
|
||||
&self,
|
||||
ctx: &mut crate::native_functions::Context<'_>,
|
||||
args: Vec<EpBinding>,
|
||||
) -> Result<EvalPlan, CompileError> {
|
||||
let mut instructions = Vec::new();
|
||||
let fn_name = "tangential_arc_to";
|
||||
// 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,
|
||||
});
|
||||
};
|
||||
let tag = match args_iter.next() {
|
||||
Some(a) => a,
|
||||
None => {
|
||||
// Write an empty string and use that.
|
||||
let empty_string_addr = ctx.next_address.offset_by(1);
|
||||
instructions.push(Instruction::SetPrimitive {
|
||||
address: empty_string_addr,
|
||||
value: String::new().into(),
|
||||
});
|
||||
EpBinding::Single(empty_string_addr)
|
||||
}
|
||||
};
|
||||
// Check the type of required params.
|
||||
let to = arg_point2d(to, fn_name, &mut instructions, ctx, 0)?;
|
||||
let sg = sg_binding(sketch_group, fn_name, "sketch group", 1)?;
|
||||
let tag = single_binding(tag, fn_name, "string tag", 2)?;
|
||||
let id = Uuid::new_v4();
|
||||
// Start of the path segment (which is a straight line).
|
||||
let length_of_3d_point = Point3d::<f64>::default().into_parts().len();
|
||||
let start_of_tangential_arc = ctx.next_address.offset_by(1);
|
||||
// Reserve space for the line's end, and the `relative: bool` field.
|
||||
ctx.next_address.offset_by(length_of_3d_point + 1);
|
||||
let new_sg_index = ctx.assign_sketch_group();
|
||||
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_tangential_arc,
|
||||
value: "TangentialArcTo".to_owned().into(),
|
||||
},
|
||||
// Then its to
|
||||
Instruction::StackPop {
|
||||
destination: Some(Destination::Address(start_of_tangential_arc + 1)),
|
||||
},
|
||||
// Then its `angle_snap_increment` field.
|
||||
Instruction::SetPrimitive {
|
||||
address: start_of_tangential_arc + 1 + length_of_3d_point,
|
||||
value: Primitive::from("None".to_owned()),
|
||||
},
|
||||
// Push the path ID onto the stack.
|
||||
Instruction::SketchGroupCopyFrom {
|
||||
destination: Destination::StackPush,
|
||||
length: 1,
|
||||
source: sg,
|
||||
offset: SketchGroup::path_id_offset(),
|
||||
},
|
||||
// Send the ExtendPath request
|
||||
Instruction::ApiRequest(ApiRequest {
|
||||
endpoint: ModelingCmdEndpoint::ExtendPath,
|
||||
store_response: None,
|
||||
arguments: vec![
|
||||
// Path ID
|
||||
InMemory::StackPop,
|
||||
// Segment
|
||||
InMemory::Address(start_of_tangential_arc),
|
||||
],
|
||||
cmd_id: id.into(),
|
||||
}),
|
||||
// Push the new segment in SketchGroup format.
|
||||
// Path tag.
|
||||
Instruction::StackPush {
|
||||
data: vec![Primitive::from("ToPoint".to_owned())],
|
||||
},
|
||||
// `BasePath::from` point.
|
||||
Instruction::SketchGroupGetLastPoint {
|
||||
source: sg,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
// `BasePath::to` point.
|
||||
Instruction::Copy {
|
||||
source: start_of_tangential_arc + 1,
|
||||
length: 2,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
// `BasePath::name` string.
|
||||
Instruction::Copy {
|
||||
source: tag,
|
||||
length: 1,
|
||||
destination: Destination::StackExtend,
|
||||
},
|
||||
// Update the SketchGroup with its new segment.
|
||||
Instruction::SketchGroupAddSegment {
|
||||
destination: new_sg_index,
|
||||
segment: InMemory::StackPop,
|
||||
source: sg,
|
||||
},
|
||||
]);
|
||||
|
||||
Ok(EvalPlan {
|
||||
instructions,
|
||||
binding: EpBinding::SketchGroup { index: new_sg_index },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1144,10 +1144,6 @@ async fn stdlib_cube_xline_yline() {
|
||||
|> close(%)
|
||||
|> extrude(100.0, %)
|
||||
"#;
|
||||
kcvm_dbg(
|
||||
program,
|
||||
"/home/lee/Code/Zoo/modeling-api/execution-plan-debugger/cube_xyline.json",
|
||||
);
|
||||
let (_plan, _scope, _last_address) = must_plan(program);
|
||||
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
@ -1218,6 +1214,93 @@ async fn stdlib_cube_xline_yline() {
|
||||
twenty_twenty::assert_image("fixtures/cube_xyLine.png", &img, 0.9999);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn stdlib_cube_with_tangential_arc_to() {
|
||||
let program = r#"
|
||||
let cube = startSketchAt([10.0, 10.0], "adam")
|
||||
|> lineTo([200.0 , 10.0], %, "side0")
|
||||
|> tangentialArcTo([210.0, 20.0], %, "arc")
|
||||
|> lineTo([210.0 , 210.0], %, "side1")
|
||||
|> lineTo([ 10.0 , 210.0], %, "side2")
|
||||
|> lineTo([ 10.0 , 10.0], %, "side3")
|
||||
|> close(%)
|
||||
|> extrude(100.0, %)
|
||||
"#;
|
||||
let (_plan, _scope, last_address) = must_plan(program);
|
||||
assert_eq!(last_address, Address::ZERO + 76);
|
||||
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||
.ast()
|
||||
.unwrap();
|
||||
let mut client = Some(test_client().await);
|
||||
let mem = match crate::execute(ast, &mut client).await {
|
||||
Ok(mem) => mem,
|
||||
Err(e) => panic!("{e}"),
|
||||
};
|
||||
let sg = &mem.sketch_groups.last().unwrap();
|
||||
assert_eq!(
|
||||
sg.path_rest,
|
||||
vec![
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 10.0, y: 10.0 },
|
||||
to: Point2d { x: 200.0, y: 10.0 },
|
||||
name: "side0".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 200.0, y: 10.0 },
|
||||
to: Point2d { x: 210.0, y: 20.0 },
|
||||
name: "arc".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 210.0, y: 20.0 },
|
||||
to: Point2d { x: 210.0, y: 210.0 },
|
||||
name: "side1".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 210.0, y: 210.0 },
|
||||
to: Point2d { x: 10.0, y: 210.0 },
|
||||
name: "side2".into(),
|
||||
}
|
||||
},
|
||||
sketch_types::PathSegment::ToPoint {
|
||||
base: sketch_types::BasePath {
|
||||
from: Point2d { x: 10.0, y: 210.0 },
|
||||
to: Point2d { x: 10.0, y: 10.0 },
|
||||
name: "side3".into(),
|
||||
}
|
||||
},
|
||||
]
|
||||
);
|
||||
use kittycad_modeling_cmds::{each_cmd, ok_response::OkModelingCmdResponse, ImageFormat};
|
||||
let out = client
|
||||
.unwrap()
|
||||
.run_command(
|
||||
uuid::Uuid::new_v4().into(),
|
||||
kittycad_modeling_cmds::ModelingCmd::from(each_cmd::TakeSnapshot {
|
||||
format: ImageFormat::Png,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let out = match out {
|
||||
OkModelingCmdResponse::TakeSnapshot(kittycad_modeling_cmds::output::TakeSnapshot { contents: b }) => b,
|
||||
other => panic!("wrong output: {other:?}"),
|
||||
};
|
||||
use image::io::Reader as ImageReader;
|
||||
let img = ImageReader::new(std::io::Cursor::new(out))
|
||||
.with_guessed_format()
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
twenty_twenty::assert_image("fixtures/cube_tangentialArcTo.png", &img, 0.9999);
|
||||
}
|
||||
|
||||
async fn test_client() -> Session {
|
||||
let kittycad_api_token = env::var("KITTYCAD_API_TOKEN").expect("You must set $KITTYCAD_API_TOKEN");
|
||||
let kittycad_api_client = kittycad::Client::new(kittycad_api_token);
|
||||
|
||||
Reference in New Issue
Block a user