KCL: Absolute point bezier curves (#7172)
Previously KCL bezier curves could only use relative control points. Now you can use absolute control points too. Here's an example of the new arguments: ```kcl startSketchOn(XY) |> startProfile(at = [300, 300]) |> bezierCurve(control1Absolute = [600, 300], control2Absolute = [-300, -100], endAbsolute = [600, 300]) |> close() |> extrude(length = 10) ``` Closes https://github.com/KittyCAD/modeling-app/issues/7083
This commit is contained in:
@ -2154,12 +2154,27 @@ async fn inner_tangential_arc_to_point(
|
||||
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let sketch =
|
||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
let end: [TyF64; 2] = args.get_kw_arg_typed("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let control1: [TyF64; 2] = args.get_kw_arg_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
||||
let control2: [TyF64; 2] = args.get_kw_arg_typed("control2", &RuntimeType::point2d(), exec_state)?;
|
||||
let control1 = args.get_kw_arg_opt_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
||||
let control2 = args.get_kw_arg_opt_typed("control2", &RuntimeType::point2d(), exec_state)?;
|
||||
let end = args.get_kw_arg_opt_typed("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let control1_absolute = args.get_kw_arg_opt_typed("control1Absolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let control2_absolute = args.get_kw_arg_opt_typed("control2Absolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt_typed("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
||||
let new_sketch = inner_bezier_curve(sketch, control1, control2, end, tag, exec_state, args).await?;
|
||||
let new_sketch = inner_bezier_curve(
|
||||
sketch,
|
||||
control1,
|
||||
control2,
|
||||
end,
|
||||
control1_absolute,
|
||||
control2_absolute,
|
||||
end_absolute,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
@ -2170,6 +2185,7 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
||||
/// shape.
|
||||
///
|
||||
/// ```no_run
|
||||
/// // Example using relative control points.
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> line(end = [0, 10])
|
||||
@ -2183,51 +2199,101 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
||||
///
|
||||
/// example = extrude(exampleSketch, length = 10)
|
||||
/// ```
|
||||
/// ```no_run
|
||||
/// // Example using absolute control points.
|
||||
/// startSketchOn(XY)
|
||||
/// |> startProfile(at = [300, 300])
|
||||
/// |> bezierCurve(control1Absolute = [600, 300], control2Absolute = [-300, -100], endAbsolute = [600, 600])
|
||||
/// |> close()
|
||||
/// |> extrude(length = 10)
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "bezierCurve",
|
||||
unlabeled_first = true,
|
||||
args = {
|
||||
sketch = { docs = "Which sketch should this path be added to?"},
|
||||
end = { docs = "How far away (along the X and Y axes) should this line go?" },
|
||||
control1 = { docs = "First control point for the cubic" },
|
||||
control2 = { docs = "Second control point for the cubic" },
|
||||
end = { docs = "How far away (along the X and Y axes) should this line go?" },
|
||||
control1_absolute = { docs = "First control point for the cubic. Absolute point." },
|
||||
control2_absolute = { docs = "Second control point for the cubic. Absolute point." },
|
||||
end_absolute = { docs = "Coordinate on the plane at which this line should end." },
|
||||
tag = { docs = "Create a new tag which refers to this line"},
|
||||
},
|
||||
tags = ["sketch"]
|
||||
}]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn inner_bezier_curve(
|
||||
sketch: Sketch,
|
||||
control1: [TyF64; 2],
|
||||
control2: [TyF64; 2],
|
||||
end: [TyF64; 2],
|
||||
control1: Option<[TyF64; 2]>,
|
||||
control2: Option<[TyF64; 2]>,
|
||||
end: Option<[TyF64; 2]>,
|
||||
control1_absolute: Option<[TyF64; 2]>,
|
||||
control2_absolute: Option<[TyF64; 2]>,
|
||||
end_absolute: Option<[TyF64; 2]>,
|
||||
tag: Option<TagNode>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let from = sketch.current_pen_position()?;
|
||||
|
||||
let relative = true;
|
||||
let delta = end.clone();
|
||||
let to = [
|
||||
from.x + end[0].to_length_units(from.units),
|
||||
from.y + end[1].to_length_units(from.units),
|
||||
];
|
||||
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Bezier {
|
||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
||||
end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
|
||||
relative,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let to = match (
|
||||
control1,
|
||||
control2,
|
||||
end,
|
||||
control1_absolute,
|
||||
control2_absolute,
|
||||
end_absolute,
|
||||
) {
|
||||
// Relative
|
||||
(Some(control1), Some(control2), Some(end), None, None, None) => {
|
||||
let delta = end.clone();
|
||||
let to = [
|
||||
from.x + end[0].to_length_units(from.units),
|
||||
from.y + end[1].to_length_units(from.units),
|
||||
];
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Bezier {
|
||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
||||
end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
|
||||
relative: true,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
to
|
||||
}
|
||||
// Absolute
|
||||
(None, None, None, Some(control1), Some(control2), Some(end)) => {
|
||||
let to = [end[0].to_length_units(from.units), end[1].to_length_units(from.units)];
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Bezier {
|
||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
||||
end: KPoint2d::from(point_to_mm(end)).with_z(0.0).map(LengthUnit),
|
||||
relative: false,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
to
|
||||
}
|
||||
_ => {
|
||||
return Err(KclError::Semantic(KclErrorDetails::new(
|
||||
"You must either give `control1`, `control2` and `end`, or `control1Absolute`, `control2Absolute` and `endAbsolute`.".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
}
|
||||
};
|
||||
|
||||
let current_path = Path::ToPoint {
|
||||
base: BasePath {
|
||||
|
Reference in New Issue
Block a user