Skip to content

Commit

Permalink
Add tangentialArcTo to grackle stdlib (#1731)
Browse files Browse the repository at this point in the history
* Add tangentialArcTo to grackle stdlib

* Clean up test
  • Loading branch information
iterion authored Mar 20, 2024
1 parent 46358b4 commit 4f82121
Show file tree
Hide file tree
Showing 6 changed files with 227 additions and 5 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/wasm-lib/grackle/src/binding_scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand Down
2 changes: 2 additions & 0 deletions src/wasm-lib/grackle/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 3 additions & 1 deletion src/wasm-lib/grackle/src/native_functions/sketch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
131 changes: 131 additions & 0 deletions src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
})
}
}
91 changes: 87 additions & 4 deletions src/wasm-lib/grackle/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 4f82121

Please sign in to comment.