Skip to content

Commit f606b9b

Browse files
authored
Major axis of ellipse (#7953)
Added `majorAxis` to the KCL functions `ellipse` and `elliptic` to allow ellipse to be defined off of the X and Y axes without having to rotate. majorRadius is still available, and maps to majorAxis = [majorRadius, 0]
1 parent 8a00364 commit f606b9b

File tree

9 files changed

+117
-36
lines changed

9 files changed

+117
-36
lines changed

rust/Cargo.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ dashmap = { version = "6.1.0" }
3636
http = "1"
3737
indexmap = "2.10.0"
3838
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
39-
kittycad-modeling-cmds = { version = "0.2.128", features = ["ts-rs", "websocket"] }
39+
kittycad-modeling-cmds = { version = "0.2.130", features = ["ts-rs", "websocket"] }
4040
lazy_static = "1.5.0"
4141
miette = "7.6.0"
4242
pyo3 = { version = "0.24.2" }

rust/kcl-lib/src/execution/geometry.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,7 @@ pub(crate) enum GetTangentialInfoFromPathsResult {
742742
Ellipse {
743743
center: [f64; 2],
744744
ccw: bool,
745-
major_radius: f64,
745+
major_axis: [f64; 2],
746746
_minor_radius: f64,
747747
},
748748
}
@@ -761,10 +761,10 @@ impl GetTangentialInfoFromPathsResult {
761761
} => [center[0] + radius, center[1] + if *ccw { -1.0 } else { 1.0 }],
762762
GetTangentialInfoFromPathsResult::Ellipse {
763763
center,
764-
major_radius,
764+
major_axis,
765765
ccw,
766766
..
767-
} => [center[0] + major_radius, center[1] + if *ccw { -1.0 } else { 1.0 }],
767+
} => [center[0] + major_axis[0], center[1] + if *ccw { -1.0 } else { 1.0 }],
768768
}
769769
}
770770
}
@@ -1272,7 +1272,7 @@ pub enum Path {
12721272
#[serde(flatten)]
12731273
base: BasePath,
12741274
center: [f64; 2],
1275-
major_radius: f64,
1275+
major_axis: [f64; 2],
12761276
minor_radius: f64,
12771277
ccw: bool,
12781278
},
@@ -1524,13 +1524,13 @@ impl Path {
15241524
// TODO: (bc) fix me
15251525
Path::Ellipse {
15261526
center,
1527-
major_radius,
1527+
major_axis,
15281528
minor_radius,
15291529
ccw,
15301530
..
15311531
} => GetTangentialInfoFromPathsResult::Ellipse {
15321532
center: *center,
1533-
major_radius: *major_radius,
1533+
major_axis: *major_axis,
15341534
_minor_radius: *minor_radius,
15351535
ccw: *ccw,
15361536
},

rust/kcl-lib/src/std/shapes.rs

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -535,14 +535,16 @@ pub async fn ellipse(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
535535
let sketch_or_surface =
536536
args.get_unlabeled_kw_arg("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?;
537537
let center = args.get_kw_arg("center", &RuntimeType::point2d(), exec_state)?;
538-
let major_radius = args.get_kw_arg("majorRadius", &RuntimeType::length(), exec_state)?;
538+
let major_radius = args.get_kw_arg_opt("majorRadius", &RuntimeType::length(), exec_state)?;
539+
let major_axis = args.get_kw_arg_opt("majorAxis", &RuntimeType::point2d(), exec_state)?;
539540
let minor_radius = args.get_kw_arg("minorRadius", &RuntimeType::length(), exec_state)?;
540541
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
541542

542543
let sketch = inner_ellipse(
543544
sketch_or_surface,
544545
center,
545546
major_radius,
547+
major_axis,
546548
minor_radius,
547549
tag,
548550
exec_state,
@@ -554,10 +556,12 @@ pub async fn ellipse(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
554556
})
555557
}
556558

559+
#[allow(clippy::too_many_arguments)]
557560
async fn inner_ellipse(
558561
sketch_surface_or_group: SketchOrSurface,
559562
center: [TyF64; 2],
560-
major_radius: TyF64,
563+
major_radius: Option<TyF64>,
564+
major_axis: Option<[TyF64; 2]>,
561565
minor_radius: TyF64,
562566
tag: Option<TagNode>,
563567
exec_state: &mut ExecState,
@@ -570,7 +574,27 @@ async fn inner_ellipse(
570574
let (center_u, ty) = untype_point(center.clone());
571575
let units = ty.expect_length();
572576

573-
let from = [center_u[0] + major_radius.to_length_units(units), center_u[1]];
577+
let major_axis = match (major_axis, major_radius) {
578+
(Some(_), Some(_)) | (None, None) => {
579+
return Err(KclError::new_type(KclErrorDetails::new(
580+
"Provide either `majorAxis` or `majorRadius` but not both.".to_string(),
581+
vec![args.source_range],
582+
)));
583+
}
584+
(Some(major_axis), None) => major_axis,
585+
(None, Some(major_radius)) => [
586+
major_radius.clone(),
587+
TyF64 {
588+
n: 0.0,
589+
ty: major_radius.ty,
590+
},
591+
],
592+
};
593+
594+
let from = [
595+
center_u[0] + major_axis[0].to_length_units(units),
596+
center_u[1] + major_axis[1].to_length_units(units),
597+
];
574598
let from_t = [TyF64::new(from[0], ty), TyF64::new(from[1], ty)];
575599

576600
let sketch =
@@ -581,14 +605,15 @@ async fn inner_ellipse(
581605

582606
let id = exec_state.next_uuid();
583607

608+
let axis = KPoint2d::from(untyped_point_to_mm([major_axis[0].n, major_axis[1].n], units)).map(LengthUnit);
584609
exec_state
585610
.batch_modeling_cmd(
586611
ModelingCmdMeta::from_args_id(&args, id),
587612
ModelingCmd::from(mcmd::ExtendPath {
588613
path: sketch.id.into(),
589614
segment: PathSegment::Ellipse {
590615
center: KPoint2d::from(point_to_mm(center)).map(LengthUnit),
591-
major_radius: LengthUnit(major_radius.to_mm()),
616+
major_axis: axis,
592617
minor_radius: LengthUnit(minor_radius.to_mm()),
593618
start_angle: Angle::from_degrees(angle_start.to_degrees()),
594619
end_angle: Angle::from_degrees(angle_end.to_degrees()),
@@ -608,7 +633,7 @@ async fn inner_ellipse(
608633
metadata: args.source_range.into(),
609634
},
610635
},
611-
major_radius: major_radius.to_length_units(units),
636+
major_axis: major_axis.map(|x| x.to_length_units(units)),
612637
minor_radius: minor_radius.to_length_units(units),
613638
center: center_u,
614639
ccw: angle_start < angle_end,

rust/kcl-lib/src/std/sketch.rs

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,7 +1997,8 @@ pub async fn elliptic(exec_state: &mut ExecState, args: Args) -> Result<KclValue
19971997
let center = args.get_kw_arg("center", &RuntimeType::point2d(), exec_state)?;
19981998
let angle_start = args.get_kw_arg("angleStart", &RuntimeType::degrees(), exec_state)?;
19991999
let angle_end = args.get_kw_arg("angleEnd", &RuntimeType::degrees(), exec_state)?;
2000-
let major_radius = args.get_kw_arg("majorRadius", &RuntimeType::length(), exec_state)?;
2000+
let major_radius = args.get_kw_arg_opt("majorRadius", &RuntimeType::length(), exec_state)?;
2001+
let major_axis = args.get_kw_arg_opt("majorAxis", &RuntimeType::point2d(), exec_state)?;
20012002
let minor_radius = args.get_kw_arg("minorRadius", &RuntimeType::length(), exec_state)?;
20022003
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
20032004

@@ -2007,6 +2008,7 @@ pub async fn elliptic(exec_state: &mut ExecState, args: Args) -> Result<KclValue
20072008
angle_start,
20082009
angle_end,
20092010
major_radius,
2011+
major_axis,
20102012
minor_radius,
20112013
tag,
20122014
exec_state,
@@ -2024,7 +2026,8 @@ pub(crate) async fn inner_elliptic(
20242026
center: [TyF64; 2],
20252027
angle_start: TyF64,
20262028
angle_end: TyF64,
2027-
major_radius: TyF64,
2029+
major_radius: Option<TyF64>,
2030+
major_axis: Option<[TyF64; 2]>,
20282031
minor_radius: TyF64,
20292032
tag: Option<TagNode>,
20302033
exec_state: &mut ExecState,
@@ -2035,21 +2038,47 @@ pub(crate) async fn inner_elliptic(
20352038

20362039
let (center_u, _) = untype_point(center);
20372040

2041+
let major_axis = match (major_axis, major_radius) {
2042+
(Some(_), Some(_)) | (None, None) => {
2043+
return Err(KclError::new_type(KclErrorDetails::new(
2044+
"Provide either `majorAxis` or `majorRadius` but not both.".to_string(),
2045+
vec![args.source_range],
2046+
)));
2047+
}
2048+
(Some(major_axis), None) => major_axis,
2049+
(None, Some(major_radius)) => [
2050+
major_radius.clone(),
2051+
TyF64 {
2052+
n: 0.0,
2053+
ty: major_radius.ty,
2054+
},
2055+
],
2056+
};
20382057
let start_angle = Angle::from_degrees(angle_start.to_degrees());
20392058
let end_angle = Angle::from_degrees(angle_end.to_degrees());
2059+
let major_axis_magnitude = (major_axis[0].to_length_units(from.units) * major_axis[0].to_length_units(from.units)
2060+
+ major_axis[1].to_length_units(from.units) * major_axis[1].to_length_units(from.units))
2061+
.sqrt();
20402062
let to = [
2041-
center_u[0] + major_radius.to_length_units(from.units) * libm::cos(end_angle.to_radians()),
2042-
center_u[1] + minor_radius.to_length_units(from.units) * libm::sin(end_angle.to_radians()),
2063+
major_axis_magnitude * libm::cos(end_angle.to_radians()),
2064+
minor_radius.to_length_units(from.units) * libm::sin(end_angle.to_radians()),
20432065
];
2066+
let major_axis_angle = libm::atan2(major_axis[1].n, major_axis[0].n);
20442067

2068+
let point = [
2069+
center_u[0] + to[0] * libm::cos(major_axis_angle) - to[1] * libm::sin(major_axis_angle),
2070+
center_u[1] + to[0] * libm::sin(major_axis_angle) + to[1] * libm::cos(major_axis_angle),
2071+
];
2072+
2073+
let axis = major_axis.map(|x| x.to_mm());
20452074
exec_state
20462075
.batch_modeling_cmd(
20472076
ModelingCmdMeta::from_args_id(&args, id),
20482077
ModelingCmd::from(mcmd::ExtendPath {
20492078
path: sketch.id.into(),
20502079
segment: PathSegment::Ellipse {
20512080
center: KPoint2d::from(untyped_point_to_mm(center_u, from.units)).map(LengthUnit),
2052-
major_radius: LengthUnit(major_radius.to_mm()),
2081+
major_axis: axis.map(LengthUnit).into(),
20532082
minor_radius: LengthUnit(minor_radius.to_mm()),
20542083
start_angle,
20552084
end_angle,
@@ -2061,11 +2090,11 @@ pub(crate) async fn inner_elliptic(
20612090
let current_path = Path::Ellipse {
20622091
ccw: start_angle < end_angle,
20632092
center: center_u,
2064-
major_radius: major_radius.to_mm(),
2093+
major_axis: axis,
20652094
minor_radius: minor_radius.to_mm(),
20662095
base: BasePath {
20672096
from: from.ignore_units(),
2068-
to,
2097+
to: point,
20692098
tag: tag.clone(),
20702099
units: sketch.units,
20712100
geo_meta: GeoMeta {

rust/kcl-lib/std/sketch.kcl

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -362,10 +362,12 @@ export fn ellipse(
362362
/// The center of the ellipse.
363363
@(snippetArray = ["0", "0"])
364364
center: Point2d,
365-
/// The major radius of the ellipse.
366-
majorRadius: number(Length),
367365
/// The minor radius of the ellipse.
368366
minorRadius: number(Length),
367+
/// The major radius of the ellipse. Equivalent to majorAxis = [majorRadius, 0].
368+
majorRadius?: number(Length),
369+
/// The major axis of the ellipse.
370+
majorAxis?: Point2d,
369371
/// Create a new tag which refers to this ellipse.
370372
tag?: tag,
371373
): Sketch {}
@@ -2228,12 +2230,14 @@ export fn elliptic(
22282230
/// Where along the ellptic should this segment end?
22292231
@(includeInSnippet = true)
22302232
angleEnd: number(Angle),
2231-
/// The major radius, a, of the elliptic equation x^2 / a^2 + y^2 / b^2 = 1.
2232-
@(includeInSnippet = true)
2233-
majorRadius: number(Length),
22342233
/// The minor radius, b, of the elliptic equation x^2 / a^2 + y^2 / b^2 = 1.
22352234
@(includeInSnippet = true)
22362235
minorRadius: number(Length),
2236+
/// The major radius, a, of the elliptic equation x^2 / a^2 + y^2 / b^2 = 1. Equivalent to majorAxis = [majorRadius, 0].
2237+
majorRadius?: number(Length),
2238+
/// The major axis of the elliptic.
2239+
@(includeInSnippet = true)
2240+
majorAxis?: Point2d,
22372241
/// Create a new tag which refers to this arc.
22382242
tag?: tag,
22392243
): Sketch {}

rust/kcl-lib/tests/elliptic_curve_inches_regression/ast.snap

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -526,22 +526,45 @@ description: Result of parsing elliptic_curve_inches_regression.kcl
526526
"commentStart": 0,
527527
"end": 0,
528528
"moduleId": 0,
529-
"name": "majorRadius",
529+
"name": "majorAxis",
530530
"start": 0,
531531
"type": "Identifier"
532532
},
533533
"arg": {
534534
"commentStart": 0,
535+
"elements": [
536+
{
537+
"commentStart": 0,
538+
"end": 0,
539+
"moduleId": 0,
540+
"raw": "90.658755",
541+
"start": 0,
542+
"type": "Literal",
543+
"type": "Literal",
544+
"value": {
545+
"value": 90.658755,
546+
"suffix": "None"
547+
}
548+
},
549+
{
550+
"commentStart": 0,
551+
"end": 0,
552+
"moduleId": 0,
553+
"raw": "0",
554+
"start": 0,
555+
"type": "Literal",
556+
"type": "Literal",
557+
"value": {
558+
"value": 0.0,
559+
"suffix": "None"
560+
}
561+
}
562+
],
535563
"end": 0,
536564
"moduleId": 0,
537-
"raw": "90.658755",
538565
"start": 0,
539-
"type": "Literal",
540-
"type": "Literal",
541-
"value": {
542-
"value": 90.658755,
543-
"suffix": "None"
544-
}
566+
"type": "ArrayExpression",
567+
"type": "ArrayExpression"
545568
}
546569
},
547570
{

rust/kcl-lib/tests/elliptic_curve_inches_regression/input.kcl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ sketch000Profile003 = startProfile(sketch000, at = [90.65875528983305, 60.855170
1717
center = [0, 60.855171],
1818
angleStart = 0deg,
1919
angleEnd = 180deg,
20-
majorRadius = 90.658755,
20+
majorAxis = [90.658755, 0],
2121
minorRadius = 16.805477,
2222
)
2323
|> bezierCurve(end = [50.20468, -26.67249], control1 = [15.007446, -25.538491], control2 = [32.368565, -29.659071])

rust/kcl-lib/tests/elliptic_curve_inches_regression/unparsed.snap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ sketch000Profile003 = startProfile(sketch000, at = [90.65875528983305, 60.855170
2121
center = [0, 60.855171],
2222
angleStart = 0deg,
2323
angleEnd = 180deg,
24-
majorRadius = 90.658755,
24+
majorAxis = [90.658755, 0],
2525
minorRadius = 16.805477,
2626
)
2727
|> bezierCurve(end = [50.20468, -26.67249], control1 = [15.007446, -25.538491], control2 = [32.368565, -29.659071])

0 commit comments

Comments
 (0)