Define transform patterns
Defines a `pattern` stdlib fn and parses args for it. TODO: The actual body of the `pattern` stdlib fn.
This commit is contained in:
		@ -189,6 +189,15 @@ pub enum SketchGroupSet {
 | 
			
		||||
    SketchGroups(Vec<Box<SketchGroup>>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SketchGroupSet {
 | 
			
		||||
    pub fn ids(&self) -> Vec<uuid::Uuid> {
 | 
			
		||||
        match self {
 | 
			
		||||
            SketchGroupSet::SketchGroup(s) => vec![s.id],
 | 
			
		||||
            SketchGroupSet::SketchGroups(s) => s.iter().map(|s| s.id).collect(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A extrude group or a group of extrude groups.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -198,6 +207,15 @@ pub enum ExtrudeGroupSet {
 | 
			
		||||
    ExtrudeGroups(Vec<Box<ExtrudeGroup>>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ExtrudeGroupSet {
 | 
			
		||||
    pub fn ids(&self) -> Vec<uuid::Uuid> {
 | 
			
		||||
        match self {
 | 
			
		||||
            ExtrudeGroupSet::ExtrudeGroup(s) => vec![s.id],
 | 
			
		||||
            ExtrudeGroupSet::ExtrudeGroups(s) => s.iter().map(|s| s.id).collect(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Data for an imported geometry.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -298,6 +316,34 @@ pub struct UserVal {
 | 
			
		||||
    pub meta: Vec<Metadata>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wrap MemoryFunction to give it (shitty) JSON schema.
 | 
			
		||||
pub struct MemoryFunctionWrapper<'a> {
 | 
			
		||||
    pub inner: &'a MemoryFunction,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> From<&'a MemoryFunction> for MemoryFunctionWrapper<'a> {
 | 
			
		||||
    fn from(inner: &'a MemoryFunction) -> Self {
 | 
			
		||||
        Self { inner }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> From<MemoryFunctionWrapper<'a>> for &'a MemoryFunction {
 | 
			
		||||
    fn from(value: MemoryFunctionWrapper<'a>) -> Self {
 | 
			
		||||
        value.inner
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'a> JsonSchema for MemoryFunctionWrapper<'a> {
 | 
			
		||||
    fn schema_name() -> String {
 | 
			
		||||
        "Function".to_owned()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
 | 
			
		||||
        // TODO: Actually generate a reasonable schema.
 | 
			
		||||
        gen.subschema_for::<()>()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub type MemoryFunction =
 | 
			
		||||
    fn(
 | 
			
		||||
        s: Vec<MemoryItem>,
 | 
			
		||||
@ -413,6 +459,84 @@ impl MemoryItem {
 | 
			
		||||
        };
 | 
			
		||||
        func(args, memory, expression.clone(), meta.clone(), ctx).await
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn as_user_val(&self) -> Option<&UserVal> {
 | 
			
		||||
        if let MemoryItem::UserVal(x) = self {
 | 
			
		||||
            Some(x)
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this value is of type function, return it.
 | 
			
		||||
    pub fn get_function(&self, source_ranges: Vec<SourceRange>) -> Result<&MemoryFunction, KclError> {
 | 
			
		||||
        let MemoryItem::Function {
 | 
			
		||||
            func,
 | 
			
		||||
            expression,
 | 
			
		||||
            meta: _,
 | 
			
		||||
        } = &self
 | 
			
		||||
        else {
 | 
			
		||||
            return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "not a in memory function".to_string(),
 | 
			
		||||
                source_ranges,
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        func.as_ref().ok_or_else(|| {
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: format!("Not a function: {:?}", expression),
 | 
			
		||||
                source_ranges,
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this value is of type u32, return it.
 | 
			
		||||
    pub fn get_u32(&self, source_ranges: Vec<SourceRange>) -> Result<u32, KclError> {
 | 
			
		||||
        let err = KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message: "Expected an integer >= 0".to_owned(),
 | 
			
		||||
            source_ranges,
 | 
			
		||||
        });
 | 
			
		||||
        self.as_user_val()
 | 
			
		||||
            .and_then(|uv| uv.value.as_number())
 | 
			
		||||
            .and_then(|n| n.as_u64())
 | 
			
		||||
            .and_then(|n| u32::try_from(n).ok())
 | 
			
		||||
            .ok_or(err)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this contains a sketch group set, return it.
 | 
			
		||||
    pub(crate) fn as_sketch_group_set(&self, sr: SourceRange) -> Result<SketchGroupSet, KclError> {
 | 
			
		||||
        let sketch_set = if let MemoryItem::SketchGroup(sg) = self {
 | 
			
		||||
            SketchGroupSet::SketchGroup(sg.clone())
 | 
			
		||||
        } else if let MemoryItem::SketchGroups { value } = self {
 | 
			
		||||
            SketchGroupSet::SketchGroups(value.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected a SketchGroup or Vector of SketchGroups as this argument, found {:?}",
 | 
			
		||||
                    self,
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![sr],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        Ok(sketch_set)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// If this contains an extrude group set, return it.
 | 
			
		||||
    pub(crate) fn as_extrude_group_set(&self, sr: SourceRange) -> Result<ExtrudeGroupSet, KclError> {
 | 
			
		||||
        let sketch_set = if let MemoryItem::ExtrudeGroup(sg) = self {
 | 
			
		||||
            ExtrudeGroupSet::ExtrudeGroup(sg.clone())
 | 
			
		||||
        } else if let MemoryItem::ExtrudeGroups { value } = self {
 | 
			
		||||
            ExtrudeGroupSet::ExtrudeGroups(value.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected an ExtrudeGroup or Vector of ExtrudeGroups as this argument, found {:?}",
 | 
			
		||||
                    self,
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![sr],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        Ok(sketch_set)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A sketch group is a collection of paths.
 | 
			
		||||
 | 
			
		||||
@ -25,14 +25,15 @@ use lazy_static::lazy_static;
 | 
			
		||||
use parse_display::{Display, FromStr};
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::parse_json_number_as_f64,
 | 
			
		||||
    docs::StdLibFn,
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryItem, Metadata, SketchGroup, SketchGroupSet,
 | 
			
		||||
        SketchSurface, SourceRange,
 | 
			
		||||
        ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryFunction, MemoryItem, Metadata, SketchGroup,
 | 
			
		||||
        SketchGroupSet, SketchSurface, SourceRange,
 | 
			
		||||
    },
 | 
			
		||||
    std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
 | 
			
		||||
};
 | 
			
		||||
@ -84,6 +85,7 @@ lazy_static! {
 | 
			
		||||
        Box::new(crate::std::patterns::PatternLinear3D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternCircular2D),
 | 
			
		||||
        Box::new(crate::std::patterns::PatternCircular3D),
 | 
			
		||||
        Box::new(crate::std::patterns::Pattern),
 | 
			
		||||
        Box::new(crate::std::chamfer::Chamfer),
 | 
			
		||||
        Box::new(crate::std::fillet::Fillet),
 | 
			
		||||
        Box::new(crate::std::fillet::GetOppositeEdge),
 | 
			
		||||
@ -387,6 +389,41 @@ impl Args {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Works with either 2D or 3D solids.
 | 
			
		||||
    fn get_pattern_args(&self) -> std::result::Result<(u32, &MemoryFunction, Vec<Uuid>), KclError> {
 | 
			
		||||
        let sr = vec![self.source_range];
 | 
			
		||||
        let mut args = self.args.iter();
 | 
			
		||||
        let num_repetitions = args.next().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: "Missing first argument (should be the number of repetitions)".to_owned(),
 | 
			
		||||
                source_ranges: sr.clone(),
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
        let num_repetitions = num_repetitions.get_u32(sr.clone())?;
 | 
			
		||||
        let transform = args.next().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: "Missing second argument (should be the transform function)".to_owned(),
 | 
			
		||||
                source_ranges: sr.clone(),
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
        let transform = transform.get_function(sr.clone())?;
 | 
			
		||||
        let sg = args.next().ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: "Missing third argument (should be a Sketch/ExtrudeGroup or an array of Sketch/ExtrudeGroups)"
 | 
			
		||||
                    .to_owned(),
 | 
			
		||||
                source_ranges: sr.clone(),
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
        let sketch_ids = sg.as_sketch_group_set(self.source_range);
 | 
			
		||||
        let extrude_ids = sg.as_extrude_group_set(self.source_range);
 | 
			
		||||
        let entity_ids = match (sketch_ids, extrude_ids) {
 | 
			
		||||
            (Ok(group), _) => group.ids(),
 | 
			
		||||
            (_, Ok(group)) => group.ids(),
 | 
			
		||||
            (Err(e), _) => return Err(e),
 | 
			
		||||
        };
 | 
			
		||||
        Ok((num_repetitions, transform, entity_ids))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn get_segment_name_sketch_group(&self) -> Result<(String, Box<SketchGroup>), KclError> {
 | 
			
		||||
        // Iterate over our args, the first argument should be a UserVal with a string value.
 | 
			
		||||
        // The second argument should be a SketchGroup.
 | 
			
		||||
@ -437,19 +474,7 @@ impl Args {
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let sketch_set = if let MemoryItem::SketchGroup(sg) = first_value {
 | 
			
		||||
            SketchGroupSet::SketchGroup(sg.clone())
 | 
			
		||||
        } else if let MemoryItem::SketchGroups { value } = first_value {
 | 
			
		||||
            SketchGroupSet::SketchGroups(value.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected a SketchGroup or Vector of SketchGroups as the first argument, found `{:?}`",
 | 
			
		||||
                    self.args
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        let sketch_set = first_value.as_sketch_group_set(self.source_range)?;
 | 
			
		||||
 | 
			
		||||
        let second_value = self.args.get(1).ok_or_else(|| {
 | 
			
		||||
            KclError::Type(KclErrorDetails {
 | 
			
		||||
@ -672,19 +697,7 @@ impl Args {
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
 | 
			
		||||
            SketchGroupSet::SketchGroup(sg.clone())
 | 
			
		||||
        } else if let MemoryItem::SketchGroups { value } = second_value {
 | 
			
		||||
            SketchGroupSet::SketchGroups(value.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected a SketchGroup or Vector of SketchGroups as the second argument, found `{:?}`",
 | 
			
		||||
                    self.args
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        let sketch_set = second_value.as_sketch_group_set(self.source_range)?;
 | 
			
		||||
 | 
			
		||||
        Ok((data, sketch_set))
 | 
			
		||||
    }
 | 
			
		||||
@ -953,19 +966,7 @@ impl Args {
 | 
			
		||||
            })
 | 
			
		||||
        })?;
 | 
			
		||||
 | 
			
		||||
        let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
 | 
			
		||||
            SketchGroupSet::SketchGroup(sg.clone())
 | 
			
		||||
        } else if let MemoryItem::SketchGroups { value } = second_value {
 | 
			
		||||
            SketchGroupSet::SketchGroups(value.clone())
 | 
			
		||||
        } else {
 | 
			
		||||
            return Err(KclError::Type(KclErrorDetails {
 | 
			
		||||
                message: format!(
 | 
			
		||||
                    "Expected a SketchGroup or Vector of SketchGroups as the second argument, found `{:?}`",
 | 
			
		||||
                    self.args
 | 
			
		||||
                ),
 | 
			
		||||
                source_ranges: vec![self.source_range],
 | 
			
		||||
            }));
 | 
			
		||||
        };
 | 
			
		||||
        let sketch_set = second_value.as_sketch_group_set(self.source_range)?;
 | 
			
		||||
 | 
			
		||||
        Ok((number, sketch_set))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -5,13 +5,38 @@ use derive_docs::stdlib;
 | 
			
		||||
use kittycad::types::ModelingCmd;
 | 
			
		||||
use schemars::JsonSchema;
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use uuid::Uuid;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::{ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
 | 
			
		||||
    executor::{
 | 
			
		||||
        ExtrudeGroup, ExtrudeGroupSet, Geometries, Geometry, MemoryFunctionWrapper, MemoryItem, Point3d, SketchGroup,
 | 
			
		||||
        SketchGroupSet,
 | 
			
		||||
    },
 | 
			
		||||
    std::{types::Uint, Args},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const CANNOT_USE_ZERO_VECTOR: &str =
 | 
			
		||||
    "The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place.";
 | 
			
		||||
 | 
			
		||||
/// How to change each element of a pattern.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct LinearTransform {
 | 
			
		||||
    /// Translate the replica this far along each dimension.
 | 
			
		||||
    /// Defaults to zero vector (i.e. same position as the original).
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub translate: Option<Point3d>,
 | 
			
		||||
    /// Scale the replica's size along each axis.
 | 
			
		||||
    /// Defaults to (1, 1, 1) (i.e. the same size as the original).
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub scale: Option<Point3d>,
 | 
			
		||||
    /// Whether to replicate the original solid in this instance.
 | 
			
		||||
    #[serde(default)]
 | 
			
		||||
    pub replicate: Option<bool>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Data for a linear pattern on a 2D sketch.
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -70,15 +95,29 @@ impl LinearPattern {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern, either 2D or 3D.
 | 
			
		||||
/// Each element in the pattern repeats a particular piece of geometry.
 | 
			
		||||
/// The repetitions can be transformed by the `transform` parameter.
 | 
			
		||||
pub async fn pattern(args: Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (num_repetitions, transform, entity_ids) = args.get_pattern_args()?;
 | 
			
		||||
 | 
			
		||||
    let sketch_groups = inner_pattern(
 | 
			
		||||
        num_repetitions,
 | 
			
		||||
        MemoryFunctionWrapper::from(transform),
 | 
			
		||||
        entity_ids,
 | 
			
		||||
        &args,
 | 
			
		||||
    )
 | 
			
		||||
    .await?;
 | 
			
		||||
    Ok(MemoryItem::SketchGroups { value: sketch_groups })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 2D sketch.
 | 
			
		||||
pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    let (data, sketch_group_set): (LinearPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
 | 
			
		||||
 | 
			
		||||
    if data.axis == [0.0, 0.0] {
 | 
			
		||||
        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
            message:
 | 
			
		||||
                "The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
 | 
			
		||||
                    .to_string(),
 | 
			
		||||
            message: CANNOT_USE_ZERO_VECTOR.to_string(),
 | 
			
		||||
            source_ranges: vec![args.source_range],
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
@ -87,6 +126,44 @@ pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
 | 
			
		||||
    Ok(MemoryItem::SketchGroups { value: sketch_groups })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 2D or 3D solid.
 | 
			
		||||
/// Each repetition of the pattern can be transformed (e.g. scaled, translated, hidden, etc).
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
/// The vase is 100 layers tall.
 | 
			
		||||
/// The 100 layers are replica of each other, with a slight transformation applied to each.
 | 
			
		||||
/// let vase = layer() |> pattern(100, transform, %)
 | 
			
		||||
/// // base radius
 | 
			
		||||
/// const r = 50
 | 
			
		||||
/// // layer height
 | 
			
		||||
/// const h = 10
 | 
			
		||||
/// // taper factor [0 - 1)
 | 
			
		||||
/// const t = 0.005
 | 
			
		||||
/// // Each layer is just a pretty thin cylinder.
 | 
			
		||||
/// fn layer = () => {
 | 
			
		||||
///   return startSketchOn("XY") // or some other plane idk
 | 
			
		||||
///     |> circle([0, 0], 1, %)
 | 
			
		||||
///     |> extrude(h, %)
 | 
			
		||||
/// // Change each replica's radius and shift it up the Z axis.
 | 
			
		||||
/// fn transform = (replicaId) => {
 | 
			
		||||
///   return {
 | 
			
		||||
///     translate: [0, 0, replicaId*10]
 | 
			
		||||
///     scale: r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))
 | 
			
		||||
///   }
 | 
			
		||||
/// }
 | 
			
		||||
/// ```
 | 
			
		||||
#[stdlib {
 | 
			
		||||
    name = "pattern",
 | 
			
		||||
}]
 | 
			
		||||
async fn inner_pattern<'a>(
 | 
			
		||||
    _num_repetitions: u32,
 | 
			
		||||
    _transform: MemoryFunctionWrapper<'a>,
 | 
			
		||||
    _ids: Vec<Uuid>,
 | 
			
		||||
    _args: &'a Args,
 | 
			
		||||
) -> Result<Vec<Box<SketchGroup>>, KclError> {
 | 
			
		||||
    todo!()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// A linear pattern on a 2D sketch.
 | 
			
		||||
///
 | 
			
		||||
/// ```no_run
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										31
									
								
								src/wasm-lib/tests/executor/inputs/pattern_vase.kcl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/wasm-lib/tests/executor/inputs/pattern_vase.kcl
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,31 @@
 | 
			
		||||
// Defines a vase.
 | 
			
		||||
// The vase is made of 100 layers.
 | 
			
		||||
 | 
			
		||||
// Parameters
 | 
			
		||||
const r = 50    // base radius
 | 
			
		||||
const h = 10    // layer height
 | 
			
		||||
const t = 0.005 // taper factor [0-1)
 | 
			
		||||
 | 
			
		||||
// Defines how to modify each layer of the vase.
 | 
			
		||||
// Each replica is shifted up the Z axis, and has a smoothly-varying radius
 | 
			
		||||
fn transform = (replicaId) => {
 | 
			
		||||
  return {
 | 
			
		||||
    translate: [0, 0, replicaId * 10],
 | 
			
		||||
    scale: r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Each layer is just a pretty thin cylinder with a fillet.
 | 
			
		||||
fn layer = () => {
 | 
			
		||||
  return startSketchOn("XY") // or some other plane idk
 | 
			
		||||
    |> circle([0, 0], 1, %, 'tag1')
 | 
			
		||||
    |> extrude(h, %)
 | 
			
		||||
    |> fillet({
 | 
			
		||||
           radius: h / 2.01,
 | 
			
		||||
           tags: ["tag1", getOppositeEdge("tag1", %)]
 | 
			
		||||
       }, %)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The vase is 100 layers tall.
 | 
			
		||||
// The 100 layers are replica of each other, with a slight transformation applied to each.
 | 
			
		||||
let vase = layer() |> pattern(100, transform, %)
 | 
			
		||||
@ -92,6 +92,13 @@ async fn serial_test_riddle_small() {
 | 
			
		||||
    twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, 0.999);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
async fn serial_test_pattern_vase() {
 | 
			
		||||
    let code = include_str!("inputs/pattern_vase.kcl");
 | 
			
		||||
    let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap();
 | 
			
		||||
    twenty_twenty::assert_image("tests/executor/outputs/pattern_vase.png", &result, 0.999);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
async fn serial_test_lego() {
 | 
			
		||||
    let code = include_str!("inputs/lego.kcl");
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user