kcl_lib/std/
mod.rs

1//! Functions implemented for language execution.
2
3pub mod appearance;
4pub mod args;
5pub mod array;
6pub mod assert;
7pub mod axis_or_reference;
8pub mod chamfer;
9pub mod convert;
10pub mod csg;
11pub mod edge;
12pub mod extrude;
13pub mod fillet;
14pub mod helix;
15pub mod loft;
16pub mod math;
17pub mod mirror;
18pub mod patterns;
19pub mod planes;
20pub mod revolve;
21pub mod segment;
22pub mod shapes;
23pub mod shell;
24pub mod sketch;
25pub mod sweep;
26pub mod transform;
27pub mod units;
28pub mod utils;
29
30use anyhow::Result;
31pub use args::Args;
32use indexmap::IndexMap;
33use kcl_derive_docs::stdlib;
34use lazy_static::lazy_static;
35use parse_display::{Display, FromStr};
36use schemars::JsonSchema;
37use serde::{Deserialize, Serialize};
38
39use crate::{
40    docs::StdLibFn,
41    errors::KclError,
42    execution::{types::PrimitiveType, ExecState, KclValue},
43    parsing::ast::types::Name,
44};
45
46pub type StdFn = fn(
47    &mut ExecState,
48    Args,
49) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<KclValue, KclError>> + Send + '_>>;
50
51lazy_static! {
52    static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
53        Box::new(LegLen),
54        Box::new(LegAngX),
55        Box::new(LegAngY),
56        Box::new(crate::std::appearance::Appearance),
57        Box::new(crate::std::convert::Int),
58        Box::new(crate::std::extrude::Extrude),
59        Box::new(crate::std::segment::SegEnd),
60        Box::new(crate::std::segment::SegEndX),
61        Box::new(crate::std::segment::SegEndY),
62        Box::new(crate::std::segment::SegStart),
63        Box::new(crate::std::segment::SegStartX),
64        Box::new(crate::std::segment::SegStartY),
65        Box::new(crate::std::segment::LastSegX),
66        Box::new(crate::std::segment::LastSegY),
67        Box::new(crate::std::segment::SegLen),
68        Box::new(crate::std::segment::SegAng),
69        Box::new(crate::std::segment::TangentToEnd),
70        Box::new(crate::std::segment::AngleToMatchLengthX),
71        Box::new(crate::std::segment::AngleToMatchLengthY),
72        Box::new(crate::std::shapes::CircleThreePoint),
73        Box::new(crate::std::shapes::Polygon),
74        Box::new(crate::std::sketch::InvoluteCircular),
75        Box::new(crate::std::sketch::Line),
76        Box::new(crate::std::sketch::XLine),
77        Box::new(crate::std::sketch::YLine),
78        Box::new(crate::std::sketch::AngledLine),
79        Box::new(crate::std::sketch::AngledLineThatIntersects),
80        Box::new(crate::std::sketch::StartSketchOn),
81        Box::new(crate::std::sketch::StartProfileAt),
82        Box::new(crate::std::sketch::ProfileStartX),
83        Box::new(crate::std::sketch::ProfileStartY),
84        Box::new(crate::std::sketch::ProfileStart),
85        Box::new(crate::std::sketch::Close),
86        Box::new(crate::std::sketch::Arc),
87        Box::new(crate::std::sketch::ArcTo),
88        Box::new(crate::std::sketch::TangentialArc),
89        Box::new(crate::std::sketch::BezierCurve),
90        Box::new(crate::std::sketch::Hole),
91        Box::new(crate::std::patterns::PatternLinear2D),
92        Box::new(crate::std::patterns::PatternLinear3D),
93        Box::new(crate::std::patterns::PatternCircular2D),
94        Box::new(crate::std::patterns::PatternCircular3D),
95        Box::new(crate::std::patterns::PatternTransform),
96        Box::new(crate::std::patterns::PatternTransform2D),
97        Box::new(crate::std::array::Reduce),
98        Box::new(crate::std::array::Map),
99        Box::new(crate::std::array::Push),
100        Box::new(crate::std::array::Pop),
101        Box::new(crate::std::chamfer::Chamfer),
102        Box::new(crate::std::fillet::Fillet),
103        Box::new(crate::std::edge::GetOppositeEdge),
104        Box::new(crate::std::edge::GetNextAdjacentEdge),
105        Box::new(crate::std::edge::GetPreviousAdjacentEdge),
106        Box::new(crate::std::edge::GetCommonEdge),
107        Box::new(crate::std::shell::Shell),
108        Box::new(crate::std::shell::Hollow),
109        Box::new(crate::std::sweep::Sweep),
110        Box::new(crate::std::loft::Loft),
111        Box::new(crate::std::planes::OffsetPlane),
112        Box::new(crate::std::math::Acos),
113        Box::new(crate::std::math::Asin),
114        Box::new(crate::std::math::Atan),
115        Box::new(crate::std::math::Atan2),
116        Box::new(crate::std::math::Pi),
117        Box::new(crate::std::math::E),
118        Box::new(crate::std::math::Tau),
119        Box::new(crate::std::math::Sqrt),
120        Box::new(crate::std::math::Abs),
121        Box::new(crate::std::math::Rem),
122        Box::new(crate::std::math::Round),
123        Box::new(crate::std::math::Floor),
124        Box::new(crate::std::math::Ceil),
125        Box::new(crate::std::math::Min),
126        Box::new(crate::std::math::Max),
127        Box::new(crate::std::math::Pow),
128        Box::new(crate::std::math::Log),
129        Box::new(crate::std::math::Log2),
130        Box::new(crate::std::math::Log10),
131        Box::new(crate::std::math::Ln),
132        Box::new(crate::std::math::ToDegrees),
133        Box::new(crate::std::math::ToRadians),
134        Box::new(crate::std::units::FromMm),
135        Box::new(crate::std::units::FromInches),
136        Box::new(crate::std::units::FromFt),
137        Box::new(crate::std::units::FromM),
138        Box::new(crate::std::units::FromCm),
139        Box::new(crate::std::units::FromYd),
140        Box::new(crate::std::assert::Assert),
141        Box::new(crate::std::assert::AssertEqual),
142        Box::new(crate::std::assert::AssertLessThan),
143        Box::new(crate::std::assert::AssertGreaterThan),
144        Box::new(crate::std::assert::AssertLessThanOrEq),
145        Box::new(crate::std::assert::AssertGreaterThanOrEq),
146        Box::new(crate::std::transform::Scale),
147        Box::new(crate::std::transform::Translate),
148        Box::new(crate::std::transform::Rotate),
149        Box::new(crate::std::csg::Intersect),
150        Box::new(crate::std::csg::Union),
151        Box::new(crate::std::csg::Subtract),
152    ];
153}
154
155pub fn name_in_stdlib(name: &str) -> bool {
156    CORE_FNS.iter().any(|f| f.name() == name)
157}
158
159pub fn get_stdlib_fn(name: &str) -> Option<Box<dyn StdLibFn>> {
160    CORE_FNS.iter().find(|f| f.name() == name).cloned()
161}
162
163#[derive(Clone, Debug, PartialEq, Eq)]
164pub struct StdFnProps {
165    pub name: String,
166    pub deprecated: bool,
167    pub include_in_feature_tree: bool,
168}
169
170impl StdFnProps {
171    fn default(name: &str) -> Self {
172        Self {
173            name: name.to_owned(),
174            deprecated: false,
175            include_in_feature_tree: false,
176        }
177    }
178
179    fn include_in_feature_tree(mut self) -> Self {
180        self.include_in_feature_tree = true;
181        self
182    }
183}
184
185pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProps) {
186    match (path, fn_name) {
187        ("math", "cos") => (
188            |e, a| Box::pin(crate::std::math::cos(e, a)),
189            StdFnProps::default("std::math::cos"),
190        ),
191        ("math", "sin") => (
192            |e, a| Box::pin(crate::std::math::sin(e, a)),
193            StdFnProps::default("std::math::sin"),
194        ),
195        ("math", "tan") => (
196            |e, a| Box::pin(crate::std::math::tan(e, a)),
197            StdFnProps::default("std::math::tan"),
198        ),
199        ("sketch", "circle") => (
200            |e, a| Box::pin(crate::std::shapes::circle(e, a)),
201            StdFnProps::default("std::sketch::circle"),
202        ),
203        ("prelude", "helix") => (
204            |e, a| Box::pin(crate::std::helix::helix(e, a)),
205            StdFnProps::default("std::helix").include_in_feature_tree(),
206        ),
207        ("sketch", "mirror2d") => (
208            |e, a| Box::pin(crate::std::mirror::mirror_2d(e, a)),
209            StdFnProps::default("std::sketch::mirror2d"),
210        ),
211        ("prelude", "revolve") => (
212            |e, a| Box::pin(crate::std::revolve::revolve(e, a)),
213            StdFnProps::default("std::revolve").include_in_feature_tree(),
214        ),
215        _ => unreachable!(),
216    }
217}
218
219pub(crate) fn std_ty(path: &str, fn_name: &str) -> (PrimitiveType, StdFnProps) {
220    match (path, fn_name) {
221        ("prelude", "Sketch") => (PrimitiveType::Sketch, StdFnProps::default("std::Sketch")),
222        ("prelude", "Solid") => (PrimitiveType::Solid, StdFnProps::default("std::Solid")),
223        ("prelude", "Plane") => (PrimitiveType::Plane, StdFnProps::default("std::Plane")),
224        ("prelude", "Face") => (PrimitiveType::Face, StdFnProps::default("std::Face")),
225        ("prelude", "Helix") => (PrimitiveType::Helix, StdFnProps::default("std::Helix")),
226        ("prelude", "Edge") => (PrimitiveType::Edge, StdFnProps::default("std::Edge")),
227        ("prelude", "Axis2d") => (PrimitiveType::Axis2d, StdFnProps::default("std::Axis2d")),
228        ("prelude", "Axis3d") => (PrimitiveType::Axis3d, StdFnProps::default("std::Axis3d")),
229        _ => unreachable!(),
230    }
231}
232
233pub struct StdLib {
234    pub fns: IndexMap<String, Box<dyn StdLibFn>>,
235}
236
237impl std::fmt::Debug for StdLib {
238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239        f.debug_struct("StdLib").field("fns.len()", &self.fns.len()).finish()
240    }
241}
242
243impl StdLib {
244    pub fn new() -> Self {
245        let fns = CORE_FNS
246            .clone()
247            .into_iter()
248            .map(|internal_fn| (internal_fn.name(), internal_fn))
249            .collect();
250
251        Self { fns }
252    }
253
254    // Get the combined hashmaps.
255    pub fn combined(&self) -> IndexMap<String, Box<dyn StdLibFn>> {
256        self.fns.clone()
257    }
258
259    pub fn get(&self, name: &str) -> Option<Box<dyn StdLibFn>> {
260        self.fns.get(name).cloned()
261    }
262
263    pub fn get_either(&self, name: &Name) -> FunctionKind {
264        if let Some(name) = name.local_ident() {
265            if let Some(f) = self.get(name.inner) {
266                return FunctionKind::Core(f);
267            }
268        }
269
270        FunctionKind::UserDefined
271    }
272
273    pub fn contains_key(&self, key: &str) -> bool {
274        self.fns.contains_key(key)
275    }
276}
277
278impl Default for StdLib {
279    fn default() -> Self {
280        Self::new()
281    }
282}
283
284#[derive(Debug)]
285pub enum FunctionKind {
286    Core(Box<dyn StdLibFn>),
287    UserDefined,
288}
289
290/// The default tolerance for modeling commands in [`kittycad_modeling_cmds::length_unit::LengthUnit`].
291const DEFAULT_TOLERANCE: f64 = 0.0000001;
292
293/// Compute the length of the given leg.
294pub async fn leg_length(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
295    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
296    let result = inner_leg_length(hypotenuse, leg);
297    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
298}
299
300/// Compute the length of the given leg.
301///
302/// ```no_run
303/// legLen(5, 3)
304/// ```
305#[stdlib {
306    name = "legLen",
307    tags = ["utilities"],
308}]
309fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
310    (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt()
311}
312
313/// Compute the angle of the given leg for x.
314pub async fn leg_angle_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
315    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
316    let result = inner_leg_angle_x(hypotenuse, leg);
317    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
318}
319
320/// Compute the angle of the given leg for x.
321///
322/// ```no_run
323/// legAngX(5, 3)
324/// ```
325#[stdlib {
326    name = "legAngX",
327    tags = ["utilities"],
328}]
329fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
330    (leg.min(hypotenuse) / hypotenuse).acos().to_degrees()
331}
332
333/// Compute the angle of the given leg for y.
334pub async fn leg_angle_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
335    let (hypotenuse, leg, ty) = args.get_hypotenuse_leg()?;
336    let result = inner_leg_angle_y(hypotenuse, leg);
337    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
338}
339
340/// Compute the angle of the given leg for y.
341///
342/// ```no_run
343/// legAngY(5, 3)
344/// ```
345#[stdlib {
346    name = "legAngY",
347    tags = ["utilities"],
348}]
349fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 {
350    (leg.min(hypotenuse) / hypotenuse).asin().to_degrees()
351}
352
353/// The primitive types that can be used in a KCL file.
354#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
355#[serde(rename_all = "lowercase")]
356#[display(style = "lowercase")]
357pub enum Primitive {
358    /// A boolean value.
359    Bool,
360    /// A number value.
361    Number,
362    /// A string value.
363    String,
364    /// A uuid value.
365    Uuid,
366}