merge main

This commit is contained in:
benjamaan476
2025-06-05 12:31:11 +01:00
429 changed files with 37330 additions and 270988 deletions

View File

@ -1,7 +1,6 @@
//! Standard library shapes.
use anyhow::Result;
use kcl_derive_docs::stdlib;
use kcmc::{
each_cmd as mcmd,
length_unit::LengthUnit,
@ -29,6 +28,7 @@ use crate::{
utils::{calculate_circle_center, distance},
Args,
},
SourceRange,
};
/// A sketch surface or a sketch.
@ -42,7 +42,8 @@ pub enum SketchOrSurface {
/// Sketch a circle.
pub async fn circle(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_or_surface = args.get_unlabeled_kw_arg("sketchOrSurface")?;
let sketch_or_surface =
args.get_unlabeled_kw_arg_typed("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?;
let center = args.get_kw_arg_typed("center", &RuntimeType::point2d(), exec_state)?;
let radius: Option<TyF64> = args.get_kw_arg_opt_typed("radius", &RuntimeType::length(), exec_state)?;
let diameter: Option<TyF64> = args.get_kw_arg_opt_typed("diameter", &RuntimeType::length(), exec_state)?;
@ -70,25 +71,7 @@ async fn inner_circle(
let (center_u, ty) = untype_point(center.clone());
let units = ty.expect_length();
let radius = match (radius, diameter) {
(Some(radius), None) => radius,
(None, Some(mut diameter)) => {
diameter.n /= 2.0;
diameter
}
(None, None) => {
return Err(KclError::Type(KclErrorDetails::new(
"This function needs either `diameter` or `radius`".to_string(),
vec![args.source_range],
)))
}
(Some(_), Some(_)) => {
return Err(KclError::Type(KclErrorDetails::new(
"You cannot specify both `diameter` and `radius`, please remove one".to_string(),
vec![args.source_range],
)))
}
};
let radius = get_radius(radius, diameter, args.source_range)?;
let from = [center_u[0] + radius.to_length_units(units), center_u[1]];
let from_t = [TyF64::new(from[0], ty.clone()), TyF64::new(from[1], ty)];
@ -146,38 +129,19 @@ 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 sketch_or_surface =
args.get_unlabeled_kw_arg_typed("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?;
let p1 = args.get_kw_arg_typed("p1", &RuntimeType::point2d(), exec_state)?;
let p2 = args.get_kw_arg_typed("p2", &RuntimeType::point2d(), exec_state)?;
let p3 = args.get_kw_arg_typed("p3", &RuntimeType::point2d(), exec_state)?;
let tag = args.get_kw_arg_opt("tag")?;
let sketch = inner_circle_three_point(sketch_surface_or_group, p1, p2, p3, tag, exec_state, args).await?;
let sketch = inner_circle_three_point(sketch_or_surface, p1, p2, p3, tag, exec_state, args).await?;
Ok(KclValue::Sketch {
value: Box::new(sketch),
})
}
/// Construct a circle derived from 3 points.
///
/// ```no_run
/// exampleSketch = startSketchOn(XY)
/// |> circleThreePoint(p1 = [10,10], p2 = [20,8], p3 = [15,5])
/// |> extrude(length = 5)
/// ```
#[stdlib {
name = "circleThreePoint",
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."},
tag = {docs = "Identifier for the circle to reference elsewhere."},
},
tags = ["sketch"]
}]
// 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(
@ -274,14 +238,15 @@ pub enum PolygonType {
/// Create a regular polygon with the specified number of sides and radius.
pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
let sketch_surface_or_group = args.get_unlabeled_kw_arg("sketchOrSurface")?;
let sketch_or_surface =
args.get_unlabeled_kw_arg_typed("sketchOrSurface", &RuntimeType::sketch_or_surface(), exec_state)?;
let radius: TyF64 = args.get_kw_arg_typed("radius", &RuntimeType::length(), exec_state)?;
let num_sides: TyF64 = args.get_kw_arg_typed("numSides", &RuntimeType::count(), exec_state)?;
let center = args.get_kw_arg_typed("center", &RuntimeType::point2d(), exec_state)?;
let inscribed = args.get_kw_arg_opt_typed("inscribed", &RuntimeType::bool(), exec_state)?;
let sketch = inner_polygon(
sketch_surface_or_group,
sketch_or_surface,
radius,
num_sides.n as u64,
center,
@ -295,44 +260,6 @@ pub async fn polygon(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
})
}
/// Create a regular polygon with the specified number of sides that is either inscribed or circumscribed around a circle of the specified radius.
///
/// ```no_run
/// // Create a regular hexagon inscribed in a circle of radius 10
/// hex = startSketchOn(XY)
/// |> polygon(
/// radius = 10,
/// numSides = 6,
/// center = [0, 0],
/// inscribed = true,
/// )
///
/// example = extrude(hex, length = 5)
/// ```
///
/// ```no_run
/// // Create a square circumscribed around a circle of radius 5
/// square = startSketchOn(XY)
/// |> polygon(
/// radius = 5.0,
/// numSides = 4,
/// center = [10, 10],
/// inscribed = false,
/// )
/// example = extrude(square, length = 5)
/// ```
#[stdlib {
name = "polygon",
unlabeled_first = true,
args = {
sketch_surface_or_group = { docs = "Plane or surface to sketch on" },
radius = { docs = "The radius of the polygon", include_in_snippet = true },
num_sides = { docs = "The number of sides in the polygon", include_in_snippet = true },
center = { docs = "The center point of the polygon", snippet_value_array = ["0", "0"] },
inscribed = { docs = "Whether the polygon is inscribed (true, the default) or circumscribed (false) about a circle with the specified radius" },
},
tags = ["sketch"]
}]
#[allow(clippy::too_many_arguments)]
async fn inner_polygon(
sketch_surface_or_group: SketchOrSurface,
@ -344,14 +271,14 @@ async fn inner_polygon(
args: Args,
) -> Result<Sketch, KclError> {
if num_sides < 3 {
return Err(KclError::Type(KclErrorDetails::new(
return Err(KclError::new_type(KclErrorDetails::new(
"Polygon must have at least 3 sides".to_string(),
vec![args.source_range],
)));
}
if radius.n <= 0.0 {
return Err(KclError::Type(KclErrorDetails::new(
return Err(KclError::new_type(KclErrorDetails::new(
"Radius must be greater than 0".to_string(),
vec![args.source_range],
)));
@ -565,4 +492,22 @@ async fn inner_ellipse(
.await?;
Ok(new_sketch)
pub(crate) fn get_radius(
radius: Option<TyF64>,
diameter: Option<TyF64>,
source_range: SourceRange,
) -> Result<TyF64, KclError> {
match (radius, diameter) {
(Some(radius), None) => Ok(radius),
(None, Some(diameter)) => Ok(TyF64::new(diameter.n / 2.0, diameter.ty)),
(None, None) => Err(KclError::new_type(KclErrorDetails::new(
"This function needs either `diameter` or `radius`".to_string(),
vec![source_range],
))),
(Some(_), Some(_)) => Err(KclError::new_type(KclErrorDetails::new(
"You cannot specify both `diameter` and `radius`, please remove one".to_string(),
vec![source_range],
))),
}
}