Move some more functions to be declared in KCL (#6856)
* Move the leg functions to KCL Signed-off-by: Nick Cameron <nrc@ncameron.org> * Move array functions to KCL Signed-off-by: Nick Cameron <nrc@ncameron.org> * Move clone to KCL Signed-off-by: Nick Cameron <nrc@ncameron.org> * Add a function type Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
@ -16,7 +16,7 @@ use crate::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Types with special handling.
 | 
			
		||||
const SPECIAL_TYPES: [&str; 5] = ["TagDeclarator", "TagIdentifier", "Start", "End", "ImportedGeometry"];
 | 
			
		||||
const SPECIAL_TYPES: [&str; 4] = ["TagDeclarator", "TagIdentifier", "Start", "End"];
 | 
			
		||||
 | 
			
		||||
const TYPE_REWRITES: [(&str, &str); 11] = [
 | 
			
		||||
    ("TagNode", "TagDeclarator"),
 | 
			
		||||
 | 
			
		||||
@ -626,6 +626,8 @@ impl FnData {
 | 
			
		||||
    pub(super) fn to_autocomplete_snippet(&self) -> String {
 | 
			
		||||
        if self.name == "loft" {
 | 
			
		||||
            return "loft([${0:sketch000}, ${1:sketch001}])".to_owned();
 | 
			
		||||
        } else if self.name == "clone" {
 | 
			
		||||
            return "clone(${0:part001})".to_owned();
 | 
			
		||||
        } else if self.name == "hole" {
 | 
			
		||||
            return "hole(${0:holeSketch}, ${1:%})".to_owned();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,7 @@ use std::{
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use kcl_doc::ModData;
 | 
			
		||||
use parse_display::Display;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use tower_lsp::lsp_types::{
 | 
			
		||||
@ -18,15 +19,27 @@ use tower_lsp::lsp_types::{
 | 
			
		||||
    MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    execution::{types::NumericType, Sketch},
 | 
			
		||||
    std::Primitive,
 | 
			
		||||
};
 | 
			
		||||
use crate::execution::{types::NumericType, Sketch};
 | 
			
		||||
 | 
			
		||||
// These types are declared in (KCL) std.
 | 
			
		||||
const DECLARED_TYPES: [&str; 15] = [
 | 
			
		||||
    "any", "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Edge", "Point2d",
 | 
			
		||||
    "Point3d", "Axis2d", "Axis3d",
 | 
			
		||||
const DECLARED_TYPES: [&str; 17] = [
 | 
			
		||||
    "any",
 | 
			
		||||
    "number",
 | 
			
		||||
    "string",
 | 
			
		||||
    "tag",
 | 
			
		||||
    "bool",
 | 
			
		||||
    "Sketch",
 | 
			
		||||
    "Solid",
 | 
			
		||||
    "Plane",
 | 
			
		||||
    "Helix",
 | 
			
		||||
    "Face",
 | 
			
		||||
    "Edge",
 | 
			
		||||
    "Point2d",
 | 
			
		||||
    "Point3d",
 | 
			
		||||
    "Axis2d",
 | 
			
		||||
    "Axis3d",
 | 
			
		||||
    "ImportedGeometry",
 | 
			
		||||
    "Fn",
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
lazy_static::lazy_static! {
 | 
			
		||||
@ -38,6 +51,21 @@ lazy_static::lazy_static! {
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The primitive types that can be used in a KCL file.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema, Display)]
 | 
			
		||||
#[serde(rename_all = "lowercase")]
 | 
			
		||||
#[display(style = "lowercase")]
 | 
			
		||||
enum Primitive {
 | 
			
		||||
    /// A boolean value.
 | 
			
		||||
    Bool,
 | 
			
		||||
    /// A number value.
 | 
			
		||||
    Number,
 | 
			
		||||
    /// A string value.
 | 
			
		||||
    String,
 | 
			
		||||
    /// A uuid value.
 | 
			
		||||
    Uuid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
@ -534,8 +562,6 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
 | 
			
		||||
    fn to_autocomplete_snippet(&self) -> Result<String> {
 | 
			
		||||
        if self.name() == "loft" {
 | 
			
		||||
            return Ok("loft([${0:sketch000}, ${1:sketch001}])".to_string());
 | 
			
		||||
        } else if self.name() == "clone" {
 | 
			
		||||
            return Ok("clone(${0:part001})".to_string());
 | 
			
		||||
        } else if self.name() == "union" {
 | 
			
		||||
            return Ok("union([${0:extrude001}, ${1:extrude002}])".to_string());
 | 
			
		||||
        } else if self.name() == "subtract" {
 | 
			
		||||
@ -702,7 +728,7 @@ pub fn get_description_string_from_schema(schema: &schemars::schema::RootSchema)
 | 
			
		||||
    None
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn is_primitive(schema: &schemars::schema::Schema) -> Result<Option<Primitive>> {
 | 
			
		||||
fn is_primitive(schema: &schemars::schema::Schema) -> Result<Option<Primitive>> {
 | 
			
		||||
    match schema {
 | 
			
		||||
        schemars::schema::Schema::Object(o) => {
 | 
			
		||||
            if o.enum_values.is_some() {
 | 
			
		||||
@ -1008,9 +1034,12 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn get_autocomplete_snippet_map() {
 | 
			
		||||
        let map_fn: Box<dyn StdLibFn> = Box::new(crate::std::array::Map);
 | 
			
		||||
        let snippet = map_fn.to_autocomplete_snippet().unwrap();
 | 
			
		||||
        assert_eq!(snippet, r#"map(${0:[0..9]})"#);
 | 
			
		||||
        let data = kcl_doc::walk_prelude();
 | 
			
		||||
        let DocData::Fn(map_fn) = data.find_by_name("map").unwrap() else {
 | 
			
		||||
            panic!();
 | 
			
		||||
        };
 | 
			
		||||
        let snippet = map_fn.to_autocomplete_snippet();
 | 
			
		||||
        assert_eq!(snippet, r#"map()"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
@ -1130,8 +1159,11 @@ mod tests {
 | 
			
		||||
    #[test]
 | 
			
		||||
    #[allow(clippy::literal_string_with_formatting_args)]
 | 
			
		||||
    fn get_autocomplete_snippet_clone() {
 | 
			
		||||
        let clone_fn: Box<dyn StdLibFn> = Box::new(crate::std::clone::Clone);
 | 
			
		||||
        let snippet = clone_fn.to_autocomplete_snippet().unwrap();
 | 
			
		||||
        let data = kcl_doc::walk_prelude();
 | 
			
		||||
        let DocData::Fn(clone_fn) = data.find_by_name("clone").unwrap() else {
 | 
			
		||||
            panic!();
 | 
			
		||||
        };
 | 
			
		||||
        let snippet = clone_fn.to_autocomplete_snippet();
 | 
			
		||||
        assert_eq!(snippet, r#"clone(${0:part001})"#);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -182,6 +182,8 @@ impl RuntimeType {
 | 
			
		||||
            }
 | 
			
		||||
            AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
 | 
			
		||||
            AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
 | 
			
		||||
            AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
 | 
			
		||||
            AstPrimitiveType::Function => RuntimeType::Primitive(PrimitiveType::Function),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -363,6 +365,7 @@ pub enum PrimitiveType {
 | 
			
		||||
    Axis2d,
 | 
			
		||||
    Axis3d,
 | 
			
		||||
    ImportedGeometry,
 | 
			
		||||
    Function,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl PrimitiveType {
 | 
			
		||||
@ -382,6 +385,7 @@ impl PrimitiveType {
 | 
			
		||||
            PrimitiveType::Axis2d => "2d axes".to_owned(),
 | 
			
		||||
            PrimitiveType::Axis3d => "3d axes".to_owned(),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
 | 
			
		||||
            PrimitiveType::Function => "functions".to_owned(),
 | 
			
		||||
            PrimitiveType::Tag => "tags".to_owned(),
 | 
			
		||||
            PrimitiveType::TagId => "tag identifiers".to_owned(),
 | 
			
		||||
        }
 | 
			
		||||
@ -418,6 +422,7 @@ impl fmt::Display for PrimitiveType {
 | 
			
		||||
            PrimitiveType::Axis3d => write!(f, "Axis3d"),
 | 
			
		||||
            PrimitiveType::Helix => write!(f, "Helix"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
 | 
			
		||||
            PrimitiveType::Function => write!(f, "function"),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1184,6 +1189,10 @@ impl KclValue {
 | 
			
		||||
                KclValue::ImportedGeometry { .. } => Ok(value.clone()),
 | 
			
		||||
                _ => Err(self.into()),
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::Function => match value {
 | 
			
		||||
                KclValue::Function { .. } => Ok(value.clone()),
 | 
			
		||||
                _ => Err(self.into()),
 | 
			
		||||
            },
 | 
			
		||||
            PrimitiveType::TagId => match value {
 | 
			
		||||
                KclValue::TagIdentifier { .. } => Ok(value.clone()),
 | 
			
		||||
                _ => Err(self.into()),
 | 
			
		||||
@ -1372,12 +1381,10 @@ impl KclValue {
 | 
			
		||||
            KclValue::HomArray { ty, value, .. } => {
 | 
			
		||||
                Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::TagIdentifier(_) | KclValue::TagDeclarator(_) | KclValue::Uuid { .. } => {
 | 
			
		||||
                Some(RuntimeType::Primitive(PrimitiveType::Tag))
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::Function { .. } | KclValue::Module { .. } | KclValue::KclNone { .. } | KclValue::Type { .. } => {
 | 
			
		||||
                None
 | 
			
		||||
            }
 | 
			
		||||
            KclValue::TagIdentifier(_) => Some(RuntimeType::Primitive(PrimitiveType::TagId)),
 | 
			
		||||
            KclValue::TagDeclarator(_) | KclValue::Uuid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Tag)),
 | 
			
		||||
            KclValue::Function { .. } => Some(RuntimeType::Primitive(PrimitiveType::Function)),
 | 
			
		||||
            KclValue::Module { .. } | KclValue::KclNone { .. } | KclValue::Type { .. } => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -232,6 +232,8 @@ impl PrimitiveType {
 | 
			
		||||
            PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
 | 
			
		||||
            PrimitiveType::Boolean => hasher.update(b"bool"),
 | 
			
		||||
            PrimitiveType::Tag => hasher.update(b"tag"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => hasher.update(b"ImportedGeometry"),
 | 
			
		||||
            PrimitiveType::Function => hasher.update(b"Fn"),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        hasher.finalize().into()
 | 
			
		||||
 | 
			
		||||
@ -3197,6 +3197,10 @@ pub enum PrimitiveType {
 | 
			
		||||
    Boolean,
 | 
			
		||||
    /// A tag.
 | 
			
		||||
    Tag,
 | 
			
		||||
    /// Imported from other CAD system.
 | 
			
		||||
    ImportedGeometry,
 | 
			
		||||
    /// `Fn`, type of functions.
 | 
			
		||||
    Function,
 | 
			
		||||
    /// An identifier used as a type (not really a primitive type, but whatever).
 | 
			
		||||
    Named(Node<Identifier>),
 | 
			
		||||
}
 | 
			
		||||
@ -3210,6 +3214,8 @@ impl PrimitiveType {
 | 
			
		||||
            ("tag", None) => Some(PrimitiveType::Tag),
 | 
			
		||||
            ("number", None) => Some(PrimitiveType::Number(NumericSuffix::None)),
 | 
			
		||||
            ("number", Some(s)) => Some(PrimitiveType::Number(s)),
 | 
			
		||||
            ("ImportedGeometry", None) => Some(PrimitiveType::ImportedGeometry),
 | 
			
		||||
            ("Fn", None) => Some(PrimitiveType::Function),
 | 
			
		||||
            _ => None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -3229,6 +3235,8 @@ impl fmt::Display for PrimitiveType {
 | 
			
		||||
            PrimitiveType::String => write!(f, "string"),
 | 
			
		||||
            PrimitiveType::Boolean => write!(f, "bool"),
 | 
			
		||||
            PrimitiveType::Tag => write!(f, "tag"),
 | 
			
		||||
            PrimitiveType::ImportedGeometry => write!(f, "ImportedGeometry"),
 | 
			
		||||
            PrimitiveType::Function => write!(f, "Fn"),
 | 
			
		||||
            PrimitiveType::Named(n) => write!(f, "{}", n.name),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -34,14 +34,12 @@ lazy_static! {
 | 
			
		||||
        set.insert("true", TokenType::Keyword);
 | 
			
		||||
        set.insert("false", TokenType::Keyword);
 | 
			
		||||
        set.insert("nil", TokenType::Keyword);
 | 
			
		||||
        // This isn't a type because brackets are used for the type.
 | 
			
		||||
        set.insert("array", TokenType::Keyword);
 | 
			
		||||
        set.insert("and", TokenType::Keyword);
 | 
			
		||||
        set.insert("or", TokenType::Keyword);
 | 
			
		||||
        set.insert("not", TokenType::Keyword);
 | 
			
		||||
        set.insert("var", TokenType::Keyword);
 | 
			
		||||
        set.insert("const", TokenType::Keyword);
 | 
			
		||||
        // "import" is special because of import().
 | 
			
		||||
        set.insert("import", TokenType::Keyword);
 | 
			
		||||
        set.insert("export", TokenType::Keyword);
 | 
			
		||||
        set.insert("type", TokenType::Keyword);
 | 
			
		||||
        set.insert("interface", TokenType::Keyword);
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
use kcl_derive_docs::stdlib;
 | 
			
		||||
 | 
			
		||||
use super::{
 | 
			
		||||
    args::{Arg, KwArgs},
 | 
			
		||||
@ -27,46 +26,6 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Apply a function to every element of a list.
 | 
			
		||||
///
 | 
			
		||||
/// Given a list like `[a, b, c]`, and a function like `f`, returns
 | 
			
		||||
/// `[f(a), f(b), f(c)]`
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// r = 10 // radius
 | 
			
		||||
/// fn drawCircle(@id) {
 | 
			
		||||
///   return startSketchOn(XY)
 | 
			
		||||
///     |> circle( center= [id * 2 * r, 0], radius= r)
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// // Call `drawCircle`, passing in each element of the array.
 | 
			
		||||
/// // The outputs from each `drawCircle` form a new array,
 | 
			
		||||
/// // which is the return value from `map`.
 | 
			
		||||
/// circles = map(
 | 
			
		||||
///   [1..3],
 | 
			
		||||
///   f = drawCircle
 | 
			
		||||
/// )
 | 
			
		||||
/// ```
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// r = 10 // radius
 | 
			
		||||
/// // Call `map`, using an anonymous function instead of a named one.
 | 
			
		||||
/// circles = map(
 | 
			
		||||
///   [1..3],
 | 
			
		||||
///   f = fn(@id) {
 | 
			
		||||
///     return startSketchOn(XY)
 | 
			
		||||
///       |> circle( center= [id * 2 * r, 0], radius= r)
 | 
			
		||||
///   }
 | 
			
		||||
/// )
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "map",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = true,
 | 
			
		||||
    args = {
 | 
			
		||||
        array = { docs = "Input array. The output array is this input array, but every element has had the function `f` run on it." },
 | 
			
		||||
        f = { docs = "A function. The output array is just the input array, but `f` has been run on every item." },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["array"]
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_map<'a>(
 | 
			
		||||
    array: Vec<KclValue>,
 | 
			
		||||
    f: &'a FunctionSource,
 | 
			
		||||
@ -118,96 +77,6 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
 | 
			
		||||
    inner_reduce(array, initial, f, exec_state, &args).await
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Take a starting value. Then, for each element of an array, calculate the next value,
 | 
			
		||||
/// using the previous value and the element.
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // This function adds two numbers.
 | 
			
		||||
/// fn add(@a, accum) { return a + accum }
 | 
			
		||||
///
 | 
			
		||||
/// // This function adds an array of numbers.
 | 
			
		||||
/// // It uses the `reduce` function, to call the `add` function on every
 | 
			
		||||
/// // element of the `arr` parameter. The starting value is 0.
 | 
			
		||||
/// fn sum(@arr) { return reduce(arr, initial = 0, f = add) }
 | 
			
		||||
///
 | 
			
		||||
/// /*
 | 
			
		||||
/// The above is basically like this pseudo-code:
 | 
			
		||||
/// fn sum(arr):
 | 
			
		||||
///     sumSoFar = 0
 | 
			
		||||
///     for i in arr:
 | 
			
		||||
///         sumSoFar = add(i, sumSoFar)
 | 
			
		||||
///     return sumSoFar
 | 
			
		||||
/// */
 | 
			
		||||
///
 | 
			
		||||
/// // We use `assert` to check that our `sum` function gives the
 | 
			
		||||
/// // expected result. It's good to check your work!
 | 
			
		||||
/// assert(sum([1, 2, 3]), isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
 | 
			
		||||
/// ```
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // This example works just like the previous example above, but it uses
 | 
			
		||||
/// // an anonymous `add` function as its parameter, instead of declaring a
 | 
			
		||||
/// // named function outside.
 | 
			
		||||
/// arr = [1, 2, 3]
 | 
			
		||||
/// sum = reduce(arr, initial = 0, f = fn (@i, accum) { return i + accum })
 | 
			
		||||
///
 | 
			
		||||
/// // We use `assert` to check that our `sum` function gives the
 | 
			
		||||
/// // expected result. It's good to check your work!
 | 
			
		||||
/// assert(sum, isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
 | 
			
		||||
/// ```
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Declare a function that sketches a decagon.
 | 
			
		||||
/// fn decagon(@radius) {
 | 
			
		||||
///   // Each side of the decagon is turned this many radians from the previous angle.
 | 
			
		||||
///   stepAngle = ((1/10) * TAU): number(rad)
 | 
			
		||||
///
 | 
			
		||||
///   // Start the decagon sketch at this point.
 | 
			
		||||
///   startOfDecagonSketch = startSketchOn(XY)
 | 
			
		||||
///     |> startProfile(at = [(cos(0)*radius), (sin(0) * radius)])
 | 
			
		||||
///
 | 
			
		||||
///   // Use a `reduce` to draw the remaining decagon sides.
 | 
			
		||||
///   // For each number in the array 1..10, run the given function,
 | 
			
		||||
///   // which takes a partially-sketched decagon and adds one more edge to it.
 | 
			
		||||
///   fullDecagon = reduce([1..10], initial = startOfDecagonSketch, f = fn(@i, accum) {
 | 
			
		||||
///       // Draw one edge of the decagon.
 | 
			
		||||
///       x = cos(stepAngle * i) * radius
 | 
			
		||||
///       y = sin(stepAngle * i) * radius
 | 
			
		||||
///       return line(accum, end = [x, y])
 | 
			
		||||
///   })
 | 
			
		||||
///
 | 
			
		||||
///   return fullDecagon
 | 
			
		||||
///
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// /*
 | 
			
		||||
/// The `decagon` above is basically like this pseudo-code:
 | 
			
		||||
/// fn decagon(radius):
 | 
			
		||||
///     stepAngle = ((1/10) * TAU): number(rad)
 | 
			
		||||
///     plane = startSketchOn(XY)
 | 
			
		||||
///     startOfDecagonSketch = startProfile(plane, at = [(cos(0)*radius), (sin(0) * radius)])
 | 
			
		||||
///
 | 
			
		||||
///     // Here's the reduce part.
 | 
			
		||||
///     partialDecagon = startOfDecagonSketch
 | 
			
		||||
///     for i in [1..10]:
 | 
			
		||||
///         x = cos(stepAngle * i) * radius
 | 
			
		||||
///         y = sin(stepAngle * i) * radius
 | 
			
		||||
///         partialDecagon = line(partialDecagon, end = [x, y])
 | 
			
		||||
///     fullDecagon = partialDecagon // it's now full
 | 
			
		||||
///     return fullDecagon
 | 
			
		||||
/// */
 | 
			
		||||
///
 | 
			
		||||
/// // Use the `decagon` function declared above, to sketch a decagon with radius 5.
 | 
			
		||||
/// decagon(5.0) |> close()
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "reduce",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = true,
 | 
			
		||||
    args = {
 | 
			
		||||
        array = { docs = "Each element of this array gets run through the function `f`, combined with the previous output from `f`, and then used for the next run." },
 | 
			
		||||
        initial = { docs = "The first time `f` is run, it will be called with the first item of `array` and this initial starting value."},
 | 
			
		||||
        f = { docs = "Run once per item in the input `array`. This function takes an item from the array, and the previous output from `f` (or `initial` on the very first run). The final time `f` is run, its output is returned as the final output from `reduce`." },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["array"]
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_reduce<'a>(
 | 
			
		||||
    array: Vec<KclValue>,
 | 
			
		||||
    initial: KclValue,
 | 
			
		||||
@ -285,28 +154,8 @@ pub async fn push(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
 | 
			
		||||
    Ok(KclValue::HomArray { value: new_array, ty })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Append an element to the end of an array.
 | 
			
		||||
///
 | 
			
		||||
/// Returns a new array with the element appended.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// arr = [1, 2, 3]
 | 
			
		||||
/// new_arr = push(arr, item = 4)
 | 
			
		||||
/// assert(new_arr[3], isEqualTo = 4, tolerance = 0.1, error = "4 was added to the end of the array")
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "push",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = true,
 | 
			
		||||
    args = {
 | 
			
		||||
        array = { docs = "The array which you're adding a new item to." },
 | 
			
		||||
        item = { docs = "The new item to add to the array" },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["array"]
 | 
			
		||||
}]
 | 
			
		||||
fn inner_push(mut array: Vec<KclValue>, item: KclValue) -> Vec<KclValue> {
 | 
			
		||||
    array.push(item);
 | 
			
		||||
 | 
			
		||||
    array
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -322,30 +171,9 @@ pub async fn pop(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let new_array = inner_pop(values, &args)?;
 | 
			
		||||
 | 
			
		||||
    Ok(KclValue::HomArray { value: new_array, ty })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Remove the last element from an array.
 | 
			
		||||
///
 | 
			
		||||
/// Returns a new array with the last element removed.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// arr = [1, 2, 3, 4]
 | 
			
		||||
/// new_arr = pop(arr)
 | 
			
		||||
/// assert(new_arr[0], isEqualTo = 1, tolerance = 0.00001, error = "1 is the first element of the array")
 | 
			
		||||
/// assert(new_arr[1], isEqualTo = 2, tolerance = 0.00001, error = "2 is the second element of the array")
 | 
			
		||||
/// assert(new_arr[2], isEqualTo = 3, tolerance = 0.00001, error = "3 is the third element of the array")
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "pop",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = true,
 | 
			
		||||
    args = {
 | 
			
		||||
        array = { docs = "The array to pop from. Must not be empty."},
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["array"]
 | 
			
		||||
}]
 | 
			
		||||
fn inner_pop(array: Vec<KclValue>, args: &Args) -> Result<Vec<KclValue>, KclError> {
 | 
			
		||||
    if array.is_empty() {
 | 
			
		||||
        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
 | 
			
		||||
@ -3,7 +3,6 @@
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use kcl_derive_docs::stdlib;
 | 
			
		||||
use kcmc::{
 | 
			
		||||
    each_cmd as mcmd,
 | 
			
		||||
    ok_response::{output::EntityGetAllChildUuids, OkModelingCmdResponse},
 | 
			
		||||
@ -41,240 +40,6 @@ pub async fn clone(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
 | 
			
		||||
    Ok(cloned.into())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Clone a sketch or solid.
 | 
			
		||||
///
 | 
			
		||||
/// This works essentially like a copy-paste operation. It creates a perfect replica
 | 
			
		||||
/// at that point in time that you can manipulate individually afterwards.
 | 
			
		||||
///
 | 
			
		||||
/// This doesn't really have much utility unless you need the equivalent of a double
 | 
			
		||||
/// instance pattern with zero transformations.
 | 
			
		||||
///
 | 
			
		||||
/// Really only use this function if YOU ARE SURE you need it. In most cases you
 | 
			
		||||
/// do not need clone and using a pattern with `instance = 2` is more appropriate.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Clone a basic sketch and move it and extrude it.
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10])
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// clonedSketch = clone(exampleSketch)
 | 
			
		||||
///     |> scale(
 | 
			
		||||
///     x = 1.0,
 | 
			
		||||
///     y = 1.0,
 | 
			
		||||
///     z = 2.5,
 | 
			
		||||
///     )
 | 
			
		||||
///     |> translate(
 | 
			
		||||
///         x = 15.0,
 | 
			
		||||
///         y = 0,
 | 
			
		||||
///         z = 0,
 | 
			
		||||
///     )
 | 
			
		||||
///     |> extrude(length = 5)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Clone a basic solid and move it.
 | 
			
		||||
///
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10])
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// myPart = extrude(exampleSketch, length = 5)
 | 
			
		||||
/// clonedPart = clone(myPart)
 | 
			
		||||
///     |> translate(
 | 
			
		||||
///         x = 25.0,
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Translate and rotate a cloned sketch to create a loft.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///         |> startProfile(at = [-10, 10])
 | 
			
		||||
///         |> xLine(length = 20)
 | 
			
		||||
///         |> yLine(length = -20)
 | 
			
		||||
///         |> xLine(length = -20)
 | 
			
		||||
///         |> close()
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 0, y = 0, z = 20)
 | 
			
		||||
///     |> rotate(axis = [0, 0, 1.0], angle = 45)
 | 
			
		||||
///
 | 
			
		||||
/// loft([sketch001, sketch002])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Translate a cloned solid. Fillet only the clone.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///         |> startProfile(at = [-10, 10])
 | 
			
		||||
///         |> xLine(length = 20)
 | 
			
		||||
///         |> yLine(length = -20)
 | 
			
		||||
///         |> xLine(length = -20, tag = $filletTag)
 | 
			
		||||
///         |> close()
 | 
			
		||||
///         |> extrude(length = 5)
 | 
			
		||||
///
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 0, y = 0, z = 20)
 | 
			
		||||
///     |> fillet(
 | 
			
		||||
///     radius = 2,
 | 
			
		||||
///     tags = [getNextAdjacentEdge(filletTag)],
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // You can reuse the tags from the original geometry with the cloned geometry.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10], tag = $sketchingFace)
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 10, y = 20, z = 0)
 | 
			
		||||
///     |> extrude(length = 5)
 | 
			
		||||
///
 | 
			
		||||
/// startSketchOn(sketch002, face = sketchingFace)
 | 
			
		||||
///   |> startProfile(at = [1, 1])
 | 
			
		||||
///   |> line(end = [8, 0])
 | 
			
		||||
///   |> line(end = [0, 8])
 | 
			
		||||
///   |> line(end = [-8, 0])
 | 
			
		||||
///   |> close(tag = $sketchingFace002)
 | 
			
		||||
///   |> extrude(length = 10)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // You can also use the tags from the original geometry to fillet the cloned geometry.
 | 
			
		||||
///
 | 
			
		||||
/// width = 20
 | 
			
		||||
/// length = 10
 | 
			
		||||
/// thickness = 1
 | 
			
		||||
/// filletRadius = 2
 | 
			
		||||
///
 | 
			
		||||
/// mountingPlateSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [-width/2, -length/2])
 | 
			
		||||
///   |> line(endAbsolute = [width/2, -length/2], tag = $edge1)
 | 
			
		||||
///   |> line(endAbsolute = [width/2, length/2], tag = $edge2)
 | 
			
		||||
///   |> line(endAbsolute = [-width/2, length/2], tag = $edge3)
 | 
			
		||||
///   |> close(tag = $edge4)
 | 
			
		||||
///
 | 
			
		||||
/// mountingPlate = extrude(mountingPlateSketch, length = thickness)
 | 
			
		||||
///
 | 
			
		||||
/// clonedMountingPlate = clone(mountingPlate)
 | 
			
		||||
///   |> fillet(
 | 
			
		||||
///     radius = filletRadius,
 | 
			
		||||
///     tags = [
 | 
			
		||||
///       getNextAdjacentEdge(edge1),
 | 
			
		||||
///       getNextAdjacentEdge(edge2),
 | 
			
		||||
///       getNextAdjacentEdge(edge3),
 | 
			
		||||
///       getNextAdjacentEdge(edge4)
 | 
			
		||||
///     ],
 | 
			
		||||
///   )
 | 
			
		||||
///   |> translate(x = 0, y = 50, z = 0)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Create a spring by sweeping around a helix path from a cloned sketch.
 | 
			
		||||
///
 | 
			
		||||
/// // Create a helix around the Z axis.
 | 
			
		||||
/// helixPath = helix(
 | 
			
		||||
///     angleStart = 0,
 | 
			
		||||
///     ccw = true,
 | 
			
		||||
///     revolutions = 4,
 | 
			
		||||
///     length = 10,
 | 
			
		||||
///     radius = 5,
 | 
			
		||||
///     axis = Z,
 | 
			
		||||
///  )
 | 
			
		||||
///
 | 
			
		||||
///
 | 
			
		||||
/// springSketch = startSketchOn(YZ)
 | 
			
		||||
///     |> circle( center = [0, 0], radius = 1)
 | 
			
		||||
///
 | 
			
		||||
/// // Create a spring by sweeping around the helix path.
 | 
			
		||||
/// sweepedSpring = clone(springSketch)
 | 
			
		||||
///     |> translate(x=100)
 | 
			
		||||
///     |> sweep(path = helixPath)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```
 | 
			
		||||
/// // A donut shape from a cloned sketch.
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///     |> circle( center = [15, 0], radius = 5 )
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///    |> translate( z = 30)
 | 
			
		||||
///     |> revolve(
 | 
			
		||||
///         angle = 360,
 | 
			
		||||
///         axis = Y,
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Sketch on the end of a revolved face by tagging the end face.
 | 
			
		||||
/// // This shows the cloned geometry will have the same tags as the original geometry.
 | 
			
		||||
///
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [4, 12])
 | 
			
		||||
///   |> line(end = [2, 0])
 | 
			
		||||
///   |> line(end = [0, -6])
 | 
			
		||||
///   |> line(end = [4, -6])
 | 
			
		||||
///   |> line(end = [0, -6])
 | 
			
		||||
///   |> line(end = [-3.75, -4.5])
 | 
			
		||||
///   |> line(end = [0, -5.5])
 | 
			
		||||
///   |> line(end = [-2, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// example001 = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
 | 
			
		||||
///
 | 
			
		||||
/// // example002 = clone(example001)
 | 
			
		||||
/// // |> translate(x = 0, y = 20, z = 0)
 | 
			
		||||
///
 | 
			
		||||
/// // Sketch on the cloned face.
 | 
			
		||||
/// // exampleSketch002 = startSketchOn(example002, face = end01)
 | 
			
		||||
/// //  |> startProfile(at = [4.5, -5])
 | 
			
		||||
/// //  |> line(end = [0, 5])
 | 
			
		||||
/// //  |> line(end = [5, 0])
 | 
			
		||||
/// //  |> line(end = [0, -5])
 | 
			
		||||
/// //  |> close()
 | 
			
		||||
///
 | 
			
		||||
/// // example003 = extrude(exampleSketch002, length = 5)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Clone an imported model.
 | 
			
		||||
///
 | 
			
		||||
/// import "tests/inputs/cube.sldprt" as cube
 | 
			
		||||
///
 | 
			
		||||
/// myCube = cube
 | 
			
		||||
///
 | 
			
		||||
/// clonedCube = clone(myCube)
 | 
			
		||||
///    |> translate(
 | 
			
		||||
///    x = 1020,
 | 
			
		||||
///    )
 | 
			
		||||
///    |> appearance(
 | 
			
		||||
///        color = "#ff0000",
 | 
			
		||||
///        metalness = 50,
 | 
			
		||||
///        roughness = 50
 | 
			
		||||
///    )
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "clone",
 | 
			
		||||
    feature_tree_operation = true,
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = true,
 | 
			
		||||
    args = {
 | 
			
		||||
        geometry = { docs = "The sketch, solid, or imported geometry to be cloned" },
 | 
			
		||||
    }
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_clone(
 | 
			
		||||
    geometry: GeometryWithImportedGeometry,
 | 
			
		||||
    exec_state: &mut ExecState,
 | 
			
		||||
 | 
			
		||||
@ -231,3 +231,38 @@ pub async fn ln(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclE
 | 
			
		||||
 | 
			
		||||
    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the length of the given leg.
 | 
			
		||||
pub async fn leg_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt();
 | 
			
		||||
    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for x.
 | 
			
		||||
pub async fn leg_angle_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = (leg.min(hypotenuse) / hypotenuse).acos().to_degrees();
 | 
			
		||||
    Ok(KclValue::from_number_with_type(
 | 
			
		||||
        result,
 | 
			
		||||
        NumericType::degrees(),
 | 
			
		||||
        vec![args.into()],
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for y.
 | 
			
		||||
pub async fn leg_angle_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = (leg.min(hypotenuse) / hypotenuse).asin().to_degrees();
 | 
			
		||||
    Ok(KclValue::from_number_with_type(
 | 
			
		||||
        result,
 | 
			
		||||
        NumericType::degrees(),
 | 
			
		||||
        vec![args.into()],
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -28,21 +28,13 @@ pub mod utils;
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
pub use args::Args;
 | 
			
		||||
use args::TyF64;
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
use kcl_derive_docs::stdlib;
 | 
			
		||||
use lazy_static::lazy_static;
 | 
			
		||||
use parse_display::{Display, FromStr};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    execution::{
 | 
			
		||||
        types::{NumericType, PrimitiveType, RuntimeType, UnitAngle, UnitType},
 | 
			
		||||
        ExecState, KclValue,
 | 
			
		||||
    },
 | 
			
		||||
    execution::{types::PrimitiveType, ExecState, KclValue},
 | 
			
		||||
    parsing::ast::types::Name,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -53,9 +45,6 @@ pub type StdFn = fn(
 | 
			
		||||
 | 
			
		||||
lazy_static! {
 | 
			
		||||
    static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
 | 
			
		||||
        Box::new(LegLen),
 | 
			
		||||
        Box::new(LegAngX),
 | 
			
		||||
        Box::new(LegAngY),
 | 
			
		||||
        Box::new(crate::std::appearance::Appearance),
 | 
			
		||||
        Box::new(crate::std::extrude::Extrude),
 | 
			
		||||
        Box::new(crate::std::segment::SegEnd),
 | 
			
		||||
@ -87,17 +76,12 @@ lazy_static! {
 | 
			
		||||
        Box::new(crate::std::sketch::TangentialArc),
 | 
			
		||||
        Box::new(crate::std::sketch::BezierCurve),
 | 
			
		||||
        Box::new(crate::std::sketch::Subtract2D),
 | 
			
		||||
        Box::new(crate::std::clone::Clone),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternLinear2D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternLinear3D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternCircular2D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternCircular3D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternTransform),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternTransform2D),
 | 
			
		||||
        Box::new(crate::std::array::Reduce),
 | 
			
		||||
        Box::new(crate::std::array::Map),
 | 
			
		||||
        Box::new(crate::std::array::Push),
 | 
			
		||||
        Box::new(crate::std::array::Pop),
 | 
			
		||||
        Box::new(crate::std::edge::GetOppositeEdge),
 | 
			
		||||
        Box::new(crate::std::edge::GetNextAdjacentEdge),
 | 
			
		||||
        Box::new(crate::std::edge::GetPreviousAdjacentEdge),
 | 
			
		||||
@ -149,84 +133,96 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
 | 
			
		||||
    match (path, fn_name) {
 | 
			
		||||
        ("math", "cos") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::cos(e, a)),
 | 
			
		||||
            StdFnProps::default("std::cos"),
 | 
			
		||||
            StdFnProps::default("std::math::cos"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "sin") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::sin(e, a)),
 | 
			
		||||
            StdFnProps::default("std::sin"),
 | 
			
		||||
            StdFnProps::default("std::math::sin"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "tan") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::tan(e, a)),
 | 
			
		||||
            StdFnProps::default("std::tan"),
 | 
			
		||||
            StdFnProps::default("std::math::tan"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "acos") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::acos(e, a)),
 | 
			
		||||
            StdFnProps::default("std::acos"),
 | 
			
		||||
            StdFnProps::default("std::math::acos"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "asin") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::asin(e, a)),
 | 
			
		||||
            StdFnProps::default("std::asin"),
 | 
			
		||||
            StdFnProps::default("std::math::asin"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "atan") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::atan(e, a)),
 | 
			
		||||
            StdFnProps::default("std::atan"),
 | 
			
		||||
            StdFnProps::default("std::math::atan"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "atan2") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::atan2(e, a)),
 | 
			
		||||
            StdFnProps::default("std::atan2"),
 | 
			
		||||
            StdFnProps::default("std::math::atan2"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "sqrt") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::sqrt(e, a)),
 | 
			
		||||
            StdFnProps::default("std::sqrt"),
 | 
			
		||||
            StdFnProps::default("std::math::sqrt"),
 | 
			
		||||
        ),
 | 
			
		||||
 | 
			
		||||
        ("math", "abs") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::abs(e, a)),
 | 
			
		||||
            StdFnProps::default("std::abs"),
 | 
			
		||||
            StdFnProps::default("std::math::abs"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "rem") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::rem(e, a)),
 | 
			
		||||
            StdFnProps::default("std::rem"),
 | 
			
		||||
            StdFnProps::default("std::math::rem"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "round") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::round(e, a)),
 | 
			
		||||
            StdFnProps::default("std::round"),
 | 
			
		||||
            StdFnProps::default("std::math::round"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "floor") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::floor(e, a)),
 | 
			
		||||
            StdFnProps::default("std::floor"),
 | 
			
		||||
            StdFnProps::default("std::math::floor"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "ceil") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::ceil(e, a)),
 | 
			
		||||
            StdFnProps::default("std::ceil"),
 | 
			
		||||
            StdFnProps::default("std::math::ceil"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "min") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::min(e, a)),
 | 
			
		||||
            StdFnProps::default("std::min"),
 | 
			
		||||
            StdFnProps::default("std::math::min"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "max") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::max(e, a)),
 | 
			
		||||
            StdFnProps::default("std::max"),
 | 
			
		||||
            StdFnProps::default("std::math::max"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "pow") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::pow(e, a)),
 | 
			
		||||
            StdFnProps::default("std::pow"),
 | 
			
		||||
            StdFnProps::default("std::math::pow"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "log") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::log(e, a)),
 | 
			
		||||
            StdFnProps::default("std::log"),
 | 
			
		||||
            StdFnProps::default("std::math::log"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "log2") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::log2(e, a)),
 | 
			
		||||
            StdFnProps::default("std::log2"),
 | 
			
		||||
            StdFnProps::default("std::math::log2"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "log10") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::log10(e, a)),
 | 
			
		||||
            StdFnProps::default("std::log10"),
 | 
			
		||||
            StdFnProps::default("std::math::log10"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "ln") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::ln(e, a)),
 | 
			
		||||
            StdFnProps::default("std::ln"),
 | 
			
		||||
            StdFnProps::default("std::math::ln"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "legLen") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::leg_length(e, a)),
 | 
			
		||||
            StdFnProps::default("std::math::legLen"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "legAngX") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::math::leg_angle_x(e, a)),
 | 
			
		||||
            StdFnProps::default("std::math::legAngX"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("math", "legAngY") => (
 | 
			
		||||
            |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)),
 | 
			
		||||
@ -264,6 +260,26 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
 | 
			
		||||
            |e, a| Box::pin(crate::std::shell::hollow(e, a)),
 | 
			
		||||
            StdFnProps::default("std::solid::hollow").include_in_feature_tree(),
 | 
			
		||||
        ),
 | 
			
		||||
        ("array", "map") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::array::map(e, a)),
 | 
			
		||||
            StdFnProps::default("std::array::map"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("array", "reduce") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::array::reduce(e, a)),
 | 
			
		||||
            StdFnProps::default("std::array::reduce"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("array", "push") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::array::push(e, a)),
 | 
			
		||||
            StdFnProps::default("std::array::push"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("array", "pop") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::array::pop(e, a)),
 | 
			
		||||
            StdFnProps::default("std::array::pop"),
 | 
			
		||||
        ),
 | 
			
		||||
        ("prelude", "clone") => (
 | 
			
		||||
            |e, a| Box::pin(crate::std::clone::clone(e, a)),
 | 
			
		||||
            StdFnProps::default("std::clone").include_in_feature_tree(),
 | 
			
		||||
        ),
 | 
			
		||||
        _ => unreachable!(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -341,110 +357,3 @@ pub enum FunctionKind {
 | 
			
		||||
 | 
			
		||||
/// The default tolerance for modeling commands in [`kittycad_modeling_cmds::length_unit::LengthUnit`].
 | 
			
		||||
const DEFAULT_TOLERANCE: f64 = 0.0000001;
 | 
			
		||||
 | 
			
		||||
/// Compute the length of the given leg.
 | 
			
		||||
pub async fn leg_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = inner_leg_length(hypotenuse, leg);
 | 
			
		||||
    Ok(KclValue::from_number_with_type(result, ty, vec![args.into()]))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the length of the given leg.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legLen(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "legLen",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = false,
 | 
			
		||||
    args = {
 | 
			
		||||
        hypotenuse = { docs = "The length of the triangle's hypotenuse" },
 | 
			
		||||
        leg = { docs = "The length of one of the triangle's legs (i.e. non-hypotenuse side)" },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["math"],
 | 
			
		||||
}]
 | 
			
		||||
fn inner_leg_length(hypotenuse: f64, leg: f64) -> f64 {
 | 
			
		||||
    (hypotenuse.powi(2) - f64::min(hypotenuse.abs(), leg.abs()).powi(2)).sqrt()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for x.
 | 
			
		||||
pub async fn leg_angle_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = inner_leg_angle_x(hypotenuse, leg);
 | 
			
		||||
    Ok(KclValue::from_number_with_type(
 | 
			
		||||
        result,
 | 
			
		||||
        NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
 | 
			
		||||
        vec![args.into()],
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for x.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legAngX(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "legAngX",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = false,
 | 
			
		||||
    args = {
 | 
			
		||||
        hypotenuse = { docs = "The length of the triangle's hypotenuse" },
 | 
			
		||||
        leg = { docs = "The length of one of the triangle's legs (i.e. non-hypotenuse side)" },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["math"],
 | 
			
		||||
}]
 | 
			
		||||
fn inner_leg_angle_x(hypotenuse: f64, leg: f64) -> f64 {
 | 
			
		||||
    (leg.min(hypotenuse) / hypotenuse).acos().to_degrees()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for y.
 | 
			
		||||
pub async fn leg_angle_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let hypotenuse: TyF64 = args.get_kw_arg_typed("hypotenuse", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let leg: TyF64 = args.get_kw_arg_typed("leg", &RuntimeType::length(), exec_state)?;
 | 
			
		||||
    let (hypotenuse, leg, _ty) = NumericType::combine_eq_coerce(hypotenuse, leg);
 | 
			
		||||
    let result = inner_leg_angle_y(hypotenuse, leg);
 | 
			
		||||
    Ok(KclValue::from_number_with_type(
 | 
			
		||||
        result,
 | 
			
		||||
        NumericType::Known(UnitType::Angle(UnitAngle::Degrees)),
 | 
			
		||||
        vec![args.into()],
 | 
			
		||||
    ))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for y.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legAngY(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "legAngY",
 | 
			
		||||
    keywords = true,
 | 
			
		||||
    unlabeled_first = false,
 | 
			
		||||
    args = {
 | 
			
		||||
        hypotenuse = { docs = "The length of the triangle's hypotenuse" },
 | 
			
		||||
        leg = { docs = "The length of one of the triangle's legs (i.e. non-hypotenuse side)" },
 | 
			
		||||
    },
 | 
			
		||||
    tags = ["math"],
 | 
			
		||||
}]
 | 
			
		||||
fn inner_leg_angle_y(hypotenuse: f64, leg: f64) -> f64 {
 | 
			
		||||
    (leg.min(hypotenuse) / hypotenuse).asin().to_degrees()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// The primitive types that can be used in a KCL file.
 | 
			
		||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, Display, FromStr)]
 | 
			
		||||
#[serde(rename_all = "lowercase")]
 | 
			
		||||
#[display(style = "lowercase")]
 | 
			
		||||
pub enum Primitive {
 | 
			
		||||
    /// A boolean value.
 | 
			
		||||
    Bool,
 | 
			
		||||
    /// A number value.
 | 
			
		||||
    Number,
 | 
			
		||||
    /// A string value.
 | 
			
		||||
    String,
 | 
			
		||||
    /// A uuid value.
 | 
			
		||||
    Uuid,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2,3 +2,169 @@
 | 
			
		||||
 | 
			
		||||
@no_std
 | 
			
		||||
@settings(defaultLengthUnit = mm, kclVersion = 1.0)
 | 
			
		||||
 | 
			
		||||
/// Apply a function to every element of a list.
 | 
			
		||||
///
 | 
			
		||||
/// Given a list like `[a, b, c]`, and a function like `f`, returns
 | 
			
		||||
/// `[f(a), f(b), f(c)]`
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// r = 10 // radius
 | 
			
		||||
/// fn drawCircle(@id) {
 | 
			
		||||
///   return startSketchOn(XY)
 | 
			
		||||
///     |> circle( center= [id * 2 * r, 0], radius= r)
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// // Call `drawCircle`, passing in each element of the array.
 | 
			
		||||
/// // The outputs from each `drawCircle` form a new array,
 | 
			
		||||
/// // which is the return value from `map`.
 | 
			
		||||
/// circles = map(
 | 
			
		||||
///   [1..3],
 | 
			
		||||
///   f = drawCircle
 | 
			
		||||
/// )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// r = 10 // radius
 | 
			
		||||
/// // Call `map`, using an anonymous function instead of a named one.
 | 
			
		||||
/// circles = map(
 | 
			
		||||
///   [1..3],
 | 
			
		||||
///   f = fn(@id) {
 | 
			
		||||
///     return startSketchOn(XY)
 | 
			
		||||
///       |> circle( center= [id * 2 * r, 0], radius= r)
 | 
			
		||||
///   }
 | 
			
		||||
/// )
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn map(
 | 
			
		||||
  /// Input array. The output array is this input array, but every element has had the function `f` run on it.
 | 
			
		||||
  @array: [any],
 | 
			
		||||
  /// A function. The output array is just the input array, but `f` has been run on every item.
 | 
			
		||||
  f: Fn,
 | 
			
		||||
): [any] {}
 | 
			
		||||
 | 
			
		||||
/// Take a starting value. Then, for each element of an array, calculate the next value,
 | 
			
		||||
/// using the previous value and the element.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // This function adds two numbers.
 | 
			
		||||
/// fn add(@a, accum) { return a + accum }
 | 
			
		||||
///
 | 
			
		||||
/// // This function adds an array of numbers.
 | 
			
		||||
/// // It uses the `reduce` function, to call the `add` function on every
 | 
			
		||||
/// // element of the `arr` parameter. The starting value is 0.
 | 
			
		||||
/// fn sum(@arr) { return reduce(arr, initial = 0, f = add) }
 | 
			
		||||
///
 | 
			
		||||
/// /*
 | 
			
		||||
/// The above is basically like this pseudo-code:
 | 
			
		||||
/// fn sum(arr):
 | 
			
		||||
///     sumSoFar = 0
 | 
			
		||||
///     for i in arr:
 | 
			
		||||
///         sumSoFar = add(i, sumSoFar)
 | 
			
		||||
///     return sumSoFar
 | 
			
		||||
/// */
 | 
			
		||||
///
 | 
			
		||||
/// // We use `assert` to check that our `sum` function gives the
 | 
			
		||||
/// // expected result. It's good to check your work!
 | 
			
		||||
/// assert(sum([1, 2, 3]), isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // This example works just like the previous example above, but it uses
 | 
			
		||||
/// // an anonymous `add` function as its parameter, instead of declaring a
 | 
			
		||||
/// // named function outside.
 | 
			
		||||
/// arr = [1, 2, 3]
 | 
			
		||||
/// sum = reduce(arr, initial = 0, f = fn (@i, accum) { return i + accum })
 | 
			
		||||
///
 | 
			
		||||
/// // We use `assert` to check that our `sum` function gives the
 | 
			
		||||
/// // expected result. It's good to check your work!
 | 
			
		||||
/// assert(sum, isEqualTo = 6, tolerance = 0.1, error = "1 + 2 + 3 summed is 6")
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Declare a function that sketches a decagon.
 | 
			
		||||
/// fn decagon(@radius) {
 | 
			
		||||
///   // Each side of the decagon is turned this many radians from the previous angle.
 | 
			
		||||
///   stepAngle = ((1/10) * TAU): number(rad)
 | 
			
		||||
///
 | 
			
		||||
///   // Start the decagon sketch at this point.
 | 
			
		||||
///   startOfDecagonSketch = startSketchOn(XY)
 | 
			
		||||
///     |> startProfile(at = [(cos(0)*radius), (sin(0) * radius)])
 | 
			
		||||
///
 | 
			
		||||
///   // Use a `reduce` to draw the remaining decagon sides.
 | 
			
		||||
///   // For each number in the array 1..10, run the given function,
 | 
			
		||||
///   // which takes a partially-sketched decagon and adds one more edge to it.
 | 
			
		||||
///   fullDecagon = reduce([1..10], initial = startOfDecagonSketch, f = fn(@i, accum) {
 | 
			
		||||
///       // Draw one edge of the decagon.
 | 
			
		||||
///       x = cos(stepAngle * i) * radius
 | 
			
		||||
///       y = sin(stepAngle * i) * radius
 | 
			
		||||
///       return line(accum, end = [x, y])
 | 
			
		||||
///   })
 | 
			
		||||
///
 | 
			
		||||
///   return fullDecagon
 | 
			
		||||
///
 | 
			
		||||
/// }
 | 
			
		||||
///
 | 
			
		||||
/// /*
 | 
			
		||||
/// The `decagon` above is basically like this pseudo-code:
 | 
			
		||||
/// fn decagon(radius):
 | 
			
		||||
///     stepAngle = ((1/10) * TAU): number(rad)
 | 
			
		||||
///     plane = startSketchOn(XY)
 | 
			
		||||
///     startOfDecagonSketch = startProfile(plane, at = [(cos(0)*radius), (sin(0) * radius)])
 | 
			
		||||
///
 | 
			
		||||
///     // Here's the reduce part.
 | 
			
		||||
///     partialDecagon = startOfDecagonSketch
 | 
			
		||||
///     for i in [1..10]:
 | 
			
		||||
///         x = cos(stepAngle * i) * radius
 | 
			
		||||
///         y = sin(stepAngle * i) * radius
 | 
			
		||||
///         partialDecagon = line(partialDecagon, end = [x, y])
 | 
			
		||||
///     fullDecagon = partialDecagon // it's now full
 | 
			
		||||
///     return fullDecagon
 | 
			
		||||
/// */
 | 
			
		||||
///
 | 
			
		||||
/// // Use the `decagon` function declared above, to sketch a decagon with radius 5.
 | 
			
		||||
/// decagon(5.0) |> close()
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn reduce(
 | 
			
		||||
  /// Each element of this array gets run through the function `f`, combined with the previous output from `f`, and then used for the next run.
 | 
			
		||||
  @array: [any],
 | 
			
		||||
  /// The first time `f` is run, it will be called with the first item of `array` and this initial starting value.
 | 
			
		||||
  initial: any,
 | 
			
		||||
  /// Run once per item in the input `array`. This function takes an item from the array, and the previous output from `f` (or `initial` on the very first run). The final time `f` is run, its output is returned as the final output from `reduce`.
 | 
			
		||||
  f: Fn,
 | 
			
		||||
): [any] {}
 | 
			
		||||
 | 
			
		||||
/// Append an element to the end of an array.
 | 
			
		||||
///
 | 
			
		||||
/// Returns a new array with the element appended.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// arr = [1, 2, 3]
 | 
			
		||||
/// new_arr = push(arr, item = 4)
 | 
			
		||||
/// assert(new_arr[3], isEqualTo = 4, tolerance = 0.1, error = "4 was added to the end of the array")
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn push(
 | 
			
		||||
  /// The array which you're adding a new item to.
 | 
			
		||||
  @array: [any],
 | 
			
		||||
  /// The new item to add to the array
 | 
			
		||||
  item: any,
 | 
			
		||||
): [any; 1+] {}
 | 
			
		||||
 | 
			
		||||
/// Remove the last element from an array.
 | 
			
		||||
///
 | 
			
		||||
/// Returns a new array with the last element removed.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// arr = [1, 2, 3, 4]
 | 
			
		||||
/// new_arr = pop(arr)
 | 
			
		||||
/// assert(new_arr[0], isEqualTo = 1, tolerance = 0.00001, error = "1 is the first element of the array")
 | 
			
		||||
/// assert(new_arr[1], isEqualTo = 2, tolerance = 0.00001, error = "2 is the second element of the array")
 | 
			
		||||
/// assert(new_arr[2], isEqualTo = 3, tolerance = 0.00001, error = "3 is the third element of the array")
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn pop(
 | 
			
		||||
  /// The array to pop from. Must not be empty.
 | 
			
		||||
  @array: [any; 1+],
 | 
			
		||||
): [any] {}
 | 
			
		||||
 | 
			
		||||
@ -436,3 +436,42 @@ export fn log10(@input: number): number {}
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn ln(@input: number): number {}
 | 
			
		||||
 | 
			
		||||
/// Compute the length of the given leg.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legLen(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn legLen(
 | 
			
		||||
  /// The length of the triangle's hypotenuse.
 | 
			
		||||
  hypotenuse: number(Length),
 | 
			
		||||
  /// The length of one of the triangle's legs (i.e. non-hypotenuse side).
 | 
			
		||||
  leg: number(Length),
 | 
			
		||||
): number(deg) {}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for x.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legAngX(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn legAngX(
 | 
			
		||||
  /// The length of the triangle's hypotenuse.
 | 
			
		||||
  hypotenuse: number(Length),
 | 
			
		||||
  /// The length of one of the triangle's legs (i.e. non-hypotenuse side).
 | 
			
		||||
  leg: number(Length),
 | 
			
		||||
): number(deg) {}
 | 
			
		||||
 | 
			
		||||
/// Compute the angle of the given leg for y.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl,no_run
 | 
			
		||||
/// legAngY(hypotenuse = 5, leg = 3)
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn legAngY(
 | 
			
		||||
  /// The length of the triangle's hypotenuse.
 | 
			
		||||
  hypotenuse: number(Length),
 | 
			
		||||
  /// The length of one of the triangle's legs (i.e. non-hypotenuse side).
 | 
			
		||||
  leg: number(Length),
 | 
			
		||||
): number(deg) {}
 | 
			
		||||
 | 
			
		||||
@ -251,3 +251,234 @@ export fn offsetPlane(
 | 
			
		||||
  /// Distance from the standard plane this new plane will be created at.
 | 
			
		||||
  offset: number(Length),
 | 
			
		||||
): Plane {}
 | 
			
		||||
 | 
			
		||||
/// Clone a sketch or solid.
 | 
			
		||||
///
 | 
			
		||||
/// This works essentially like a copy-paste operation. It creates a perfect replica
 | 
			
		||||
/// at that point in time that you can manipulate individually afterwards.
 | 
			
		||||
///
 | 
			
		||||
/// This doesn't really have much utility unless you need the equivalent of a double
 | 
			
		||||
/// instance pattern with zero transformations.
 | 
			
		||||
///
 | 
			
		||||
/// Really only use this function if YOU ARE SURE you need it. In most cases you
 | 
			
		||||
/// do not need clone and using a pattern with `instance = 2` is more appropriate.
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Clone a basic sketch and move it and extrude it.
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10])
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// clonedSketch = clone(exampleSketch)
 | 
			
		||||
///     |> scale(
 | 
			
		||||
///     x = 1.0,
 | 
			
		||||
///     y = 1.0,
 | 
			
		||||
///     z = 2.5,
 | 
			
		||||
///     )
 | 
			
		||||
///     |> translate(
 | 
			
		||||
///         x = 15.0,
 | 
			
		||||
///         y = 0,
 | 
			
		||||
///         z = 0,
 | 
			
		||||
///     )
 | 
			
		||||
///     |> extrude(length = 5)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Clone a basic solid and move it.
 | 
			
		||||
///
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10])
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// myPart = extrude(exampleSketch, length = 5)
 | 
			
		||||
/// clonedPart = clone(myPart)
 | 
			
		||||
///     |> translate(
 | 
			
		||||
///         x = 25.0,
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Translate and rotate a cloned sketch to create a loft.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///         |> startProfile(at = [-10, 10])
 | 
			
		||||
///         |> xLine(length = 20)
 | 
			
		||||
///         |> yLine(length = -20)
 | 
			
		||||
///         |> xLine(length = -20)
 | 
			
		||||
///         |> close()
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 0, y = 0, z = 20)
 | 
			
		||||
///     |> rotate(axis = [0, 0, 1.0], angle = 45)
 | 
			
		||||
///
 | 
			
		||||
/// loft([sketch001, sketch002])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Translate a cloned solid. Fillet only the clone.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///         |> startProfile(at = [-10, 10])
 | 
			
		||||
///         |> xLine(length = 20)
 | 
			
		||||
///         |> yLine(length = -20)
 | 
			
		||||
///         |> xLine(length = -20, tag = $filletTag)
 | 
			
		||||
///         |> close()
 | 
			
		||||
///         |> extrude(length = 5)
 | 
			
		||||
///
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 0, y = 0, z = 20)
 | 
			
		||||
///     |> fillet(
 | 
			
		||||
///     radius = 2,
 | 
			
		||||
///     tags = [getNextAdjacentEdge(filletTag)],
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // You can reuse the tags from the original geometry with the cloned geometry.
 | 
			
		||||
///
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [0, 0])
 | 
			
		||||
///   |> line(end = [10, 0])
 | 
			
		||||
///   |> line(end = [0, 10], tag = $sketchingFace)
 | 
			
		||||
///   |> line(end = [-10, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///     |> translate(x = 10, y = 20, z = 0)
 | 
			
		||||
///     |> extrude(length = 5)
 | 
			
		||||
///
 | 
			
		||||
/// startSketchOn(sketch002, face = sketchingFace)
 | 
			
		||||
///   |> startProfile(at = [1, 1])
 | 
			
		||||
///   |> line(end = [8, 0])
 | 
			
		||||
///   |> line(end = [0, 8])
 | 
			
		||||
///   |> line(end = [-8, 0])
 | 
			
		||||
///   |> close(tag = $sketchingFace002)
 | 
			
		||||
///   |> extrude(length = 10)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // You can also use the tags from the original geometry to fillet the cloned geometry.
 | 
			
		||||
///
 | 
			
		||||
/// width = 20
 | 
			
		||||
/// length = 10
 | 
			
		||||
/// thickness = 1
 | 
			
		||||
/// filletRadius = 2
 | 
			
		||||
///
 | 
			
		||||
/// mountingPlateSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [-width/2, -length/2])
 | 
			
		||||
///   |> line(endAbsolute = [width/2, -length/2], tag = $edge1)
 | 
			
		||||
///   |> line(endAbsolute = [width/2, length/2], tag = $edge2)
 | 
			
		||||
///   |> line(endAbsolute = [-width/2, length/2], tag = $edge3)
 | 
			
		||||
///   |> close(tag = $edge4)
 | 
			
		||||
///
 | 
			
		||||
/// mountingPlate = extrude(mountingPlateSketch, length = thickness)
 | 
			
		||||
///
 | 
			
		||||
/// clonedMountingPlate = clone(mountingPlate)
 | 
			
		||||
///   |> fillet(
 | 
			
		||||
///     radius = filletRadius,
 | 
			
		||||
///     tags = [
 | 
			
		||||
///       getNextAdjacentEdge(edge1),
 | 
			
		||||
///       getNextAdjacentEdge(edge2),
 | 
			
		||||
///       getNextAdjacentEdge(edge3),
 | 
			
		||||
///       getNextAdjacentEdge(edge4)
 | 
			
		||||
///     ],
 | 
			
		||||
///   )
 | 
			
		||||
///   |> translate(x = 0, y = 50, z = 0)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Create a spring by sweeping around a helix path from a cloned sketch.
 | 
			
		||||
///
 | 
			
		||||
/// // Create a helix around the Z axis.
 | 
			
		||||
/// helixPath = helix(
 | 
			
		||||
///     angleStart = 0,
 | 
			
		||||
///     ccw = true,
 | 
			
		||||
///     revolutions = 4,
 | 
			
		||||
///     length = 10,
 | 
			
		||||
///     radius = 5,
 | 
			
		||||
///     axis = Z,
 | 
			
		||||
///  )
 | 
			
		||||
///
 | 
			
		||||
///
 | 
			
		||||
/// springSketch = startSketchOn(YZ)
 | 
			
		||||
///     |> circle( center = [0, 0], radius = 1)
 | 
			
		||||
///
 | 
			
		||||
/// // Create a spring by sweeping around the helix path.
 | 
			
		||||
/// sweepedSpring = clone(springSketch)
 | 
			
		||||
///     |> translate(x=100)
 | 
			
		||||
///     |> sweep(path = helixPath)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // A donut shape from a cloned sketch.
 | 
			
		||||
/// sketch001 = startSketchOn(XY)
 | 
			
		||||
///     |> circle( center = [15, 0], radius = 5 )
 | 
			
		||||
///
 | 
			
		||||
/// sketch002 = clone(sketch001)
 | 
			
		||||
///    |> translate( z = 30)
 | 
			
		||||
///     |> revolve(
 | 
			
		||||
///         angle = 360,
 | 
			
		||||
///         axis = Y,
 | 
			
		||||
///     )
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Sketch on the end of a revolved face by tagging the end face.
 | 
			
		||||
/// // This shows the cloned geometry will have the same tags as the original geometry.
 | 
			
		||||
///
 | 
			
		||||
/// exampleSketch = startSketchOn(XY)
 | 
			
		||||
///   |> startProfile(at = [4, 12])
 | 
			
		||||
///   |> line(end = [2, 0])
 | 
			
		||||
///   |> line(end = [0, -6])
 | 
			
		||||
///   |> line(end = [4, -6])
 | 
			
		||||
///   |> line(end = [0, -6])
 | 
			
		||||
///   |> line(end = [-3.75, -4.5])
 | 
			
		||||
///   |> line(end = [0, -5.5])
 | 
			
		||||
///   |> line(end = [-2, 0])
 | 
			
		||||
///   |> close()
 | 
			
		||||
///
 | 
			
		||||
/// example001 = revolve(exampleSketch, axis = Y, angle = 180, tagEnd = $end01)
 | 
			
		||||
///
 | 
			
		||||
/// // example002 = clone(example001)
 | 
			
		||||
/// // |> translate(x = 0, y = 20, z = 0)
 | 
			
		||||
///
 | 
			
		||||
/// // Sketch on the cloned face.
 | 
			
		||||
/// // exampleSketch002 = startSketchOn(example002, face = end01)
 | 
			
		||||
/// //  |> startProfile(at = [4.5, -5])
 | 
			
		||||
/// //  |> line(end = [0, 5])
 | 
			
		||||
/// //  |> line(end = [5, 0])
 | 
			
		||||
/// //  |> line(end = [0, -5])
 | 
			
		||||
/// //  |> close()
 | 
			
		||||
///
 | 
			
		||||
/// // example003 = extrude(exampleSketch002, length = 5)
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```kcl
 | 
			
		||||
/// // Clone an imported model.
 | 
			
		||||
///
 | 
			
		||||
/// import "tests/inputs/cube.sldprt" as cube
 | 
			
		||||
///
 | 
			
		||||
/// myCube = cube
 | 
			
		||||
///
 | 
			
		||||
/// clonedCube = clone(myCube)
 | 
			
		||||
///    |> translate(
 | 
			
		||||
///    x = 1020,
 | 
			
		||||
///    )
 | 
			
		||||
///    |> appearance(
 | 
			
		||||
///        color = "#ff0000",
 | 
			
		||||
///        metalness = 50,
 | 
			
		||||
///        roughness = 50
 | 
			
		||||
///    )
 | 
			
		||||
/// ```
 | 
			
		||||
@(impl = std_rust)
 | 
			
		||||
export fn clone(
 | 
			
		||||
  /// The sketch, solid, or imported geometry to be cloned.
 | 
			
		||||
  @geometry: Sketch | Solid | ImportedGeometry,
 | 
			
		||||
): Sketch | Solid | ImportedGeometry {}
 | 
			
		||||
 | 
			
		||||
@ -163,6 +163,14 @@ export type string
 | 
			
		||||
@(impl = primitive)
 | 
			
		||||
export type tag
 | 
			
		||||
 | 
			
		||||
/// Represents geometry which is defined using some other CAD system and imported into KCL.
 | 
			
		||||
@(impl = primitive)
 | 
			
		||||
export type ImportedGeometry
 | 
			
		||||
 | 
			
		||||
/// The type of any function in KCL.
 | 
			
		||||
@(impl = primitive)
 | 
			
		||||
export type Fn
 | 
			
		||||
 | 
			
		||||
/// An abstract plane.
 | 
			
		||||
///
 | 
			
		||||
/// A plane has a position and orientation in space defined by its origin and axes. A plane is abstract
 | 
			
		||||
 | 
			
		||||
@ -4,11 +4,20 @@ description: Error from executing argument_error.kcl
 | 
			
		||||
---
 | 
			
		||||
KCL Semantic error
 | 
			
		||||
 | 
			
		||||
  × semantic: This function expected the input argument to be of type
 | 
			
		||||
  │ Vec<KclValue> but it's actually of type Function
 | 
			
		||||
   ╭─[5:5]
 | 
			
		||||
  × semantic: f requires a value with type `Fn`, but found array (list)
 | 
			
		||||
   ╭─[5:1]
 | 
			
		||||
 4 │ 
 | 
			
		||||
 5 │ map(f, f = [0, 1])
 | 
			
		||||
   ·     ┬
 | 
			
		||||
   ·     ╰── tests/argument_error/input.kcl
 | 
			
		||||
   · ─────────┬────────┬
 | 
			
		||||
   ·          │        ╰── tests/argument_error/input.kcl
 | 
			
		||||
   ·          ╰── tests/argument_error/input.kcl
 | 
			
		||||
   ╰────
 | 
			
		||||
  ╰─▶ KCL Semantic error
 | 
			
		||||
      
 | 
			
		||||
        × semantic: f requires a value with type `Fn`, but found array (list)
 | 
			
		||||
         ╭─[5:12]
 | 
			
		||||
       4 │
 | 
			
		||||
       5 │ map(f, f = [0, 1])
 | 
			
		||||
         ·            ───┬──
 | 
			
		||||
         ·               ╰── tests/argument_error/input.kcl
 | 
			
		||||
         ╰────
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,22 @@ description: Error from executing array_elem_pop_empty_fail.kcl
 | 
			
		||||
---
 | 
			
		||||
KCL Semantic error
 | 
			
		||||
 | 
			
		||||
  × semantic: Cannot pop from an empty array
 | 
			
		||||
  × semantic: The input argument of `std::array::pop` requires a value with
 | 
			
		||||
  │ type `[any; 1+]`, but found array (list)
 | 
			
		||||
   ╭─[2:8]
 | 
			
		||||
 1 │ arr = []
 | 
			
		||||
 2 │ fail = pop(arr)
 | 
			
		||||
   ·        ────┬───
 | 
			
		||||
   ·        ────┬───┬
 | 
			
		||||
   ·            │   ╰── tests/array_elem_pop_empty_fail/input.kcl
 | 
			
		||||
   ·            ╰── tests/array_elem_pop_empty_fail/input.kcl
 | 
			
		||||
   ╰────
 | 
			
		||||
  ╰─▶ KCL Semantic error
 | 
			
		||||
      
 | 
			
		||||
        × semantic: The input argument of `std::array::pop` requires a value
 | 
			
		||||
        │ with type `[any; 1+]`, but found array (list)
 | 
			
		||||
         ╭─[2:12]
 | 
			
		||||
       1 │ arr = []
 | 
			
		||||
       2 │ fail = pop(arr)
 | 
			
		||||
         ·            ─┬─
 | 
			
		||||
         ·             ╰── tests/array_elem_pop_empty_fail/input.kcl
 | 
			
		||||
         ╰────
 | 
			
		||||
 | 
			
		||||
@ -105,10 +105,8 @@ description: Operations executed clone_w_fillets.kcl
 | 
			
		||||
    "sourceRange": []
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "labeledArgs": {},
 | 
			
		||||
    "type": "KclStdLibCall",
 | 
			
		||||
    "name": "clone",
 | 
			
		||||
    "sourceRange": [],
 | 
			
		||||
    "type": "StdLibCall",
 | 
			
		||||
    "unlabeledArg": {
 | 
			
		||||
      "value": {
 | 
			
		||||
        "type": "Solid",
 | 
			
		||||
@ -117,6 +115,8 @@ description: Operations executed clone_w_fillets.kcl
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "sourceRange": []
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    "labeledArgs": {},
 | 
			
		||||
    "sourceRange": []
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@ -98,10 +98,8 @@ description: Operations executed clone_w_shell.kcl
 | 
			
		||||
    "sourceRange": []
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    "labeledArgs": {},
 | 
			
		||||
    "type": "KclStdLibCall",
 | 
			
		||||
    "name": "clone",
 | 
			
		||||
    "sourceRange": [],
 | 
			
		||||
    "type": "StdLibCall",
 | 
			
		||||
    "unlabeledArg": {
 | 
			
		||||
      "value": {
 | 
			
		||||
        "type": "Solid",
 | 
			
		||||
@ -110,6 +108,8 @@ description: Operations executed clone_w_shell.kcl
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      "sourceRange": []
 | 
			
		||||
    }
 | 
			
		||||
    },
 | 
			
		||||
    "labeledArgs": {},
 | 
			
		||||
    "sourceRange": []
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
@ -678,7 +678,7 @@ flowchart LR
 | 
			
		||||
  99 --- 245
 | 
			
		||||
  99 --- 293
 | 
			
		||||
  106 --- 193
 | 
			
		||||
  106 x--> 206
 | 
			
		||||
  106 x--> 207
 | 
			
		||||
  106 --- 272
 | 
			
		||||
  106 --- 321
 | 
			
		||||
  126 --- 194
 | 
			
		||||
@ -1012,7 +1012,7 @@ flowchart LR
 | 
			
		||||
  233 <--x 205
 | 
			
		||||
  234 <--x 205
 | 
			
		||||
  235 <--x 205
 | 
			
		||||
  272 <--x 207
 | 
			
		||||
  272 <--x 206
 | 
			
		||||
  254 <--x 213
 | 
			
		||||
  255 <--x 213
 | 
			
		||||
  256 <--x 213
 | 
			
		||||
 | 
			
		||||
@ -709,6 +709,7 @@ flowchart LR
 | 
			
		||||
  115 --- 179
 | 
			
		||||
  115 x--> 228
 | 
			
		||||
  115 --- 256
 | 
			
		||||
  115 x--> 307
 | 
			
		||||
  115 --- 308
 | 
			
		||||
  164 <--x 116
 | 
			
		||||
  116 --- 184
 | 
			
		||||
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB  | 
| 
		 Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB  | 
| 
		 Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB  | 
| 
		 Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB  | 
| 
		 Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB  | 
| 
		 Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB  | 
| 
		 Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB  | 
| 
		 Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB  | 
| 
		 Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB  | 
| 
		 Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 85 KiB  | 
| 
		 Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB  | 
| 
		 Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB  | 
| 
		 Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 30 KiB  | 
| 
		 Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 174 KiB  | 
| 
		 Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB  | 
| 
		 Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB  | 
| 
		 Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB  |