Declare std kwarg functions in KCL and migrate circle (#5955)

* Support calling KCL std KW fns, and move circle to KCL std

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Doc comments on parameters

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Update grammar

Signed-off-by: Nick Cameron <nrc@ncameron.org>

* Change use of counterClockWise to ccw

Signed-off-by: Nick Cameron <nrc@ncameron.org>

---------

Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
Nick Cameron
2025-03-24 21:55:24 +13:00
committed by GitHub
parent dddcd5ff46
commit 3b2abe5814
94 changed files with 16657 additions and 9803 deletions

View File

@ -1,6 +1,7 @@
use std::{collections::HashMap, num::NonZeroU32};
use std::num::NonZeroU32;
use anyhow::Result;
use indexmap::IndexMap;
use kcmc::{
websocket::{ModelingCmdReq, OkWebSocketResponseData},
ModelingCmd,
@ -57,7 +58,7 @@ pub struct KwArgs {
/// Unlabeled keyword args. Currently only the first arg can be unlabeled.
pub unlabeled: Option<Arg>,
/// Labeled args.
pub labeled: HashMap<String, Arg>,
pub labeled: IndexMap<String, Arg>,
}
impl KwArgs {
@ -1000,48 +1001,54 @@ where
impl<'a> FromKclValue<'a> for [f64; 2] {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let KclValue::MixedArray { value, meta: _ } = arg else {
return None;
};
if value.len() != 2 {
return None;
match arg {
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
if value.len() != 2 {
return None;
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let array = [v0.as_f64()?, v1.as_f64()?];
Some(array)
}
_ => None,
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let array = [v0.as_f64()?, v1.as_f64()?];
Some(array)
}
}
impl<'a> FromKclValue<'a> for [usize; 3] {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let KclValue::MixedArray { value, meta: _ } = arg else {
return None;
};
if value.len() != 3 {
return None;
match arg {
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
if value.len() != 3 {
return None;
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let v2 = value.get(2)?;
let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
Some(array)
}
_ => None,
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let v2 = value.get(2)?;
let array = [v0.as_usize()?, v1.as_usize()?, v2.as_usize()?];
Some(array)
}
}
impl<'a> FromKclValue<'a> for [f64; 3] {
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
let KclValue::MixedArray { value, meta: _ } = arg else {
return None;
};
if value.len() != 3 {
return None;
match arg {
KclValue::MixedArray { value, meta: _ } | KclValue::HomArray { value, .. } => {
if value.len() != 3 {
return None;
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let v2 = value.get(2)?;
let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
Some(array)
}
_ => None,
}
let v0 = value.first()?;
let v1 = value.get(1)?;
let v2 = value.get(2)?;
let array = [v0.as_f64()?, v1.as_f64()?, v2.as_f64()?];
Some(array)
}
}

View File

@ -47,19 +47,19 @@ fn inner_rem(num: f64, divisor: f64) -> f64 {
/// Compute the cosine of a number (in radians).
pub async fn cos(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let num: f64 = args.get_unlabeled_kw_arg("input")?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.cos())))
}
/// Compute the sine of a number (in radians).
pub async fn sin(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let num: f64 = args.get_unlabeled_kw_arg("input")?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.sin())))
}
/// Compute the tangent of a number (in radians).
pub async fn tan(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let num = args.get_number()?;
let num: f64 = args.get_unlabeled_kw_arg("input")?;
Ok(args.make_user_val_from_f64_with_type(TyF64::count(num.tan())))
}

View File

@ -72,7 +72,6 @@ lazy_static! {
Box::new(crate::std::segment::TangentToEnd),
Box::new(crate::std::segment::AngleToMatchLengthX),
Box::new(crate::std::segment::AngleToMatchLengthY),
Box::new(crate::std::shapes::Circle),
Box::new(crate::std::shapes::CircleThreePoint),
Box::new(crate::std::shapes::Polygon),
Box::new(crate::std::sketch::Line),
@ -203,6 +202,10 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|e, a| Box::pin(crate::std::math::tan(e, a)),
StdFnProps::default("std::math::tan"),
),
("sketch", "circle") => (
|e, a| Box::pin(crate::std::shapes::circle(e, a)),
StdFnProps::default("std::sketch::circle"),
),
_ => unreachable!(),
}
}

View File

@ -46,38 +46,6 @@ pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
})
}
/// Construct a 2-dimensional circle, of the specified radius, centered at
/// the provided (x, y) origin point.
///
/// ```no_run
/// exampleSketch = startSketchOn("-XZ")
/// |> circle( center = [0, 0], radius = 10 )
///
/// example = extrude(exampleSketch, length = 5)
/// ```
///
/// ```no_run
/// exampleSketch = startSketchOn("XZ")
/// |> startProfileAt([-15, 0], %)
/// |> line(end = [30, 0])
/// |> line(end = [0, 30])
/// |> line(end = [-30, 0])
/// |> close()
/// |> hole(circle( center = [0, 15], radius = 5), %)
///
/// example = extrude(exampleSketch, length = 5)
/// ```
#[stdlib {
name = "circle",
keywords = true,
unlabeled_first = true,
args = {
sketch_or_surface = {docs = "Plane or surface to sketch on."},
center = {docs = "The center of the circle."},
radius = {docs = "The radius of the circle."},
tag = { docs = "Create a new tag which refers to this circle"},
}
}]
async fn inner_circle(
sketch_or_surface: SketchOrSurface,
center: [f64; 2],
@ -152,13 +120,13 @@ async fn inner_circle(
/// Sketch a 3-point circle.
pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_surface_or_group = args.get_unlabeled_kw_arg("sketch_surface_or_group")?;
let p1 = args.get_kw_arg("p1")?;
let p2 = args.get_kw_arg("p2")?;
let p3 = args.get_kw_arg("p3")?;
let sketch_surface_or_group = args.get_unlabeled_kw_arg("sketch_surface_or_group")?;
let tag = args.get_kw_arg_opt("tag")?;
let sketch = inner_circle_three_point(p1, p2, p3, sketch_surface_or_group, tag, exec_state, args).await?;
let sketch = inner_circle_three_point(sketch_surface_or_group, p1, p2, p3, tag, exec_state, args).await?;
Ok(KclValue::Sketch {
value: Box::new(sketch),
})
@ -176,10 +144,10 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
keywords = true,
unlabeled_first = true,
args = {
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
p1 = {docs = "1st point to derive the circle."},
p2 = {docs = "2nd point to derive the circle."},
p3 = {docs = "3rd point to derive the circle."},
sketch_surface_or_group = {docs = "Plane or surface to sketch on."},
tag = {docs = "Identifier for the circle to reference elsewhere."},
}
}]
@ -187,10 +155,10 @@ pub async fn circle_three_point(exec_state: &mut ExecState, args: Args) -> Resul
// Similar to inner_circle, but needs to retain 3-point information in the
// path so it can be used for other features, otherwise it's lost.
async fn inner_circle_three_point(
sketch_surface_or_group: SketchOrSurface,
p1: [f64; 2],
p2: [f64; 2],
p3: [f64; 2],
sketch_surface_or_group: SketchOrSurface,
tag: Option<TagNode>,
exec_state: &mut ExecState,
args: Args,