Declare pattern transform functions in KCL (#7057)
* Declare pattern transform using KCL Signed-off-by: Nick Cameron <nrc@ncameron.org> * Boolean function param defaults Signed-off-by: Nick Cameron <nrc@ncameron.org> * Parse empty record types in fn types Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
title: "patternTransform2d"
|
title: "patternTransform2d"
|
||||||
subtitle: "Function in std::sketch"
|
subtitle: "Function in std::sketch"
|
||||||
excerpt: "Just like patternTransform, but works on 2D sketches not 3D solids."
|
excerpt: "Just like `patternTransform`, but works on 2D sketches not 3D solids."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Just like patternTransform, but works on 2D sketches not 3D solids.
|
Just like `patternTransform`, but works on 2D sketches not 3D solids.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
patternTransform2d(
|
patternTransform2d(
|
||||||
@sketches: [Sketch],
|
@sketches: [Sketch; 1+],
|
||||||
instances: number,
|
instances: number(_),
|
||||||
transform: FunctionSource,
|
transform: fn(number(_)): { },
|
||||||
useOriginal?: bool,
|
useOriginal?: boolean,
|
||||||
): [Sketch]
|
): [Sketch; 1+]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -22,14 +22,14 @@ patternTransform2d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | The sketch(es) to duplicate | Yes |
|
| `sketches` | [`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch) | The sketch(es) to duplicate. | Yes |
|
||||||
| `instances` | [`number`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
| `instances` | [`number(_)`](/docs/kcl-std/types/std-types-number) | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
||||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
| `transform` | [`fn(number(_)): { }`](/docs/kcl-std/types/std-types-fn) | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||||
| `useOriginal` | [`bool`](/docs/kcl-std/types/std-types-bool) | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
| `useOriginal` | `boolean` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`[Sketch]`](/docs/kcl-std/types/std-types-Sketch)
|
[`[Sketch; 1+]`](/docs/kcl-std/types/std-types-Sketch)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
@ -65,7 +65,7 @@ layout: manual
|
|||||||
* [`line`](/docs/kcl-std/line)
|
* [`line`](/docs/kcl-std/line)
|
||||||
* [`loft`](/docs/kcl-std/loft)
|
* [`loft`](/docs/kcl-std/loft)
|
||||||
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
|
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
|
||||||
* [`patternTransform2d`](/docs/kcl-std/patternTransform2d)
|
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||||
* [`polygon`](/docs/kcl-std/polygon)
|
* [`polygon`](/docs/kcl-std/polygon)
|
||||||
* [`profileStart`](/docs/kcl-std/profileStart)
|
* [`profileStart`](/docs/kcl-std/profileStart)
|
||||||
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
||||||
@ -94,7 +94,7 @@ layout: manual
|
|||||||
* [`intersect`](/docs/kcl-std/intersect)
|
* [`intersect`](/docs/kcl-std/intersect)
|
||||||
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
|
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
|
||||||
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
|
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
|
||||||
* [`patternTransform`](/docs/kcl-std/patternTransform)
|
* [`patternTransform`](/docs/kcl-std/functions/std-solid-patternTransform)
|
||||||
* [`shell`](/docs/kcl-std/functions/std-solid-shell)
|
* [`shell`](/docs/kcl-std/functions/std-solid-shell)
|
||||||
* [`subtract`](/docs/kcl-std/subtract)
|
* [`subtract`](/docs/kcl-std/subtract)
|
||||||
* [`union`](/docs/kcl-std/union)
|
* [`union`](/docs/kcl-std/union)
|
||||||
|
@ -30,7 +30,7 @@ This module contains functions for creating and manipulating sketches, and makin
|
|||||||
* [`line`](/docs/kcl-std/line)
|
* [`line`](/docs/kcl-std/line)
|
||||||
* [`loft`](/docs/kcl-std/loft)
|
* [`loft`](/docs/kcl-std/loft)
|
||||||
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
|
* [`patternCircular2d`](/docs/kcl-std/patternCircular2d)
|
||||||
* [`patternTransform2d`](/docs/kcl-std/patternTransform2d)
|
* [`patternTransform2d`](/docs/kcl-std/functions/std-sketch-patternTransform2d)
|
||||||
* [`polygon`](/docs/kcl-std/polygon)
|
* [`polygon`](/docs/kcl-std/polygon)
|
||||||
* [`profileStart`](/docs/kcl-std/profileStart)
|
* [`profileStart`](/docs/kcl-std/profileStart)
|
||||||
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
* [`profileStartX`](/docs/kcl-std/profileStartX)
|
||||||
|
@ -18,7 +18,7 @@ This module contains functions for modifying solids, e.g., by adding a fillet or
|
|||||||
* [`intersect`](/docs/kcl-std/intersect)
|
* [`intersect`](/docs/kcl-std/intersect)
|
||||||
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
|
* [`patternCircular3d`](/docs/kcl-std/patternCircular3d)
|
||||||
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
|
* [`patternLinear3d`](/docs/kcl-std/patternLinear3d)
|
||||||
* [`patternTransform`](/docs/kcl-std/patternTransform)
|
* [`patternTransform`](/docs/kcl-std/functions/std-solid-patternTransform)
|
||||||
* [`shell`](/docs/kcl-std/functions/std-solid-shell)
|
* [`shell`](/docs/kcl-std/functions/std-solid-shell)
|
||||||
* [`subtract`](/docs/kcl-std/subtract)
|
* [`subtract`](/docs/kcl-std/subtract)
|
||||||
* [`union`](/docs/kcl-std/union)
|
* [`union`](/docs/kcl-std/union)
|
||||||
|
16112
docs/kcl-std/std.json
@ -155,9 +155,8 @@ impl RuntimeType {
|
|||||||
.map(RuntimeType::Union),
|
.map(RuntimeType::Union),
|
||||||
Type::Object { properties } => properties
|
Type::Object { properties } => properties
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.map(|(id, ty)| {
|
||||||
RuntimeType::from_parsed(p.type_.unwrap().inner, exec_state, source_range)
|
RuntimeType::from_parsed(ty.inner, exec_state, source_range).map(|ty| (id.name.clone(), ty))
|
||||||
.map(|ty| (p.identifier.inner.name, ty))
|
|
||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, CompilationError>>()
|
.collect::<Result<Vec<_>, CompilationError>>()
|
||||||
.map(RuntimeType::Object),
|
.map(RuntimeType::Object),
|
||||||
|
@ -212,8 +212,9 @@ impl Type {
|
|||||||
Type::Object { properties } => {
|
Type::Object { properties } => {
|
||||||
hasher.update(b"FnArgType::Object");
|
hasher.update(b"FnArgType::Object");
|
||||||
hasher.update(properties.len().to_ne_bytes());
|
hasher.update(properties.len().to_ne_bytes());
|
||||||
for prop in properties.iter_mut() {
|
for (id, ty) in properties.iter_mut() {
|
||||||
hasher.update(prop.compute_digest());
|
hasher.update(id.compute_digest());
|
||||||
|
hasher.update(ty.compute_digest());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3315,7 +3315,7 @@ pub enum Type {
|
|||||||
},
|
},
|
||||||
// An object type.
|
// An object type.
|
||||||
Object {
|
Object {
|
||||||
properties: Vec<Parameter>,
|
properties: Vec<(Node<Identifier>, Node<Type>)>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3348,10 +3348,8 @@ impl fmt::Display for Type {
|
|||||||
} else {
|
} else {
|
||||||
write!(f, ",")?;
|
write!(f, ",")?;
|
||||||
}
|
}
|
||||||
write!(f, " {}:", p.identifier.name)?;
|
write!(f, " {}:", p.0.name)?;
|
||||||
if let Some(ty) = &p.type_ {
|
write!(f, " {}", p.1)?;
|
||||||
write!(f, " {}", ty.inner)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
write!(f, " }}")
|
write!(f, " }}")
|
||||||
}
|
}
|
||||||
@ -3988,7 +3986,7 @@ cylinder = startSketchOn(-XZ)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_parse_type_args_object_on_functions() {
|
async fn test_parse_type_args_object_on_functions() {
|
||||||
let some_program_string = r#"fn thing(arg0: [number], arg1: {thing: number, things: [string], more?: string}, tag?: string) {
|
let some_program_string = r#"fn thing(arg0: [number], arg1: {thing: number, things: [string], more: string}, tag?: string) {
|
||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
@ -4015,8 +4013,8 @@ cylinder = startSketchOn(-XZ)
|
|||||||
params[1].type_.as_ref().unwrap().inner,
|
params[1].type_.as_ref().unwrap().inner,
|
||||||
Type::Object {
|
Type::Object {
|
||||||
properties: vec![
|
properties: vec![
|
||||||
Parameter {
|
(
|
||||||
identifier: Node::new(
|
Node::new(
|
||||||
Identifier {
|
Identifier {
|
||||||
name: "thing".to_owned(),
|
name: "thing".to_owned(),
|
||||||
digest: None,
|
digest: None,
|
||||||
@ -4025,18 +4023,15 @@ cylinder = startSketchOn(-XZ)
|
|||||||
37,
|
37,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(
|
Node::new(
|
||||||
Type::Primitive(PrimitiveType::Number(NumericSuffix::None)),
|
Type::Primitive(PrimitiveType::Number(NumericSuffix::None)),
|
||||||
39,
|
39,
|
||||||
45,
|
45,
|
||||||
module_id
|
module_id
|
||||||
)),
|
),
|
||||||
default_value: None,
|
),
|
||||||
labeled: true,
|
(
|
||||||
digest: None,
|
Node::new(
|
||||||
},
|
|
||||||
Parameter {
|
|
||||||
identifier: Node::new(
|
|
||||||
Identifier {
|
Identifier {
|
||||||
name: "things".to_owned(),
|
name: "things".to_owned(),
|
||||||
digest: None,
|
digest: None,
|
||||||
@ -4045,7 +4040,7 @@ cylinder = startSketchOn(-XZ)
|
|||||||
53,
|
53,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(
|
Node::new(
|
||||||
Type::Array {
|
Type::Array {
|
||||||
ty: Box::new(Type::Primitive(PrimitiveType::String)),
|
ty: Box::new(Type::Primitive(PrimitiveType::String)),
|
||||||
len: ArrayLen::None
|
len: ArrayLen::None
|
||||||
@ -4053,13 +4048,10 @@ cylinder = startSketchOn(-XZ)
|
|||||||
56,
|
56,
|
||||||
62,
|
62,
|
||||||
module_id
|
module_id
|
||||||
)),
|
)
|
||||||
default_value: None,
|
),
|
||||||
labeled: true,
|
(
|
||||||
digest: None
|
Node::new(
|
||||||
},
|
|
||||||
Parameter {
|
|
||||||
identifier: Node::new(
|
|
||||||
Identifier {
|
Identifier {
|
||||||
name: "more".to_owned(),
|
name: "more".to_owned(),
|
||||||
digest: None
|
digest: None
|
||||||
@ -4068,11 +4060,8 @@ cylinder = startSketchOn(-XZ)
|
|||||||
69,
|
69,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(Type::Primitive(PrimitiveType::String), 72, 78, module_id)),
|
Node::new(Type::Primitive(PrimitiveType::String), 71, 77, module_id),
|
||||||
labeled: true,
|
)
|
||||||
default_value: Some(DefaultParamVal::none()),
|
|
||||||
digest: None
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -436,7 +436,7 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
|
fn bool_value(i: &mut TokenSlice) -> PResult<Node<Literal>> {
|
||||||
let (value, token) = any
|
let (value, token) = any
|
||||||
.try_map(|token: Token| match token.token_type {
|
.try_map(|token: Token| match token.token_type {
|
||||||
TokenType::Keyword if token.value == "true" => Ok((true, token)),
|
TokenType::Keyword if token.value == "true" => Ok((true, token)),
|
||||||
@ -448,7 +448,7 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
|
|||||||
})
|
})
|
||||||
.context(expected("a boolean literal (either true or false)"))
|
.context(expected("a boolean literal (either true or false)"))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
Ok(Box::new(Node::new(
|
Ok(Node::new(
|
||||||
Literal {
|
Literal {
|
||||||
value: LiteralValue::Bool(value),
|
value: LiteralValue::Bool(value),
|
||||||
raw: value.to_string(),
|
raw: value.to_string(),
|
||||||
@ -457,11 +457,11 @@ fn bool_value(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
|
|||||||
token.start,
|
token.start,
|
||||||
token.end,
|
token.end,
|
||||||
token.module_id,
|
token.module_id,
|
||||||
)))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn literal(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
|
fn literal(i: &mut TokenSlice) -> PResult<BoxNode<Literal>> {
|
||||||
alt((string_literal, unsigned_number_literal))
|
alt((string_literal, unsigned_number_literal, bool_value))
|
||||||
.map(Box::new)
|
.map(Box::new)
|
||||||
.context(expected("a KCL literal, like 'myPart' or 3"))
|
.context(expected("a KCL literal, like 'myPart' or 3"))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
@ -2051,7 +2051,7 @@ fn unnecessarily_bracketed(i: &mut TokenSlice) -> PResult<Expr> {
|
|||||||
fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
|
fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
|
||||||
alt((
|
alt((
|
||||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||||
bool_value.map(Expr::Literal),
|
bool_value.map(Box::new).map(Expr::Literal),
|
||||||
tag.map(Box::new).map(Expr::TagDeclarator),
|
tag.map(Box::new).map(Expr::TagDeclarator),
|
||||||
literal.map(Expr::Literal),
|
literal.map(Expr::Literal),
|
||||||
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
||||||
@ -2070,7 +2070,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
|
|||||||
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
|
fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
|
||||||
let mut expr = alt((
|
let mut expr = alt((
|
||||||
unary_expression.map(Box::new).map(Expr::UnaryExpression),
|
unary_expression.map(Box::new).map(Expr::UnaryExpression),
|
||||||
bool_value.map(Expr::Literal),
|
bool_value.map(Box::new).map(Expr::Literal),
|
||||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||||
literal.map(Expr::Literal),
|
literal.map(Expr::Literal),
|
||||||
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
||||||
@ -2780,20 +2780,24 @@ fn labeled_argument(i: &mut TokenSlice) -> PResult<LabeledArg> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn record_ty_field(i: &mut TokenSlice) -> PResult<(Node<Identifier>, Node<Type>)> {
|
||||||
|
(identifier, colon, opt(whitespace), type_)
|
||||||
|
.map(|(id, _, _, ty)| (id, ty))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a type in various positions.
|
/// Parse a type in various positions.
|
||||||
fn type_(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
fn type_(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
||||||
let type_ = alt((
|
let type_ = alt((
|
||||||
// Object types
|
// Object types
|
||||||
// TODO it is buggy to treat object fields like parameters since the parameters parser assumes a terminating `)`.
|
(
|
||||||
(open_brace, parameters, close_brace).try_map(|(open, params, close)| {
|
open_brace,
|
||||||
for p in ¶ms {
|
opt(whitespace),
|
||||||
if p.type_.is_none() {
|
separated(0.., record_ty_field, comma_sep),
|
||||||
return Err(CompilationError::fatal(
|
opt(whitespace),
|
||||||
p.identifier.as_source_range(),
|
close_brace,
|
||||||
"Missing type for field in record type",
|
)
|
||||||
));
|
.try_map(|(open, _, params, _, close)| {
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Node::new(
|
Ok(Node::new(
|
||||||
Type::Object { properties: params },
|
Type::Object { properties: params },
|
||||||
open.start,
|
open.start,
|
||||||
@ -4866,6 +4870,15 @@ let myBox = box(p=[0,0], h=-3, l=-16, w=-10)
|
|||||||
|> line(%, tag = $var01)"#;
|
|> line(%, tag = $var01)"#;
|
||||||
assert_no_err(some_program_string);
|
assert_no_err(some_program_string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_param_bool_default() {
|
||||||
|
let some_program_string = r#"fn patternTransform(
|
||||||
|
use_original?: boolean = false,
|
||||||
|
) {}"#;
|
||||||
|
assert_no_err(some_program_string);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_function_types() {
|
fn parse_function_types() {
|
||||||
let code = r#"foo = x: fn
|
let code = r#"foo = x: fn
|
||||||
@ -4875,6 +4888,8 @@ fn foo(x: fn(a, b: number(mm), c: d): number(Angle)): fn { return 0 }
|
|||||||
type fn
|
type fn
|
||||||
type foo = fn
|
type foo = fn
|
||||||
type foo = fn(a: string, b: { f: fn(): any })
|
type foo = fn(a: string, b: { f: fn(): any })
|
||||||
|
type foo = fn(a: string, b: {})
|
||||||
|
type foo = fn(a: string, b: { })
|
||||||
type foo = fn([fn])
|
type foo = fn([fn])
|
||||||
type foo = fn(fn, f: fn(number(_))): [fn([any]): string]
|
type foo = fn(fn, f: fn(number(_))): [fn([any]): string]
|
||||||
"#;
|
"#;
|
||||||
|
@ -80,8 +80,6 @@ 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::PatternTransform),
|
|
||||||
Box::new(crate::std::patterns::PatternTransform2D),
|
|
||||||
Box::new(crate::std::edge::GetOppositeEdge),
|
Box::new(crate::std::edge::GetOppositeEdge),
|
||||||
Box::new(crate::std::edge::GetNextAdjacentEdge),
|
Box::new(crate::std::edge::GetNextAdjacentEdge),
|
||||||
Box::new(crate::std::edge::GetPreviousAdjacentEdge),
|
Box::new(crate::std::edge::GetPreviousAdjacentEdge),
|
||||||
@ -280,6 +278,14 @@ pub(crate) fn std_fn(path: &str, fn_name: &str) -> (crate::std::StdFn, StdFnProp
|
|||||||
|e, a| Box::pin(crate::std::clone::clone(e, a)),
|
|e, a| Box::pin(crate::std::clone::clone(e, a)),
|
||||||
StdFnProps::default("std::clone").include_in_feature_tree(),
|
StdFnProps::default("std::clone").include_in_feature_tree(),
|
||||||
),
|
),
|
||||||
|
("solid", "patternTransform") => (
|
||||||
|
|e, a| Box::pin(crate::std::patterns::pattern_transform(e, a)),
|
||||||
|
StdFnProps::default("std::solid::patternTransform").include_in_feature_tree(),
|
||||||
|
),
|
||||||
|
("sketch", "patternTransform2d") => (
|
||||||
|
|e, a| Box::pin(crate::std::patterns::pattern_transform_2d(e, a)),
|
||||||
|
StdFnProps::default("std::sketch::patternTransform2d"),
|
||||||
|
),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,202 +57,6 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
|||||||
Ok(sketches.into())
|
Ok(sketches.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repeat a 3-dimensional solid, changing it each time.
|
|
||||||
///
|
|
||||||
/// Replicates the 3D solid, applying a transformation function to each replica.
|
|
||||||
/// Transformation function could alter rotation, scale, visibility, position, etc.
|
|
||||||
///
|
|
||||||
/// The `patternTransform` call itself takes a number for how many total instances of
|
|
||||||
/// the shape should be. For example, if you use a circle with `patternTransform(instances = 4, transform = f)`
|
|
||||||
/// then there will be 4 circles: the original, and 3 created by replicating the original and
|
|
||||||
/// calling the transform function on each.
|
|
||||||
///
|
|
||||||
/// The transform function takes a single parameter: an integer representing which
|
|
||||||
/// number replication the transform is for. E.g. the first replica to be transformed
|
|
||||||
/// will be passed the argument `1`. This simplifies your math: the transform function can
|
|
||||||
/// rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
|
|
||||||
///
|
|
||||||
/// The transform function returns a transform object. All properties of the object are optional,
|
|
||||||
/// they each default to "no change". So the overall transform object defaults to "no change" too.
|
|
||||||
/// Its properties are:
|
|
||||||
///
|
|
||||||
/// - `translate` (3D point)
|
|
||||||
///
|
|
||||||
/// Translates the replica, moving its position in space.
|
|
||||||
///
|
|
||||||
/// - `replicate` (bool)
|
|
||||||
///
|
|
||||||
/// If false, this ID will not actually copy the object. It'll be skipped.
|
|
||||||
///
|
|
||||||
/// - `scale` (3D point)
|
|
||||||
///
|
|
||||||
/// Stretches the object, multiplying its width in the given dimension by the point's component in
|
|
||||||
/// that direction.
|
|
||||||
///
|
|
||||||
/// - `rotation` (object, with the following properties)
|
|
||||||
///
|
|
||||||
/// - `rotation.axis` (a 3D point, defaults to the Z axis)
|
|
||||||
///
|
|
||||||
/// - `rotation.angle` (number of degrees)
|
|
||||||
///
|
|
||||||
/// - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// // Each instance will be shifted along the X axis.
|
|
||||||
/// fn transform(@id) {
|
|
||||||
/// return { translate = [4 * id, 0, 0] }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Sketch 4 cylinders.
|
|
||||||
/// sketch001 = startSketchOn(XZ)
|
|
||||||
/// |> circle(center = [0, 0], radius = 2)
|
|
||||||
/// |> extrude(length = 5)
|
|
||||||
/// |> patternTransform(instances = 4, transform = transform)
|
|
||||||
/// ```
|
|
||||||
/// ```no_run
|
|
||||||
/// // Each instance will be shifted along the X axis,
|
|
||||||
/// // with a gap between the original (at x = 0) and the first replica
|
|
||||||
/// // (at x = 8). This is because `id` starts at 1.
|
|
||||||
/// fn transform(@id) {
|
|
||||||
/// return { translate = [4 * (1+id), 0, 0] }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// sketch001 = startSketchOn(XZ)
|
|
||||||
/// |> circle(center = [0, 0], radius = 2)
|
|
||||||
/// |> extrude(length = 5)
|
|
||||||
/// |> patternTransform(instances = 4, transform = transform)
|
|
||||||
/// ```
|
|
||||||
/// ```no_run
|
|
||||||
/// fn cube(length, center) {
|
|
||||||
/// l = length/2
|
|
||||||
/// x = center[0]
|
|
||||||
/// y = center[1]
|
|
||||||
/// p0 = [-l + x, -l + y]
|
|
||||||
/// p1 = [-l + x, l + y]
|
|
||||||
/// p2 = [ l + x, l + y]
|
|
||||||
/// p3 = [ l + x, -l + y]
|
|
||||||
///
|
|
||||||
/// return startSketchOn(XY)
|
|
||||||
/// |> startProfile(at = p0)
|
|
||||||
/// |> line(endAbsolute = p1)
|
|
||||||
/// |> line(endAbsolute = p2)
|
|
||||||
/// |> line(endAbsolute = p3)
|
|
||||||
/// |> line(endAbsolute = p0)
|
|
||||||
/// |> close()
|
|
||||||
/// |> extrude(length = length)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// width = 20
|
|
||||||
/// fn transform(@i) {
|
|
||||||
/// return {
|
|
||||||
/// // Move down each time.
|
|
||||||
/// translate = [0, 0, -i * width],
|
|
||||||
/// // Make the cube longer, wider and flatter each time.
|
|
||||||
/// scale = [pow(1.1, exp = i), pow(1.1, exp = i), pow(0.9, exp = i)],
|
|
||||||
/// // Turn by 15 degrees each time.
|
|
||||||
/// rotation = {
|
|
||||||
/// angle = 15 * i,
|
|
||||||
/// origin = "local",
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// myCubes =
|
|
||||||
/// cube(length = width, center = [100,0])
|
|
||||||
/// |> patternTransform(instances = 25, transform = transform)
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// fn cube(length, center) {
|
|
||||||
/// l = length/2
|
|
||||||
/// x = center[0]
|
|
||||||
/// y = center[1]
|
|
||||||
/// p0 = [-l + x, -l + y]
|
|
||||||
/// p1 = [-l + x, l + y]
|
|
||||||
/// p2 = [ l + x, l + y]
|
|
||||||
/// p3 = [ l + x, -l + y]
|
|
||||||
///
|
|
||||||
/// return startSketchOn(XY)
|
|
||||||
/// |> startProfile(at = p0)
|
|
||||||
/// |> line(endAbsolute = p1)
|
|
||||||
/// |> line(endAbsolute = p2)
|
|
||||||
/// |> line(endAbsolute = p3)
|
|
||||||
/// |> line(endAbsolute = p0)
|
|
||||||
/// |> close()
|
|
||||||
/// |> extrude(length = length)
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// width = 20
|
|
||||||
/// fn transform(@i) {
|
|
||||||
/// return {
|
|
||||||
/// translate = [0, 0, -i * width],
|
|
||||||
/// rotation = {
|
|
||||||
/// angle = 90 * i,
|
|
||||||
/// // Rotate around the overall scene's origin.
|
|
||||||
/// origin = "global",
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// myCubes =
|
|
||||||
/// cube(length = width, center = [100,100])
|
|
||||||
/// |> patternTransform(instances = 4, transform = transform)
|
|
||||||
/// ```
|
|
||||||
/// ```no_run
|
|
||||||
/// // Parameters
|
|
||||||
/// r = 50 // base radius
|
|
||||||
/// h = 10 // layer height
|
|
||||||
/// 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) {
|
|
||||||
/// scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
|
|
||||||
/// return {
|
|
||||||
/// translate = [0, 0, replicaId * 10],
|
|
||||||
/// scale = [scale, scale, 0],
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
/// // Each layer is just a pretty thin cylinder.
|
|
||||||
/// fn layer() {
|
|
||||||
/// return startSketchOn(XY) // or some other plane idk
|
|
||||||
/// |> circle(center = [0, 0], radius = 1, tag = $tag1)
|
|
||||||
/// |> extrude(length = h)
|
|
||||||
/// }
|
|
||||||
/// // The vase is 100 layers tall.
|
|
||||||
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
|
|
||||||
/// vase = layer() |> patternTransform(instances = 100, transform = transform)
|
|
||||||
/// ```
|
|
||||||
/// ```
|
|
||||||
/// fn transform(@i) {
|
|
||||||
/// // Transform functions can return multiple transforms. They'll be applied in order.
|
|
||||||
/// return [
|
|
||||||
/// { translate = [30 * i, 0, 0] },
|
|
||||||
/// { rotation = { angle = 45 * i } },
|
|
||||||
/// ]
|
|
||||||
/// }
|
|
||||||
/// startSketchOn(XY)
|
|
||||||
/// |> startProfile(at = [0, 0])
|
|
||||||
/// |> polygon(
|
|
||||||
/// radius = 10,
|
|
||||||
/// numSides = 4,
|
|
||||||
/// center = [0, 0],
|
|
||||||
/// inscribed = false,
|
|
||||||
/// )
|
|
||||||
/// |> extrude(length = 4)
|
|
||||||
/// |> patternTransform(instances = 3, transform = transform)
|
|
||||||
/// ```
|
|
||||||
#[stdlib {
|
|
||||||
name = "patternTransform",
|
|
||||||
feature_tree_operation = true,
|
|
||||||
keywords = true,
|
|
||||||
unlabeled_first = true,
|
|
||||||
args = {
|
|
||||||
solids = { docs = "The solid(s) to duplicate" },
|
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
|
||||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
|
||||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
|
||||||
},
|
|
||||||
tags = ["solid"]
|
|
||||||
}]
|
|
||||||
async fn inner_pattern_transform<'a>(
|
async fn inner_pattern_transform<'a>(
|
||||||
solids: Vec<Solid>,
|
solids: Vec<Solid>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
@ -283,30 +87,6 @@ async fn inner_pattern_transform<'a>(
|
|||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Just like patternTransform, but works on 2D sketches not 3D solids.
|
|
||||||
/// ```no_run
|
|
||||||
/// // Each instance will be shifted along the X axis.
|
|
||||||
/// fn transform(@id) {
|
|
||||||
/// return { translate = [4 * id, 0] }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Sketch 4 circles.
|
|
||||||
/// sketch001 = startSketchOn(XZ)
|
|
||||||
/// |> circle(center = [0, 0], radius= 2)
|
|
||||||
/// |> patternTransform2d(instances = 4, transform = transform)
|
|
||||||
/// ```
|
|
||||||
#[stdlib {
|
|
||||||
name = "patternTransform2d",
|
|
||||||
keywords = true,
|
|
||||||
unlabeled_first = true,
|
|
||||||
args = {
|
|
||||||
sketches = { docs = "The sketch(es) to duplicate" },
|
|
||||||
instances = { docs = "The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect." },
|
|
||||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
|
||||||
use_original = { docs = "If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false." },
|
|
||||||
},
|
|
||||||
tags = ["sketch"]
|
|
||||||
}]
|
|
||||||
async fn inner_pattern_transform_2d<'a>(
|
async fn inner_pattern_transform_2d<'a>(
|
||||||
sketches: Vec<Sketch>,
|
sketches: Vec<Sketch>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
|
@ -278,3 +278,28 @@ export fn revolve(
|
|||||||
/// A named tag for the face at the end of the revolve.
|
/// A named tag for the face at the end of the revolve.
|
||||||
tagEnd?: tag,
|
tagEnd?: tag,
|
||||||
): [Solid; 1+] {}
|
): [Solid; 1+] {}
|
||||||
|
|
||||||
|
/// Just like `patternTransform`, but works on 2D sketches not 3D solids.
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// // Each instance will be shifted along the X axis.
|
||||||
|
/// fn transform(@id) {
|
||||||
|
/// return { translate = [4 * id, 0] }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Sketch 4 circles.
|
||||||
|
/// sketch001 = startSketchOn(XZ)
|
||||||
|
/// |> circle(center = [0, 0], radius = 2)
|
||||||
|
/// |> patternTransform2d(instances = 4, transform = transform)
|
||||||
|
/// ```
|
||||||
|
@(impl = std_rust)
|
||||||
|
export fn patternTransform2d(
|
||||||
|
/// The sketch(es) to duplicate.
|
||||||
|
@sketches: [Sketch; 1+],
|
||||||
|
/// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
|
||||||
|
instances: number(Count),
|
||||||
|
/// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
|
||||||
|
transform: fn(number(Count)): {},
|
||||||
|
/// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
|
||||||
|
useOriginal?: boolean = false,
|
||||||
|
): [Sketch; 1+] {}
|
||||||
|
@ -365,3 +365,202 @@ export fn hollow(
|
|||||||
/// The thickness of the remaining shell
|
/// The thickness of the remaining shell
|
||||||
thickness: number(Length),
|
thickness: number(Length),
|
||||||
): Solid {}
|
): Solid {}
|
||||||
|
|
||||||
|
/// Repeat a 3-dimensional solid, changing it each time.
|
||||||
|
///
|
||||||
|
/// Replicates the 3D solid, applying a transformation function to each replica.
|
||||||
|
/// Transformation function could alter rotation, scale, visibility, position, etc.
|
||||||
|
///
|
||||||
|
/// The `patternTransform` call itself takes a number for how many total instances of
|
||||||
|
/// the shape should be. For example, if you use a circle with `patternTransform(instances = 4, transform = f)`
|
||||||
|
/// then there will be 4 circles: the original, and 3 created by replicating the original and
|
||||||
|
/// calling the transform function on each.
|
||||||
|
///
|
||||||
|
/// The transform function takes a single parameter: an integer representing which
|
||||||
|
/// number replication the transform is for. E.g. the first replica to be transformed
|
||||||
|
/// will be passed the argument `1`. This simplifies your math: the transform function can
|
||||||
|
/// rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
|
||||||
|
///
|
||||||
|
/// The transform function returns a transform object. All properties of the object are optional,
|
||||||
|
/// they each default to "no change". So the overall transform object defaults to "no change" too.
|
||||||
|
/// Its properties are:
|
||||||
|
///
|
||||||
|
/// - `translate` (3D point)
|
||||||
|
///
|
||||||
|
/// Translates the replica, moving its position in space.
|
||||||
|
///
|
||||||
|
/// - `replicate` (bool)
|
||||||
|
///
|
||||||
|
/// If false, this ID will not actually copy the object. It'll be skipped.
|
||||||
|
///
|
||||||
|
/// - `scale` (3D point)
|
||||||
|
///
|
||||||
|
/// Stretches the object, multiplying its width in the given dimension by the point's component in
|
||||||
|
/// that direction.
|
||||||
|
///
|
||||||
|
/// - `rotation` (object, with the following properties)
|
||||||
|
///
|
||||||
|
/// - `rotation.axis` (a 3D point, defaults to the Z axis)
|
||||||
|
///
|
||||||
|
/// - `rotation.angle` (number of degrees)
|
||||||
|
///
|
||||||
|
/// - `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// // Each instance will be shifted along the X axis.
|
||||||
|
/// fn transform(@id) {
|
||||||
|
/// return { translate = [4 * id, 0, 0] }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Sketch 4 cylinders.
|
||||||
|
/// sketch001 = startSketchOn(XZ)
|
||||||
|
/// |> circle(center = [0, 0], radius = 2)
|
||||||
|
/// |> extrude(length = 5)
|
||||||
|
/// |> patternTransform(instances = 4, transform = transform)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// // Each instance will be shifted along the X axis,
|
||||||
|
/// // with a gap between the original (at x = 0) and the first replica
|
||||||
|
/// // (at x = 8). This is because `id` starts at 1.
|
||||||
|
/// fn transform(@id) {
|
||||||
|
/// return { translate = [4 * (1+id), 0, 0] }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// sketch001 = startSketchOn(XZ)
|
||||||
|
/// |> circle(center = [0, 0], radius = 2)
|
||||||
|
/// |> extrude(length = 5)
|
||||||
|
/// |> patternTransform(instances = 4, transform = transform)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// fn cube(length, center) {
|
||||||
|
/// l = length/2
|
||||||
|
/// x = center[0]
|
||||||
|
/// y = center[1]
|
||||||
|
/// p0 = [-l + x, -l + y]
|
||||||
|
/// p1 = [-l + x, l + y]
|
||||||
|
/// p2 = [ l + x, l + y]
|
||||||
|
/// p3 = [ l + x, -l + y]
|
||||||
|
///
|
||||||
|
/// return startSketchOn(XY)
|
||||||
|
/// |> startProfile(at = p0)
|
||||||
|
/// |> line(endAbsolute = p1)
|
||||||
|
/// |> line(endAbsolute = p2)
|
||||||
|
/// |> line(endAbsolute = p3)
|
||||||
|
/// |> line(endAbsolute = p0)
|
||||||
|
/// |> close()
|
||||||
|
/// |> extrude(length = length)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// width = 20
|
||||||
|
/// fn transform(@i) {
|
||||||
|
/// return {
|
||||||
|
/// // Move down each time.
|
||||||
|
/// translate = [0, 0, -i * width],
|
||||||
|
/// // Make the cube longer, wider and flatter each time.
|
||||||
|
/// scale = [pow(1.1, exp = i), pow(1.1, exp = i), pow(0.9, exp = i)],
|
||||||
|
/// // Turn by 15 degrees each time.
|
||||||
|
/// rotation = {
|
||||||
|
/// angle = 15 * i,
|
||||||
|
/// origin = "local",
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// myCubes =
|
||||||
|
/// cube(length = width, center = [100,0])
|
||||||
|
/// |> patternTransform(instances = 25, transform = transform)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// fn cube(length, center) {
|
||||||
|
/// l = length/2
|
||||||
|
/// x = center[0]
|
||||||
|
/// y = center[1]
|
||||||
|
/// p0 = [-l + x, -l + y]
|
||||||
|
/// p1 = [-l + x, l + y]
|
||||||
|
/// p2 = [ l + x, l + y]
|
||||||
|
/// p3 = [ l + x, -l + y]
|
||||||
|
///
|
||||||
|
/// return startSketchOn(XY)
|
||||||
|
/// |> startProfile(at = p0)
|
||||||
|
/// |> line(endAbsolute = p1)
|
||||||
|
/// |> line(endAbsolute = p2)
|
||||||
|
/// |> line(endAbsolute = p3)
|
||||||
|
/// |> line(endAbsolute = p0)
|
||||||
|
/// |> close()
|
||||||
|
/// |> extrude(length = length)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// width = 20
|
||||||
|
/// fn transform(@i) {
|
||||||
|
/// return {
|
||||||
|
/// translate = [0, 0, -i * width],
|
||||||
|
/// rotation = {
|
||||||
|
/// angle = 90 * i,
|
||||||
|
/// // Rotate around the overall scene's origin.
|
||||||
|
/// origin = "global",
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// myCubes =
|
||||||
|
/// cube(length = width, center = [100,100])
|
||||||
|
/// |> patternTransform(instances = 4, transform = transform)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// // Parameters
|
||||||
|
/// r = 50 // base radius
|
||||||
|
/// h = 10 // layer height
|
||||||
|
/// 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) {
|
||||||
|
/// scale = r * abs(1 - (t * replicaId)) * (5 + cos((replicaId / 8): number(rad)))
|
||||||
|
/// return {
|
||||||
|
/// translate = [0, 0, replicaId * 10],
|
||||||
|
/// scale = [scale, scale, 0],
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// // Each layer is just a pretty thin cylinder.
|
||||||
|
/// fn layer() {
|
||||||
|
/// return startSketchOn(XY) // or some other plane idk
|
||||||
|
/// |> circle(center = [0, 0], radius = 1, tag = $tag1)
|
||||||
|
/// |> extrude(length = h)
|
||||||
|
/// }
|
||||||
|
/// // The vase is 100 layers tall.
|
||||||
|
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
|
||||||
|
/// vase = layer() |> patternTransform(instances = 100, transform = transform)
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// ```kcl
|
||||||
|
/// fn transform(@i) {
|
||||||
|
/// // Transform functions can return multiple transforms. They'll be applied in order.
|
||||||
|
/// return [
|
||||||
|
/// { translate = [30 * i, 0, 0] },
|
||||||
|
/// { rotation = { angle = 45 * i } },
|
||||||
|
/// ]
|
||||||
|
/// }
|
||||||
|
/// startSketchOn(XY)
|
||||||
|
/// |> startProfile(at = [0, 0])
|
||||||
|
/// |> polygon(
|
||||||
|
/// radius = 10,
|
||||||
|
/// numSides = 4,
|
||||||
|
/// center = [0, 0],
|
||||||
|
/// inscribed = false,
|
||||||
|
/// )
|
||||||
|
/// |> extrude(length = 4)
|
||||||
|
/// |> patternTransform(instances = 3, transform = transform)
|
||||||
|
/// ```
|
||||||
|
@(impl = std_rust)
|
||||||
|
export fn patternTransform(
|
||||||
|
/// The solid(s) to duplicate.
|
||||||
|
@solids: [Solid; 1+],
|
||||||
|
/// The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect.
|
||||||
|
instances: number(Count),
|
||||||
|
/// How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples.
|
||||||
|
transform: fn(number(Count)): {},
|
||||||
|
/// If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid.
|
||||||
|
useOriginal?: boolean = false,
|
||||||
|
): [Solid; 1+] {}
|
||||||
|
@ -111,9 +111,9 @@ flowchart LR
|
|||||||
8 --- 19
|
8 --- 19
|
||||||
8 --- 20
|
8 --- 20
|
||||||
8 ---- 25
|
8 ---- 25
|
||||||
12 <--x 32
|
12 --- 32
|
||||||
12 <--x 33
|
12 <--x 33
|
||||||
12 --- 34
|
12 <--x 34
|
||||||
13 --- 31
|
13 --- 31
|
||||||
13 x--> 35
|
13 x--> 35
|
||||||
13 --- 39
|
13 --- 39
|
||||||
|
@ -805,13 +805,8 @@ description: Operations executed spinning-highrise-tower.kcl
|
|||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 17.0,
|
"value": 17.0,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Default",
|
"type": "Known",
|
||||||
"len": {
|
"type": "Count"
|
||||||
"type": "M"
|
|
||||||
},
|
|
||||||
"angle": {
|
|
||||||
"type": "Degrees"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
@ -1275,13 +1270,8 @@ description: Operations executed spinning-highrise-tower.kcl
|
|||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 17.0,
|
"value": 17.0,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Default",
|
"type": "Known",
|
||||||
"len": {
|
"type": "Count"
|
||||||
"type": "M"
|
|
||||||
},
|
|
||||||
"angle": {
|
|
||||||
"type": "Degrees"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
|
@ -108,13 +108,8 @@ description: Operations executed multi_transform.kcl
|
|||||||
"type": "Number",
|
"type": "Number",
|
||||||
"value": 3.0,
|
"value": 3.0,
|
||||||
"ty": {
|
"ty": {
|
||||||
"type": "Default",
|
"type": "Known",
|
||||||
"len": {
|
"type": "Count"
|
||||||
"type": "Mm"
|
|
||||||
},
|
|
||||||
"angle": {
|
|
||||||
"type": "Degrees"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sourceRange": []
|
"sourceRange": []
|
||||||
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 77 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 233 KiB After Width: | Height: | Size: 233 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 45 KiB |
@ -89,9 +89,9 @@ flowchart LR
|
|||||||
12 <--x 22
|
12 <--x 22
|
||||||
12 --- 23
|
12 --- 23
|
||||||
12 <--x 24
|
12 <--x 24
|
||||||
16 <--x 25
|
16 --- 25
|
||||||
16 <--x 26
|
16 <--x 26
|
||||||
16 --- 27
|
16 <--x 27
|
||||||
19 --- 22
|
19 --- 22
|
||||||
19 --- 23
|
19 --- 23
|
||||||
19 --- 24
|
19 --- 24
|
||||||
|