Add tangentialArcTo to grackle stdlib (#1731)

* Add tangentialArcTo to grackle stdlib

* Clean up test
This commit is contained in:
Adam Sunderland
2024-03-20 14:54:28 -04:00
committed by GitHub
parent 46358b41a2
commit 4f82121105
6 changed files with 227 additions and 5 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

View File

@ -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)),

View File

@ -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),

View File

@ -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,
};

View File

@ -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 },
})
}
}

View File

@ -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);