Allow transform patterns to return multiple transforms (#4531)

* Support multiple transforms in patterns
* New examples in docs
* Refactor: move large block into its own function
This commit is contained in:
Adam Chalmers
2024-11-21 08:49:54 -06:00
committed by GitHub
parent 59e0df7879
commit fcb0a6d9ca
13 changed files with 1642 additions and 24 deletions

File diff suppressed because one or more lines are too long

View File

@ -123905,7 +123905,8 @@
"// Each instance will be shifted along the X axis,\n// with a gap between the original (at x = 0) and the first replica\n// (at x = 8). This is because `id` starts at 1.\nfn transform = (id) => {\n return { translate: [4 * (1 + id), 0, 0] }\n}\n\nsketch001 = startSketchOn('XZ')\n |> circle({ center: [0, 0], radius: 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
"fn cube = (length, center) => {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform = (i) => {\n return {\n // Move down each time.\n translate: [0, 0, -i * width],\n // Make the cube longer, wider and flatter each time.\n scale: [pow(1.1, i), pow(1.1, i), pow(0.9, i)],\n // Turn by 15 degrees each time.\n rotation: { angle: 15 * i, origin: \"local\" }\n }\n}\n\nmyCubes = cube(width, [100, 0])\n |> patternTransform(25, transform, %)",
"fn cube = (length, center) => {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform = (i) => {\n return {\n translate: [0, 0, -i * width],\n rotation: {\n angle: 90 * i,\n // Rotate around the overall scene's origin.\n origin: \"global\"\n }\n }\n}\nmyCubes = cube(width, [100, 100])\n |> patternTransform(4, transform, %)",
"// Parameters\nr = 50 // base radius\nh = 10 // layer height\nt = 0.005 // taper factor [0-1)\n// Defines how to modify each layer of the vase.\n// Each replica is shifted up the Z axis, and has a smoothly-varying radius\nfn transform = (replicaId) => {\n scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))\n return {\n translate: [0, 0, replicaId * 10],\n scale: [scale, scale, 0]\n }\n}\n// Each layer is just a pretty thin cylinder.\nfn layer = () => {\n return startSketchOn(\"XY\")\n // or some other plane idk\n |> circle({ center: [0, 0], radius: 1 }, %, $tag1)\n |> extrude(h, %)\n}\n// The vase is 100 layers tall.\n// The 100 layers are replica of each other, with a slight transformation applied to each.\nvase = layer()\n |> patternTransform(100, transform, %)"
"// Parameters\nr = 50 // base radius\nh = 10 // layer height\nt = 0.005 // taper factor [0-1)\n// Defines how to modify each layer of the vase.\n// Each replica is shifted up the Z axis, and has a smoothly-varying radius\nfn transform = (replicaId) => {\n scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))\n return {\n translate: [0, 0, replicaId * 10],\n scale: [scale, scale, 0]\n }\n}\n// Each layer is just a pretty thin cylinder.\nfn layer = () => {\n return startSketchOn(\"XY\")\n // or some other plane idk\n |> circle({ center: [0, 0], radius: 1 }, %, $tag1)\n |> extrude(h, %)\n}\n// The vase is 100 layers tall.\n// The 100 layers are replica of each other, with a slight transformation applied to each.\nvase = layer()\n |> patternTransform(100, transform, %)",
"fn transform = (i) => {\n // Transform functions can return multiple transforms. They'll be applied in order.\n return [\n { translate: [30 * i, 0, 0] },\n { rotation: { angle: 45 * i } }\n ]\n}\nstartSketchAt([0, 0])\n |> polygon({\n radius: 10,\n numSides: 4,\n center: [0, 0],\n inscribed: false\n }, %)\n |> extrude(4, %)\n |> patternTransform(3, transform, %)"
]
},
{

View File

@ -1806,9 +1806,9 @@ dependencies = [
[[package]]
name = "kittycad-modeling-cmds"
version = "0.2.72"
version = "0.2.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e41880dbe19df3150992988f438c9ba9022ea822d6e4e5ed53d28965de198ec7"
checksum = "2394fe2b28a1c6bd524dec1dbcd7e839c2782a6ecc743085e122cde77ee19cfa"
dependencies = [
"anyhow",
"chrono",
@ -1816,7 +1816,7 @@ dependencies = [
"enum-iterator",
"enum-iterator-derive",
"euler",
"http 0.2.12",
"http 1.1.0",
"kittycad-modeling-cmds-macros",
"kittycad-unit-conversion-derive",
"measurements",
@ -3131,9 +3131,9 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
[[package]]
name = "serde"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
dependencies = [
"serde_derive",
]
@ -3149,9 +3149,9 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.214"
version = "1.0.215"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
dependencies = [
"proc-macro2",
"quote",
@ -3171,9 +3171,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.132"
version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
dependencies = [
"indexmap 2.6.0",
"itoa",

View File

@ -75,7 +75,7 @@ members = [
[workspace.dependencies]
http = "1"
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
kittycad-modeling-cmds = { version = "0.2.72", features = ["websocket"] }
kittycad-modeling-cmds = { version = "0.2.76", features = ["websocket"] }
[[test]]
name = "executor"

View File

@ -5,6 +5,10 @@ cita := "cargo insta test --accept"
lint:
cargo clippy --workspace --all-targets -- -D warnings
# Run the stdlib docs generation
redo-kcl-stdlib-docs-no-imgs:
EXPECTORATE=overwrite {{cnr}} -p kcl-lib docs::gen_std_tests::test_generate_stdlib
# Generate the stdlib image artifacts
# Then run the stdlib docs generation
redo-kcl-stdlib-docs:
@ -13,6 +17,9 @@ redo-kcl-stdlib-docs:
# Create a new KCL deterministic simulation test case.
new-sim-test test_name render_to_png="true":
# Add the various tests for this new test case.
cat kcl/tests/simtest.tmpl | sed "s/TEST_NAME_HERE/{{test_name}}/" | sed "s/RENDER_TO_PNG/{{render_to_png}}/" >> kcl/src/simulation_tests.rs
# Run all the tests for the first time, in the right order.
{{cita}} -p kcl-lib -- tests::{{test_name}}::tokenize
{{cita}} -p kcl-lib -- tests::{{test_name}}::parse
{{cita}} -p kcl-lib -- tests::{{test_name}}::unparse

View File

@ -1030,6 +1030,14 @@ impl KclValue {
}
}
pub fn into_object(self) -> Option<HashMap<String, KclValue>> {
if let KclValue::Object { value, meta: _ } = self {
Some(value)
} else {
None
}
}
pub fn as_str(&self) -> Option<&str> {
if let KclValue::String { value, meta: _ } = &self {
Some(value)

View File

@ -1,6 +1,6 @@
//! Standard library patterns.
use std::cmp::Ordering;
use std::{cmp::Ordering, collections::HashMap};
use anyhow::Result;
use derive_docs::stdlib;
@ -270,6 +270,24 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result
/// // The 100 layers are replica of each other, with a slight transformation applied to each.
/// let vase = layer() |> patternTransform(100, 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 } },
/// ]
/// }
/// startSketchAt([0, 0])
/// |> polygon({
/// radius: 10,
/// numSides: 4,
/// center: [0, 0],
/// inscribed: false
/// }, %)
/// |> extrude(4, %)
/// |> patternTransform(3, transform, %)
/// ```
#[stdlib {
name = "patternTransform",
}]
@ -315,7 +333,7 @@ async fn inner_pattern_transform<'a>(
async fn send_pattern_transform(
// This should be passed via reference, see
// https://github.com/KittyCAD/modeling-app/issues/2821
transform: Vec<Transform>,
transforms: Vec<Vec<Transform>>,
solid: &Solid,
exec_state: &mut ExecState,
args: &Args,
@ -327,7 +345,8 @@ async fn send_pattern_transform(
id,
ModelingCmd::from(mcmd::EntityLinearPatternTransform {
entity_id: solid.id,
transform,
transform: Default::default(),
transforms,
}),
)
.await?;
@ -356,7 +375,7 @@ async fn make_transform<'a>(
transform_function: &FunctionParam<'a>,
source_range: SourceRange,
exec_state: &mut ExecState,
) -> Result<Transform, KclError> {
) -> Result<Vec<Transform>, KclError> {
// Call the transform fn for this repetition.
let repetition_num = KclValue::Int {
value: i.into(),
@ -373,13 +392,38 @@ async fn make_transform<'a>(
source_ranges: source_ranges.clone(),
})
})?;
let KclValue::Object { value: transform, meta } = transform_fn_return else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Transform function must return a transform object".to_string(),
source_ranges: source_ranges.clone(),
}));
let transforms = match transform_fn_return {
KclValue::Object { value, meta: _ } => vec![value],
KclValue::Array { value, meta: _ } => {
let transforms: Vec<_> = value
.into_iter()
.map(|val| {
val.into_object().ok_or(KclError::Semantic(KclErrorDetails {
message: "Transform function must return a transform object".to_string(),
source_ranges: source_ranges.clone(),
}))
})
.collect::<Result<_, _>>()?;
transforms
}
_ => {
return Err(KclError::Semantic(KclErrorDetails {
message: "Transform function must return a transform object".to_string(),
source_ranges: source_ranges.clone(),
}))
}
};
transforms
.into_iter()
.map(|obj| transform_from_obj_fields(obj, source_ranges.clone()))
.collect()
}
fn transform_from_obj_fields(
transform: HashMap<String, KclValue>,
source_ranges: Vec<SourceRange>,
) -> Result<Transform, KclError> {
// Apply defaults to the transform.
let replicate = match transform.get("replicate") {
Some(KclValue::Bool { value: true, .. }) => true,
@ -420,7 +464,7 @@ async fn make_transform<'a>(
_ => {
return Err(KclError::Semantic(KclErrorDetails {
message: "The 'rotation.angle' key must be a number (of degrees)".to_string(),
source_ranges: meta.iter().map(|m| m.source_range).collect(),
source_ranges: source_ranges.clone(),
}));
}
}
@ -436,13 +480,12 @@ async fn make_transform<'a>(
};
}
}
let t = Transform {
Ok(Transform {
replicate,
scale: scale.into(),
translate: translate.into(),
rotation,
};
Ok(t)
})
}
fn array_to_point3d(val: &KclValue, source_ranges: Vec<SourceRange>) -> Result<Point3d, KclError> {

View File

@ -0,0 +1,450 @@
---
source: kcl/src/simulation_tests.rs
description: Result of parsing multi_transform.kcl
snapshot_kind: text
---
{
"Ok": {
"body": [
{
"declarations": [
{
"end": 111,
"id": {
"end": 12,
"name": "transform",
"start": 3,
"type": "Identifier"
},
"init": {
"body": {
"body": [
{
"argument": {
"elements": [
{
"end": 68,
"properties": [
{
"end": 66,
"key": {
"end": 50,
"name": "translate",
"start": 41,
"type": "Identifier"
},
"start": 41,
"type": "ObjectProperty",
"value": {
"elements": [
{
"end": 59,
"left": {
"end": 55,
"raw": "30",
"start": 53,
"type": "Literal",
"type": "Literal",
"value": 30
},
"operator": "*",
"right": {
"end": 59,
"name": "i",
"start": 58,
"type": "Identifier",
"type": "Identifier"
},
"start": 53,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 62,
"raw": "0",
"start": 61,
"type": "Literal",
"type": "Literal",
"value": 0
},
{
"end": 65,
"raw": "0",
"start": 64,
"type": "Literal",
"type": "Literal",
"value": 0
}
],
"end": 66,
"start": 52,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"start": 39,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
{
"end": 105,
"properties": [
{
"end": 103,
"key": {
"end": 84,
"name": "rotation",
"start": 76,
"type": "Identifier"
},
"start": 76,
"type": "ObjectProperty",
"value": {
"end": 103,
"properties": [
{
"end": 101,
"key": {
"end": 93,
"name": "angle",
"start": 88,
"type": "Identifier"
},
"start": 88,
"type": "ObjectProperty",
"value": {
"end": 101,
"left": {
"end": 97,
"raw": "45",
"start": 95,
"type": "Literal",
"type": "Literal",
"value": 45
},
"operator": "*",
"right": {
"end": 101,
"name": "i",
"start": 100,
"type": "Identifier",
"type": "Identifier"
},
"start": 95,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
}
],
"start": 86,
"type": "ObjectExpression",
"type": "ObjectExpression"
}
}
],
"start": 74,
"type": "ObjectExpression",
"type": "ObjectExpression"
}
],
"end": 109,
"start": 33,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"end": 109,
"start": 26,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 111,
"start": 22
},
"end": 111,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"name": "i",
"start": 16,
"type": "Identifier"
},
"optional": false
}
],
"start": 15,
"type": "FunctionExpression",
"type": "FunctionExpression"
},
"start": 3,
"type": "VariableDeclarator"
}
],
"end": 111,
"kind": "fn",
"start": 0,
"type": "VariableDeclaration",
"type": "VariableDeclaration"
},
{
"end": 303,
"expression": {
"body": [
{
"arguments": [
{
"elements": [
{
"end": 128,
"raw": "0",
"start": 127,
"type": "Literal",
"type": "Literal",
"value": 0
},
{
"end": 131,
"raw": "0",
"start": 130,
"type": "Literal",
"type": "Literal",
"value": 0
}
],
"end": 132,
"start": 126,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
],
"callee": {
"end": 125,
"name": "startSketchAt",
"start": 112,
"type": "Identifier"
},
"end": 133,
"optional": false,
"start": 112,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 241,
"properties": [
{
"end": 166,
"key": {
"end": 162,
"name": "radius",
"start": 156,
"type": "Identifier"
},
"start": 156,
"type": "ObjectProperty",
"value": {
"end": 166,
"raw": "10",
"start": 164,
"type": "Literal",
"type": "Literal",
"value": 10
}
},
{
"end": 186,
"key": {
"end": 183,
"name": "numSides",
"start": 175,
"type": "Identifier"
},
"start": 175,
"type": "ObjectProperty",
"value": {
"end": 186,
"raw": "4",
"start": 185,
"type": "Literal",
"type": "Literal",
"value": 4
}
},
{
"end": 209,
"key": {
"end": 201,
"name": "center",
"start": 195,
"type": "Identifier"
},
"start": 195,
"type": "ObjectProperty",
"value": {
"elements": [
{
"end": 205,
"raw": "0",
"start": 204,
"type": "Literal",
"type": "Literal",
"value": 0
},
{
"end": 208,
"raw": "0",
"start": 207,
"type": "Literal",
"type": "Literal",
"value": 0
}
],
"end": 209,
"start": 203,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
},
{
"end": 234,
"key": {
"end": 227,
"name": "inscribed",
"start": 218,
"type": "Identifier"
},
"start": 218,
"type": "ObjectProperty",
"value": {
"end": 234,
"raw": "false",
"start": 229,
"type": "Literal",
"type": "Literal",
"value": false
}
}
],
"start": 147,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
{
"end": 244,
"start": 243,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 146,
"name": "polygon",
"start": 139,
"type": "Identifier"
},
"end": 245,
"optional": false,
"start": 139,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 260,
"raw": "4",
"start": 259,
"type": "Literal",
"type": "Literal",
"value": 4
},
{
"end": 263,
"start": 262,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 258,
"name": "extrude",
"start": 251,
"type": "Identifier"
},
"end": 264,
"optional": false,
"start": 251,
"type": "CallExpression",
"type": "CallExpression"
},
{
"arguments": [
{
"end": 288,
"raw": "3",
"start": 287,
"type": "Literal",
"type": "Literal",
"value": 3
},
{
"end": 299,
"name": "transform",
"start": 290,
"type": "Identifier",
"type": "Identifier"
},
{
"end": 302,
"start": 301,
"type": "PipeSubstitution",
"type": "PipeSubstitution"
}
],
"callee": {
"end": 286,
"name": "patternTransform",
"start": 270,
"type": "Identifier"
},
"end": 303,
"optional": false,
"start": 270,
"type": "CallExpression",
"type": "CallExpression"
}
],
"end": 303,
"start": 112,
"type": "PipeExpression",
"type": "PipeExpression"
},
"start": 112,
"type": "ExpressionStatement",
"type": "ExpressionStatement"
}
],
"end": 305,
"nonCodeMeta": {
"nonCodeNodes": {
"1": [
{
"end": 305,
"start": 303,
"type": "NonCodeNode",
"value": {
"type": "newLine"
}
}
]
},
"startNodes": []
},
"start": 0
}
}

View File

@ -0,0 +1,16 @@
fn transform = (i) => {
return [
{ translate: [30 * i, 0, 0] },
{ rotation: { angle: 45 * i } }
]
}
startSketchAt([0, 0])
|> polygon({
radius: 10,
numSides: 4,
center: [0, 0],
inscribed: false
}, %)
|> extrude(4, %)
|> patternTransform(3, transform, %)

View File

@ -0,0 +1,241 @@
---
source: kcl/src/simulation_tests.rs
description: Program memory after executing multi_transform.kcl
snapshot_kind: text
---
{
"environments": [
{
"bindings": {
"HALF_TURN": {
"type": "Number",
"value": 180.0,
"__meta": []
},
"QUARTER_TURN": {
"type": "Number",
"value": 90.0,
"__meta": []
},
"THREE_QUARTER_TURN": {
"type": "Number",
"value": 270.0,
"__meta": []
},
"ZERO": {
"type": "Number",
"value": 0.0,
"__meta": []
},
"transform": {
"type": "Function",
"expression": {
"body": {
"body": [
{
"argument": {
"elements": [
{
"end": 68,
"properties": [
{
"end": 66,
"key": {
"end": 50,
"name": "translate",
"start": 41,
"type": "Identifier"
},
"start": 41,
"type": "ObjectProperty",
"value": {
"elements": [
{
"end": 59,
"left": {
"end": 55,
"raw": "30",
"start": 53,
"type": "Literal",
"type": "Literal",
"value": 30
},
"operator": "*",
"right": {
"end": 59,
"name": "i",
"start": 58,
"type": "Identifier",
"type": "Identifier"
},
"start": 53,
"type": "BinaryExpression",
"type": "BinaryExpression"
},
{
"end": 62,
"raw": "0",
"start": 61,
"type": "Literal",
"type": "Literal",
"value": 0
},
{
"end": 65,
"raw": "0",
"start": 64,
"type": "Literal",
"type": "Literal",
"value": 0
}
],
"end": 66,
"start": 52,
"type": "ArrayExpression",
"type": "ArrayExpression"
}
}
],
"start": 39,
"type": "ObjectExpression",
"type": "ObjectExpression"
},
{
"end": 105,
"properties": [
{
"end": 103,
"key": {
"end": 84,
"name": "rotation",
"start": 76,
"type": "Identifier"
},
"start": 76,
"type": "ObjectProperty",
"value": {
"end": 103,
"properties": [
{
"end": 101,
"key": {
"end": 93,
"name": "angle",
"start": 88,
"type": "Identifier"
},
"start": 88,
"type": "ObjectProperty",
"value": {
"end": 101,
"left": {
"end": 97,
"raw": "45",
"start": 95,
"type": "Literal",
"type": "Literal",
"value": 45
},
"operator": "*",
"right": {
"end": 101,
"name": "i",
"start": 100,
"type": "Identifier",
"type": "Identifier"
},
"start": 95,
"type": "BinaryExpression",
"type": "BinaryExpression"
}
}
],
"start": 86,
"type": "ObjectExpression",
"type": "ObjectExpression"
}
}
],
"start": 74,
"type": "ObjectExpression",
"type": "ObjectExpression"
}
],
"end": 109,
"start": 33,
"type": "ArrayExpression",
"type": "ArrayExpression"
},
"end": 109,
"start": 26,
"type": "ReturnStatement",
"type": "ReturnStatement"
}
],
"end": 111,
"start": 22
},
"end": 111,
"params": [
{
"type": "Parameter",
"identifier": {
"end": 17,
"name": "i",
"start": 16,
"type": "Identifier"
},
"optional": false
}
],
"start": 15,
"type": "FunctionExpression"
},
"memory": {
"environments": [
{
"bindings": {
"HALF_TURN": {
"type": "Number",
"value": 180.0,
"__meta": []
},
"QUARTER_TURN": {
"type": "Number",
"value": 90.0,
"__meta": []
},
"THREE_QUARTER_TURN": {
"type": "Number",
"value": 270.0,
"__meta": []
},
"ZERO": {
"type": "Number",
"value": 0.0,
"__meta": []
}
},
"parent": null
}
],
"currentEnv": 0,
"return": null
},
"__meta": [
{
"sourceRange": [
15,
111,
0
]
}
]
}
},
"parent": null
}
],
"currentEnv": 0,
"return": null
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -0,0 +1,831 @@
---
source: kcl/src/simulation_tests.rs
description: Result of tokenizing multi_transform.kcl
snapshot_kind: text
---
{
"Ok": [
{
"type": "keyword",
"start": 0,
"end": 2,
"value": "fn"
},
{
"type": "whitespace",
"start": 2,
"end": 3,
"value": " "
},
{
"type": "word",
"start": 3,
"end": 12,
"value": "transform"
},
{
"type": "whitespace",
"start": 12,
"end": 13,
"value": " "
},
{
"type": "operator",
"start": 13,
"end": 14,
"value": "="
},
{
"type": "whitespace",
"start": 14,
"end": 15,
"value": " "
},
{
"type": "brace",
"start": 15,
"end": 16,
"value": "("
},
{
"type": "word",
"start": 16,
"end": 17,
"value": "i"
},
{
"type": "brace",
"start": 17,
"end": 18,
"value": ")"
},
{
"type": "whitespace",
"start": 18,
"end": 19,
"value": " "
},
{
"type": "operator",
"start": 19,
"end": 21,
"value": "=>"
},
{
"type": "whitespace",
"start": 21,
"end": 22,
"value": " "
},
{
"type": "brace",
"start": 22,
"end": 23,
"value": "{"
},
{
"type": "whitespace",
"start": 23,
"end": 26,
"value": "\n "
},
{
"type": "keyword",
"start": 26,
"end": 32,
"value": "return"
},
{
"type": "whitespace",
"start": 32,
"end": 33,
"value": " "
},
{
"type": "brace",
"start": 33,
"end": 34,
"value": "["
},
{
"type": "whitespace",
"start": 34,
"end": 39,
"value": "\n "
},
{
"type": "brace",
"start": 39,
"end": 40,
"value": "{"
},
{
"type": "whitespace",
"start": 40,
"end": 41,
"value": " "
},
{
"type": "word",
"start": 41,
"end": 50,
"value": "translate"
},
{
"type": "colon",
"start": 50,
"end": 51,
"value": ":"
},
{
"type": "whitespace",
"start": 51,
"end": 52,
"value": " "
},
{
"type": "brace",
"start": 52,
"end": 53,
"value": "["
},
{
"type": "number",
"start": 53,
"end": 55,
"value": "30"
},
{
"type": "whitespace",
"start": 55,
"end": 56,
"value": " "
},
{
"type": "operator",
"start": 56,
"end": 57,
"value": "*"
},
{
"type": "whitespace",
"start": 57,
"end": 58,
"value": " "
},
{
"type": "word",
"start": 58,
"end": 59,
"value": "i"
},
{
"type": "comma",
"start": 59,
"end": 60,
"value": ","
},
{
"type": "whitespace",
"start": 60,
"end": 61,
"value": " "
},
{
"type": "number",
"start": 61,
"end": 62,
"value": "0"
},
{
"type": "comma",
"start": 62,
"end": 63,
"value": ","
},
{
"type": "whitespace",
"start": 63,
"end": 64,
"value": " "
},
{
"type": "number",
"start": 64,
"end": 65,
"value": "0"
},
{
"type": "brace",
"start": 65,
"end": 66,
"value": "]"
},
{
"type": "whitespace",
"start": 66,
"end": 67,
"value": " "
},
{
"type": "brace",
"start": 67,
"end": 68,
"value": "}"
},
{
"type": "comma",
"start": 68,
"end": 69,
"value": ","
},
{
"type": "whitespace",
"start": 69,
"end": 74,
"value": "\n "
},
{
"type": "brace",
"start": 74,
"end": 75,
"value": "{"
},
{
"type": "whitespace",
"start": 75,
"end": 76,
"value": " "
},
{
"type": "word",
"start": 76,
"end": 84,
"value": "rotation"
},
{
"type": "colon",
"start": 84,
"end": 85,
"value": ":"
},
{
"type": "whitespace",
"start": 85,
"end": 86,
"value": " "
},
{
"type": "brace",
"start": 86,
"end": 87,
"value": "{"
},
{
"type": "whitespace",
"start": 87,
"end": 88,
"value": " "
},
{
"type": "word",
"start": 88,
"end": 93,
"value": "angle"
},
{
"type": "colon",
"start": 93,
"end": 94,
"value": ":"
},
{
"type": "whitespace",
"start": 94,
"end": 95,
"value": " "
},
{
"type": "number",
"start": 95,
"end": 97,
"value": "45"
},
{
"type": "whitespace",
"start": 97,
"end": 98,
"value": " "
},
{
"type": "operator",
"start": 98,
"end": 99,
"value": "*"
},
{
"type": "whitespace",
"start": 99,
"end": 100,
"value": " "
},
{
"type": "word",
"start": 100,
"end": 101,
"value": "i"
},
{
"type": "whitespace",
"start": 101,
"end": 102,
"value": " "
},
{
"type": "brace",
"start": 102,
"end": 103,
"value": "}"
},
{
"type": "whitespace",
"start": 103,
"end": 104,
"value": " "
},
{
"type": "brace",
"start": 104,
"end": 105,
"value": "}"
},
{
"type": "whitespace",
"start": 105,
"end": 108,
"value": "\n "
},
{
"type": "brace",
"start": 108,
"end": 109,
"value": "]"
},
{
"type": "whitespace",
"start": 109,
"end": 110,
"value": "\n"
},
{
"type": "brace",
"start": 110,
"end": 111,
"value": "}"
},
{
"type": "whitespace",
"start": 111,
"end": 112,
"value": "\n"
},
{
"type": "word",
"start": 112,
"end": 125,
"value": "startSketchAt"
},
{
"type": "brace",
"start": 125,
"end": 126,
"value": "("
},
{
"type": "brace",
"start": 126,
"end": 127,
"value": "["
},
{
"type": "number",
"start": 127,
"end": 128,
"value": "0"
},
{
"type": "comma",
"start": 128,
"end": 129,
"value": ","
},
{
"type": "whitespace",
"start": 129,
"end": 130,
"value": " "
},
{
"type": "number",
"start": 130,
"end": 131,
"value": "0"
},
{
"type": "brace",
"start": 131,
"end": 132,
"value": "]"
},
{
"type": "brace",
"start": 132,
"end": 133,
"value": ")"
},
{
"type": "whitespace",
"start": 133,
"end": 136,
"value": "\n "
},
{
"type": "operator",
"start": 136,
"end": 138,
"value": "|>"
},
{
"type": "whitespace",
"start": 138,
"end": 139,
"value": " "
},
{
"type": "word",
"start": 139,
"end": 146,
"value": "polygon"
},
{
"type": "brace",
"start": 146,
"end": 147,
"value": "("
},
{
"type": "brace",
"start": 147,
"end": 148,
"value": "{"
},
{
"type": "whitespace",
"start": 148,
"end": 156,
"value": "\n "
},
{
"type": "word",
"start": 156,
"end": 162,
"value": "radius"
},
{
"type": "colon",
"start": 162,
"end": 163,
"value": ":"
},
{
"type": "whitespace",
"start": 163,
"end": 164,
"value": " "
},
{
"type": "number",
"start": 164,
"end": 166,
"value": "10"
},
{
"type": "comma",
"start": 166,
"end": 167,
"value": ","
},
{
"type": "whitespace",
"start": 167,
"end": 175,
"value": "\n "
},
{
"type": "word",
"start": 175,
"end": 183,
"value": "numSides"
},
{
"type": "colon",
"start": 183,
"end": 184,
"value": ":"
},
{
"type": "whitespace",
"start": 184,
"end": 185,
"value": " "
},
{
"type": "number",
"start": 185,
"end": 186,
"value": "4"
},
{
"type": "comma",
"start": 186,
"end": 187,
"value": ","
},
{
"type": "whitespace",
"start": 187,
"end": 195,
"value": "\n "
},
{
"type": "word",
"start": 195,
"end": 201,
"value": "center"
},
{
"type": "colon",
"start": 201,
"end": 202,
"value": ":"
},
{
"type": "whitespace",
"start": 202,
"end": 203,
"value": " "
},
{
"type": "brace",
"start": 203,
"end": 204,
"value": "["
},
{
"type": "number",
"start": 204,
"end": 205,
"value": "0"
},
{
"type": "comma",
"start": 205,
"end": 206,
"value": ","
},
{
"type": "whitespace",
"start": 206,
"end": 207,
"value": " "
},
{
"type": "number",
"start": 207,
"end": 208,
"value": "0"
},
{
"type": "brace",
"start": 208,
"end": 209,
"value": "]"
},
{
"type": "comma",
"start": 209,
"end": 210,
"value": ","
},
{
"type": "whitespace",
"start": 210,
"end": 218,
"value": "\n "
},
{
"type": "word",
"start": 218,
"end": 227,
"value": "inscribed"
},
{
"type": "colon",
"start": 227,
"end": 228,
"value": ":"
},
{
"type": "whitespace",
"start": 228,
"end": 229,
"value": " "
},
{
"type": "keyword",
"start": 229,
"end": 234,
"value": "false"
},
{
"type": "whitespace",
"start": 234,
"end": 240,
"value": "\n "
},
{
"type": "brace",
"start": 240,
"end": 241,
"value": "}"
},
{
"type": "comma",
"start": 241,
"end": 242,
"value": ","
},
{
"type": "whitespace",
"start": 242,
"end": 243,
"value": " "
},
{
"type": "operator",
"start": 243,
"end": 244,
"value": "%"
},
{
"type": "brace",
"start": 244,
"end": 245,
"value": ")"
},
{
"type": "whitespace",
"start": 245,
"end": 248,
"value": "\n "
},
{
"type": "operator",
"start": 248,
"end": 250,
"value": "|>"
},
{
"type": "whitespace",
"start": 250,
"end": 251,
"value": " "
},
{
"type": "word",
"start": 251,
"end": 258,
"value": "extrude"
},
{
"type": "brace",
"start": 258,
"end": 259,
"value": "("
},
{
"type": "number",
"start": 259,
"end": 260,
"value": "4"
},
{
"type": "comma",
"start": 260,
"end": 261,
"value": ","
},
{
"type": "whitespace",
"start": 261,
"end": 262,
"value": " "
},
{
"type": "operator",
"start": 262,
"end": 263,
"value": "%"
},
{
"type": "brace",
"start": 263,
"end": 264,
"value": ")"
},
{
"type": "whitespace",
"start": 264,
"end": 267,
"value": "\n "
},
{
"type": "operator",
"start": 267,
"end": 269,
"value": "|>"
},
{
"type": "whitespace",
"start": 269,
"end": 270,
"value": " "
},
{
"type": "word",
"start": 270,
"end": 286,
"value": "patternTransform"
},
{
"type": "brace",
"start": 286,
"end": 287,
"value": "("
},
{
"type": "number",
"start": 287,
"end": 288,
"value": "3"
},
{
"type": "comma",
"start": 288,
"end": 289,
"value": ","
},
{
"type": "whitespace",
"start": 289,
"end": 290,
"value": " "
},
{
"type": "word",
"start": 290,
"end": 299,
"value": "transform"
},
{
"type": "comma",
"start": 299,
"end": 300,
"value": ","
},
{
"type": "whitespace",
"start": 300,
"end": 301,
"value": " "
},
{
"type": "operator",
"start": 301,
"end": 302,
"value": "%"
},
{
"type": "brace",
"start": 302,
"end": 303,
"value": ")"
},
{
"type": "whitespace",
"start": 303,
"end": 305,
"value": "\n\n"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB