added parabolic and hyperbolic curves

This commit is contained in:
benjamaan476
2025-05-30 14:46:47 +01:00
parent 9f9601d49a
commit fad8b8209f
4 changed files with 241 additions and 29 deletions

View File

@ -58,12 +58,14 @@ lazy_static! {
Box::new(crate::std::segment::SegAng),
Box::new(crate::std::segment::TangentToEnd),
Box::new(crate::std::shapes::CircleThreePoint),
Box::new(crate::std::shapes::Ellipse),
// Box::new(crate::std::shapes::Ellipse),
Box::new(crate::std::shapes::Polygon),
Box::new(crate::std::sketch::Conic),
Box::new(crate::std::sketch::EllipticalArc),
Box::new(crate::std::sketch::Elliptic),
Box::new(crate::std::sketch::Hyperbolic),
Box::new(crate::std::sketch::InvoluteCircular),
Box::new(crate::std::sketch::Line),
Box::new(crate::std::sketch::Parabolic),
Box::new(crate::std::sketch::XLine),
Box::new(crate::std::sketch::YLine),
Box::new(crate::std::sketch::AngledLine),
@ -223,6 +225,10 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::shapes::circle(e, a)),
StdFnProps::default("std::sketch::circle"),
),
("sketch", "ellipse") => (
|e, a| Box::pin(crate::std::shapes::ellipse(e, a)),
StdFnProps::default("std::sketch::ellipse"),
),
("prelude", "helix") => (
|e, a| Box::pin(crate::std::helix::helix(e, a)),
StdFnProps::default("std::helix").include_in_feature_tree(),

View File

@ -495,25 +495,6 @@ pub async fn ellipse(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
})
}
/// Construct an ellipse derived from center and major/minor axes.
///
/// ```no_run
/// exampleSketch = startSketchOn(XY)
/// |> ellipse(center = [10,10], majorRadius = 5, minorRadius = 2)
/// |> extrude(length = 5)
/// ```
#[stdlib {
name = "ellipse",
unlabeled_first = true,
args = {
sketch_surface_or_group = { docs = "Plane or surface to sketch on" },
center = {docs = "The center of the ellipse."},
major_radius = {docs = "The length along the x axis."},
minor_radius = {docs = "The length along the y axis."},
tag = {docs = "Identifier for the circle to reference elsewhere."},
},
tags = ["sketch"]
}]
async fn inner_ellipse(
sketch_surface_or_group: SketchOrSurface,
center: [TyF64; 2],

View File

@ -2413,7 +2413,7 @@ async fn inner_subtract_2d(
}
/// Draw an elliptical arc.
pub async fn elliptical_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
pub async fn elliptic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch =
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
@ -2427,7 +2427,7 @@ pub async fn elliptical_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
let interior_absolute: Option<[TyF64; 2]> =
args.get_kw_arg_opt_typed("interiorAbsolute", &RuntimeType::point2d(), exec_state)?;
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_elliptical_arc(
let new_sketch = inner_elliptic(
sketch,
center,
angle_start,
@ -2449,7 +2449,7 @@ pub async fn elliptical_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [0, 0])
/// |> ellipticalArc(
/// |> elliptic(
/// endAbsolute = [10,0],
/// interiorAbsolute = [5,5]
/// )
@ -2457,7 +2457,7 @@ pub async fn elliptical_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
/// example = extrude(exampleSketch, length = 10)
/// ```
#[stdlib {
name = "ellipticalArc",
name = "elliptic",
unlabeled_first = true,
args = {
sketch = { docs = "Which sketch should this path be added to?" },
@ -2473,7 +2473,7 @@ pub async fn elliptical_arc(exec_state: &mut ExecState, args: Args) -> Result<Kc
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn inner_elliptical_arc(
pub(crate) async fn inner_elliptic(
sketch: Sketch,
center: [TyF64; 2],
angle_start: TyF64,
@ -2554,11 +2554,207 @@ pub(crate) async fn inner_elliptical_arc(
Ok(new_sketch)
}
pub async fn hyperbolic(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 semi_major: TyF64 = args.get_kw_arg_typed("semiMajor", &RuntimeType::length(), exec_state)?;
let semi_minor: TyF64 = args.get_kw_arg_typed("semiMinor", &RuntimeType::length(), exec_state)?;
let interior: [TyF64; 2] = args.get_kw_arg_typed("interior", &RuntimeType::point2d(), exec_state)?;
let end: [TyF64; 2] = args.get_kw_arg_typed("end", &RuntimeType::point2d(), exec_state)?;
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_hyperbolic(sketch, semi_major, semi_minor, interior, end, tag, exec_state, args).await?;
Ok(KclValue::Sketch {
value: Box::new(new_sketch),
})
}
/// Calculate the tangent of a hyperbolic given a point on the curve
fn hyperbolic_tangent(point: Point2d, semi_major: f64, semi_minor: f64) -> [f64; 2] {
(point.y * semi_major.powf(2.0), point.x * semi_minor.powf(2.0)).into()
}
/// ```no_run
/// exampleSketch = startSketchOn(XY)
/// |> startProfile(at = [0,0])
/// |> hyperbolic(
/// end = [10,0],
/// semiMajor = 2,
/// semiMinor = 1,
/// interior = [0,0]
/// )
/// |>close()
///```
#[stdlib {
name = "hyperbolic",
unlabeled_first = true,
args = {
sketch = { docs = "Which sketch should this path be added to?" },
semi_major = { docs = "The tangent of the conic at the start point (the end of the previous path segement)" },
semi_minor = { docs = "The tangent of the conic at the end point" },
interior = { docs = "Any point between the arc's start and end?" },
end = { docs = "Where should this arc end?" },
tag = { docs = "Create a new tag which refers to this line"},
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn inner_hyperbolic(sketch: Sketch, semi_major: TyF64, semi_minor: TyF64, interior: [TyF64; 2], end: [TyF64; 2], tag: Option<TagNode>, exec_state: &mut ExecState, args: Args
) -> Result<Sketch, KclError> {
let from = sketch.current_pen_position()?;
let id = exec_state.next_uuid();
let (interior, _) = untype_point(interior);
let (end, _) = untype_point(end);
let end_point = Point2d {
x: end[0],
y: end[1],
units: from.units,
};
let semi_major_u = semi_major.to_length_units(from.units);
let semi_minor_u = semi_minor.to_length_units(from.units);
let start_tangent = hyperbolic_tangent(from, semi_major_u, semi_minor_u);
let end_tangent = hyperbolic_tangent(end_point, semi_major_u, semi_minor_u);
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(),
segment: PathSegment::ConicTo {
start_tangent: KPoint2d::from(untyped_point_to_mm(start_tangent, from.units)).map(LengthUnit),
end_tangent: KPoint2d::from(untyped_point_to_mm(end_tangent, from.units)).map(LengthUnit),
end: KPoint2d::from(untyped_point_to_mm(end, from.units)).map(LengthUnit),
interior: KPoint2d::from(untyped_point_to_mm(interior, from.units)).map(LengthUnit),
relative: false,
},
}),
).await?;
let current_path = Path::Conic {
base: BasePath {
from: from.ignore_units(),
to: end,
tag: tag.clone(),
units: sketch.units,
geo_meta: GeoMeta {
id,
metadata: args.source_range.into(),
},
},
};
let mut new_sketch = sketch.clone();
if let Some(tag) = &tag {
new_sketch.add_tag(tag, &current_path, exec_state);
}
new_sketch.paths.push(current_path);
Ok(new_sketch)
}
pub async fn parabolic(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 coefficient: TyF64 = args.get_kw_arg_typed("coefficient", &RuntimeType::count(), exec_state)?;
let interior: [TyF64; 2] = args.get_kw_arg_typed("interior", &RuntimeType::point2d(), exec_state)?;
let end: [TyF64; 2] = args.get_kw_arg_typed("end", &RuntimeType::point2d(), exec_state)?;
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
let new_sketch = inner_parabolic(sketch, coefficient, interior, end, tag, exec_state, args).await?;
Ok(KclValue::Sketch {
value: Box::new(new_sketch),
})
}
fn parabolic_tangent(point: Point2d, coefficient: f64) -> [f64; 2] {
(1.0, 2.0 * coefficient * point.x).into()
}
/// ```no_run
/// exampleSketch = startSketchOn(XY)
/// |> startProfile(at = [0,0])
/// |> parabolic(
/// end = [10,0],
/// coefficient = 2,
/// interior = [0,0]
/// )
/// |>close()
///```
#[stdlib {
name = "parabolic",
unlabeled_first = true,
args = {
sketch = { docs = "Which sketch should this path be added to?" },
coefficient = { docs = "The tangent of the conic at the end point" },
interior = { docs = "Any point between the arc's start and end?" },
end = { docs = "Where should this arc end?" },
tag = { docs = "Create a new tag which refers to this line"},
},
tags = ["sketch"]
}]
pub(crate) async fn inner_parabolic(sketch: Sketch, coefficient: TyF64, interior: [TyF64; 2], end: [TyF64; 2], tag: Option<TagNode>, exec_state: &mut ExecState, args: Args
) -> Result<Sketch, KclError> {
let from = sketch.current_pen_position()?;
let id = exec_state.next_uuid();
let (interior, _) = untype_point(interior);
let (end, _) = untype_point(end);
let end_point = Point2d {
x: end[0],
y: end[1],
units: from.units,
};
let start_tangent = parabolic_tangent(from, coefficient.n);
let end_tangent = parabolic_tangent(end_point, coefficient.n);
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::ExtendPath {
path: sketch.id.into(),
segment: PathSegment::ConicTo {
start_tangent: KPoint2d::from(untyped_point_to_mm(start_tangent, from.units)).map(LengthUnit),
end_tangent: KPoint2d::from(untyped_point_to_mm(end_tangent, from.units)).map(LengthUnit),
end: KPoint2d::from(untyped_point_to_mm(end, from.units)).map(LengthUnit),
interior: KPoint2d::from(untyped_point_to_mm(interior, from.units)).map(LengthUnit),
relative: false,
},
}),
).await?;
let current_path = Path::Conic {
base: BasePath {
from: from.ignore_units(),
to: end,
tag: tag.clone(),
units: sketch.units,
geo_meta: GeoMeta {
id,
metadata: args.source_range.into(),
},
},
};
let mut new_sketch = sketch.clone();
if let Some(tag) = &tag {
new_sketch.add_tag(tag, &current_path, exec_state);
}
new_sketch.paths.push(current_path);
Ok(new_sketch)
}
/// Draw a conic section
pub async fn conic(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 start_tangent: [TyF64; 2] = args.get_kw_arg_typed("startTangent", &RuntimeType::point2d(), exec_state)?;
let start_tangent: Option<[TyF64; 2]> = args.get_kw_arg_opt_typed("startTangent", &RuntimeType::point2d(), exec_state)?;
let end_tangent: [TyF64; 2] = args.get_kw_arg_typed("endTangent", &RuntimeType::point2d(), exec_state)?;
let end: [TyF64; 2] = args.get_kw_arg_typed("end", &RuntimeType::point2d(), exec_state)?;
let interior: [TyF64; 2] = args.get_kw_arg_typed("interior", &RuntimeType::point2d(), exec_state)?;
@ -2570,6 +2766,7 @@ pub async fn conic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
})
}
/// ```no_run
/// exampleSketch = startSketchOn(XZ)
/// |> startProfile(at = [0, 0])
@ -2596,15 +2793,22 @@ pub async fn conic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
pub(crate) async fn inner_conic(sketch: Sketch, start_tangent: [TyF64; 2], end: [TyF64; 2], end_tangent: [TyF64; 2], interior: [TyF64; 2], tag: Option<TagNode>, exec_state: &mut ExecState, args: Args
pub(crate) async fn inner_conic(sketch: Sketch, start_tangent: Option<[TyF64; 2]>, end: [TyF64; 2], end_tangent: [TyF64; 2], interior: [TyF64; 2], tag: Option<TagNode>, exec_state: &mut ExecState, args: Args
) -> Result<Sketch, KclError> {
let from: Point2d = sketch.current_pen_position()?;
let id = exec_state.next_uuid();
let (start_tangent, _) = untype_point(start_tangent);
let (end_tangent, _) = untype_point(end_tangent);
let (end, _) = untype_point(end);
let (interior, _) = untype_point(interior);
let (start_tangent, _) = if let Some(start_tangent) = start_tangent {
untype_point(start_tangent)
} else {
let previous_point = sketch.get_tangential_info_from_paths().tan_previous_point(from.ignore_units());
let from = from.ignore_units();
([from[0] - previous_point[0], from[1] - previous_point[1]], NumericType::Any)
};
args.batch_modeling_cmd(
id,
ModelingCmd::from(mcmd::ExtendPath {