Merge branch 'main' into cut-release-v0.25.0
@ -56,6 +56,7 @@ layout: manual
 | 
			
		||||
* [`line`](kcl/line)
 | 
			
		||||
* [`lineTo`](kcl/lineTo)
 | 
			
		||||
* [`ln`](kcl/ln)
 | 
			
		||||
* [`loft`](kcl/loft)
 | 
			
		||||
* [`log`](kcl/log)
 | 
			
		||||
* [`log10`](kcl/log10)
 | 
			
		||||
* [`log2`](kcl/log2)
 | 
			
		||||
@ -63,6 +64,7 @@ layout: manual
 | 
			
		||||
* [`max`](kcl/max)
 | 
			
		||||
* [`min`](kcl/min)
 | 
			
		||||
* [`mm`](kcl/mm)
 | 
			
		||||
* [`offsetPlane`](kcl/offsetPlane)
 | 
			
		||||
* [`patternCircular2d`](kcl/patternCircular2d)
 | 
			
		||||
* [`patternCircular3d`](kcl/patternCircular3d)
 | 
			
		||||
* [`patternLinear2d`](kcl/patternLinear2d)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										477
									
								
								docs/kcl/loft.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										138
									
								
								docs/kcl/offsetPlane.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										4938
									
								
								docs/kcl/std.json
									
									
									
									
									
								
							
							
						
						
							
								
								
									
										2
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						@ -1345,7 +1345,7 @@ dependencies = [
 | 
			
		||||
 | 
			
		||||
[[package]]
 | 
			
		||||
name = "kcl-lib"
 | 
			
		||||
version = "0.2.12"
 | 
			
		||||
version = "0.2.13"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "anyhow",
 | 
			
		||||
 "approx",
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,7 @@
 | 
			
		||||
[package]
 | 
			
		||||
name = "kcl-lib"
 | 
			
		||||
description = "KittyCAD Language implementation and tools"
 | 
			
		||||
version = "0.2.12"
 | 
			
		||||
version = "0.2.13"
 | 
			
		||||
edition = "2021"
 | 
			
		||||
license = "MIT"
 | 
			
		||||
repository = "https://github.com/KittyCAD/modeling-app"
 | 
			
		||||
 | 
			
		||||
@ -294,6 +294,13 @@ impl Args {
 | 
			
		||||
        FromArgs::from_args(self, 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_sketch_groups_and_data<'a, T>(&'a self) -> Result<(Vec<SketchGroup>, Option<T>), KclError>
 | 
			
		||||
    where
 | 
			
		||||
        T: FromArgs<'a> + serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
 | 
			
		||||
    {
 | 
			
		||||
        FromArgs::from_args(self, 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_data_and_optional_tag<'a, T>(&'a self) -> Result<(T, Option<FaceTag>), KclError>
 | 
			
		||||
    where
 | 
			
		||||
        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
 | 
			
		||||
@ -360,6 +367,13 @@ impl Args {
 | 
			
		||||
        FromArgs::from_args(self, 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_data_and_float<'a, T>(&'a self) -> Result<(T, f64), KclError>
 | 
			
		||||
    where
 | 
			
		||||
        T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
 | 
			
		||||
    {
 | 
			
		||||
        FromArgs::from_args(self, 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn get_number_sketch_group_set(&self) -> Result<(f64, SketchGroupSet), KclError> {
 | 
			
		||||
        FromArgs::from_args(self, 0)
 | 
			
		||||
    }
 | 
			
		||||
@ -620,6 +634,8 @@ impl_from_arg_via_json!(super::revolve::RevolveData);
 | 
			
		||||
impl_from_arg_via_json!(super::sketch::SketchData);
 | 
			
		||||
impl_from_arg_via_json!(crate::std::import::ImportFormat);
 | 
			
		||||
impl_from_arg_via_json!(crate::std::polar::PolarCoordsData);
 | 
			
		||||
impl_from_arg_via_json!(crate::std::loft::LoftData);
 | 
			
		||||
impl_from_arg_via_json!(crate::std::planes::StandardPlane);
 | 
			
		||||
impl_from_arg_via_json!(SketchGroup);
 | 
			
		||||
impl_from_arg_via_json!(FaceTag);
 | 
			
		||||
impl_from_arg_via_json!(String);
 | 
			
		||||
@ -690,3 +706,13 @@ impl<'a> FromKclValue<'a> for SketchSurface {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> FromKclValue<'a> for Vec<SketchGroup> {
 | 
			
		||||
    fn from_mem_item(arg: &'a KclValue) -> Option<Self> {
 | 
			
		||||
        let KclValue::UserVal(uv) = arg else {
 | 
			
		||||
            return None;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        uv.get::<Vec<SketchGroup>>().map(|x| x.0)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										141
									
								
								src/wasm-lib/kcl/src/std/loft.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,141 @@
 | 
			
		||||
//! Standard library lofts.
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use kittycad::types::ModelingCmd;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, KclValue, SketchGroup},
 | 
			
		||||
    std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const DEFAULT_V_DEGREE: u32 = 1;
 | 
			
		||||
 | 
			
		||||
/// Data for a loft.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct LoftData {
 | 
			
		||||
    /// Degree of the interpolation. Must be greater than zero.
 | 
			
		||||
    /// For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction.
 | 
			
		||||
    /// This defaults to 2, if not specified.
 | 
			
		||||
    pub v_degree: Option<std::num::NonZeroU32>,
 | 
			
		||||
    /// Attempt to approximate rational curves (such as arcs) using a bezier.
 | 
			
		||||
    /// This will remove banding around interpolations between arcs and non-arcs.  It may produce errors in other scenarios
 | 
			
		||||
    /// Over time, this field won't be necessary.
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub bez_approximate_rational: Option<bool>,
 | 
			
		||||
    /// This can be set to override the automatically determined topological base curve, which is usually the first section encountered.
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub base_curve_index: Option<u32>,
 | 
			
		||||
    /// Tolerance for the loft operation.
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub tolerance: Option<f64>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for LoftData {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            // This unwrap is safe because the default value is always greater than zero.
 | 
			
		||||
            v_degree: Some(std::num::NonZeroU32::new(DEFAULT_V_DEGREE).unwrap()),
 | 
			
		||||
            bez_approximate_rational: None,
 | 
			
		||||
            base_curve_index: None,
 | 
			
		||||
            tolerance: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
 | 
			
		||||
pub async fn loft(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (sketch_groups, data): (Vec<SketchGroup>, Option<LoftData>) = args.get_sketch_groups_and_data()?;
 | 
			
		||||
 | 
			
		||||
    let extrude_group = inner_loft(sketch_groups, data, args).await?;
 | 
			
		||||
    Ok(KclValue::ExtrudeGroup(extrude_group))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
 | 
			
		||||
///
 | 
			
		||||
/// The sketches need to closed and on the same plane.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square and a triangle.
 | 
			
		||||
/// const squareSketch = startSketchOn('XY')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const triangleSketch = startSketchOn(offsetPlane('XY', 75))
 | 
			
		||||
///     |> startProfileAt([0, 125], %)
 | 
			
		||||
///     |> line([-15, -30], %)
 | 
			
		||||
///     |> line([30, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, triangleSketch])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square, a circle, and another circle.
 | 
			
		||||
/// const squareSketch = startSketchOn('XY')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch0 = startSketchOn(offsetPlane('XY', 75))
 | 
			
		||||
///     |> circle([0, 100], 50, %)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch1 = startSketchOn(offsetPlane('XY', 150))
 | 
			
		||||
///     |> circle([0, 100], 20, %)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, circleSketch0, circleSketch1])
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "loft",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_loft(
 | 
			
		||||
    sketch_groups: Vec<SketchGroup>,
 | 
			
		||||
    data: Option<LoftData>,
 | 
			
		||||
    args: Args,
 | 
			
		||||
) -> Result<Box<ExtrudeGroup>, KclError> {
 | 
			
		||||
    // Make sure we have at least two sketches.
 | 
			
		||||
    if sketch_groups.len() < 2 {
 | 
			
		||||
        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: format!(
 | 
			
		||||
                "Loft requires at least two sketches, but only {} were provided.",
 | 
			
		||||
                sketch_groups.len()
 | 
			
		||||
            ),
 | 
			
		||||
            source_ranges: vec![args.source_range],
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Get the loft data.
 | 
			
		||||
    let data = data.unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
    let id = uuid::Uuid::new_v4();
 | 
			
		||||
    args.batch_modeling_cmd(
 | 
			
		||||
        id,
 | 
			
		||||
        ModelingCmd::Loft {
 | 
			
		||||
            section_ids: sketch_groups.iter().map(|group| group.id).collect(),
 | 
			
		||||
            base_curve_index: data.base_curve_index,
 | 
			
		||||
            bez_approximate_rational: data.bez_approximate_rational.unwrap_or(false),
 | 
			
		||||
            tolerance: data.tolerance.unwrap_or(default_tolerance(&args.ctx.settings.units)),
 | 
			
		||||
            v_degree: data
 | 
			
		||||
                .v_degree
 | 
			
		||||
                .unwrap_or_else(|| std::num::NonZeroU32::new(DEFAULT_V_DEGREE).unwrap())
 | 
			
		||||
                .into(),
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
 | 
			
		||||
    // Using the first sketch as the base curve, idk we might want to change this later.
 | 
			
		||||
    do_post_extrude(sketch_groups[0].clone(), 0.0, id, args).await
 | 
			
		||||
}
 | 
			
		||||
@ -9,8 +9,10 @@ pub mod fillet;
 | 
			
		||||
pub mod helix;
 | 
			
		||||
pub mod import;
 | 
			
		||||
pub mod kcl_stdlib;
 | 
			
		||||
pub mod loft;
 | 
			
		||||
pub mod math;
 | 
			
		||||
pub mod patterns;
 | 
			
		||||
pub mod planes;
 | 
			
		||||
pub mod polar;
 | 
			
		||||
pub mod revolve;
 | 
			
		||||
pub mod segment;
 | 
			
		||||
@ -98,6 +100,8 @@ lazy_static! {
 | 
			
		||||
        Box::new(crate::std::shell::Shell),
 | 
			
		||||
        Box::new(crate::std::shell::Hollow),
 | 
			
		||||
        Box::new(crate::std::revolve::Revolve),
 | 
			
		||||
        Box::new(crate::std::loft::Loft),
 | 
			
		||||
        Box::new(crate::std::planes::OffsetPlane),
 | 
			
		||||
        Box::new(crate::std::import::Import),
 | 
			
		||||
        Box::new(crate::std::math::Cos),
 | 
			
		||||
        Box::new(crate::std::math::Sin),
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										168
									
								
								src/wasm-lib/kcl/src/std/planes.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						@ -0,0 +1,168 @@
 | 
			
		||||
//! Standard library plane helpers.
 | 
			
		||||
 | 
			
		||||
use derive_docs::stdlib;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{KclValue, Metadata, Plane, UserVal},
 | 
			
		||||
    std::{sketch::PlaneData, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// One of the standard planes.
 | 
			
		||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, JsonSchema)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub enum StandardPlane {
 | 
			
		||||
    /// The XY plane.
 | 
			
		||||
    #[serde(rename = "XY", alias = "xy")]
 | 
			
		||||
    XY,
 | 
			
		||||
    /// The opposite side of the XY plane.
 | 
			
		||||
    #[serde(rename = "-XY", alias = "-xy")]
 | 
			
		||||
    NegXY,
 | 
			
		||||
    /// The XZ plane.
 | 
			
		||||
    #[serde(rename = "XZ", alias = "xz")]
 | 
			
		||||
    XZ,
 | 
			
		||||
    /// The opposite side of the XZ plane.
 | 
			
		||||
    #[serde(rename = "-XZ", alias = "-xz")]
 | 
			
		||||
    NegXZ,
 | 
			
		||||
    /// The YZ plane.
 | 
			
		||||
    #[serde(rename = "YZ", alias = "yz")]
 | 
			
		||||
    YZ,
 | 
			
		||||
    /// The opposite side of the YZ plane.
 | 
			
		||||
    #[serde(rename = "-YZ", alias = "-yz")]
 | 
			
		||||
    NegYZ,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<StandardPlane> for PlaneData {
 | 
			
		||||
    fn from(value: StandardPlane) -> Self {
 | 
			
		||||
        match value {
 | 
			
		||||
            StandardPlane::XY => PlaneData::XY,
 | 
			
		||||
            StandardPlane::NegXY => PlaneData::NegXY,
 | 
			
		||||
            StandardPlane::XZ => PlaneData::XZ,
 | 
			
		||||
            StandardPlane::NegXZ => PlaneData::NegXZ,
 | 
			
		||||
            StandardPlane::YZ => PlaneData::YZ,
 | 
			
		||||
            StandardPlane::NegYZ => PlaneData::NegYZ,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Offset a plane by a distance along its normal.
 | 
			
		||||
pub async fn offset_plane(args: Args) -> Result<KclValue, KclError> {
 | 
			
		||||
    let (std_plane, offset): (StandardPlane, f64) = args.get_data_and_float()?;
 | 
			
		||||
 | 
			
		||||
    let plane = inner_offset_plane(std_plane, offset).await?;
 | 
			
		||||
 | 
			
		||||
    Ok(KclValue::UserVal(UserVal::set(
 | 
			
		||||
        vec![Metadata {
 | 
			
		||||
            source_range: args.source_range,
 | 
			
		||||
        }],
 | 
			
		||||
        plane,
 | 
			
		||||
    )))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Offset a plane by a distance along its normal.
 | 
			
		||||
///
 | 
			
		||||
/// For example, if you offset the 'XZ' plane by 10, the new plane will be parallel to the 'XZ'
 | 
			
		||||
/// plane and 10 units away from it.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square and a circle on the `XY` plane using offset.
 | 
			
		||||
/// const squareSketch = startSketchOn('XY')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch = startSketchOn(offsetPlane('XY', 150))
 | 
			
		||||
///     |> circle([0, 100], 50, %)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, circleSketch])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square and a circle on the `XZ` plane using offset.
 | 
			
		||||
/// const squareSketch = startSketchOn('XZ')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch = startSketchOn(offsetPlane('XZ', 150))
 | 
			
		||||
///     |> circle([0, 100], 50, %)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, circleSketch])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square and a circle on the `YZ` plane using offset.
 | 
			
		||||
/// const squareSketch = startSketchOn('YZ')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch = startSketchOn(offsetPlane('YZ', 150))
 | 
			
		||||
///     |> circle([0, 100], 50, %)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, circleSketch])
 | 
			
		||||
/// ```
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// // Loft a square and a circle on the `-XZ` plane using offset.
 | 
			
		||||
/// const squareSketch = startSketchOn('-XZ')
 | 
			
		||||
///     |> startProfileAt([-100, 200], %)
 | 
			
		||||
///     |> line([200, 0], %)
 | 
			
		||||
///     |> line([0, -200], %)
 | 
			
		||||
///     |> line([-200, 0], %)
 | 
			
		||||
///     |> lineTo([profileStartX(%), profileStartY(%)], %)
 | 
			
		||||
///     |> close(%)
 | 
			
		||||
///
 | 
			
		||||
/// const circleSketch = startSketchOn(offsetPlane('-XZ', -150))
 | 
			
		||||
///     |> circle([0, 100], 50, %)
 | 
			
		||||
///
 | 
			
		||||
/// loft([squareSketch, circleSketch])
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "offsetPlane",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_offset_plane(std_plane: StandardPlane, offset: f64) -> Result<PlaneData, KclError> {
 | 
			
		||||
    // Convert to the plane type.
 | 
			
		||||
    let plane_data: PlaneData = std_plane.into();
 | 
			
		||||
    // Convert to a plane.
 | 
			
		||||
    let mut plane = Plane::from(plane_data);
 | 
			
		||||
 | 
			
		||||
    match std_plane {
 | 
			
		||||
        StandardPlane::XY => {
 | 
			
		||||
            plane.origin.z += offset;
 | 
			
		||||
        }
 | 
			
		||||
        StandardPlane::XZ => {
 | 
			
		||||
            plane.origin.y -= offset;
 | 
			
		||||
        }
 | 
			
		||||
        StandardPlane::YZ => {
 | 
			
		||||
            plane.origin.x += offset;
 | 
			
		||||
        }
 | 
			
		||||
        StandardPlane::NegXY => {
 | 
			
		||||
            plane.origin.z -= offset;
 | 
			
		||||
        }
 | 
			
		||||
        StandardPlane::NegXZ => {
 | 
			
		||||
            plane.origin.y += offset;
 | 
			
		||||
        }
 | 
			
		||||
        StandardPlane::NegYZ => {
 | 
			
		||||
            plane.origin.x -= offset;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(PlaneData::Plane {
 | 
			
		||||
        origin: Box::new(plane.origin),
 | 
			
		||||
        x_axis: Box::new(plane.x_axis),
 | 
			
		||||
        y_axis: Box::new(plane.y_axis),
 | 
			
		||||
        z_axis: Box::new(plane.z_axis),
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								src/wasm-lib/kcl/tests/outputs/serial_test_example_loft0.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 99 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								src/wasm-lib/kcl/tests/outputs/serial_test_example_loft1.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 126 KiB  | 
| 
		 After Width: | Height: | Size: 98 KiB  | 
| 
		 After Width: | Height: | Size: 64 KiB  | 
| 
		 After Width: | Height: | Size: 96 KiB  | 
| 
		 After Width: | Height: | Size: 56 KiB  | 
| 
		 After Width: | Height: | Size: 56 KiB  |