diff --git a/src/wasm-lib/kcl/src/simulation_tests.rs b/src/wasm-lib/kcl/src/simulation_tests.rs
index b968a6ea1..22fe70078 100644
--- a/src/wasm-lib/kcl/src/simulation_tests.rs
+++ b/src/wasm-lib/kcl/src/simulation_tests.rs
@@ -1796,6 +1796,27 @@ mod kw_fn_with_defaults {
super::execute(TEST_NAME, false).await
}
}
+mod loft_graph {
+ const TEST_NAME: &str = "loft_graph";
+
+ /// Test parsing KCL.
+ #[test]
+ fn parse() {
+ super::parse(TEST_NAME)
+ }
+
+ /// Test that parsing and unparsing KCL produces the original KCL input.
+ #[test]
+ fn unparse() {
+ super::unparse(TEST_NAME)
+ }
+
+ /// Test that KCL is executed correctly.
+ #[tokio::test(flavor = "multi_thread")]
+ async fn kcl_test_execute() {
+ super::execute(TEST_NAME, true).await
+ }
+}
mod boolean_logical_and {
const TEST_NAME: &str = "boolean_logical_and";
diff --git a/src/wasm-lib/kcl/tests/loft_graph/artifact_commands.snap b/src/wasm-lib/kcl/tests/loft_graph/artifact_commands.snap
new file mode 100644
index 000000000..dbcf77df2
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/artifact_commands.snap
@@ -0,0 +1,641 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Artifact commands loft_graph.kcl
+---
+[
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 1.0,
+ "z": 0.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "plane_set_color",
+ "plane_id": "[uuid]",
+ "color": {
+ "r": 0.7,
+ "g": 0.28,
+ "b": 0.28,
+ "a": 0.4
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 0.0,
+ "y": 1.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "plane_set_color",
+ "plane_id": "[uuid]",
+ "color": {
+ "r": 0.28,
+ "g": 0.7,
+ "b": 0.28,
+ "a": 0.4
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "plane_set_color",
+ "plane_id": "[uuid]",
+ "color": {
+ "r": 0.28,
+ "g": 0.28,
+ "b": 0.7,
+ "a": 0.4
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": -1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 1.0,
+ "z": 0.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": -1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "edge_lines_visible",
+ "hidden": false
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "set_scene_units",
+ "unit": "mm"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "object_visible",
+ "object_id": "[uuid]",
+ "hidden": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 0,
+ 0,
+ 0
+ ],
+ "command": {
+ "type": "object_visible",
+ "object_id": "[uuid]",
+ "hidden": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 12,
+ 31,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 60.0,
+ "clobber": false,
+ "hide": true
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 45,
+ 80,
+ 0
+ ],
+ "command": {
+ "type": "enable_sketch_mode",
+ "entity_id": "[uuid]",
+ "ortho": false,
+ "animated": false,
+ "adjust_camera": false,
+ "planar_normal": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 45,
+ 80,
+ 0
+ ],
+ "command": {
+ "type": "start_path"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 45,
+ 80,
+ 0
+ ],
+ "command": {
+ "type": "move_path_pen",
+ "path": "[uuid]",
+ "to": {
+ "x": 34.0,
+ "y": 42.0,
+ "z": 0.0
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 86,
+ 108,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": 102.0,
+ "y": 152.0,
+ "z": 0.0
+ },
+ "relative": true
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 114,
+ 136,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": 76.0,
+ "y": -138.0,
+ "z": 0.0
+ },
+ "relative": true
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 142,
+ 198,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": 34.0,
+ "y": 42.0,
+ "z": 0.0
+ },
+ "relative": false
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 204,
+ 211,
+ 0
+ ],
+ "command": {
+ "type": "close_path",
+ "path_id": "[uuid]"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 204,
+ 211,
+ 0
+ ],
+ "command": {
+ "type": "sketch_mode_disable"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 223,
+ 244,
+ 0
+ ],
+ "command": {
+ "type": "make_plane",
+ "origin": {
+ "x": 0.0,
+ "y": -50.0,
+ "z": 0.0
+ },
+ "x_axis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "y_axis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "size": 100.0,
+ "clobber": false,
+ "hide": false
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 223,
+ 244,
+ 0
+ ],
+ "command": {
+ "type": "plane_set_color",
+ "plane_id": "[uuid]",
+ "color": {
+ "r": 0.6,
+ "g": 0.6,
+ "b": 0.6,
+ "a": 0.3
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 294,
+ 330,
+ 0
+ ],
+ "command": {
+ "type": "enable_sketch_mode",
+ "entity_id": "[uuid]",
+ "ortho": false,
+ "animated": false,
+ "adjust_camera": false,
+ "planar_normal": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 294,
+ 330,
+ 0
+ ],
+ "command": {
+ "type": "start_path"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 294,
+ 330,
+ 0
+ ],
+ "command": {
+ "type": "move_path_pen",
+ "path": "[uuid]",
+ "to": {
+ "x": 39.0,
+ "y": 172.0,
+ "z": 0.0
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 336,
+ 349,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": 184.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "relative": true
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 355,
+ 378,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": -78.0,
+ "y": -145.0,
+ "z": 0.0
+ },
+ "relative": true
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 384,
+ 440,
+ 0
+ ],
+ "command": {
+ "type": "extend_path",
+ "path": "[uuid]",
+ "segment": {
+ "type": "line",
+ "end": {
+ "x": 39.0,
+ "y": 172.0,
+ "z": 0.0
+ },
+ "relative": false
+ }
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 446,
+ 453,
+ 0
+ ],
+ "command": {
+ "type": "close_path",
+ "path_id": "[uuid]"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 446,
+ 453,
+ 0
+ ],
+ "command": {
+ "type": "sketch_mode_disable"
+ }
+ },
+ {
+ "cmdId": "[uuid]",
+ "range": [
+ 294,
+ 330,
+ 0
+ ],
+ "command": {
+ "type": "object_visible",
+ "object_id": "[uuid]",
+ "hidden": true
+ }
+ }
+]
diff --git a/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap
new file mode 100644
index 000000000..74ca9fa50
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap
@@ -0,0 +1,6 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Artifact graph flowchart loft_graph.kcl
+extension: md
+snapshot_kind: binary
+---
diff --git a/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap.md b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap.md
new file mode 100644
index 000000000..6a1fa24b8
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_flowchart.snap.md
@@ -0,0 +1,33 @@
+```mermaid
+flowchart LR
+ subgraph path2 [Path]
+ 2["Path
[45, 80, 0]"]
+ 3["Segment
[86, 108, 0]"]
+ 4["Segment
[114, 136, 0]"]
+ 5["Segment
[142, 198, 0]"]
+ 6["Segment
[204, 211, 0]"]
+ 7[Solid2d]
+ end
+ subgraph path9 [Path]
+ 9["Path
[294, 330, 0]"]
+ 10["Segment
[336, 349, 0]"]
+ 11["Segment
[355, 378, 0]"]
+ 12["Segment
[384, 440, 0]"]
+ 13["Segment
[446, 453, 0]"]
+ 14[Solid2d]
+ end
+ 1["Plane
[12, 31, 0]"]
+ 8["Plane
[223, 244, 0]"]
+ 1 --- 2
+ 2 --- 3
+ 2 --- 4
+ 2 --- 5
+ 2 --- 6
+ 2 --- 7
+ 8 --- 9
+ 9 --- 10
+ 9 --- 11
+ 9 --- 12
+ 9 --- 13
+ 9 --- 14
+```
diff --git a/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap
new file mode 100644
index 000000000..5eca1dbe4
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap
@@ -0,0 +1,6 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Artifact graph mind map loft_graph.kcl
+extension: md
+snapshot_kind: binary
+---
diff --git a/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap.md b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap.md
new file mode 100644
index 000000000..f47a0f066
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/artifact_graph_mind_map.snap.md
@@ -0,0 +1,18 @@
+```mermaid
+mindmap
+ root
+ Plane
+ Path
+ Segment
+ Segment
+ Segment
+ Segment
+ Solid2d
+ Plane
+ Path
+ Segment
+ Segment
+ Segment
+ Segment
+ Solid2d
+```
diff --git a/src/wasm-lib/kcl/tests/loft_graph/ast.snap b/src/wasm-lib/kcl/tests/loft_graph/ast.snap
new file mode 100644
index 000000000..eef1ed6be
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/ast.snap
@@ -0,0 +1,670 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Result of parsing loft_graph.kcl
+---
+{
+ "Ok": {
+ "body": [
+ {
+ "declaration": {
+ "end": 31,
+ "id": {
+ "end": 9,
+ "name": "sketch001",
+ "start": 0,
+ "type": "Identifier"
+ },
+ "init": {
+ "arguments": [
+ {
+ "end": 30,
+ "raw": "'XZ'",
+ "start": 26,
+ "type": "Literal",
+ "type": "Literal",
+ "value": "XZ"
+ }
+ ],
+ "callee": {
+ "end": 25,
+ "name": "startSketchOn",
+ "start": 12,
+ "type": "Identifier"
+ },
+ "end": 31,
+ "start": 12,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ "start": 0,
+ "type": "VariableDeclarator"
+ },
+ "end": 31,
+ "kind": "const",
+ "start": 0,
+ "type": "VariableDeclaration",
+ "type": "VariableDeclaration"
+ },
+ {
+ "declaration": {
+ "end": 211,
+ "id": {
+ "end": 42,
+ "name": "profile001",
+ "start": 32,
+ "type": "Identifier"
+ },
+ "init": {
+ "body": [
+ {
+ "arguments": [
+ {
+ "elements": [
+ {
+ "end": 63,
+ "raw": "34",
+ "start": 61,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 34.0,
+ "suffix": "None"
+ }
+ },
+ {
+ "end": 67,
+ "raw": "42",
+ "start": 65,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 42.0,
+ "suffix": "None"
+ }
+ }
+ ],
+ "end": 68,
+ "start": 60,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ },
+ {
+ "end": 79,
+ "name": "sketch001",
+ "start": 70,
+ "type": "Identifier",
+ "type": "Identifier"
+ }
+ ],
+ "callee": {
+ "end": 59,
+ "name": "startProfileAt",
+ "start": 45,
+ "type": "Identifier"
+ },
+ "end": 80,
+ "start": 45,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ {
+ "arguments": [
+ {
+ "type": "LabeledArg",
+ "label": {
+ "type": "Identifier",
+ "name": "end"
+ },
+ "arg": {
+ "elements": [
+ {
+ "end": 101,
+ "raw": "102",
+ "start": 98,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 102.0,
+ "suffix": "None"
+ }
+ },
+ {
+ "end": 106,
+ "raw": "152",
+ "start": 103,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 152.0,
+ "suffix": "None"
+ }
+ }
+ ],
+ "end": 107,
+ "start": 97,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ }
+ }
+ ],
+ "callee": {
+ "end": 90,
+ "name": "line",
+ "start": 86,
+ "type": "Identifier"
+ },
+ "end": 108,
+ "start": 86,
+ "type": "CallExpressionKw",
+ "type": "CallExpressionKw",
+ "unlabeled": null
+ },
+ {
+ "arguments": [
+ {
+ "type": "LabeledArg",
+ "label": {
+ "type": "Identifier",
+ "name": "end"
+ },
+ "arg": {
+ "elements": [
+ {
+ "end": 128,
+ "raw": "76",
+ "start": 126,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 76.0,
+ "suffix": "None"
+ }
+ },
+ {
+ "argument": {
+ "end": 134,
+ "raw": "138",
+ "start": 131,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 138.0,
+ "suffix": "None"
+ }
+ },
+ "end": 134,
+ "operator": "-",
+ "start": 130,
+ "type": "UnaryExpression",
+ "type": "UnaryExpression"
+ }
+ ],
+ "end": 135,
+ "start": 125,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ }
+ }
+ ],
+ "callee": {
+ "end": 118,
+ "name": "line",
+ "start": 114,
+ "type": "Identifier"
+ },
+ "end": 136,
+ "start": 114,
+ "type": "CallExpressionKw",
+ "type": "CallExpressionKw",
+ "unlabeled": null
+ },
+ {
+ "arguments": [
+ {
+ "type": "LabeledArg",
+ "label": {
+ "type": "Identifier",
+ "name": "endAbsolute"
+ },
+ "arg": {
+ "elements": [
+ {
+ "arguments": [
+ {
+ "end": 177,
+ "start": 176,
+ "type": "PipeSubstitution",
+ "type": "PipeSubstitution"
+ }
+ ],
+ "callee": {
+ "end": 175,
+ "name": "profileStartX",
+ "start": 162,
+ "type": "Identifier"
+ },
+ "end": 178,
+ "start": 162,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ {
+ "arguments": [
+ {
+ "end": 195,
+ "start": 194,
+ "type": "PipeSubstitution",
+ "type": "PipeSubstitution"
+ }
+ ],
+ "callee": {
+ "end": 193,
+ "name": "profileStartY",
+ "start": 180,
+ "type": "Identifier"
+ },
+ "end": 196,
+ "start": 180,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ }
+ ],
+ "end": 197,
+ "start": 161,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ }
+ }
+ ],
+ "callee": {
+ "end": 146,
+ "name": "line",
+ "start": 142,
+ "type": "Identifier"
+ },
+ "end": 198,
+ "start": 142,
+ "type": "CallExpressionKw",
+ "type": "CallExpressionKw",
+ "unlabeled": null
+ },
+ {
+ "arguments": [],
+ "callee": {
+ "end": 209,
+ "name": "close",
+ "start": 204,
+ "type": "Identifier"
+ },
+ "end": 211,
+ "start": 204,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ }
+ ],
+ "end": 211,
+ "start": 45,
+ "type": "PipeExpression",
+ "type": "PipeExpression"
+ },
+ "start": 32,
+ "type": "VariableDeclarator"
+ },
+ "end": 211,
+ "kind": "const",
+ "start": 32,
+ "type": "VariableDeclaration",
+ "type": "VariableDeclaration"
+ },
+ {
+ "declaration": {
+ "end": 244,
+ "id": {
+ "end": 220,
+ "name": "plane001",
+ "start": 212,
+ "type": "Identifier"
+ },
+ "init": {
+ "arguments": [
+ {
+ "end": 239,
+ "raw": "'XZ'",
+ "start": 235,
+ "type": "Literal",
+ "type": "Literal",
+ "value": "XZ"
+ },
+ {
+ "end": 243,
+ "raw": "50",
+ "start": 241,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 50.0,
+ "suffix": "None"
+ }
+ }
+ ],
+ "callee": {
+ "end": 234,
+ "name": "offsetPlane",
+ "start": 223,
+ "type": "Identifier"
+ },
+ "end": 244,
+ "start": 223,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ "start": 212,
+ "type": "VariableDeclarator"
+ },
+ "end": 244,
+ "kind": "const",
+ "start": 212,
+ "type": "VariableDeclaration",
+ "type": "VariableDeclaration"
+ },
+ {
+ "declaration": {
+ "end": 280,
+ "id": {
+ "end": 254,
+ "name": "sketch002",
+ "start": 245,
+ "type": "Identifier"
+ },
+ "init": {
+ "arguments": [
+ {
+ "end": 279,
+ "name": "plane001",
+ "start": 271,
+ "type": "Identifier",
+ "type": "Identifier"
+ }
+ ],
+ "callee": {
+ "end": 270,
+ "name": "startSketchOn",
+ "start": 257,
+ "type": "Identifier"
+ },
+ "end": 280,
+ "start": 257,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ "start": 245,
+ "type": "VariableDeclarator"
+ },
+ "end": 280,
+ "kind": "const",
+ "start": 245,
+ "type": "VariableDeclaration",
+ "type": "VariableDeclaration"
+ },
+ {
+ "declaration": {
+ "end": 453,
+ "id": {
+ "end": 291,
+ "name": "profile002",
+ "start": 281,
+ "type": "Identifier"
+ },
+ "init": {
+ "body": [
+ {
+ "arguments": [
+ {
+ "elements": [
+ {
+ "end": 312,
+ "raw": "39",
+ "start": 310,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 39.0,
+ "suffix": "None"
+ }
+ },
+ {
+ "end": 317,
+ "raw": "172",
+ "start": 314,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 172.0,
+ "suffix": "None"
+ }
+ }
+ ],
+ "end": 318,
+ "start": 309,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ },
+ {
+ "end": 329,
+ "name": "sketch002",
+ "start": 320,
+ "type": "Identifier",
+ "type": "Identifier"
+ }
+ ],
+ "callee": {
+ "end": 308,
+ "name": "startProfileAt",
+ "start": 294,
+ "type": "Identifier"
+ },
+ "end": 330,
+ "start": 294,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ {
+ "arguments": [
+ {
+ "end": 345,
+ "raw": "184",
+ "start": 342,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 184.0,
+ "suffix": "None"
+ }
+ },
+ {
+ "end": 348,
+ "start": 347,
+ "type": "PipeSubstitution",
+ "type": "PipeSubstitution"
+ }
+ ],
+ "callee": {
+ "end": 341,
+ "name": "xLine",
+ "start": 336,
+ "type": "Identifier"
+ },
+ "end": 349,
+ "start": 336,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ {
+ "arguments": [
+ {
+ "type": "LabeledArg",
+ "label": {
+ "type": "Identifier",
+ "name": "end"
+ },
+ "arg": {
+ "elements": [
+ {
+ "argument": {
+ "end": 370,
+ "raw": "78",
+ "start": 368,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 78.0,
+ "suffix": "None"
+ }
+ },
+ "end": 370,
+ "operator": "-",
+ "start": 367,
+ "type": "UnaryExpression",
+ "type": "UnaryExpression"
+ },
+ {
+ "argument": {
+ "end": 376,
+ "raw": "145",
+ "start": 373,
+ "type": "Literal",
+ "type": "Literal",
+ "value": {
+ "value": 145.0,
+ "suffix": "None"
+ }
+ },
+ "end": 376,
+ "operator": "-",
+ "start": 372,
+ "type": "UnaryExpression",
+ "type": "UnaryExpression"
+ }
+ ],
+ "end": 377,
+ "start": 366,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ }
+ }
+ ],
+ "callee": {
+ "end": 359,
+ "name": "line",
+ "start": 355,
+ "type": "Identifier"
+ },
+ "end": 378,
+ "start": 355,
+ "type": "CallExpressionKw",
+ "type": "CallExpressionKw",
+ "unlabeled": null
+ },
+ {
+ "arguments": [
+ {
+ "type": "LabeledArg",
+ "label": {
+ "type": "Identifier",
+ "name": "endAbsolute"
+ },
+ "arg": {
+ "elements": [
+ {
+ "arguments": [
+ {
+ "end": 419,
+ "start": 418,
+ "type": "PipeSubstitution",
+ "type": "PipeSubstitution"
+ }
+ ],
+ "callee": {
+ "end": 417,
+ "name": "profileStartX",
+ "start": 404,
+ "type": "Identifier"
+ },
+ "end": 420,
+ "start": 404,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ },
+ {
+ "arguments": [
+ {
+ "end": 437,
+ "start": 436,
+ "type": "PipeSubstitution",
+ "type": "PipeSubstitution"
+ }
+ ],
+ "callee": {
+ "end": 435,
+ "name": "profileStartY",
+ "start": 422,
+ "type": "Identifier"
+ },
+ "end": 438,
+ "start": 422,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ }
+ ],
+ "end": 439,
+ "start": 403,
+ "type": "ArrayExpression",
+ "type": "ArrayExpression"
+ }
+ }
+ ],
+ "callee": {
+ "end": 388,
+ "name": "line",
+ "start": 384,
+ "type": "Identifier"
+ },
+ "end": 440,
+ "start": 384,
+ "type": "CallExpressionKw",
+ "type": "CallExpressionKw",
+ "unlabeled": null
+ },
+ {
+ "arguments": [],
+ "callee": {
+ "end": 451,
+ "name": "close",
+ "start": 446,
+ "type": "Identifier"
+ },
+ "end": 453,
+ "start": 446,
+ "type": "CallExpression",
+ "type": "CallExpression"
+ }
+ ],
+ "end": 453,
+ "start": 294,
+ "type": "PipeExpression",
+ "type": "PipeExpression"
+ },
+ "start": 281,
+ "type": "VariableDeclarator"
+ },
+ "end": 453,
+ "kind": "const",
+ "start": 281,
+ "type": "VariableDeclaration",
+ "type": "VariableDeclaration"
+ }
+ ],
+ "end": 454,
+ "start": 0
+ }
+}
diff --git a/src/wasm-lib/kcl/tests/loft_graph/input.kcl b/src/wasm-lib/kcl/tests/loft_graph/input.kcl
new file mode 100644
index 000000000..d2cbb7457
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/input.kcl
@@ -0,0 +1,13 @@
+sketch001 = startSketchOn('XZ')
+profile001 = startProfileAt([34, 42], sketch001)
+ |> line(end = [102, 152])
+ |> line(end = [76, -138])
+ |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
+ |> close()
+plane001 = offsetPlane('XZ', 50)
+sketch002 = startSketchOn(plane001)
+profile002 = startProfileAt([39, 172], sketch002)
+ |> xLine(184, %)
+ |> line(end = [-78, -145])
+ |> line(endAbsolute = [profileStartX(%), profileStartY(%)])
+ |> close()
diff --git a/src/wasm-lib/kcl/tests/loft_graph/ops.snap b/src/wasm-lib/kcl/tests/loft_graph/ops.snap
new file mode 100644
index 000000000..deb3ce5fb
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/ops.snap
@@ -0,0 +1,70 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Operations executed loft_graph.kcl
+---
+[
+ {
+ "labeledArgs": {
+ "data": {
+ "sourceRange": [
+ 26,
+ 30,
+ 0
+ ]
+ }
+ },
+ "name": "startSketchOn",
+ "sourceRange": [
+ 12,
+ 31,
+ 0
+ ],
+ "type": "StdLibCall",
+ "unlabeledArg": null
+ },
+ {
+ "labeledArgs": {
+ "offset": {
+ "sourceRange": [
+ 241,
+ 243,
+ 0
+ ]
+ },
+ "std_plane": {
+ "sourceRange": [
+ 235,
+ 239,
+ 0
+ ]
+ }
+ },
+ "name": "offsetPlane",
+ "sourceRange": [
+ 223,
+ 244,
+ 0
+ ],
+ "type": "StdLibCall",
+ "unlabeledArg": null
+ },
+ {
+ "labeledArgs": {
+ "data": {
+ "sourceRange": [
+ 271,
+ 279,
+ 0
+ ]
+ }
+ },
+ "name": "startSketchOn",
+ "sourceRange": [
+ 257,
+ 280,
+ 0
+ ],
+ "type": "StdLibCall",
+ "unlabeledArg": null
+ }
+]
diff --git a/src/wasm-lib/kcl/tests/loft_graph/program_memory.snap b/src/wasm-lib/kcl/tests/loft_graph/program_memory.snap
new file mode 100644
index 000000000..ac69ead70
--- /dev/null
+++ b/src/wasm-lib/kcl/tests/loft_graph/program_memory.snap
@@ -0,0 +1,435 @@
+---
+source: kcl/src/simulation_tests.rs
+description: Program memory after executing loft_graph.kcl
+---
+{
+ "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": []
+ },
+ "plane001": {
+ "type": "Plane",
+ "value": {
+ "id": "[uuid]",
+ "artifactId": "[uuid]",
+ "value": "Custom",
+ "origin": {
+ "x": 0.0,
+ "y": -50.0,
+ "z": 0.0
+ },
+ "xAxis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "yAxis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zAxis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": []
+ }
+ },
+ "profile001": {
+ "type": "Sketch",
+ "value": {
+ "type": "Sketch",
+ "id": "[uuid]",
+ "paths": [
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 86,
+ 108,
+ 0
+ ]
+ },
+ "from": [
+ 34.0,
+ 42.0
+ ],
+ "tag": null,
+ "to": [
+ 136.0,
+ 194.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 114,
+ 136,
+ 0
+ ]
+ },
+ "from": [
+ 136.0,
+ 194.0
+ ],
+ "tag": null,
+ "to": [
+ 212.0,
+ 56.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 142,
+ 198,
+ 0
+ ]
+ },
+ "from": [
+ 212.0,
+ 56.0
+ ],
+ "tag": null,
+ "to": [
+ 34.0,
+ 42.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 204,
+ 211,
+ 0
+ ]
+ },
+ "from": [
+ 34.0,
+ 42.0
+ ],
+ "tag": null,
+ "to": [
+ 34.0,
+ 42.0
+ ],
+ "type": "ToPoint"
+ }
+ ],
+ "on": {
+ "type": "plane",
+ "id": "[uuid]",
+ "artifactId": "[uuid]",
+ "value": "XZ",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "xAxis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "yAxis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zAxis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": []
+ },
+ "start": {
+ "from": [
+ 34.0,
+ 42.0
+ ],
+ "to": [
+ 34.0,
+ 42.0
+ ],
+ "tag": null,
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 45,
+ 80,
+ 0
+ ]
+ }
+ },
+ "artifactId": "[uuid]",
+ "originalId": "[uuid]",
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": [
+ {
+ "sourceRange": [
+ 45,
+ 80,
+ 0
+ ]
+ }
+ ]
+ }
+ },
+ "profile002": {
+ "type": "Sketch",
+ "value": {
+ "type": "Sketch",
+ "id": "[uuid]",
+ "paths": [
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 336,
+ 349,
+ 0
+ ]
+ },
+ "from": [
+ 39.0,
+ 172.0
+ ],
+ "tag": null,
+ "to": [
+ 223.0,
+ 172.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 355,
+ 378,
+ 0
+ ]
+ },
+ "from": [
+ 223.0,
+ 172.0
+ ],
+ "tag": null,
+ "to": [
+ 145.0,
+ 27.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 384,
+ 440,
+ 0
+ ]
+ },
+ "from": [
+ 145.0,
+ 27.0
+ ],
+ "tag": null,
+ "to": [
+ 39.0,
+ 172.0
+ ],
+ "type": "ToPoint"
+ },
+ {
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 446,
+ 453,
+ 0
+ ]
+ },
+ "from": [
+ 39.0,
+ 172.0
+ ],
+ "tag": null,
+ "to": [
+ 39.0,
+ 172.0
+ ],
+ "type": "ToPoint"
+ }
+ ],
+ "on": {
+ "type": "plane",
+ "id": "[uuid]",
+ "artifactId": "[uuid]",
+ "value": "Custom",
+ "origin": {
+ "x": 0.0,
+ "y": -50.0,
+ "z": 0.0
+ },
+ "xAxis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "yAxis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zAxis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": []
+ },
+ "start": {
+ "from": [
+ 39.0,
+ 172.0
+ ],
+ "to": [
+ 39.0,
+ 172.0
+ ],
+ "tag": null,
+ "__geoMeta": {
+ "id": "[uuid]",
+ "sourceRange": [
+ 294,
+ 330,
+ 0
+ ]
+ }
+ },
+ "artifactId": "[uuid]",
+ "originalId": "[uuid]",
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": [
+ {
+ "sourceRange": [
+ 294,
+ 330,
+ 0
+ ]
+ }
+ ]
+ }
+ },
+ "sketch001": {
+ "type": "Plane",
+ "value": {
+ "id": "[uuid]",
+ "artifactId": "[uuid]",
+ "value": "XZ",
+ "origin": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "xAxis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "yAxis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zAxis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": []
+ }
+ },
+ "sketch002": {
+ "type": "Plane",
+ "value": {
+ "id": "[uuid]",
+ "artifactId": "[uuid]",
+ "value": "Custom",
+ "origin": {
+ "x": 0.0,
+ "y": -50.0,
+ "z": 0.0
+ },
+ "xAxis": {
+ "x": 1.0,
+ "y": 0.0,
+ "z": 0.0
+ },
+ "yAxis": {
+ "x": 0.0,
+ "y": 0.0,
+ "z": 1.0
+ },
+ "zAxis": {
+ "x": 0.0,
+ "y": -1.0,
+ "z": 0.0
+ },
+ "units": {
+ "type": "Mm"
+ },
+ "__meta": []
+ }
+ }
+ },
+ "parent": null
+ }
+ ],
+ "currentEnv": 0,
+ "return": null
+}
diff --git a/src/wasm-lib/kcl/tests/loft_graph/rendered_model.png b/src/wasm-lib/kcl/tests/loft_graph/rendered_model.png
new file mode 100644
index 000000000..0bf2acfc7
Binary files /dev/null and b/src/wasm-lib/kcl/tests/loft_graph/rendered_model.png differ