Compare commits
44 Commits
main
...
ben/conics
Author | SHA1 | Date | |
---|---|---|---|
550fe929ae | |||
fb36c1703a | |||
dbed075648 | |||
2112cd0994 | |||
5372731ac1 | |||
ba2aa7e57d | |||
c7825e3cfd | |||
8fa429fe20 | |||
9b41be763f | |||
a8d923ad19 | |||
c0253e7a8e | |||
8ab5c52a4f | |||
5e34562630 | |||
2adcf393a8 | |||
f2426b9776 | |||
4faaccbae0 | |||
80f869d1c0 | |||
eb8499f35b | |||
94d2a8addb | |||
3a6d5c0005 | |||
67122d66e0 | |||
d3d2349a80 | |||
756b5541e5 | |||
9f34c0d0c0 | |||
dc0c5d5278 | |||
7b490da1b4 | |||
37e9e9015a | |||
3fea2efd81 | |||
96eebc3d27 | |||
c4caf1ec94 | |||
0f51113009 | |||
8d4a4b529b | |||
9b35ca06da | |||
75c1a35a25 | |||
b6ba5ef4be | |||
0ed27a6c33 | |||
fc0c0bf817 | |||
9637f1943f | |||
64c4d8eb24 | |||
f928c88b59 | |||
afe0550e3e | |||
fad8b8209f | |||
9f9601d49a | |||
8cf67a29bf |
@ -54,16 +54,24 @@ layout: manual
|
||||
* [`circle`](/docs/kcl-std/functions/std-sketch-circle)
|
||||
* [`circleThreePoint`](/docs/kcl-std/functions/std-sketch-circleThreePoint)
|
||||
* [`close`](/docs/kcl-std/functions/std-sketch-close)
|
||||
* [`conic`](/docs/kcl-std/functions/std-sketch-conic)
|
||||
* [`ellipse`](/docs/kcl-std/functions/std-sketch-ellipse)
|
||||
* [`elliptic`](/docs/kcl-std/functions/std-sketch-elliptic)
|
||||
* [`ellipticPoint`](/docs/kcl-std/functions/std-sketch-ellipticPoint)
|
||||
* [`extrude`](/docs/kcl-std/functions/std-sketch-extrude)
|
||||
* [`getCommonEdge`](/docs/kcl-std/functions/std-sketch-getCommonEdge)
|
||||
* [`getNextAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
* [`getPreviousAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getPreviousAdjacentEdge)
|
||||
* [`hyperbolic`](/docs/kcl-std/functions/std-sketch-hyperbolic)
|
||||
* [`hyperbolicPoint`](/docs/kcl-std/functions/std-sketch-hyperbolicPoint)
|
||||
* [`involuteCircular`](/docs/kcl-std/functions/std-sketch-involuteCircular)
|
||||
* [`lastSegX`](/docs/kcl-std/functions/std-sketch-lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/functions/std-sketch-lastSegY)
|
||||
* [`line`](/docs/kcl-std/functions/std-sketch-line)
|
||||
* [`loft`](/docs/kcl-std/functions/std-sketch-loft)
|
||||
* [`parabolic`](/docs/kcl-std/functions/std-sketch-parabolic)
|
||||
* [`parabolicPoint`](/docs/kcl-std/functions/std-sketch-parabolicPoint)
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
|
||||
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||
|
@ -19,16 +19,24 @@ This module contains functions for creating and manipulating sketches, and makin
|
||||
* [`circle`](/docs/kcl-std/functions/std-sketch-circle)
|
||||
* [`circleThreePoint`](/docs/kcl-std/functions/std-sketch-circleThreePoint)
|
||||
* [`close`](/docs/kcl-std/functions/std-sketch-close)
|
||||
* [`conic`](/docs/kcl-std/functions/std-sketch-conic)
|
||||
* [`ellipse`](/docs/kcl-std/functions/std-sketch-ellipse)
|
||||
* [`elliptic`](/docs/kcl-std/functions/std-sketch-elliptic)
|
||||
* [`ellipticPoint`](/docs/kcl-std/functions/std-sketch-ellipticPoint)
|
||||
* [`extrude`](/docs/kcl-std/functions/std-sketch-extrude)
|
||||
* [`getCommonEdge`](/docs/kcl-std/functions/std-sketch-getCommonEdge)
|
||||
* [`getNextAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
* [`getPreviousAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getPreviousAdjacentEdge)
|
||||
* [`hyperbolic`](/docs/kcl-std/functions/std-sketch-hyperbolic)
|
||||
* [`hyperbolicPoint`](/docs/kcl-std/functions/std-sketch-hyperbolicPoint)
|
||||
* [`involuteCircular`](/docs/kcl-std/functions/std-sketch-involuteCircular)
|
||||
* [`lastSegX`](/docs/kcl-std/functions/std-sketch-lastSegX)
|
||||
* [`lastSegY`](/docs/kcl-std/functions/std-sketch-lastSegY)
|
||||
* [`line`](/docs/kcl-std/functions/std-sketch-line)
|
||||
* [`loft`](/docs/kcl-std/functions/std-sketch-loft)
|
||||
* [`parabolic`](/docs/kcl-std/functions/std-sketch-parabolic)
|
||||
* [`parabolicPoint`](/docs/kcl-std/functions/std-sketch-parabolicPoint)
|
||||
* [`patternCircular2d`](/docs/kcl-std/functions/std-sketch-patternCircular2d)
|
||||
* [`patternLinear2d`](/docs/kcl-std/functions/std-sketch-patternLinear2d)
|
||||
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||
|
4
rust/Cargo.lock
generated
4
rust/Cargo.lock
generated
@ -2071,9 +2071,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.2.125"
|
||||
version = "0.2.126"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfd09d95f8bbeb090d4d1137c9bf421eb75763f7a30e4a9e8eefa249ddf20bd3"
|
||||
checksum = "58ca9ecdacf1161add6d1c0ea4c6765449a57b5e50dea26c84629ea39a3d51b7"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
@ -36,7 +36,7 @@ dashmap = { version = "6.1.0" }
|
||||
http = "1"
|
||||
indexmap = "2.9.0"
|
||||
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.125", features = ["ts-rs", "websocket"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.126", features = ["ts-rs", "websocket"] }
|
||||
lazy_static = "1.5.0"
|
||||
miette = "7.6.0"
|
||||
pyo3 = { version = "0.24.2" }
|
||||
@ -61,5 +61,6 @@ result_large_err = "allow"
|
||||
|
||||
# Example: how to point modeling-app at a different repo (e.g. a branch or a local clone)
|
||||
# [patch.crates-io]
|
||||
# kittycad-modeling-cmds = { path = "../../modeling-api/modeling-cmds/" }
|
||||
# kittycad-modeling-session = { path = "../../modeling-api/modeling-session" }
|
||||
# kittycad-modeling-cmds = { git = "https://github.com/KittyCAD/modeling-api.git", branch = "ben/conics" }
|
||||
# kittycad-modeling-cmds = { path = "../../modeling-api/modeling-cmds" }
|
||||
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||
|
@ -177,6 +177,14 @@ pub const TEST_NAMES: &[&str] = &[
|
||||
"std-sketch-line-0",
|
||||
"std-sketch-subtract2d-0",
|
||||
"std-sketch-subtract2d-1",
|
||||
"std-sketch-conic-0",
|
||||
"std-sketch-parabolic-0",
|
||||
"std-sketch-parabolicPoint-0",
|
||||
"std-sketch-hyperbolic-0",
|
||||
"std-sketch-hyperbolicPoint-0",
|
||||
"std-sketch-ellipse-0",
|
||||
"std-sketch-elliptic-0",
|
||||
"std-sketch-ellipticPoint-0",
|
||||
"std-sketch-tangentialArc-0",
|
||||
"std-sketch-tangentialArc-1",
|
||||
"std-sketch-tangentialArc-2",
|
||||
|
@ -663,8 +663,21 @@ impl SketchSurface {
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) enum GetTangentialInfoFromPathsResult {
|
||||
PreviousPoint([f64; 2]),
|
||||
Arc { center: [f64; 2], ccw: bool },
|
||||
Circle { center: [f64; 2], ccw: bool, radius: f64 },
|
||||
Arc {
|
||||
center: [f64; 2],
|
||||
ccw: bool,
|
||||
},
|
||||
Circle {
|
||||
center: [f64; 2],
|
||||
ccw: bool,
|
||||
radius: f64,
|
||||
},
|
||||
Ellipse {
|
||||
center: [f64; 2],
|
||||
ccw: bool,
|
||||
major_radius: f64,
|
||||
_minor_radius: f64,
|
||||
},
|
||||
}
|
||||
|
||||
impl GetTangentialInfoFromPathsResult {
|
||||
@ -679,6 +692,12 @@ impl GetTangentialInfoFromPathsResult {
|
||||
GetTangentialInfoFromPathsResult::Circle {
|
||||
center, radius, ccw, ..
|
||||
} => [center[0] + radius, center[1] + if *ccw { -1.0 } else { 1.0 }],
|
||||
GetTangentialInfoFromPathsResult::Ellipse {
|
||||
center,
|
||||
major_radius,
|
||||
ccw,
|
||||
..
|
||||
} => [center[0] + major_radius, center[1] + if *ccw { -1.0 } else { 1.0 }],
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1134,6 +1153,19 @@ pub enum Path {
|
||||
/// True if the arc is counterclockwise.
|
||||
ccw: bool,
|
||||
},
|
||||
Ellipse {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
center: [f64; 2],
|
||||
major_radius: f64,
|
||||
minor_radius: f64,
|
||||
ccw: bool,
|
||||
},
|
||||
//TODO: (bc) figure this out
|
||||
Conic {
|
||||
#[serde(flatten)]
|
||||
base: BasePath,
|
||||
},
|
||||
}
|
||||
|
||||
/// What kind of path is this?
|
||||
@ -1148,6 +1180,8 @@ enum PathType {
|
||||
Horizontal,
|
||||
AngledLineTo,
|
||||
Arc,
|
||||
Ellipse,
|
||||
Conic,
|
||||
}
|
||||
|
||||
impl From<&Path> for PathType {
|
||||
@ -1163,6 +1197,8 @@ impl From<&Path> for PathType {
|
||||
Path::Base { .. } => Self::Base,
|
||||
Path::Arc { .. } => Self::Arc,
|
||||
Path::ArcThreePoint { .. } => Self::Arc,
|
||||
Path::Ellipse { .. } => Self::Ellipse,
|
||||
Path::Conic { .. } => Self::Conic,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1180,6 +1216,8 @@ impl Path {
|
||||
Path::CircleThreePoint { base, .. } => base.geo_meta.id,
|
||||
Path::Arc { base, .. } => base.geo_meta.id,
|
||||
Path::ArcThreePoint { base, .. } => base.geo_meta.id,
|
||||
Path::Ellipse { base, .. } => base.geo_meta.id,
|
||||
Path::Conic { base, .. } => base.geo_meta.id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1195,6 +1233,8 @@ impl Path {
|
||||
Path::CircleThreePoint { base, .. } => base.geo_meta.id = id,
|
||||
Path::Arc { base, .. } => base.geo_meta.id = id,
|
||||
Path::ArcThreePoint { base, .. } => base.geo_meta.id = id,
|
||||
Path::Ellipse { base, .. } => base.geo_meta.id = id,
|
||||
Path::Conic { base, .. } => base.geo_meta.id = id,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1210,6 +1250,8 @@ impl Path {
|
||||
Path::CircleThreePoint { base, .. } => base.tag.clone(),
|
||||
Path::Arc { base, .. } => base.tag.clone(),
|
||||
Path::ArcThreePoint { base, .. } => base.tag.clone(),
|
||||
Path::Ellipse { base, .. } => base.tag.clone(),
|
||||
Path::Conic { base, .. } => base.tag.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1225,6 +1267,8 @@ impl Path {
|
||||
Path::CircleThreePoint { base, .. } => base,
|
||||
Path::Arc { base, .. } => base,
|
||||
Path::ArcThreePoint { base, .. } => base,
|
||||
Path::Ellipse { base, .. } => base,
|
||||
Path::Conic { base, .. } => base,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1256,11 +1300,12 @@ impl Path {
|
||||
(*p, ty)
|
||||
}
|
||||
|
||||
/// Length of this path segment, in cartesian plane.
|
||||
pub fn length(&self) -> TyF64 {
|
||||
/// Length of this path segment, in cartesian plane. Not all segment types
|
||||
/// are supported.
|
||||
pub fn length(&self) -> Option<TyF64> {
|
||||
let n = match self {
|
||||
Self::ToPoint { .. } | Self::Base { .. } | Self::Horizontal { .. } | Self::AngledLineTo { .. } => {
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
Some(linear_distance(&self.get_base().from, &self.get_base().to))
|
||||
}
|
||||
Self::TangentialArc {
|
||||
base: _,
|
||||
@ -1277,9 +1322,9 @@ impl Path {
|
||||
let radius = linear_distance(&self.get_base().from, center);
|
||||
debug_assert_eq!(radius, linear_distance(&self.get_base().to, center));
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
Some(linear_distance(&self.get_base().from, &self.get_base().to))
|
||||
}
|
||||
Self::Circle { radius, .. } => 2.0 * std::f64::consts::PI * radius,
|
||||
Self::Circle { radius, .. } => Some(2.0 * std::f64::consts::PI * radius),
|
||||
Self::CircleThreePoint { .. } => {
|
||||
let circle_center = crate::std::utils::calculate_circle_from_3_points([
|
||||
self.get_base().from,
|
||||
@ -1290,18 +1335,26 @@ impl Path {
|
||||
&[circle_center.center[0], circle_center.center[1]],
|
||||
&self.get_base().from,
|
||||
);
|
||||
2.0 * std::f64::consts::PI * radius
|
||||
Some(2.0 * std::f64::consts::PI * radius)
|
||||
}
|
||||
Self::Arc { .. } => {
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
Some(linear_distance(&self.get_base().from, &self.get_base().to))
|
||||
}
|
||||
Self::ArcThreePoint { .. } => {
|
||||
// TODO: Call engine utils to figure this out.
|
||||
linear_distance(&self.get_base().from, &self.get_base().to)
|
||||
Some(linear_distance(&self.get_base().from, &self.get_base().to))
|
||||
}
|
||||
Self::Ellipse { .. } => {
|
||||
// Not supported.
|
||||
None
|
||||
}
|
||||
Self::Conic { .. } => {
|
||||
// Not supported.
|
||||
None
|
||||
}
|
||||
};
|
||||
TyF64::new(n, self.get_base().units.into())
|
||||
n.map(|n| TyF64::new(n, self.get_base().units.into()))
|
||||
}
|
||||
|
||||
pub fn get_base_mut(&mut self) -> Option<&mut BasePath> {
|
||||
@ -1316,6 +1369,8 @@ impl Path {
|
||||
Path::CircleThreePoint { base, .. } => Some(base),
|
||||
Path::Arc { base, .. } => Some(base),
|
||||
Path::ArcThreePoint { base, .. } => Some(base),
|
||||
Path::Ellipse { base, .. } => Some(base),
|
||||
Path::Conic { base, .. } => Some(base),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1351,7 +1406,24 @@ impl Path {
|
||||
radius: circle.radius,
|
||||
}
|
||||
}
|
||||
Path::ToPoint { .. } | Path::Horizontal { .. } | Path::AngledLineTo { .. } | Path::Base { .. } => {
|
||||
// TODO: (bc) fix me
|
||||
Path::Ellipse {
|
||||
center,
|
||||
major_radius,
|
||||
minor_radius,
|
||||
ccw,
|
||||
..
|
||||
} => GetTangentialInfoFromPathsResult::Ellipse {
|
||||
center: *center,
|
||||
major_radius: *major_radius,
|
||||
_minor_radius: *minor_radius,
|
||||
ccw: *ccw,
|
||||
},
|
||||
Path::Conic { .. }
|
||||
| Path::ToPoint { .. }
|
||||
| Path::Horizontal { .. }
|
||||
| Path::AngledLineTo { .. }
|
||||
| Path::Base { .. } => {
|
||||
let base = self.get_base();
|
||||
GetTangentialInfoFromPathsResult::PreviousPoint(base.from)
|
||||
}
|
||||
|
@ -1079,6 +1079,34 @@ impl<'a> FromKclValue<'a> for [TyF64; 3] {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for [TyF64; 6] {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
match arg {
|
||||
KclValue::Tuple { value, meta: _ } | KclValue::HomArray { value, .. } => {
|
||||
if value.len() != 6 {
|
||||
return None;
|
||||
}
|
||||
let v0 = value.first()?;
|
||||
let v1 = value.get(1)?;
|
||||
let v2 = value.get(2)?;
|
||||
let v3 = value.get(3)?;
|
||||
let v4 = value.get(4)?;
|
||||
let v5 = value.get(5)?;
|
||||
let array = [
|
||||
v0.as_ty_f64()?,
|
||||
v1.as_ty_f64()?,
|
||||
v2.as_ty_f64()?,
|
||||
v3.as_ty_f64()?,
|
||||
v4.as_ty_f64()?,
|
||||
v5.as_ty_f64()?,
|
||||
];
|
||||
Some(array)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromKclValue<'a> for Sketch {
|
||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||
let KclValue::Sketch { value } = arg else {
|
||||
|
@ -251,6 +251,9 @@ pub(crate) async fn do_post_extrude<'a>(
|
||||
Path::Arc { .. }
|
||||
| Path::TangentialArc { .. }
|
||||
| Path::TangentialArcTo { .. }
|
||||
// TODO: (bc) fix me
|
||||
| Path::Ellipse { .. }
|
||||
| Path::Conic {.. }
|
||||
| Path::Circle { .. }
|
||||
| Path::CircleThreePoint { .. } => {
|
||||
let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::execution::ExtrudeArc {
|
||||
|
@ -156,6 +156,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
||||
|e, a| Box::pin(crate::std::math::leg_angle_y(e, a)),
|
||||
StdFnProps::default("std::math::legAngY"),
|
||||
),
|
||||
("sketch", "circle") => (
|
||||
|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").include_in_feature_tree(),
|
||||
),
|
||||
("prelude", "helix") => (
|
||||
|e, a| Box::pin(crate::std::helix::helix(e, a)),
|
||||
StdFnProps::default("std::helix").include_in_feature_tree(),
|
||||
@ -252,9 +260,33 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
||||
|e, a| Box::pin(crate::std::clone::clone(e, a)),
|
||||
StdFnProps::default("std::clone").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "circle") => (
|
||||
|e, a| Box::pin(crate::std::shapes::circle(e, a)),
|
||||
StdFnProps::default("std::sketch::circle"),
|
||||
("sketch", "conic") => (
|
||||
|e, a| Box::pin(crate::std::sketch::conic(e, a)),
|
||||
StdFnProps::default("std::sketch::conic").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "parabolic") => (
|
||||
|e, a| Box::pin(crate::std::sketch::parabolic(e, a)),
|
||||
StdFnProps::default("std::sketch::parabolic").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "parabolicPoint") => (
|
||||
|e, a| Box::pin(crate::std::sketch::parabolic_point(e, a)),
|
||||
StdFnProps::default("std::sketch::parabolicPoint"),
|
||||
),
|
||||
("sketch", "hyperbolic") => (
|
||||
|e, a| Box::pin(crate::std::sketch::hyperbolic(e, a)),
|
||||
StdFnProps::default("std::sketch::hyperbolic").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "hyperbolicPoint") => (
|
||||
|e, a| Box::pin(crate::std::sketch::hyperbolic_point(e, a)),
|
||||
StdFnProps::default("std::sketch::hyperbolicPoint"),
|
||||
),
|
||||
("sketch", "elliptic") => (
|
||||
|e, a| Box::pin(crate::std::sketch::elliptic(e, a)),
|
||||
StdFnProps::default("std::sketch::elliptic").include_in_feature_tree(),
|
||||
),
|
||||
("sketch", "ellipticPoint") => (
|
||||
|e, a| Box::pin(crate::std::sketch::elliptic_point(e, a)),
|
||||
StdFnProps::default("std::sketch::ellipticPoint"),
|
||||
),
|
||||
("sketch", "rectangle") => (
|
||||
|e, a| Box::pin(crate::std::shapes::rectangle(e, a)),
|
||||
|
@ -200,7 +200,12 @@ fn inner_segment_length(tag: &TagIdentifier, exec_state: &mut ExecState, args: A
|
||||
))
|
||||
})?;
|
||||
|
||||
Ok(path.length())
|
||||
path.length().ok_or_else(|| {
|
||||
KclError::new_semantic(KclErrorDetails::new(
|
||||
"Computing the length of this segment type is unsupported".to_owned(),
|
||||
vec![args.source_range],
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns the angle of the segment.
|
||||
|
@ -520,6 +520,114 @@ async fn inner_polygon(
|
||||
Ok(sketch)
|
||||
}
|
||||
|
||||
/// Sketch an ellipse.
|
||||
pub async fn ellipse(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
exec_state.warn(crate::CompilationError {
|
||||
source_range: args.source_range,
|
||||
message: "Use of ellipse is currently experimental and the interface may change.".to_string(),
|
||||
suggestion: None,
|
||||
severity: crate::errors::Severity::Warning,
|
||||
tag: crate::errors::Tag::None,
|
||||
});
|
||||
let sketch_or_surface =
|
||||
args.get_unlabeled_kw_arg("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?;
|
||||
let center = args.get_kw_arg("center", &RuntimeType::point2d(), exec_state)?;
|
||||
let major_radius = args.get_kw_arg("majorRadius", &RuntimeType::length(), exec_state)?;
|
||||
let minor_radius = args.get_kw_arg("minorRadius", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||
|
||||
let sketch = inner_ellipse(
|
||||
sketch_or_surface,
|
||||
center,
|
||||
major_radius,
|
||||
minor_radius,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
})
|
||||
}
|
||||
|
||||
async fn inner_ellipse(
|
||||
sketch_surface_or_group: SketchOrSurface,
|
||||
center: [TyF64; 2],
|
||||
major_radius: TyF64,
|
||||
minor_radius: TyF64,
|
||||
tag: Option<TagNode>,
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let sketch_surface = match sketch_surface_or_group {
|
||||
SketchOrSurface::SketchSurface(surface) => surface,
|
||||
SketchOrSurface::Sketch(group) => group.on,
|
||||
};
|
||||
let (center_u, ty) = untype_point(center.clone());
|
||||
let units = ty.expect_length();
|
||||
|
||||
let from = [center_u[0] + major_radius.to_length_units(units), center_u[1]];
|
||||
let from_t = [TyF64::new(from[0], ty), TyF64::new(from[1], ty)];
|
||||
|
||||
let sketch =
|
||||
crate::std::sketch::inner_start_profile(sketch_surface, from_t, None, exec_state, args.clone()).await?;
|
||||
|
||||
let angle_start = Angle::zero();
|
||||
let angle_end = Angle::turn();
|
||||
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, id),
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Ellipse {
|
||||
center: KPoint2d::from(point_to_mm(center)).map(LengthUnit),
|
||||
major_radius: LengthUnit(major_radius.to_mm()),
|
||||
minor_radius: LengthUnit(minor_radius.to_mm()),
|
||||
start_angle: Angle::from_degrees(angle_start.to_degrees()),
|
||||
end_angle: Angle::from_degrees(angle_end.to_degrees()),
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_path = Path::Ellipse {
|
||||
base: BasePath {
|
||||
from,
|
||||
to: from,
|
||||
tag: tag.clone(),
|
||||
units,
|
||||
geo_meta: GeoMeta {
|
||||
id,
|
||||
metadata: args.source_range.into(),
|
||||
},
|
||||
},
|
||||
major_radius: major_radius.to_length_units(units),
|
||||
minor_radius: minor_radius.to_length_units(units),
|
||||
center: center_u,
|
||||
ccw: angle_start < angle_end,
|
||||
};
|
||||
|
||||
let mut new_sketch = sketch.clone();
|
||||
if let Some(tag) = &tag {
|
||||
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||
}
|
||||
|
||||
new_sketch.paths.push(current_path);
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, id),
|
||||
ModelingCmd::from(mcmd::ClosePath { path_id: new_sketch.id }),
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
|
||||
pub(crate) fn get_radius(
|
||||
radius: Option<TyF64>,
|
||||
diameter: Option<TyF64>,
|
||||
|
@ -11,7 +11,10 @@ use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::shapes::{get_radius, get_radius_labelled};
|
||||
use super::{
|
||||
shapes::{get_radius, get_radius_labelled},
|
||||
utils::{untype_array, untype_point},
|
||||
};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::execution::{Artifact, ArtifactId, CodeRef, StartSketchOnFace, StartSketchOnPlane};
|
||||
use crate::{
|
||||
@ -1777,6 +1780,727 @@ async fn inner_subtract_2d(
|
||||
Ok(sketch)
|
||||
}
|
||||
|
||||
/// Calculate the (x, y) point on an ellipse given x or y and the major/minor radii of the ellipse.
|
||||
pub async fn elliptic_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let x = args.get_kw_arg_opt("x", &RuntimeType::length(), exec_state)?;
|
||||
let y = args.get_kw_arg_opt("y", &RuntimeType::length(), exec_state)?;
|
||||
let major_radius = args.get_kw_arg("majorRadius", &RuntimeType::num_any(), exec_state)?;
|
||||
let minor_radius = args.get_kw_arg("minorRadius", &RuntimeType::num_any(), exec_state)?;
|
||||
|
||||
let elliptic_point = inner_elliptic_point(x, y, major_radius, minor_radius, &args).await?;
|
||||
|
||||
args.make_kcl_val_from_point(elliptic_point, exec_state.length_unit().into())
|
||||
}
|
||||
|
||||
async fn inner_elliptic_point(
|
||||
x: Option<TyF64>,
|
||||
y: Option<TyF64>,
|
||||
major_radius: TyF64,
|
||||
minor_radius: TyF64,
|
||||
args: &Args,
|
||||
) -> Result<[f64; 2], KclError> {
|
||||
let major_radius = major_radius.n;
|
||||
let minor_radius = minor_radius.n;
|
||||
if let Some(x) = x {
|
||||
if x.n > major_radius {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
format!(
|
||||
"Invalid input. The x value, {}, cannot be larger than the major radius {}.",
|
||||
x.n, major_radius
|
||||
)
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
} else {
|
||||
Ok((
|
||||
x.n,
|
||||
minor_radius * (1.0 - x.n.powf(2.0) / major_radius.powf(2.0)).sqrt(),
|
||||
)
|
||||
.into())
|
||||
}
|
||||
} else if let Some(y) = y {
|
||||
if y.n > minor_radius {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
format!(
|
||||
"Invalid input. The y value, {}, cannot be larger than the minor radius {}.",
|
||||
y.n, minor_radius
|
||||
)
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
} else {
|
||||
Ok((
|
||||
major_radius * (1.0 - y.n.powf(2.0) / minor_radius.powf(2.0)).sqrt(),
|
||||
y.n,
|
||||
)
|
||||
.into())
|
||||
}
|
||||
} else {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid input. Must have either x or y, you cannot have both or neither.".to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw an elliptical arc.
|
||||
pub async fn elliptic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
exec_state.warn(crate::CompilationError {
|
||||
source_range: args.source_range,
|
||||
message: "Use of elliptic is currently experimental and the interface may change.".to_string(),
|
||||
suggestion: None,
|
||||
severity: crate::errors::Severity::Warning,
|
||||
tag: crate::errors::Tag::None,
|
||||
});
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
|
||||
let center = args.get_kw_arg("center", &RuntimeType::point2d(), exec_state)?;
|
||||
let angle_start = args.get_kw_arg("angleStart", &RuntimeType::degrees(), exec_state)?;
|
||||
let angle_end = args.get_kw_arg("angleEnd", &RuntimeType::degrees(), exec_state)?;
|
||||
let major_radius = args.get_kw_arg("majorRadius", &RuntimeType::length(), exec_state)?;
|
||||
let minor_radius = args.get_kw_arg("minorRadius", &RuntimeType::length(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||
|
||||
let new_sketch = inner_elliptic(
|
||||
sketch,
|
||||
center,
|
||||
angle_start,
|
||||
angle_end,
|
||||
major_radius,
|
||||
minor_radius,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn inner_elliptic(
|
||||
sketch: Sketch,
|
||||
center: [TyF64; 2],
|
||||
angle_start: TyF64,
|
||||
angle_end: TyF64,
|
||||
major_radius: TyF64,
|
||||
minor_radius: TyF64,
|
||||
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 (center_u, _) = untype_point(center);
|
||||
|
||||
let start_angle = Angle::from_degrees(angle_start.to_degrees());
|
||||
let end_angle = Angle::from_degrees(angle_end.to_degrees());
|
||||
let to = [
|
||||
center_u[0] + major_radius.to_length_units(from.units) * libm::cos(end_angle.to_radians()),
|
||||
center_u[1] + minor_radius.to_length_units(from.units) * libm::sin(end_angle.to_radians()),
|
||||
];
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, id),
|
||||
ModelingCmd::from(mcmd::ExtendPath {
|
||||
path: sketch.id.into(),
|
||||
segment: PathSegment::Ellipse {
|
||||
center: KPoint2d::from(untyped_point_to_mm(center_u, from.units)).map(LengthUnit),
|
||||
major_radius: LengthUnit(from.units.adjust_to(major_radius.to_mm(), UnitLen::Mm).0),
|
||||
minor_radius: LengthUnit(from.units.adjust_to(minor_radius.to_mm(), UnitLen::Mm).0),
|
||||
start_angle,
|
||||
end_angle,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let current_path = Path::Ellipse {
|
||||
ccw: start_angle < end_angle,
|
||||
center: center_u,
|
||||
major_radius: major_radius.to_mm(),
|
||||
minor_radius: minor_radius.to_mm(),
|
||||
base: BasePath {
|
||||
from: from.ignore_units(),
|
||||
to,
|
||||
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, ¤t_path, exec_state);
|
||||
}
|
||||
|
||||
new_sketch.paths.push(current_path);
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
|
||||
/// Calculate the (x, y) point on an hyperbola given x or y and the semi major/minor of the ellipse.
|
||||
pub async fn hyperbolic_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let x = args.get_kw_arg_opt("x", &RuntimeType::length(), exec_state)?;
|
||||
let y = args.get_kw_arg_opt("y", &RuntimeType::length(), exec_state)?;
|
||||
let semi_major = args.get_kw_arg("semiMajor", &RuntimeType::num_any(), exec_state)?;
|
||||
let semi_minor = args.get_kw_arg("semiMinor", &RuntimeType::num_any(), exec_state)?;
|
||||
|
||||
let hyperbolic_point = inner_hyperbolic_point(x, y, semi_major, semi_minor, &args).await?;
|
||||
|
||||
args.make_kcl_val_from_point(hyperbolic_point, exec_state.length_unit().into())
|
||||
}
|
||||
|
||||
async fn inner_hyperbolic_point(
|
||||
x: Option<TyF64>,
|
||||
y: Option<TyF64>,
|
||||
semi_major: TyF64,
|
||||
semi_minor: TyF64,
|
||||
args: &Args,
|
||||
) -> Result<[f64; 2], KclError> {
|
||||
let semi_major = semi_major.n;
|
||||
let semi_minor = semi_minor.n;
|
||||
if let Some(x) = x {
|
||||
if x.n < semi_major {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
format!(
|
||||
"Invalid input. The x value, {}, cannot be less than the semi major value, {}.",
|
||||
x.n, semi_major
|
||||
)
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
} else {
|
||||
Ok((x.n, semi_minor * (x.n.powf(2.0) / semi_major.powf(2.0) - 1.0).sqrt()).into())
|
||||
}
|
||||
} else if let Some(y) = y {
|
||||
Ok((semi_major * (y.n.powf(2.0) / semi_minor.powf(2.0) + 1.0).sqrt(), y.n).into())
|
||||
} else {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid input. Must have either x or y, cannot have both or neither.".to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a hyperbolic arc.
|
||||
pub async fn hyperbolic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
exec_state.warn(crate::CompilationError {
|
||||
source_range: args.source_range,
|
||||
message: "Use of hyperbolic is currently experimental and the interface may change.".to_string(),
|
||||
suggestion: None,
|
||||
severity: crate::errors::Severity::Warning,
|
||||
tag: crate::errors::Tag::None,
|
||||
});
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
|
||||
let semi_major = args.get_kw_arg("semiMajor", &RuntimeType::length(), exec_state)?;
|
||||
let semi_minor = args.get_kw_arg("semiMinor", &RuntimeType::length(), exec_state)?;
|
||||
let interior = args.get_kw_arg_opt("interior", &RuntimeType::point2d(), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let interior_absolute = args.get_kw_arg_opt("interiorAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||
|
||||
let new_sketch = inner_hyperbolic(
|
||||
sketch,
|
||||
semi_major,
|
||||
semi_minor,
|
||||
interior,
|
||||
end,
|
||||
interior_absolute,
|
||||
end_absolute,
|
||||
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()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn inner_hyperbolic(
|
||||
sketch: Sketch,
|
||||
semi_major: TyF64,
|
||||
semi_minor: TyF64,
|
||||
interior: Option<[TyF64; 2]>,
|
||||
end: Option<[TyF64; 2]>,
|
||||
interior_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 id = exec_state.next_uuid();
|
||||
|
||||
let (interior, end, relative) = match (interior, end, interior_absolute, end_absolute) {
|
||||
(Some(interior), Some(end), None, None) => (interior, end, true),
|
||||
(None, None, Some(interior_absolute), Some(end_absolute)) => (interior_absolute, end_absolute, false),
|
||||
_ => return Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide (end, interior) or (endAbsolute, interiorAbsolute)"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, 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,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.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, ¤t_path, exec_state);
|
||||
}
|
||||
|
||||
new_sketch.paths.push(current_path);
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
|
||||
/// Calculate the point on a parabola given the coefficient of the parabola and either x or y
|
||||
pub async fn parabolic_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
let x = args.get_kw_arg_opt("x", &RuntimeType::length(), exec_state)?;
|
||||
let y = args.get_kw_arg_opt("y", &RuntimeType::length(), exec_state)?;
|
||||
let coefficients = args.get_kw_arg(
|
||||
"coefficients",
|
||||
&RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::Known(3)),
|
||||
exec_state,
|
||||
)?;
|
||||
|
||||
let parabolic_point = inner_parabolic_point(x, y, &coefficients, &args).await?;
|
||||
|
||||
args.make_kcl_val_from_point(parabolic_point, exec_state.length_unit().into())
|
||||
}
|
||||
|
||||
async fn inner_parabolic_point(
|
||||
x: Option<TyF64>,
|
||||
y: Option<TyF64>,
|
||||
coefficients: &[TyF64; 3],
|
||||
args: &Args,
|
||||
) -> Result<[f64; 2], KclError> {
|
||||
let a = coefficients[0].n;
|
||||
let b = coefficients[1].n;
|
||||
let c = coefficients[2].n;
|
||||
if let Some(x) = x {
|
||||
Ok((x.n, a * x.n.powf(2.0) + b * x.n + c).into())
|
||||
} else if let Some(y) = y {
|
||||
let det = (b.powf(2.0) - 4.0 * a * (c - y.n)).sqrt();
|
||||
Ok(((-b + det) / (2.0 * a), y.n).into())
|
||||
} else {
|
||||
Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid input. Must have either x or y, cannot have both or neither.".to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw a parabolic arc.
|
||||
pub async fn parabolic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
exec_state.warn(crate::CompilationError {
|
||||
source_range: args.source_range,
|
||||
message: "Use of parabolic is currently experimental and the interface may change.".to_string(),
|
||||
suggestion: None,
|
||||
severity: crate::errors::Severity::Warning,
|
||||
tag: crate::errors::Tag::None,
|
||||
});
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
|
||||
let coefficients = args.get_kw_arg_opt(
|
||||
"coefficients",
|
||||
&RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::Known(3)),
|
||||
exec_state,
|
||||
)?;
|
||||
let interior = args.get_kw_arg_opt("interior", &RuntimeType::point2d(), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let interior_absolute = args.get_kw_arg_opt("interiorAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||
|
||||
let new_sketch = inner_parabolic(
|
||||
sketch,
|
||||
coefficients,
|
||||
interior,
|
||||
end,
|
||||
interior_absolute,
|
||||
end_absolute,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
fn parabolic_tangent(point: Point2d, a: f64, b: f64) -> [f64; 2] {
|
||||
//f(x) = ax^2 + bx + c
|
||||
//f'(x) = 2ax + b
|
||||
(1.0, 2.0 * a * point.x + b).into()
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn inner_parabolic(
|
||||
sketch: Sketch,
|
||||
coefficients: Option<[TyF64; 3]>,
|
||||
interior: Option<[TyF64; 2]>,
|
||||
end: Option<[TyF64; 2]>,
|
||||
interior_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 id = exec_state.next_uuid();
|
||||
|
||||
if (coefficients.is_some() && interior.is_some()) || (coefficients.is_none() && interior.is_none()) {
|
||||
return Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide (a, b, c) or (interior)".to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
let (interior, end, relative) = match (coefficients.clone(), interior, end, interior_absolute, end_absolute) {
|
||||
(None, Some(interior), Some(end), None, None) => {
|
||||
let (interior, _) = untype_point(interior);
|
||||
let (end, _) = untype_point(end);
|
||||
(interior,end, true)
|
||||
},
|
||||
(None, None, None, Some(interior_absolute), Some(end_absolute)) => {
|
||||
let (interior_absolute, _) = untype_point(interior_absolute);
|
||||
let (end_absolute, _) = untype_point(end_absolute);
|
||||
(interior_absolute, end_absolute, false)
|
||||
}
|
||||
(Some(coefficients), _, Some(end), _, _) => {
|
||||
let (end, _) = untype_point(end);
|
||||
let interior =
|
||||
inner_parabolic_point(
|
||||
Some(TyF64::count(0.5 * (from.x + end[0]))),
|
||||
None,
|
||||
&coefficients,
|
||||
&args,
|
||||
)
|
||||
.await?;
|
||||
(interior, end, true)
|
||||
}
|
||||
(Some(coefficients), _, _, _, Some(end)) => {
|
||||
let (end, _) = untype_point(end);
|
||||
let interior =
|
||||
inner_parabolic_point(
|
||||
Some(TyF64::count(0.5 * (from.x + end[0]))),
|
||||
None,
|
||||
&coefficients,
|
||||
&args,
|
||||
)
|
||||
.await?;
|
||||
(interior, end, false)
|
||||
}
|
||||
_ => return
|
||||
Err(KclError::Type{details: KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide (end, interior) or (endAbsolute, interiorAbsolute) if coefficients are not provided."
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
)}),
|
||||
};
|
||||
|
||||
let end_point = Point2d {
|
||||
x: end[0],
|
||||
y: end[1],
|
||||
units: from.units,
|
||||
};
|
||||
|
||||
let (a, b, _c) = if let Some([a, b, c]) = coefficients {
|
||||
(a.n, b.n, c.n)
|
||||
} else {
|
||||
// Any three points is enough to uniquely define a parabola
|
||||
let denom = (from.x - interior[0]) * (from.x - end_point.x) * (interior[0] - end_point.x);
|
||||
let a = (end_point.x * (interior[1] - from.y)
|
||||
+ interior[0] * (from.y - end_point.y)
|
||||
+ from.x * (end_point.y - interior[1]))
|
||||
/ denom;
|
||||
let b = (end_point.x.powf(2.0) * (from.y - interior[1])
|
||||
+ interior[0].powf(2.0) * (end_point.y - from.y)
|
||||
+ from.x.powf(2.0) * (interior[1] - end_point.y))
|
||||
/ denom;
|
||||
let c = (interior[0] * end_point.x * (interior[0] - end_point.x) * from.y
|
||||
+ end_point.x * from.x * (end_point.x - from.x) * interior[1]
|
||||
+ from.x * interior[0] * (from.x - interior[0]) * end_point.y)
|
||||
/ denom;
|
||||
|
||||
(a, b, c)
|
||||
};
|
||||
|
||||
let start_tangent = parabolic_tangent(from, a, b);
|
||||
let end_tangent = parabolic_tangent(end_point, a, b);
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, 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,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.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, ¤t_path, exec_state);
|
||||
}
|
||||
|
||||
new_sketch.paths.push(current_path);
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
|
||||
fn conic_tangent(coefficients: [f64; 6], point: [f64; 2]) -> [f64; 2] {
|
||||
let [a, b, c, d, e, _] = coefficients;
|
||||
|
||||
(
|
||||
c * point[0] + 2.0 * b * point[1] + e,
|
||||
-(2.0 * a * point[0] + c * point[1] + d),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Draw a conic section
|
||||
pub async fn conic(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
exec_state.warn(crate::CompilationError {
|
||||
source_range: args.source_range,
|
||||
message: "Use of conics is currently experimental and the interface may change.".to_string(),
|
||||
suggestion: None,
|
||||
severity: crate::errors::Severity::Warning,
|
||||
tag: crate::errors::Tag::None,
|
||||
});
|
||||
let sketch = args.get_unlabeled_kw_arg("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||
|
||||
let start_tangent = args.get_kw_arg_opt("startTangent", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_tangent = args.get_kw_arg_opt("endTangent", &RuntimeType::point2d(), exec_state)?;
|
||||
let end = args.get_kw_arg_opt("end", &RuntimeType::point2d(), exec_state)?;
|
||||
let interior = args.get_kw_arg_opt("interior", &RuntimeType::point2d(), exec_state)?;
|
||||
let end_absolute = args.get_kw_arg_opt("endAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let interior_absolute = args.get_kw_arg_opt("interiorAbsolute", &RuntimeType::point2d(), exec_state)?;
|
||||
let coefficients = args.get_kw_arg_opt(
|
||||
"coefficients",
|
||||
&RuntimeType::Array(Box::new(RuntimeType::num_any()), ArrayLen::Known(6)),
|
||||
exec_state,
|
||||
)?;
|
||||
let tag = args.get_kw_arg_opt("tag", &RuntimeType::tag_decl(), exec_state)?;
|
||||
|
||||
let new_sketch = inner_conic(
|
||||
sketch,
|
||||
start_tangent,
|
||||
end,
|
||||
end_tangent,
|
||||
interior,
|
||||
coefficients,
|
||||
interior_absolute,
|
||||
end_absolute,
|
||||
tag,
|
||||
exec_state,
|
||||
args,
|
||||
)
|
||||
.await?;
|
||||
Ok(KclValue::Sketch {
|
||||
value: Box::new(new_sketch),
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) async fn inner_conic(
|
||||
sketch: Sketch,
|
||||
start_tangent: Option<[TyF64; 2]>,
|
||||
end: Option<[TyF64; 2]>,
|
||||
end_tangent: Option<[TyF64; 2]>,
|
||||
interior: Option<[TyF64; 2]>,
|
||||
coefficients: Option<[TyF64; 6]>,
|
||||
interior_absolute: Option<[TyF64; 2]>,
|
||||
end_absolute: Option<[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();
|
||||
|
||||
if (coefficients.is_some() && (start_tangent.is_some() || end_tangent.is_some()))
|
||||
|| (coefficients.is_none() && (start_tangent.is_none() && end_tangent.is_none()))
|
||||
{
|
||||
return Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide coefficients or (startTangent, endTangent)"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
let (interior, end, relative) = match (interior, end, interior_absolute, end_absolute) {
|
||||
(Some(interior), Some(end), None, None) => (interior, end, true),
|
||||
(None, None, Some(interior_absolute), Some(end_absolute)) => (interior_absolute, end_absolute, false),
|
||||
_ => return Err(KclError::Type {
|
||||
details: KclErrorDetails::new(
|
||||
"Invalid combination of arguments. Either provide (end, interior) or (endAbsolute, interiorAbsolute)"
|
||||
.to_owned(),
|
||||
vec![args.source_range],
|
||||
),
|
||||
}),
|
||||
};
|
||||
|
||||
let (end, _) = untype_array(end);
|
||||
let (interior, _) = untype_point(interior);
|
||||
|
||||
let (start_tangent, end_tangent) = if let Some(coeffs) = coefficients {
|
||||
let (coeffs, _) = untype_array(coeffs);
|
||||
(conic_tangent(coeffs, [from.x, from.y]), conic_tangent(coeffs, end))
|
||||
} else {
|
||||
let start = if let Some(start_tangent) = start_tangent {
|
||||
let (start, _) = untype_point(start_tangent);
|
||||
start
|
||||
} 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]]
|
||||
};
|
||||
|
||||
let Some(end_tangent) = end_tangent else {
|
||||
return Err(KclError::new_semantic(KclErrorDetails::new(
|
||||
"You must either provide either `coefficients` or `endTangent`.".to_owned(),
|
||||
vec![args.source_range],
|
||||
)));
|
||||
};
|
||||
let (end_tan, _) = untype_point(end_tangent);
|
||||
(start, end_tan)
|
||||
};
|
||||
|
||||
exec_state
|
||||
.batch_modeling_cmd(
|
||||
ModelingCmdMeta::from_args_id(&args, 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,
|
||||
},
|
||||
}),
|
||||
)
|
||||
.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, ¤t_path, exec_state);
|
||||
}
|
||||
|
||||
new_sketch.paths.push(current_path);
|
||||
|
||||
Ok(new_sketch)
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
|
@ -10,6 +10,15 @@ pub(crate) fn untype_point(p: [TyF64; 2]) -> ([f64; 2], NumericType) {
|
||||
([x, y], ty)
|
||||
}
|
||||
|
||||
pub(crate) fn untype_array<const N: usize>(p: [TyF64; N]) -> ([f64; N], NumericType) {
|
||||
let (vec, ty) = NumericType::combine_eq_array(&p);
|
||||
(
|
||||
vec.try_into()
|
||||
.unwrap_or_else(|v: Vec<f64>| panic!("Expected a Vec of length {} but it was {}", N, v.len())),
|
||||
ty,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn point_to_mm(p: [TyF64; 2]) -> [f64; 2] {
|
||||
[p[0].to_mm(), p[1].to_mm()]
|
||||
}
|
||||
|
@ -304,6 +304,27 @@ export fn circle(
|
||||
tag?: TagDecl,
|
||||
): Sketch {}
|
||||
|
||||
/// Construct a 2-dimensional ellipse, of the specified major/minor radius, centered at the provided (x, y) point.
|
||||
///
|
||||
/// ```
|
||||
/// exampleSketch = startSketchOn(XY)
|
||||
/// |> ellipse(center = [0, 0], majorRadius = 50, minorRadius = 20)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn ellipse(
|
||||
/// Sketch to extend, or plane or surface to sketch on.
|
||||
@sketchOrSurface: Sketch | Plane | Face,
|
||||
/// The center of the ellipse.
|
||||
@(snippetArray = ["0", "0"])
|
||||
center: Point2d,
|
||||
/// The major radius of the ellipse.
|
||||
majorRadius: number(Length),
|
||||
/// The minor radius of the ellipse.
|
||||
minorRadius: number(Length),
|
||||
/// Create a new tag which refers to this ellipse.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Extend a 2-dimensional sketch through a third dimension in order to
|
||||
/// create new 3-dimensional volume, or if extruded into an existing volume,
|
||||
/// cut into an existing solid.
|
||||
@ -1975,6 +1996,195 @@ export fn subtract2d(
|
||||
tool: [Sketch; 1+],
|
||||
): Sketch {}
|
||||
|
||||
/// Add a conic section to an existing sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XZ)
|
||||
/// |> startProfile(at = [0, 0])
|
||||
/// |> conic(
|
||||
/// end = [10,0],
|
||||
/// endTangent = [0,1],
|
||||
/// interior = [5,5],
|
||||
/// startTangent = [0, -1],
|
||||
/// )
|
||||
/// |> close()
|
||||
/// example = extrude(exampleSketch, length = 10)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn conic(
|
||||
/// Which sketch should this path be added to?
|
||||
@sketch: Sketch,
|
||||
/// Any point between the segment's start and end. Requires `endAbsolute`. Incompatible with `interior` or `end`.
|
||||
interiorAbsolute?: Point2d,
|
||||
/// Where should this segment end? Requires `interiorAbsolute`. Incompatible with `interior` or `end`.
|
||||
endAbsolute?: Point2d,
|
||||
/// Any point between the segment's start and end. This point is relative to the start point. Requires `end`. Incompatible with `interiorAbsolute` or `endAbsolute`.
|
||||
interior?: Point2d,
|
||||
/// Where should this segment end? This point is relative to the start point. Requires `interior`. Incompatible with `interiorAbsolute` or `endAbsolute`.
|
||||
end?: Point2d,
|
||||
/// The coefficients [a, b, c, d, e, f] of the generic conic equation ax^2 + by^2 + cxy + dx + ey + f = 0. If provided the start and end tangents will be calculated using this equation. Incompatible with `startTangent` and `endTangent`.
|
||||
coefficients?: [number; 6],
|
||||
/// The tangent of the conic section at the start. If not provided the tangent of the previous path segment is used. Incompatible with `coefficients`.
|
||||
startTangent?: Point2d,
|
||||
/// The tangent of the conic section at the end. Incompatible with `coefficients`.
|
||||
endTangent?: Point2d,
|
||||
/// Create a new tag which refers to this segment.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Add a parabolic segment to an existing sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0,0])
|
||||
/// |> parabolic(
|
||||
/// end = [10,0],
|
||||
/// coefficients = [2, 0, 0],
|
||||
/// )
|
||||
/// |>close()
|
||||
///```
|
||||
@(impl = std_rust)
|
||||
export fn parabolic(
|
||||
/// Which sketch should this path be added to?
|
||||
@sketch: Sketch,
|
||||
/// Where should the path end? Relative to the start point. Incompatible with `interiorAbsolute` or `endAbsolute`.
|
||||
end: Point2d,
|
||||
/// Where should this segment end? Requires `interiorAbsolute`. Incompatible with `interior` or `end`.
|
||||
endAbsolute?: Point2d,
|
||||
/// The coefficients [a, b, c] of the parabolic equation y = ax^2 + bx + c. Incompatible with `interior`.
|
||||
coefficients?: [number; 3],
|
||||
/// A point between the segment's start and end that lies on the parabola. Incompatible with `coefficients` or `interiorAbsolute` or `endAbsolute`.
|
||||
interior?: Point2d,
|
||||
/// Any point between the segment's start and end. Requires `endAbsolute`. Incompatible with `coefficients` or `interior` or `end`.
|
||||
interiorAbsolute?: Point2d,
|
||||
/// Create a new tag which refers to this segment.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Calculate the point (x, y) on a parabola given x or y and the coefficients [a, b, c] of the parabola.
|
||||
///
|
||||
/// ```kcl
|
||||
/// point001 = parabolicPoint(x = 5, coefficients = [0.1, 0, 0])
|
||||
/// point002 = parabolicPoint(y = 2.5, coefficients = [0.1, 0, 0])
|
||||
/// assert(point001[0], isEqualTo = point002[0])
|
||||
/// assert(point001[1], isEqualTo = point002[1])
|
||||
///```
|
||||
@(impl = std_rust)
|
||||
export fn parabolicPoint(
|
||||
/// The coefficients [a, b, c] of the parabolic equation y = ax^2 + bx + c.
|
||||
coefficients: [number; 3],
|
||||
/// The x value. Calculates y and returns (x, y). Incompatible with `y`.
|
||||
x?: number(Length),
|
||||
/// The y value. Calculates x and returns (x, y). Incompatible with `x`.
|
||||
y?: number(Length),
|
||||
): Point2d {}
|
||||
|
||||
/// Add a hyperbolic section to an existing sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// exampleSketch = startSketchOn(XY)
|
||||
/// |> startProfile(at = [0,0])
|
||||
/// |> hyperbolic(
|
||||
/// end = [10,0],
|
||||
/// semiMajor = 2,
|
||||
/// semiMinor = 1,
|
||||
/// interior = [0,0]
|
||||
/// )
|
||||
/// |>close()
|
||||
///```
|
||||
@(impl = std_rust)
|
||||
export fn hyperbolic(
|
||||
/// Which sketch should this path be added to?
|
||||
@sketch: Sketch,
|
||||
/// The semi major value, a, of the hyperbolic equation x^2 / a ^ 2 - y^2 / b^2 = 1.
|
||||
semiMajor: number(Length),
|
||||
/// The semi minor value, b, of the hyperbolic equation x^2 / a ^ 2 - y^2 / b^2 = 1.
|
||||
semiMinor: number(Length),
|
||||
/// Any point between the segment's start and end. Requires `endAbsolute`. Incompatible with `interior` or `end`.
|
||||
interiorAbsolute?: Point2d,
|
||||
/// Where should this segment end? Requires `interiorAbsolute`. Incompatible with `interior` or `end`.
|
||||
endAbsolute?: Point2d,
|
||||
/// Any point between the segment's start and end. This point is relative to the start point. Requires `end`. Incompatible with `interiorAbsolute` or `endAbsolute`.
|
||||
interior?: Point2d,
|
||||
/// Where should this segment end? This point is relative to the start point. Requires `interior`. Incompatible with `interiorAbsolute` or `endAbsolute`.
|
||||
end?: Point2d,
|
||||
/// Create a new tag which refers to this arc.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Calculate the point (x, y) on a hyperbola given x or y and the semi major/minor values of the hyperbolic.
|
||||
///
|
||||
/// ```kcl
|
||||
/// point = hyperbolicPoint(x = 5, semiMajor = 2, semiMinor = 1)
|
||||
///```
|
||||
@(impl = std_rust)
|
||||
export fn hyperbolicPoint(
|
||||
/// The semi major value, a, of the hyperbolic equation x^2 / a ^ 2 - y^2 / b^2 = 1.
|
||||
semiMajor: number,
|
||||
/// The semi minor value, b, of the hyperbolic equation x^2 / a ^ 2 - y^2 / b^2 = 1.
|
||||
semiMinor: number,
|
||||
/// The x value. Calculates y and returns (x, y). Incompatible with `y`.
|
||||
x?: number(Length),
|
||||
/// The y value. Calculates x and returns (x, y). Incompatible with `x`.
|
||||
y?: number(Length),
|
||||
): Point2d {}
|
||||
|
||||
/// Add a parabolic section to an existing sketch.
|
||||
///
|
||||
/// ```kcl
|
||||
/// majorRadius = 2
|
||||
/// minorRadius = 1
|
||||
/// ellip = ellipticPoint(majorRadius, minorRadius, x = 2)
|
||||
///
|
||||
/// exampleSketch = startSketchOn(XY)
|
||||
/// |> startProfile(at = ellip, tag = $start)
|
||||
/// |> elliptic(center = [0, 0], angleStart = segAng(start), angleEnd = 160, majorRadius, minorRadius)
|
||||
/// |> close()
|
||||
/// example = extrude(exampleSketch, length = 10)
|
||||
/// ```
|
||||
@(impl = std_rust)
|
||||
export fn elliptic(
|
||||
/// Which sketch should this path be added to?
|
||||
@sketch: Sketch,
|
||||
/// The center of the ellipse.
|
||||
@(snippetArray = ["0", "0"])
|
||||
center: Point2d,
|
||||
/// Where along the ellptic should this segment start?
|
||||
@(includeInSnippet = true)
|
||||
angleStart: number(Angle),
|
||||
/// Where along the ellptic should this segment end?
|
||||
@(includeInSnippet = true)
|
||||
angleEnd: number(Angle),
|
||||
/// The major radius, a, of the elliptic equation x^2 / a^2 + y^2 / b^2 = 1.
|
||||
@(includeInSnippet = true)
|
||||
majorRadius: number(Length),
|
||||
/// The minor radius, b, of the elliptic equation x^2 / a^2 + y^2 / b^2 = 1.
|
||||
@(includeInSnippet = true)
|
||||
minorRadius: number(Length),
|
||||
/// Create a new tag which refers to this arc.
|
||||
tag?: tag,
|
||||
): Sketch {}
|
||||
|
||||
/// Calculate the point (x, y) on an ellipse given x or y and the center and major/minor radii of the ellipse.
|
||||
///
|
||||
/// ```kcl
|
||||
/// point001 = ellipticPoint(x = 2, majorRadius = 2, minorRadius = 1)
|
||||
/// point002 = ellipticPoint(y = 0, majorRadius = 2, minorRadius = 1)
|
||||
/// assert(point001[0], isEqualTo = point002[0])
|
||||
/// assert(point001[1], isEqualTo = point002[1])
|
||||
///```
|
||||
@(impl = std_rust)
|
||||
export fn ellipticPoint(
|
||||
/// The major radius, a, of the elliptic equation x^2 / a ^ 2 + y^2 / b^2 = 1.
|
||||
majorRadius: number,
|
||||
/// The minor radius, b, of the hyperbolic equation x^2 / a ^ 2 + y^2 / b^2 = 1.
|
||||
minorRadius: number,
|
||||
/// The x value. Calculates y and returns (x, y). Incompatible with `y`.
|
||||
x?: number(Length),
|
||||
/// The y value. Calculates x and returns (x, y). Incompatible with `x`.
|
||||
y?: number(Length),
|
||||
): Point2d {}
|
||||
|
||||
/// Find the plane a face lies on.
|
||||
/// Returns an error if the face doesn't lie on any plane (for example, the curved face of a cylinder)
|
||||
///```kcl
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
@ -53,5 +53,5 @@ wasm-bindgen-test = "0.3.50"
|
||||
typed-path = "0.11.0"
|
||||
|
||||
# Local development only. Placeholder to speed up development cycle
|
||||
#[package.metadata.wasm-pack.profile.release]
|
||||
#wasm-opt = false
|
||||
[package.metadata.wasm-pack.profile.release]
|
||||
wasm-opt = false
|
||||
|
@ -346,6 +346,21 @@ const CustomIconMap = {
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
conic: (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="conic"
|
||||
>
|
||||
<path
|
||||
d="M3.5 15C4.32843 15 5 15.6716 5 16.5C5 17.3284 4.32843 18 3.5 18C2.67157 18 2 17.3284 2 16.5C2 15.6716 2.67157 15 3.5 15ZM16.5 15C17.3284 15 18 15.6716 18 16.5C18 17.3284 17.3284 18 16.5 18C15.6716 18 15 17.3284 15 16.5C15 15.6716 15.6716 15 16.5 15ZM10 7.00098C11.1972 7.00098 12.226 7.67379 13.0664 8.56348C13.911 9.45793 14.6248 10.6325 15.1973 11.7773C15.6329 12.6487 15.9939 13.5184 16.2744 14.2607C15.9293 14.2951 15.6085 14.4106 15.3271 14.583C15.0576 13.8719 14.7138 13.0469 14.3027 12.2246C13.7505 11.1201 13.0884 10.0439 12.3398 9.25098C11.5865 8.45334 10.8027 8.00098 10 8.00098C9.19735 8.00099 8.41347 8.45335 7.66016 9.25098C6.91156 10.0439 6.24953 11.1201 5.69727 12.2246C5.28609 13.047 4.94149 13.8718 4.67188 14.583C4.3907 14.4108 4.07043 14.2951 3.72559 14.2607C4.00605 13.5184 4.36708 12.6487 4.80273 11.7773C5.37515 10.6325 6.08899 9.45792 6.93359 8.56348C7.77395 7.67379 8.80282 7.00099 10 7.00098ZM10 2C10.8284 2 11.5 2.67157 11.5 3.5C11.5 4.32843 10.8284 5 10 5C9.17157 5 8.5 4.32843 8.5 3.5C8.5 2.67157 9.17157 2 10 2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
dimension: (
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
@ -376,6 +391,36 @@ const CustomIconMap = {
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
ellipse: (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="ellipse"
|
||||
>
|
||||
<path
|
||||
d="M16.1885 5.91834C16.3124 6.17569 16.4144 6.44755 16.4902 6.7328L16.5566 7.0199C16.6939 7.69563 16.6979 8.42142 16.5742 9.16346L16.5146 9.48182C16.3554 10.2257 16.0713 10.9738 15.6807 11.6928L15.5078 11.9984C15.0881 12.7061 14.5677 13.373 13.9707 13.9701L13.71 14.2211C13.1816 14.7135 12.6047 15.1474 11.998 15.5072L11.6924 15.681C10.8709 16.1274 10.0112 16.4332 9.16308 16.5746C8.31513 16.7158 7.48747 16.6913 6.73242 16.4906C6.07036 16.3145 5.47693 16.0054 4.98339 15.5756L4.77831 15.3851C4.31397 14.9208 3.96946 14.3512 3.75585 13.7094L3.67187 13.4301C3.49613 12.7691 3.4557 12.0527 3.54296 11.3158L3.58789 10.9994C3.71158 10.2574 3.961 9.50684 4.32031 8.78065L4.41796 8.59119C4.64341 8.84405 4.9239 9.04636 5.24218 9.17713C4.90918 9.8387 4.68293 10.5113 4.57421 11.1635L4.53515 11.434C4.46107 12.06 4.49908 12.6483 4.63867 13.1732L4.70507 13.393C4.87256 13.8965 5.13745 14.3302 5.48535 14.6781L5.63867 14.8217C6.0104 15.1455 6.46592 15.3845 6.98925 15.5238C7.58909 15.6833 8.27177 15.7094 8.99902 15.5883C9.72601 15.4671 10.4797 15.2019 11.2139 14.8031L11.4883 14.6469C12.1218 14.2711 12.7227 13.803 13.2627 13.2631L13.4902 13.0277C13.9348 12.5507 14.3245 12.0315 14.6465 11.4887L14.8027 11.2142C15.1518 10.5717 15.3988 9.91423 15.5361 9.27283L15.5879 8.9994C15.694 8.36281 15.6883 7.76055 15.5781 7.21815L15.5234 6.98963C15.512 6.94679 15.499 6.90459 15.4863 6.86268C15.7973 6.61758 16.0412 6.29277 16.1885 5.91834ZM6.0957 5.59608C6.92412 5.59608 7.5957 6.26765 7.5957 7.09608C7.59566 7.92447 6.9241 8.59608 6.0957 8.59608C5.26743 8.59591 4.59574 7.92437 4.5957 7.09608C4.5957 6.26775 5.26741 5.59624 6.0957 5.59608ZM14.0957 3.59608C14.9241 3.59608 15.5957 4.26765 15.5957 5.09608C15.5957 5.92447 14.9241 6.59608 14.0957 6.59608C13.2674 6.5959 12.5957 5.92436 12.5957 5.09608C12.5957 4.26776 13.2674 3.59626 14.0957 3.59608ZM12.4941 3.516C12.228 3.78586 12.031 4.12322 11.9277 4.4994C11.7663 4.50338 11.6009 4.51572 11.4336 4.53553L11.1631 4.57459C10.5271 4.68068 9.87038 4.89676 9.2246 5.21619L8.94824 5.36072C8.63245 5.53234 8.32398 5.72775 8.02539 5.94276C7.85008 5.65014 7.61195 5.39987 7.32714 5.21326C7.69377 4.94111 8.07613 4.69574 8.46972 4.48182L8.78027 4.32069C9.50639 3.96139 10.257 3.71199 10.999 3.58826L11.3154 3.54334C11.7161 3.4959 12.1113 3.48672 12.4941 3.516Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
elliptic: (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="elliptic"
|
||||
>
|
||||
<path
|
||||
d="M16.1892 5.91573C16.3958 6.34337 16.5373 6.81206 16.6091 7.31026L16.6492 7.68526C16.7142 8.56795 16.5719 9.50718 16.2429 10.4372L16.0906 10.8347C15.7105 11.7565 15.1551 12.6457 14.4636 13.4421C13.673 14.3524 12.7217 15.1213 11.6931 15.6804C10.6649 16.2391 9.57986 16.5755 8.53296 16.6481C7.61647 16.7116 6.7461 16.5719 5.98901 16.2233L5.67065 16.0612C4.94477 15.6515 4.37571 15.0497 4.00269 14.3064L3.85522 13.9792C3.78253 13.7991 3.72386 13.6128 3.67358 13.4226C3.85777 13.4713 4.05123 13.4997 4.25073 13.4997C4.41393 13.4997 4.57299 13.481 4.72632 13.448C4.74475 13.5005 4.76234 13.5531 4.78296 13.6042L4.89624 13.8552C5.18207 14.426 5.61321 14.8799 6.16284 15.1901L6.40601 15.3142C6.9913 15.5838 7.69036 15.7046 8.46362 15.6511L8.79858 15.6179C9.58434 15.5149 10.4096 15.2398 11.2146 14.8024C12.1344 14.3025 12.9936 13.6101 13.7087 12.7868C14.3344 12.0663 14.8298 11.2685 15.1658 10.4538L15.3005 10.1032C15.5901 9.28441 15.7052 8.48532 15.6521 7.76045L15.6189 7.45381C15.5891 7.24727 15.5444 7.04953 15.4871 6.86104C15.7982 6.61551 16.0422 6.29072 16.1892 5.91573ZM4.24976 9.74971C5.07809 9.74971 5.7496 10.4214 5.74976 11.2497C5.74976 12.0781 5.07818 12.7497 4.24976 12.7497C3.42144 12.7496 2.74976 12.0781 2.74976 11.2497C2.74991 10.4215 3.42154 9.74984 4.24976 9.74971ZM6.15503 6.96065L6.52808 7.29268C6.32258 7.5237 6.1282 7.76584 5.948 8.01534C5.77529 8.25453 5.61509 8.49986 5.47144 8.74776L5.03979 8.49776L4.60718 8.24678C4.76777 7.96963 4.94459 7.69614 5.13647 7.43037C5.33685 7.15292 5.55322 6.8847 5.78101 6.62862L6.15503 6.96065ZM14.0964 3.59639C14.9247 3.5966 15.5964 4.26809 15.5964 5.09639C15.5962 5.92451 14.9246 6.59618 14.0964 6.59639C13.2681 6.59639 12.5966 5.92464 12.5964 5.09639C12.5964 4.26796 13.268 3.59639 14.0964 3.59639ZM9.19702 4.67451L9.40698 5.12862C9.15666 5.24462 8.90703 5.37657 8.66089 5.52315C8.39796 5.67977 8.13986 5.85276 7.89136 6.03877L7.59253 5.63838L7.29272 5.23799C7.56851 5.03156 7.85465 4.83963 8.14819 4.66475C8.42345 4.50081 8.7046 4.35228 8.98706 4.22139L9.19702 4.67451ZM12.4929 3.51729C12.2269 3.78746 12.0304 4.1252 11.9275 4.50166L11.8093 4.50362C11.5429 4.51653 11.2694 4.55087 10.9929 4.60518L10.8962 4.11494L10.7996 3.62373C11.1213 3.56053 11.4438 3.52095 11.7615 3.50557L12.1775 3.49971C12.2833 3.50193 12.3888 3.50927 12.4929 3.51729Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
),
|
||||
equal: (
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
|
@ -1088,6 +1088,18 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
||||
prepareToEdit: prepareToEditEdgeTreatment,
|
||||
// modelingEvent: 'Chamfer',
|
||||
},
|
||||
conic: {
|
||||
label: 'Conic',
|
||||
icon: 'conic',
|
||||
},
|
||||
ellipse: {
|
||||
label: 'Ellipse',
|
||||
icon: 'ellipse',
|
||||
},
|
||||
elliptic: {
|
||||
label: 'Elliptic',
|
||||
icon: 'elliptic',
|
||||
},
|
||||
extrude: {
|
||||
label: 'Extrude',
|
||||
icon: 'extrude',
|
||||
@ -1109,6 +1121,10 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
||||
label: 'Subtract 2D',
|
||||
icon: 'hole',
|
||||
},
|
||||
hyperbolic: {
|
||||
label: 'Hyperbolic',
|
||||
icon: 'conic',
|
||||
},
|
||||
hollow: {
|
||||
label: 'Hollow',
|
||||
icon: 'hollow',
|
||||
@ -1139,6 +1155,10 @@ export const stdLibMap: Record<string, StdLibCallInfo> = {
|
||||
icon: 'plane',
|
||||
prepareToEdit: prepareToEditOffsetPlane,
|
||||
},
|
||||
parabolic: {
|
||||
label: 'Parabolic',
|
||||
icon: 'conic',
|
||||
},
|
||||
patternCircular2d: {
|
||||
label: 'Circular Pattern',
|
||||
icon: 'patternCircular2d',
|
||||
|
Reference in New Issue
Block a user