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>>),
|
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.
|
/// A extrude group or a group of extrude groups.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -198,6 +207,15 @@ pub enum ExtrudeGroupSet {
|
|||||||
ExtrudeGroups(Vec<Box<ExtrudeGroup>>),
|
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.
|
/// Data for an imported geometry.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -298,6 +316,34 @@ pub struct UserVal {
|
|||||||
pub meta: Vec<Metadata>,
|
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 =
|
pub type MemoryFunction =
|
||||||
fn(
|
fn(
|
||||||
s: Vec<MemoryItem>,
|
s: Vec<MemoryItem>,
|
||||||
@ -413,6 +459,84 @@ impl MemoryItem {
|
|||||||
};
|
};
|
||||||
func(args, memory, expression.clone(), meta.clone(), ctx).await
|
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.
|
/// A sketch group is a collection of paths.
|
||||||
|
|||||||
@ -25,14 +25,15 @@ use lazy_static::lazy_static;
|
|||||||
use parse_display::{Display, FromStr};
|
use parse_display::{Display, FromStr};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::parse_json_number_as_f64,
|
ast::types::parse_json_number_as_f64,
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{
|
executor::{
|
||||||
ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryItem, Metadata, SketchGroup, SketchGroupSet,
|
ExecutorContext, ExtrudeGroup, ExtrudeGroupSet, MemoryFunction, MemoryItem, Metadata, SketchGroup,
|
||||||
SketchSurface, SourceRange,
|
SketchGroupSet, SketchSurface, SourceRange,
|
||||||
},
|
},
|
||||||
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
|
std::{kcl_stdlib::KclStdLibFn, sketch::SketchOnFaceTag},
|
||||||
};
|
};
|
||||||
@ -84,6 +85,7 @@ lazy_static! {
|
|||||||
Box::new(crate::std::patterns::PatternLinear3D),
|
Box::new(crate::std::patterns::PatternLinear3D),
|
||||||
Box::new(crate::std::patterns::PatternCircular2D),
|
Box::new(crate::std::patterns::PatternCircular2D),
|
||||||
Box::new(crate::std::patterns::PatternCircular3D),
|
Box::new(crate::std::patterns::PatternCircular3D),
|
||||||
|
Box::new(crate::std::patterns::Pattern),
|
||||||
Box::new(crate::std::chamfer::Chamfer),
|
Box::new(crate::std::chamfer::Chamfer),
|
||||||
Box::new(crate::std::fillet::Fillet),
|
Box::new(crate::std::fillet::Fillet),
|
||||||
Box::new(crate::std::fillet::GetOppositeEdge),
|
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> {
|
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.
|
// Iterate over our args, the first argument should be a UserVal with a string value.
|
||||||
// The second argument should be a SketchGroup.
|
// The second argument should be a SketchGroup.
|
||||||
@ -437,19 +474,7 @@ impl Args {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sketch_set = if let MemoryItem::SketchGroup(sg) = first_value {
|
let sketch_set = first_value.as_sketch_group_set(self.source_range)?;
|
||||||
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 second_value = self.args.get(1).ok_or_else(|| {
|
let second_value = self.args.get(1).ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
@ -672,19 +697,7 @@ impl Args {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
|
let sketch_set = second_value.as_sketch_group_set(self.source_range)?;
|
||||||
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],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((data, sketch_set))
|
Ok((data, sketch_set))
|
||||||
}
|
}
|
||||||
@ -953,19 +966,7 @@ impl Args {
|
|||||||
})
|
})
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
|
let sketch_set = second_value.as_sketch_group_set(self.source_range)?;
|
||||||
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],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((number, sketch_set))
|
Ok((number, sketch_set))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,13 +5,38 @@ use derive_docs::stdlib;
|
|||||||
use kittycad::types::ModelingCmd;
|
use kittycad::types::ModelingCmd;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
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},
|
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.
|
/// Data for a linear pattern on a 2D sketch.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[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.
|
/// A linear pattern on a 2D sketch.
|
||||||
pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
|
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()?;
|
let (data, sketch_group_set): (LinearPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
|
||||||
|
|
||||||
if data.axis == [0.0, 0.0] {
|
if data.axis == [0.0, 0.0] {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
message:
|
message: CANNOT_USE_ZERO_VECTOR.to_string(),
|
||||||
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
|
||||||
.to_string(),
|
|
||||||
source_ranges: vec![args.source_range],
|
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 })
|
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.
|
/// A linear pattern on a 2D sketch.
|
||||||
///
|
///
|
||||||
/// ```no_run
|
/// ```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);
|
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")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_lego() {
|
async fn serial_test_lego() {
|
||||||
let code = include_str!("inputs/lego.kcl");
|
let code = include_str!("inputs/lego.kcl");
|
||||||
|
|||||||
Reference in New Issue
Block a user