Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tangentialArcTo to grackle stdlib #1731

Merged
merged 2 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -133,6 +133,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 @@ -267,6 +267,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 @@ -641,6 +642,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),
UserDefined(UserDefinedFunction),
Extrude(native_functions::sketch::Extrude),
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 @@
})
}
}

#[derive(Debug, Clone)]
#[cfg_attr(test, derive(Eq, PartialEq))]

Check warning on line 721 in src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs#L720-L721

Added lines #L720 - L721 were not covered by tests
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,
});

Check warning on line 739 in src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs#L735-L739

Added lines #L735 - L739 were not covered by tests
};
let Some(sketch_group) = args_iter.next() else {
return Err(CompileError::NotEnoughArgs {
fn_name: fn_name.into(),
required: 2,
actual: 1,
});

Check warning on line 746 in src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs#L742-L746

Added lines #L742 - L746 were not covered by tests
};
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 warning on line 757 in src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/grackle/src/native_functions/sketch/stdlib_functions.rs#L752-L757

Added lines #L752 - L757 were not covered by tests
}
};
// 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
Loading