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