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:
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@ -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> {
|
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch =
|
let sketch =
|
||||||
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
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 = args.get_kw_arg_opt_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
||||||
let control1: [TyF64; 2] = args.get_kw_arg_typed("control1", &RuntimeType::point2d(), exec_state)?;
|
let control2 = args.get_kw_arg_opt_typed("control2", &RuntimeType::point2d(), exec_state)?;
|
||||||
let control2: [TyF64; 2] = args.get_kw_arg_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 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 {
|
Ok(KclValue::Sketch {
|
||||||
value: Box::new(new_sketch),
|
value: Box::new(new_sketch),
|
||||||
})
|
})
|
||||||
@ -2170,6 +2185,7 @@ pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclV
|
|||||||
/// shape.
|
/// shape.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
|
/// // Example using relative control points.
|
||||||
/// exampleSketch = startSketchOn(XZ)
|
/// exampleSketch = startSketchOn(XZ)
|
||||||
/// |> startProfile(at = [0, 0])
|
/// |> startProfile(at = [0, 0])
|
||||||
/// |> line(end = [0, 10])
|
/// |> 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)
|
/// 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 {
|
#[stdlib {
|
||||||
name = "bezierCurve",
|
name = "bezierCurve",
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch = { docs = "Which sketch should this path be added to?"},
|
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" },
|
control1 = { docs = "First control point for the cubic" },
|
||||||
control2 = { docs = "Second 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"},
|
tag = { docs = "Create a new tag which refers to this line"},
|
||||||
},
|
},
|
||||||
tags = ["sketch"]
|
tags = ["sketch"]
|
||||||
}]
|
}]
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn inner_bezier_curve(
|
async fn inner_bezier_curve(
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
control1: [TyF64; 2],
|
control1: Option<[TyF64; 2]>,
|
||||||
control2: [TyF64; 2],
|
control2: Option<[TyF64; 2]>,
|
||||||
end: [TyF64; 2],
|
end: Option<[TyF64; 2]>,
|
||||||
|
control1_absolute: Option<[TyF64; 2]>,
|
||||||
|
control2_absolute: Option<[TyF64; 2]>,
|
||||||
|
end_absolute: Option<[TyF64; 2]>,
|
||||||
tag: Option<TagNode>,
|
tag: Option<TagNode>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<Sketch, KclError> {
|
||||||
let from = sketch.current_pen_position()?;
|
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();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
let to = match (
|
||||||
id,
|
control1,
|
||||||
ModelingCmd::from(mcmd::ExtendPath {
|
control2,
|
||||||
path: sketch.id.into(),
|
end,
|
||||||
segment: PathSegment::Bezier {
|
control1_absolute,
|
||||||
control1: KPoint2d::from(point_to_mm(control1)).with_z(0.0).map(LengthUnit),
|
control2_absolute,
|
||||||
control2: KPoint2d::from(point_to_mm(control2)).with_z(0.0).map(LengthUnit),
|
end_absolute,
|
||||||
end: KPoint2d::from(point_to_mm(delta)).with_z(0.0).map(LengthUnit),
|
) {
|
||||||
relative,
|
// Relative
|
||||||
},
|
(Some(control1), Some(control2), Some(end), None, None, None) => {
|
||||||
}),
|
let delta = end.clone();
|
||||||
)
|
let to = [
|
||||||
.await?;
|
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 {
|
let current_path = Path::ToPoint {
|
||||||
base: BasePath {
|
base: BasePath {
|
||||||
|
BIN
rust/kcl-lib/tests/outputs/serial_test_example_bezier_curve1.png
Normal file
BIN
rust/kcl-lib/tests/outputs/serial_test_example_bezier_curve1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
Reference in New Issue
Block a user