From 4f5731cf294e19bae7f7dc1892f9d590ae9d40bd Mon Sep 17 00:00:00 2001 From: Kurt Hutten Irev-Dev Date: Wed, 11 Sep 2024 06:49:40 +1000 Subject: [PATCH] circle --- .github/workflows/playwright.yml | 2 +- docs/kcl/angleToMatchLengthX.md | 40 ++ docs/kcl/angleToMatchLengthY.md | 40 ++ docs/kcl/angledLine.md | 80 +++ docs/kcl/angledLineOfXLength.md | 80 +++ docs/kcl/angledLineOfYLength.md | 80 +++ docs/kcl/angledLineThatIntersects.md | 80 +++ docs/kcl/angledLineToX.md | 80 +++ docs/kcl/angledLineToY.md | 80 +++ docs/kcl/arc.md | 80 +++ docs/kcl/bezierCurve.md | 80 +++ docs/kcl/chamfer.md | 40 ++ e2e/playwright/editor-tests.spec.ts | 2 +- e2e/playwright/sketch-tests.spec.ts | 94 +++- e2e/playwright/snapshot-tests.spec.ts | 58 +++ ...hould-look-right-1-Google-Chrome-linux.png | Bin 0 -> 42339 bytes ...hould-look-right-1-Google-Chrome-win32.png | Bin 0 -> 37820 bytes e2e/playwright/storageStates.ts | 4 +- .../testing-segment-overlays.spec.ts | 74 +++ src/clientSideScene/ClientSideSceneComp.tsx | 8 +- src/clientSideScene/sceneEntities.ts | 458 ++++++++++++++++-- src/clientSideScene/segments.ts | 106 ++++ src/components/ModelingMachineProvider.tsx | 20 +- src/lang/langHelpers.ts | 3 +- src/lang/std/sketch.ts | 234 ++++++++- src/lang/std/sketchcombos.ts | 27 +- src/lang/std/stdTypes.ts | 27 +- src/lib/toolbar.ts | 34 +- src/machines/modelingMachine.ts | 129 ++++- src/wasm-lib/kcl/src/docs.rs | 5 +- src/wasm-lib/kcl/src/executor.rs | 21 +- src/wasm-lib/kcl/src/std/args.rs | 4 +- src/wasm-lib/kcl/src/std/convert.rs | 2 +- src/wasm-lib/kcl/src/std/extrude.rs | 2 +- src/wasm-lib/kcl/src/std/helix.rs | 2 +- src/wasm-lib/kcl/src/std/loft.rs | 8 +- src/wasm-lib/kcl/src/std/math.rs | 2 +- src/wasm-lib/kcl/src/std/mod.rs | 2 +- src/wasm-lib/kcl/src/std/patterns.rs | 6 +- src/wasm-lib/kcl/src/std/planes.rs | 8 +- src/wasm-lib/kcl/src/std/revolve.rs | 8 +- src/wasm-lib/kcl/src/std/shapes.rs | 107 +++- src/wasm-lib/kcl/src/std/sketch.rs | 6 +- src/wasm-lib/kcl/src/unparser.rs | 16 +- ...l_test_example_get_next_adjacent_edge0.png | Bin 65258 -> 64161 bytes ...st_example_get_previous_adjacent_edge0.png | Bin 64161 -> 65258 bytes .../outputs/serial_test_example_loft1.png | Bin 138174 -> 138890 bytes .../outputs/serial_test_example_loft2.png | Bin 138174 -> 138890 bytes .../serial_test_example_offset_plane0.png | Bin 103371 -> 104052 bytes .../tests/executor/inputs/cylinder.kcl | 2 +- .../executor/inputs/fillet-and-shell.kcl | 4 +- .../focusrite_scarlett_mounting_braket.kcl | 23 +- .../tests/executor/inputs/global-tags.kcl | 22 +- .../tests/executor/inputs/helix_ccw.kcl | 2 +- .../tests/executor/inputs/helix_defaults.kcl | 2 +- .../helix_defaults_negative_extrude.kcl | 2 +- .../executor/inputs/helix_with_length.kcl | 2 +- src/wasm-lib/tests/executor/inputs/lego.kcl | 4 +- .../tests/executor/inputs/pattern_vase.kcl | 2 +- .../executor/inputs/server-rack-heavy.kcl | 18 +- .../executor/inputs/server-rack-lite.kcl | 18 +- .../inputs/sketch_on_face_circle_tagged.kcl | 2 +- .../tests/executor/inputs/slow_lego.kcl.tmpl | 4 +- src/wasm-lib/tests/executor/main.rs | 46 +- 64 files changed, 2179 insertions(+), 213 deletions(-) create mode 100644 e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png create mode 100644 e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-win32.png diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 21cfac5ade..879269cb00 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -262,7 +262,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-14] - timeout-minutes: 40 + timeout-minutes: 60 runs-on: ${{ matrix.os }} needs: check-rust-changes steps: diff --git a/docs/kcl/angleToMatchLengthX.md b/docs/kcl/angleToMatchLengthX.md index e666283df5..f66c1d9dbb 100644 --- a/docs/kcl/angleToMatchLengthX.md +++ b/docs/kcl/angleToMatchLengthX.md @@ -270,6 +270,26 @@ const extrusion = extrude(5, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -479,6 +499,26 @@ const extrusion = extrude(5, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angleToMatchLengthY.md b/docs/kcl/angleToMatchLengthY.md index 20243dab08..3ebb5278e3 100644 --- a/docs/kcl/angleToMatchLengthY.md +++ b/docs/kcl/angleToMatchLengthY.md @@ -274,6 +274,26 @@ const extrusion = extrude(5, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -483,6 +503,26 @@ const extrusion = extrude(5, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLine.md b/docs/kcl/angledLine.md index 432fcb642e..f5db4221da 100644 --- a/docs/kcl/angledLine.md +++ b/docs/kcl/angledLine.md @@ -189,6 +189,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -398,6 +418,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -609,6 +649,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -818,6 +878,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLineOfXLength.md b/docs/kcl/angledLineOfXLength.md index 2ff7573e98..d34cddd640 100644 --- a/docs/kcl/angledLineOfXLength.md +++ b/docs/kcl/angledLineOfXLength.md @@ -188,6 +188,26 @@ const extrusion = extrude(10, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -397,6 +417,26 @@ const extrusion = extrude(10, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -608,6 +648,26 @@ const extrusion = extrude(10, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -817,6 +877,26 @@ const extrusion = extrude(10, sketch001) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLineOfYLength.md b/docs/kcl/angledLineOfYLength.md index dd87e73447..5e15b2d4b4 100644 --- a/docs/kcl/angledLineOfYLength.md +++ b/docs/kcl/angledLineOfYLength.md @@ -190,6 +190,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -399,6 +419,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -610,6 +650,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -819,6 +879,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLineThatIntersects.md b/docs/kcl/angledLineThatIntersects.md index 9e039ef234..92a527010d 100644 --- a/docs/kcl/angledLineThatIntersects.md +++ b/docs/kcl/angledLineThatIntersects.md @@ -282,6 +282,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -491,6 +511,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -702,6 +742,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -911,6 +971,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLineToX.md b/docs/kcl/angledLineToX.md index 27c299817b..082017d084 100644 --- a/docs/kcl/angledLineToX.md +++ b/docs/kcl/angledLineToX.md @@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/angledLineToY.md b/docs/kcl/angledLineToY.md index ffc1f0fae0..171822fde5 100644 --- a/docs/kcl/angledLineToY.md +++ b/docs/kcl/angledLineToY.md @@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/arc.md b/docs/kcl/arc.md index ac9fca5612..faf8f55753 100644 --- a/docs/kcl/arc.md +++ b/docs/kcl/arc.md @@ -200,6 +200,26 @@ const exampleSketch = startSketchOn('XZ') to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -409,6 +429,26 @@ const exampleSketch = startSketchOn('XZ') to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -620,6 +660,26 @@ const exampleSketch = startSketchOn('XZ') to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -829,6 +889,26 @@ const exampleSketch = startSketchOn('XZ') to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/bezierCurve.md b/docs/kcl/bezierCurve.md index 9db9871a97..49ac9d1496 100644 --- a/docs/kcl/bezierCurve.md +++ b/docs/kcl/bezierCurve.md @@ -193,6 +193,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -402,6 +422,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -613,6 +653,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -822,6 +882,26 @@ const example = extrude(10, exampleSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/docs/kcl/chamfer.md b/docs/kcl/chamfer.md index 29551c4f76..bce8ae9bad 100644 --- a/docs/kcl/chamfer.md +++ b/docs/kcl/chamfer.md @@ -415,6 +415,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], @@ -819,6 +839,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch) to: [number, number], type: "TangentialArc", } | +{ + // arc's direction + ccw: bool, + // the arc's center + center: [number, number], + // The from point. + from: [number, number], + // the arc's radius + radius: number, + // The tag of the path. + tag: { + digest: [number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number, number], + end: number, + start: number, + value: string, +}, + // The to point. + to: [number, number], + type: "Circle", +} | { // The from point. from: [number, number], diff --git a/e2e/playwright/editor-tests.spec.ts b/e2e/playwright/editor-tests.spec.ts index 82c5e84886..f33e5d190f 100644 --- a/e2e/playwright/editor-tests.spec.ts +++ b/e2e/playwright/editor-tests.spec.ts @@ -558,7 +558,7 @@ test.describe('Editor tests', () => { await page.keyboard.press('ArrowDown') await page.keyboard.press('Enter') await page.keyboard.type(`const extrusion = startSketchOn('XY') - |> circle([0, 0], dia/2, %) + |> circle({ center: [0, 0], radius: dia/2 }, %) |> hole(squareHole(length, width, height), %) |> extrude(height, %)`) diff --git a/e2e/playwright/sketch-tests.spec.ts b/e2e/playwright/sketch-tests.spec.ts index 0d15c7dd14..1b90cbad0e 100644 --- a/e2e/playwright/sketch-tests.spec.ts +++ b/e2e/playwright/sketch-tests.spec.ts @@ -149,14 +149,16 @@ test.describe('Sketch tests', () => { await page.getByRole('button', { name: 'line Line', exact: true }).click() await page.waitForTimeout(100) - await page.mouse.click(700, 200) + await expect(async () => { + await page.mouse.click(700, 200) - await expect.poll(u.normalisedEditorCode) - .toBe(`const sketch001 = startSketchOn('XZ') + await expect.poll(u.normalisedEditorCode, { timeout: 1000 }) + .toBe(`const sketch001 = startSketchOn('XZ') |> startProfileAt([12.34, -12.34], %) |> line([-12.34, 12.34], %) `) + }).toPass({ timeout: 40_000, intervals: [1_000] }) }) test('Can exit selection of face', async ({ page }) => { // Load the app with the code panes @@ -344,6 +346,92 @@ test.describe('Sketch tests', () => { }) }) + test('Can edit a circle center and radius by dragging its handles', async ({ + page, + }) => { + const u = await getUtils(page) + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `const sketch001 = startSketchOn('XZ') + |> circle({ center: [4.61, -5.01], radius: 8 }, %)` + ) + }) + + await page.setViewportSize({ width: 1200, height: 500 }) + + await u.waitForAuthSkipAppStart() + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + + await page.waitForTimeout(100) + await u.openAndClearDebugPanel() + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_look_at', + vantage: { x: 0, y: -1250, z: 580 }, + center: { x: 0, y: 0, z: 0 }, + up: { x: 0, y: 0, z: 1 }, + }, + }) + await page.waitForTimeout(100) + await u.sendCustomCmd({ + type: 'modeling_cmd_req', + cmd_id: uuidv4(), + cmd: { + type: 'default_camera_get_settings', + }, + }) + await page.waitForTimeout(100) + + const startPX = [667, 325] + + const dragPX = 40 + + await page + .getByText('circle({ center: [4.61, -5.01], radius: 8 }, %)') + .click() + await expect( + page.getByRole('button', { name: 'Edit Sketch' }) + ).toBeVisible() + await page.getByRole('button', { name: 'Edit Sketch' }).click() + await page.waitForTimeout(400) + let prevContent = await page.locator('.cm-content').innerText() + + await expect(page.getByTestId('segment-overlay')).toHaveCount(1) + + await test.step('drag circle center handle', async () => { + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: startPX[0], y: startPX[1] }, + targetPosition: { x: startPX[0] + dragPX, y: startPX[1] - dragPX }, + }) + await page.waitForTimeout(100) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + }) + + await test.step('drag circle radius handle', async () => { + await page.waitForTimeout(100) + + const lineEnd = await u.getBoundingBox('[data-overlay-index="0"]') + await page.waitForTimeout(100) + await page.dragAndDrop('#stream', '#stream', { + sourcePosition: { x: lineEnd.x - 5, y: lineEnd.y }, + targetPosition: { x: lineEnd.x + dragPX, y: lineEnd.y + dragPX }, + }) + await expect(page.locator('.cm-content')).not.toHaveText(prevContent) + prevContent = await page.locator('.cm-content').innerText() + }) + + // expect the code to have changed + await expect(page.locator('.cm-content')) + .toHaveText(`const sketch001 = startSketchOn('XZ') + |> circle({ center: [7.26, -2.37], radius: 11.79 }, %) +`) + }) test('Can edit a sketch that has been extruded in the same pipe', async ({ page, }) => { diff --git a/e2e/playwright/snapshot-tests.spec.ts b/e2e/playwright/snapshot-tests.spec.ts index 9aaeda388b..c153678941 100644 --- a/e2e/playwright/snapshot-tests.spec.ts +++ b/e2e/playwright/snapshot-tests.spec.ts @@ -532,6 +532,64 @@ test( }) } ) +test( + 'Draft circle should look right', + { tag: '@snapshot' }, + async ({ page, context }) => { + // FIXME: Skip on macos its being weird. + // test.skip(process.platform === 'darwin', 'Skip on macos') + + const u = await getUtils(page) + await page.setViewportSize({ width: 1200, height: 500 }) + const PUR = 400 / 37.5 //pixeltoUnitRatio + + await u.waitForAuthSkipAppStart() + await u.openDebugPanel() + + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).not.toBeDisabled() + await expect( + page.getByRole('button', { name: 'Start Sketch' }) + ).toBeVisible() + + // click on "Start Sketch" button + await u.clearCommandLogs() + await u.doAndWaitForImageDiff( + () => page.getByRole('button', { name: 'Start Sketch' }).click(), + 200 + ) + + // select a plane + await page.mouse.click(700, 200) + + await expect(page.locator('.cm-content')).toHaveText( + `const sketch001 = startSketchOn('XZ')` + ) + + await page.waitForTimeout(500) // TODO detect animation ending, or disable animation + await u.closeDebugPanel() + + const startXPx = 600 + + // Equip the rectangle tool + // await page.getByRole('button', { name: 'line Line', exact: true }).click() + await page.getByTestId('circle-center').click() + + // Draw the rectangle + await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20) + await page.mouse.move(startXPx + PUR * 10, 500 - PUR * 10, { steps: 5 }) + + // Ensure the draft rectangle looks the same as it usually does + await expect(page).toHaveScreenshot({ + maxDiffPixels: 100, + }) + await expect(page.locator('.cm-content')).toHaveText( + `const sketch001 = startSketchOn('XZ') + |> circle({ center: [14.44, -2.44], radius: 1 }, %)` + ) + } +) test.describe( 'Client side scene scale should match engine scale', diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..41c6a71bd24d4e719294a1e2a0660eaedc818221 GIT binary patch literal 42339 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 literal 0 HcmV?d00001 diff --git a/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-win32.png b/e2e/playwright/snapshot-tests.spec.ts-snapshots/Draft-circle-should-look-right-1-Google-Chrome-win32.png new file mode 100644 index 0000000000000000000000000000000000000000..fc059e049777210a560a1142365af6bd6e54be6f GIT binary patch literal 37820 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

ZSyqy1aGO+9)-Xa4v^?^X1 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 circle([ + |> circle({ center: [ width / 2, height - (keychainHoleSize + 1.5) - ], keychainHoleSize, %) + ], radius: keychainHoleSize }, %) |> extrude(-thickness, %)` export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `const thing = 1` diff --git a/e2e/playwright/testing-segment-overlays.spec.ts b/e2e/playwright/testing-segment-overlays.spec.ts index 2a08b2a9cf..f7cc286eea 100644 --- a/e2e/playwright/testing-segment-overlays.spec.ts +++ b/e2e/playwright/testing-segment-overlays.spec.ts @@ -774,6 +774,80 @@ const part001 = startSketchOn('XZ') locator: '[data-overlay-toolbar-index="12"]', }) }) + test('for segment [circle]', async ({ page }) => { + await page.addInitScript(async () => { + localStorage.setItem( + 'persistCode', + `const part001 = startSketchOn('XZ') + |> circle({ center: [1 + 0, 0], radius: 8 }, %) + ` + ) + localStorage.setItem('disableAxis', 'true') + }) + const u = await getUtils(page) + await page.setViewportSize({ width: 1200, height: 500 }) + + await u.waitForAuthSkipAppStart() + + // wait for execution done + await u.openDebugPanel() + await u.expectCmdLog('[data-message-type="execution-done"]') + await u.closeDebugPanel() + + await page + .getByText('circle({ center: [1 + 0, 0], radius: 8 }, %)') + .click() + await page.waitForTimeout(100) + await page.getByRole('button', { name: 'Edit Sketch' }).click() + await page.waitForTimeout(500) + + await expect(page.getByTestId('segment-overlay')).toHaveCount(1) + + const clickUnconstrained = _clickUnconstrained(page) + const clickConstrained = _clickConstrained(page) + + const hoverPos = { x: 789, y: 114 } as const + let ang = await u.getAngle('[data-overlay-index="0"]') + console.log('angl', ang) + console.log('circle center x') + await clickConstrained({ + hoverPos, + constraintType: 'xAbsolute', + expectBeforeUnconstrained: + 'circle({ center: [1 + 0, 0], radius: 8 }, %)', + expectAfterUnconstrained: 'circle({ center: [1, 0], radius: 8 }, %)', + expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)', + ang: ang + 105, + steps: 6, + locator: '[data-overlay-toolbar-index="0"]', + }) + console.log('circle center y') + await clickUnconstrained({ + hoverPos, + constraintType: 'yAbsolute', + expectBeforeUnconstrained: + 'circle({ center: [xAbs001, 0], radius: 8 }, %)', + expectAfterUnconstrained: + 'circle({ center: [xAbs001, yAbs001], radius: 8 }, %)', + expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)', + ang: ang + 105, + steps: 10, + locator: '[data-overlay-toolbar-index="0"]', + }) + console.log('circle radius') + await clickUnconstrained({ + hoverPos, + constraintType: 'radius', + expectBeforeUnconstrained: + 'circle({ center: [xAbs001, 0], radius: 8 }, %)', + expectAfterUnconstrained: + 'circle({ center: [xAbs001, 0], radius: radius001 }, %)', + expectFinal: 'circle({ center: [xAbs001, 0], radius: 8 }, %)', + ang: ang + 105, + steps: 10, + locator: '[data-overlay-toolbar-index="0"]', + }) + }) }) test.describe('Testing deleting a segment', () => { const _deleteSegmentSequence = diff --git a/src/clientSideScene/ClientSideSceneComp.tsx b/src/clientSideScene/ClientSideSceneComp.tsx index 7905eccfe6..c1ddec1e3e 100644 --- a/src/clientSideScene/ClientSideSceneComp.tsx +++ b/src/clientSideScene/ClientSideSceneComp.tsx @@ -124,7 +124,8 @@ export const ClientSideScene = ({ } else if ( state.matches({ Sketch: 'Line tool' }) || state.matches({ Sketch: 'Tangential arc to' }) || - state.matches({ Sketch: 'Rectangle tool' }) + state.matches({ Sketch: 'Rectangle tool' }) || + state.matches({ Sketch: 'Circle tool' }) ) { cursor = 'crosshair' } else { @@ -512,6 +513,11 @@ const ConstraintSymbol = ({ displayName: 'Intersection Offset', iconName: 'intersection-offset', }, + radius: { + varName: 'radius', + displayName: 'Radius', + iconName: 'dimension', + }, // implicit constraints vertical: { diff --git a/src/clientSideScene/sceneEntities.ts b/src/clientSideScene/sceneEntities.ts index 1f16c10d6b..45e09202f1 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -62,6 +62,7 @@ import { import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst' import { executeAst } from 'lang/langHelpers' import { + circleSegment, createArcGeometry, dashedStraight, createProfileStartHandle, @@ -72,6 +73,7 @@ import { addCallExpressionsToPipe, addCloseToPipe, addNewSketchLn, + changeCircleArguments, changeSketchArguments, updateStartProfileAtArgs, } from 'lang/std/sketch' @@ -87,6 +89,7 @@ import { createArrayExpression, createCallExpressionStdLib, createLiteral, + createObjectExpression, createPipeExpression, createPipeSubstitution, findUniqueName, @@ -119,10 +122,18 @@ export const TANGENTIAL_ARC_TO__SEGMENT_DASH = 'tangential-arc-to-segment-body-dashed' export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment' export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body' +export const CIRCLE_SEGMENT = 'circle-segment' +export const CIRCLE_SEGMENT_BODY = 'circle-segment-body' +export const CIRCLE_SEGMENT_DASH = 'circle-segment-body-dashed' +export const CIRCLE_CENTER_HANDLE = 'circle-center-handle' export const SEGMENT_WIDTH_PX = 1.6 export const HIDE_SEGMENT_LENGTH = 75 // in pixels export const HIDE_HOVER_SEGMENT_LENGTH = 60 // in pixels -export const SEGMENT_BODIES = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT] +export const SEGMENT_BODIES = [ + STRAIGHT_SEGMENT, + TANGENTIAL_ARC_TO_SEGMENT, + CIRCLE_SEGMENT, +] export const SEGMENT_BODIES_PLUS_PROFILE_START = [ ...SEGMENT_BODIES, PROFILE_START, @@ -191,6 +202,26 @@ export class SceneEntities { }) ) } + if ( + segment.userData.from && + segment.userData.to && + segment.userData.center && + segment.userData.radius && + segment.userData.type === CIRCLE_SEGMENT + ) { + callbacks.push( + this.updateCircleSegment({ + prevSegment: segment.userData.prevSegment, + from: segment.userData.from, + to: segment.userData.to, + center: segment.userData.center, + radius: segment.userData.radius, + group: segment, + scale: factor, + }) + ) + } + if (segment.name === PROFILE_START) { segment.scale.set(factor, factor, factor) } @@ -427,19 +458,21 @@ export class SceneEntities { maybeModdedAst, sketchGroup.start.__geoMeta.sourceRange ) - const _profileStart = createProfileStartHandle({ - from: sketchGroup.start.from, - id: sketchGroup.start.__geoMeta.id, - pathToNode: segPathToNode, - scale: factor, - theme: sceneInfra._theme, - }) - _profileStart.layers.set(SKETCH_LAYER) - _profileStart.traverse((child) => { - child.layers.set(SKETCH_LAYER) - }) - group.add(_profileStart) - this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart + if (sketchGroup?.value?.[0].type !== 'Circle') { + const _profileStart = createProfileStartHandle({ + from: sketchGroup.start.from, + id: sketchGroup.start.__geoMeta.id, + pathToNode: segPathToNode, + scale: factor, + theme: sceneInfra._theme, + }) + _profileStart.layers.set(SKETCH_LAYER) + _profileStart.traverse((child) => { + child.layers.set(SKETCH_LAYER) + }) + group.add(_profileStart) + this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart + } const callbacks: (() => SegmentOverlayPayload | null)[] = [] sketchGroup.value.forEach((segment, index) => { let segPathToNode = getNodePathFromSourceRange( @@ -504,6 +537,32 @@ export class SceneEntities { scale: factor, }) ) + } else if (segment.type === 'Circle') { + seg = circleSegment({ + prevSegment: sketchGroup.value[index - 1], + from: segment.from, + to: segment.to, + center: segment.center, + radius: segment.radius, + id: segment.__geoMeta.id, + pathToNode: segPathToNode, + isDraftSegment, + scale: factor, + texture: sceneInfra.extraSegmentTexture, + theme: sceneInfra._theme, + isSelected, + }) + callbacks.push( + this.updateCircleSegment({ + prevSegment: sketchGroup.value[index - 1], + from: segment.from, + to: segment.to, + center: segment.center, + radius: segment.radius, + group: seg, + scale: factor, + }) + ) } else { seg = straightSegment({ from: segment.from, @@ -612,7 +671,6 @@ export class SceneEntities { const lastSeg = sg?.value?.slice(-1)[0] || sg.start const index = sg.value.length // because we've added a new segment that's not in the memory yet, no need for `-1` - const mod = addNewSketchLn({ node: _ast, programMemory: kclManager.programMemory, @@ -759,6 +817,11 @@ export class SceneEntities { const startSketchOn = _node1.node?.declarations const startSketchOnInit = startSketchOn?.[0]?.init + const sg = sketchGroupFromKclValue( + kclManager.programMemory.get(variableDeclarationName), + variableDeclarationName + ) + if (err(sg)) return sg const tags: [string, string, string] = [ findUniqueName(_ast, 'rectangleSegmentA'), findUniqueName(_ast, 'rectangleSegmentB'), @@ -816,7 +879,7 @@ export class SceneEntities { programMemory.get(variableDeclarationName), variableDeclarationName ) - if (err(sketchGroup)) return sketchGroup + if (err(sketchGroup)) return Promise.reject(sketchGroup) const sgPaths = sketchGroup.value const orthoFactor = orthoScale(sceneInfra.camControls.camera) @@ -873,7 +936,7 @@ export class SceneEntities { programMemory.get(variableDeclarationName), variableDeclarationName ) - if (err(sketchGroup)) return sketchGroup + if (err(sketchGroup)) return Promise.reject(sketchGroup) const sgPaths = sketchGroup.value const orthoFactor = orthoScale(sceneInfra.camControls.camera) @@ -894,6 +957,151 @@ export class SceneEntities { }, }) } + setupDraftCircle = async ( + sketchPathToNode: PathToNode, + forward: [number, number, number], + up: [number, number, number], + sketchOrigin: [number, number, number], + circleCenter: [x: number, y: number] + ) => { + let _ast = structuredClone(kclManager.ast) + + const _node1 = getNodeFromPath( + _ast, + sketchPathToNode || [], + 'VariableDeclaration' + ) + if (trap(_node1)) return Promise.reject(_node1) + const variableDeclarationName = + _node1.node?.declarations?.[0]?.id?.name || '' + const startSketchOn = _node1.node?.declarations + const startSketchOnInit = startSketchOn?.[0]?.init + + startSketchOn[0].init = createPipeExpression([ + startSketchOnInit, + createCallExpressionStdLib('circle', [ + createObjectExpression({ + center: createArrayExpression([ + createLiteral(roundOff(circleCenter[0])), + createLiteral(roundOff(circleCenter[1])), + ]), + radius: createLiteral(1), + }), + createPipeSubstitution(), + ]), + ]) + + let _recastAst = parse(recast(_ast)) + if (trap(_recastAst)) return Promise.reject(_recastAst) + _ast = _recastAst + + // do a quick mock execution to get the program memory up-to-date + await kclManager.executeAstMock(_ast) + + const { programMemoryOverride, truncatedAst } = await this.setupSketch({ + sketchPathToNode, + forward, + up, + position: sketchOrigin, + maybeModdedAst: _ast, + draftExpressionsIndices: { start: 0, end: 0 }, + }) + + sceneInfra.setCallbacks({ + onMove: async (args) => { + const pathToNodeTwo = structuredClone(sketchPathToNode) + pathToNodeTwo[1][0] = 0 + + const _node = getNodeFromPath( + truncatedAst, + pathToNodeTwo || [], + 'VariableDeclaration' + ) + let modded = structuredClone(truncatedAst) + if (trap(_node)) return Promise.reject(_node) + const sketchInit = _node.node?.declarations?.[0]?.init + + const x = (args.intersectionPoint.twoD.x || 0) - circleCenter[0] + const y = (args.intersectionPoint.twoD.y || 0) - circleCenter[1] + + if (sketchInit.type === 'PipeExpression') { + const moddedResult = changeCircleArguments( + modded, + kclManager.programMemory, + [..._node.deepPath, ['body', 'PipeExpression'], [1, 'index']], + circleCenter, + Math.sqrt(x ** 2 + y ** 2) + ) + if (err(moddedResult)) return Promise.reject(moddedResult) + modded = moddedResult.modifiedAst + } + + const { programMemory } = await executeAst({ + ast: modded, + useFakeExecutor: true, + engineCommandManager: this.engineCommandManager, + programMemoryOverride, + }) + this.sceneProgramMemory = programMemory + const sketchGroup = sketchGroupFromKclValue( + programMemory.get(variableDeclarationName), + variableDeclarationName + ) + if (err(sketchGroup)) return sketchGroup + const sgPaths = sketchGroup.value + const orthoFactor = orthoScale(sceneInfra.camControls.camera) + + this.updateSegment( + sketchGroup.start, + 0, + 0, + _ast, + orthoFactor, + sketchGroup + ) + sgPaths.forEach((seg, index) => + this.updateSegment(seg, index, 0, _ast, orthoFactor, sketchGroup) + ) + }, + onClick: async (args) => { + // Commit the rectangle to the full AST/code and return to sketch.idle + const cornerPoint = args.intersectionPoint?.twoD + if (!cornerPoint || args.mouseEvent.button !== 0) return + + const x = roundOff((cornerPoint.x || 0) - circleCenter[0]) + const y = roundOff((cornerPoint.y || 0) - circleCenter[1]) + + const _node = getNodeFromPath( + _ast, + sketchPathToNode || [], + 'VariableDeclaration' + ) + if (trap(_node)) return Promise.reject(_node) + const sketchInit = _node.node?.declarations?.[0]?.init + + let modded = structuredClone(_ast) + if (sketchInit.type === 'PipeExpression') { + const moddedResult = changeCircleArguments( + modded, + kclManager.programMemory, + [..._node.deepPath, ['body', 'PipeExpression'], [1, 'index']], + circleCenter, + Math.sqrt(x ** 2 + y ** 2) + ) + if (err(moddedResult)) return Promise.reject(moddedResult) + modded = moddedResult.modifiedAst + + let _recastAst = parse(recast(modded)) + if (trap(_recastAst)) return Promise.reject(_recastAst) + _ast = _recastAst + + // Update the primary AST and unequip the rectangle tool + await kclManager.executeAstMock(_ast) + sceneInfra.modelingSend({ type: 'Finish circle' }) + } + }, + }) + } setupSketchIdleCallbacks = ({ pathToNode, up, @@ -1065,11 +1273,8 @@ export class SceneEntities { ? new Vector2(profileStart.position.x, profileStart.position.y) : _intersection2d - const group = getParentGroup(object, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - PROFILE_START, - ]) + const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START) + const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE]) if (!group) return const pathToNode: PathToNode = structuredClone(group.userData.pathToNode) const varDecIndex = pathToNode[1][0] @@ -1117,6 +1322,42 @@ export class SceneEntities { }, previousProgramMemory: kclManager.programMemory, }) + } else if ( + group.name === CIRCLE_SEGMENT && + // !subGroup treats grabbing the outer circumference of the circle + // as a drag of the center handle + (!subGroup || subGroup?.name === ARROWHEAD) + ) { + // is dragging the radius handle + modded = changeSketchArguments( + modifiedAst, + kclManager.programMemory, + [node.start, node.end], + { + type: 'arc-segment', + from, + center: group.userData.center, + radius: Math.sqrt( + (group.userData.center[0] - dragTo[0]) ** 2 + + (group.userData.center[0] - dragTo[0]) ** 2 + ), + } + ) + } else if ( + group.name === CIRCLE_SEGMENT && + subGroup?.name === CIRCLE_CENTER_HANDLE + ) { + modded = changeSketchArguments( + modifiedAst, + kclManager.programMemory, + [node.start, node.end], + { + type: 'arc-segment', + from, + center: dragTo, + radius: group.userData.radius, + } + ) } else { modded = changeSketchArguments( modifiedAst, @@ -1244,6 +1485,20 @@ export class SceneEntities { group, scale: factor, }) + } else if ( + type === CIRCLE_SEGMENT && + 'center' in segment && + 'radius' in segment + ) { + return this.updateCircleSegment({ + prevSegment: sgPaths[index - 1], + from: segment.from, + to: segment.to, + center: segment.center, + radius: segment.radius, + group, + scale: factor, + }) } else if (type === PROFILE_START) { group.position.set(segment.from[0], segment.from[1], 0) group.scale.set(factor, factor, factor) @@ -1269,6 +1524,9 @@ export class SceneEntities { group.userData.prevSegment = prevSegment const arrowGroup = group.getObjectByName(ARROWHEAD) as Group const extraSegmentGroup = group.getObjectByName(EXTRA_SEGMENT_HANDLE) + if (!prevSegment) { + console.trace('prevSegment is undefined') + } const previousPoint = prevSegment?.type === 'TangentialArcTo' @@ -1364,6 +1622,111 @@ export class SceneEntities { angle, }) } + updateCircleSegment({ + prevSegment, + from, + to, + center, + radius, + group, + scale = 1, + }: { + prevSegment: SketchGroup['value'][number] + from: [number, number] + to: [number, number] + center: [number, number] + radius: number + group: Group + scale?: number + }): () => SegmentOverlayPayload | null { + group.userData.from = from + group.userData.to = to + group.userData.center = center + group.userData.radius = radius + group.userData.prevSegment = prevSegment + const arrowGroup = group.getObjectByName(ARROWHEAD) as Group + const circleCenterHandle = group.getObjectByName( + CIRCLE_CENTER_HANDLE + ) as Group + + const pxLength = (2 * radius * Math.PI) / scale + const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH + const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH + + const hoveredParent = + sceneInfra.hoveredObject && + getParentGroup(sceneInfra.hoveredObject, [CIRCLE_SEGMENT]) + let isHandlesVisible = !shouldHideIdle + if (hoveredParent && hoveredParent?.uuid === group?.uuid) { + isHandlesVisible = !shouldHideHover + } + + if (arrowGroup) { + arrowGroup.position.set( + center[0] + Math.cos(Math.PI / 4) * radius, + center[1] + Math.sin(Math.PI / 4) * radius, + 0 + ) + + const arrowheadAngle = Math.PI / 4 + arrowGroup.quaternion.setFromUnitVectors( + new Vector3(0, 1, 0), + new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0) + ) + arrowGroup.scale.set(scale, scale, scale) + arrowGroup.visible = isHandlesVisible + } + + if (circleCenterHandle) { + circleCenterHandle.position.set(center[0], center[1], 0) + circleCenterHandle.scale.set(scale, scale, scale) + circleCenterHandle.visible = isHandlesVisible + } + + const circleSegmentBody = group.children.find( + (child) => child.userData.type === CIRCLE_SEGMENT_BODY + ) as Mesh + + if (circleSegmentBody) { + const newGeo = createArcGeometry({ + radius, + center, + startAngle: 0, + endAngle: Math.PI * 2, + ccw: true, + scale, + }) + circleSegmentBody.geometry = newGeo + } + const circleSegmentBodyDashed = group.children.find( + (child) => child.userData.type === CIRCLE_SEGMENT_DASH + ) as Mesh + if (circleSegmentBodyDashed) { + // consider throttling the whole updateTangentialArcToSegment + // if there are more perf considerations going forward + this.throttledUpdateDashedArcGeo({ + // ...arcInfo, + center, + radius, + ccw: true, + // make the start end where the handle is + startAngle: Math.PI * 0.25, + endAngle: Math.PI * 2.25, + mesh: circleSegmentBodyDashed, + isDashed: true, + scale, + }) + } + return () => + sceneInfra.updateOverlayDetails({ + arrowGroup, + group, + isHandlesVisible, + from: to, + to: [center[0], center[1]], + angle: Math.PI / 4, + }) + } throttledUpdateDashedArcGeo = throttle( ( args: Parameters[0] & { @@ -1517,7 +1880,7 @@ export class SceneEntities { } private _tearDownSketch( callDepth = 0, - resolve: (val: unknown) => void, + resolve: any, reject: () => void, { removeAxis = true }: { removeAxis?: boolean } ) { @@ -1547,7 +1910,7 @@ export class SceneEntities { this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis }) }, delay) } else { - reject() + resolve(true) } } sceneInfra.camControls.enableRotate = true @@ -1559,7 +1922,7 @@ export class SceneEntities { removeAxis = true, }: { removeAxis?: boolean - } = {}) { + } = {}): Promise { // I think promisifying this is mostly a side effect of not having // "setupSketch" correctly capture a promise when it's done // so we're effectively waiting for to be finished setting up the scene just to tear it down @@ -1577,11 +1940,10 @@ export class SceneEntities { mat.color.set(obj.userData.baseColor) mat.color.offsetHSL(0, 0, 0.5) } - const parent = getParentGroup(selected, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - PROFILE_START, - ]) + const parent = getParentGroup( + selected, + SEGMENT_BODIES_PLUS_PROFILE_START + ) if (parent?.userData?.pathToNode) { const updatedAst = parse(recast(kclManager.ast)) if (trap(updatedAst)) return @@ -1625,6 +1987,16 @@ export class SceneEntities { group: parent, scale: factor, }) + } else if (parent.name === CIRCLE_SEGMENT) { + this.updateCircleSegment({ + prevSegment: parent.userData.prevSegment, + from: parent.userData.from, + to: parent.userData.to, + center: parent.userData.center, + radius: parent.userData.radius, + group: parent, + scale: factor, + }) } return } @@ -1632,11 +2004,10 @@ export class SceneEntities { }, onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => { editorManager.setHighlightRange([[0, 0]]) - const parent = getParentGroup(selected, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - PROFILE_START, - ]) + const parent = getParentGroup( + selected, + SEGMENT_BODIES_PLUS_PROFILE_START + ) if (parent) { const orthoFactor = orthoScale(sceneInfra.camControls.camera) @@ -1660,6 +2031,16 @@ export class SceneEntities { group: parent, scale: factor, }) + } else if (parent.name === CIRCLE_SEGMENT) { + this.updateCircleSegment({ + prevSegment: parent.userData.prevSegment, + from: parent.userData.from, + to: parent.userData.to, + center: parent.userData.center, + radius: parent.userData.radius, + group: parent, + scale: factor, + }) } } const isSelected = parent?.userData?.isSelected @@ -1822,7 +2203,7 @@ function prepareTruncatedMemoryAndAst( export function getParentGroup( object: any, - stopAt: string[] = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT] + stopAt: string[] = SEGMENT_BODIES ): Group | null { if (stopAt.includes(object?.userData?.type)) { return object @@ -1869,10 +2250,7 @@ function colorSegment(object: any, color: number) { }) return } - const straightSegmentBody = getParentGroup(object, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - ]) + const straightSegmentBody = getParentGroup(object, SEGMENT_BODIES) if (straightSegmentBody) { straightSegmentBody.traverse((child) => { if (child instanceof Mesh && !child.userData.ignoreColorChange) { diff --git a/src/clientSideScene/segments.ts b/src/clientSideScene/segments.ts index 1501d68373..47e50826f1 100644 --- a/src/clientSideScene/segments.ts +++ b/src/clientSideScene/segments.ts @@ -24,6 +24,10 @@ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm' import { + CIRCLE_CENTER_HANDLE, + CIRCLE_SEGMENT, + CIRCLE_SEGMENT_BODY, + CIRCLE_SEGMENT_DASH, EXTRA_SEGMENT_HANDLE, EXTRA_SEGMENT_OFFSET_PX, HIDE_SEGMENT_LENGTH, @@ -225,6 +229,28 @@ function createArrowhead(scale = 1, theme: Themes, color?: number): Group { arrowGroup.scale.set(scale, scale, scale) return arrowGroup } +function createCircleCenterHandle( + scale = 1, + theme: Themes, + color?: number +): Group { + const circleCenterGroup = new Group() + + const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later + const baseColor = getThemeColorForThreeJs(theme) + const body = new MeshBasicMaterial({ color }) + const mesh = new Mesh(geometry, body) + + circleCenterGroup.add(mesh) + + circleCenterGroup.userData = { + type: CIRCLE_CENTER_HANDLE, + baseColor, + } + circleCenterGroup.name = CIRCLE_CENTER_HANDLE + circleCenterGroup.scale.set(scale, scale, scale) + return circleCenterGroup +} function createExtraSegmentHandle( scale: number, @@ -300,6 +326,86 @@ function createLengthIndicator({ return lengthIndicatorGroup } +export function circleSegment({ + prevSegment, + from, + to, + center, + radius, + id, + pathToNode, + isDraftSegment, + scale = 1, + texture, + theme, + isSelected, +}: { + prevSegment: SketchGroup['value'][number] + from: Coords2d + center: Coords2d + radius: number + to: Coords2d + id: string + pathToNode: PathToNode + isDraftSegment?: boolean + scale?: number + texture: Texture + theme: Themes + isSelected?: boolean +}): Group { + const group = new Group() + + const geometry = createArcGeometry({ + center, + radius, + startAngle: 0, + endAngle: Math.PI * 2, + ccw: true, + isDashed: isDraftSegment, + scale, + }) + + const baseColor = getThemeColorForThreeJs(theme) + const color = isSelected ? 0x0000ff : baseColor + const body = new MeshBasicMaterial({ color }) + const mesh = new Mesh(geometry, body) + mesh.userData.type = isDraftSegment + ? CIRCLE_SEGMENT_DASH + : CIRCLE_SEGMENT_BODY + + group.userData = { + type: CIRCLE_SEGMENT, + id, + from, + to, + radius, + center, + ccw: true, + prevSegment, + pathToNode, + isSelected, + baseColor, + } + group.name = CIRCLE_SEGMENT + + const arrowGroup = createArrowhead(scale, theme, color) + arrowGroup.position.set( + center[0] + Math.cos(Math.PI / 4) * radius, + center[1] + Math.sin(Math.PI / 4) * radius, + 0 + ) + + const circleCenterGroup = createCircleCenterHandle(scale, theme, color) + circleCenterGroup.position.set(center[0], center[1], 0) + const arrowheadAngle = Math.PI / 4 + arrowGroup.quaternion.setFromUnitVectors( + new Vector3(0, 1, 0), + new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0) + ) + + group.add(mesh, arrowGroup, circleCenterGroup) + return group +} export function tangentialArcToSegment({ prevSegment, from, diff --git a/src/components/ModelingMachineProvider.tsx b/src/components/ModelingMachineProvider.tsx index 533d1121f5..d2ebadfa49 100644 --- a/src/components/ModelingMachineProvider.tsx +++ b/src/components/ModelingMachineProvider.tsx @@ -50,8 +50,7 @@ import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance' import useStateMachineCommands from 'hooks/useStateMachineCommands' import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig' import { - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, + SEGMENT_BODIES, getParentGroup, getSketchOrientationDetails, } from 'clientSideScene/sceneEntities' @@ -168,10 +167,7 @@ export const ModelingMachineProvider = ({ if (event.type !== 'Set mouse state') return {} const nextSegmentHoverMap = () => { if (event.data.type === 'isHovering') { - const parent = getParentGroup(event.data.on, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - ]) + const parent = getParentGroup(event.data.on, SEGMENT_BODIES) const pathToNode = parent?.userData?.pathToNode const pathToNodeString = JSON.stringify(pathToNode) if (!parent || !pathToNode) return context.segmentHoverMap @@ -187,10 +183,10 @@ export const ModelingMachineProvider = ({ event.data.type === 'idle' && context.mouseState.type === 'isHovering' ) { - const mouseOnParent = getParentGroup(context.mouseState.on, [ - STRAIGHT_SEGMENT, - TANGENTIAL_ARC_TO_SEGMENT, - ]) + const mouseOnParent = getParentGroup( + context.mouseState.on, + SEGMENT_BODIES + ) if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode) return context.segmentHoverMap const pathToNodeString = JSON.stringify( @@ -204,8 +200,8 @@ export const ModelingMachineProvider = ({ pathToNodeString, }, }) - // overlay timeout - }, 800) as unknown as number + // overlay timeout is 1s + }, 1000) as unknown as number return { ...context.segmentHoverMap, [pathToNodeString]: timeoutId, diff --git a/src/lang/langHelpers.ts b/src/lang/langHelpers.ts index bd39f4e814..d436a6120a 100644 --- a/src/lang/langHelpers.ts +++ b/src/lang/langHelpers.ts @@ -24,8 +24,9 @@ export type ToolTip = | 'yLineTo' | 'angledLineThatIntersects' | 'tangentialArcTo' + | 'circle' -export const toolTips = [ +export const toolTips: Array = [ 'line', 'lineTo', 'angledLine', diff --git a/src/lang/std/sketch.ts b/src/lang/std/sketch.ts index 755041cbba..7d3827b9e6 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -17,6 +17,7 @@ import { getNodeFromPath, getNodeFromPathCurry, getNodePathFromSourceRange, + getObjExpProperty, } from 'lang/queryAst' import { isLiteralArrayOrStatic, @@ -57,6 +58,7 @@ import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator' const STRAIGHT_SEGMENT_ERR = new Error( 'Invalid input, expected "straight-segment"' ) +const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"') export type Coords2d = [number, number] @@ -911,6 +913,171 @@ export const tangentialArcTo: SketchLineHelper = { ] }, } +export const circle: SketchLineHelper = { + add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => { + if (segmentInput.type !== 'arc-segment') return ARC_SEGMENT_ERR + + const { center, radius } = segmentInput + const _node = { ...node } + const nodeMeta = getNodeFromPath( + _node, + pathToNode, + 'PipeExpression' + ) + if (err(nodeMeta)) return nodeMeta + + const { node: pipe } = nodeMeta + + const x = createLiteral(roundOff(center[0], 2)) + const y = createLiteral(roundOff(center[1], 2)) + + const radiusExp = createLiteral(roundOff(radius, 2)) + + if (replaceExistingCallback) { + const { callExp, valueUsedInTransform } = replaceExistingCallback([ + { + type: 'arrayInObject', + index: 0, + key: 'center', + argType: 'xAbsolute', + expr: x, + }, + { + type: 'arrayInObject', + index: 1, + key: 'center', + argType: 'yAbsolute', + expr: y, + }, + { + type: 'objectProperty', + key: 'radius', + argType: 'radius', + expr: radiusExp, + }, + ]) + + const { index: callIndex } = splitPathAtPipeExpression(pathToNode) + pipe.body[callIndex] = callExp + + return { + modifiedAst: _node, + pathToNode, + valueUsedInTransform, + } + } + return new Error('not implemented') + }, + updateArgs: ({ node, pathToNode, input }) => { + if (input.type !== 'arc-segment') return ARC_SEGMENT_ERR + const { center, radius } = input + const _node = { ...node } + const nodeMeta = getNodeFromPath(_node, pathToNode) + if (err(nodeMeta)) return nodeMeta + + const { node: callExpression, shallowPath } = nodeMeta + + const firstArg = callExpression.arguments?.[0] + const newCenter = createArrayExpression([ + createLiteral(roundOff(center[0])), + createLiteral(roundOff(center[1])), + ]) + mutateObjExpProp(firstArg, newCenter, 'center') + const newRadius = createLiteral(roundOff(radius)) + mutateObjExpProp(firstArg, newRadius, 'radius') + return { + modifiedAst: _node, + pathToNode: shallowPath, + } + }, + getTag: getTag(3), + addTag: addTag(3), + getConstraintInfo: (callExp: CallExpression, code, pathToNode) => { + if (callExp.type !== 'CallExpression') return [] + const firstArg = callExp.arguments?.[0] + if (firstArg.type !== 'ObjectExpression') return [] + const centerDetails = getObjExpProperty(firstArg, 'center') + const radiusDetails = getObjExpProperty(firstArg, 'radius') + if (!centerDetails || !radiusDetails) return [] + if (centerDetails.exp.type !== 'ArrayExpression') return [] + + const pathToCenterArrayExpression: PathToNode = [ + ...pathToNode, + ['arguments', 'CallExpression'], + [0, 'index'], + ['properties', 'ObjectExpression'], + [centerDetails.index, 'index'], + ['value', 'Property'], + ['elements', 'ArrayExpression'], + ] + const pathToRadiusLiteral: PathToNode = [ + ...pathToNode, + ['arguments', 'CallExpression'], + [0, 'index'], + ['properties', 'ObjectExpression'], + [radiusDetails.index, 'index'], + ['value', 'Property'], + ] + const pathToXArg: PathToNode = [ + ...pathToCenterArrayExpression, + [0, 'index'], + ] + const pathToYArg: PathToNode = [ + ...pathToCenterArrayExpression, + [1, 'index'], + ] + + return [ + constrainInfo( + 'radius', + isNotLiteralArrayOrStatic(radiusDetails.exp), + code.slice(radiusDetails.exp.start, radiusDetails.exp.end), + 'circle', + 'radius', + [radiusDetails.exp.start, radiusDetails.exp.end], + pathToRadiusLiteral + ), + { + stdLibFnName: 'circle', + type: 'xAbsolute', + isConstrained: isNotLiteralArrayOrStatic(centerDetails.exp.elements[0]), + sourceRange: [ + centerDetails.exp.elements[0].start, + centerDetails.exp.elements[0].end, + ], + pathToNode: pathToXArg, + value: code.slice( + centerDetails.exp.elements[0].start, + centerDetails.exp.elements[0].end + ), + argPosition: { + type: 'arrayInObject', + index: 0, + key: 'center', + }, + }, + { + stdLibFnName: 'circle', + type: 'yAbsolute', + isConstrained: isNotLiteralArrayOrStatic(centerDetails.exp.elements[1]), + sourceRange: [ + centerDetails.exp.elements[1].start, + centerDetails.exp.elements[1].end, + ], + pathToNode: pathToYArg, + value: code.slice( + centerDetails.exp.elements[1].start, + centerDetails.exp.elements[1].end + ), + argPosition: { + type: 'arrayInObject', + index: 1, + key: 'center', + }, + }, + ] + }, +} export const angledLine: SketchLineHelper = { add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => { if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR @@ -1656,8 +1823,44 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = { angledLineToY, angledLineThatIntersects, tangentialArcTo, + circle, } as const +/** + * @deprecated Use {@link changeSketchArguments} instead + */ +export function changeCircleArguments( + node: Program, + programMemory: ProgramMemory, + pathToNode: PathToNode, + center: [number, number], + radius: number +): { modifiedAst: Program; pathToNode: PathToNode } | Error { + const _node = { ...node } + const nodeMeta = getNodeFromPath(_node, pathToNode) + if (err(nodeMeta)) return nodeMeta + + const { node: callExpression, shallowPath } = nodeMeta + + if (callExpression?.callee?.name === 'circle') { + const newCenter = createArrayExpression([ + createLiteral(roundOff(center[0])), + createLiteral(roundOff(center[1])), + ]) + const newRadius = createLiteral(roundOff(radius)) + callExpression.arguments[0] = createObjectExpression({ + center: newCenter, + radius: newRadius, + }) + return { + modifiedAst: _node, + pathToNode: shallowPath, + } + } + + return new Error(`There was a problem: ${callExpression?.callee?.name}`) +} + export function changeSketchArguments( node: Program, programMemory: ProgramMemory, @@ -1843,7 +2046,7 @@ export function replaceSketchLine({ pathToNode: PathToNode } | Error { - if (![...toolTips, 'intersect'].includes(fnName)) { + if (![...toolTips, 'intersect', 'circle'].includes(fnName)) { return new Error(`The following function name is not tooltip: ${fnName}`) } const _node = { ...node } @@ -2033,6 +2236,32 @@ function getFirstArgValuesForXYLineFns(callExpression: CallExpression): { } } +const getCircle = ( + callExp: CallExpression +): + | { + val: [Expr, Expr, Expr] + tag?: Expr + } + | Error => { + const firstArg = callExp.arguments[0] + if (firstArg.type === 'ObjectExpression') { + const centerDetails = getObjExpProperty(firstArg, 'center') + const radiusDetails = getObjExpProperty(firstArg, 'radius') + const tag = callExp.arguments[2] + if (centerDetails?.exp?.type === 'ArrayExpression' && radiusDetails) { + return { + val: [ + centerDetails?.exp.elements[0], + centerDetails?.exp.elements[1], + radiusDetails.exp, + ], + tag, + } + } + } + return new Error('expected ArrayExpression or ObjectExpression') +} const getAngledLineThatIntersects = ( callExp: CallExpression ): @@ -2092,5 +2321,8 @@ export function getFirstArg(callExp: CallExpression): // TODO probably needs it's own implementation return getFirstArgValuesForXYFns(callExp) } + if (name === 'circle') { + return getCircle(callExp) + } return new Error('unexpected call expression: ' + name) } diff --git a/src/lang/std/sketchcombos.ts b/src/lang/std/sketchcombos.ts index d63097f354..932256be5b 100644 --- a/src/lang/std/sketchcombos.ts +++ b/src/lang/std/sketchcombos.ts @@ -59,6 +59,7 @@ export type LineInputsType = | 'length' | 'intersectionOffset' | 'intersectionTag' + | 'radius' export type ConstraintType = | 'equalLength' @@ -81,7 +82,10 @@ function createCallWrapper( tag?: Expr, valueUsedInTransform?: number ): CreatedSketchExprResult { - const args = [createFirstArg(tooltip, val), createPipeSubstitution()] + const args = + tooltip === 'circle' + ? [] + : [createFirstArg(tooltip, val), createPipeSubstitution()] if (tag) { args.push(tag) } @@ -1711,11 +1715,20 @@ export function transformAstSketchLines({ pathToNode: _pathToNode, referencedSegment, fnName: transformTo || (callExp.node.callee.name as ToolTip), - segmentInput: { - type: 'straight-segment', - to, - from, - }, + segmentInput: + seg.type === 'Circle' + ? { + type: 'arc-segment', + center: seg.center, + radius: seg.radius, + from, + } + : { + type: 'straight-segment', + to, + from, + }, + replaceExistingCallback: (rawArgs) => callBack({ referenceSegName: _referencedSegmentName, @@ -1863,6 +1876,6 @@ function isExprBinaryPart(expr: Expr): expr is BinaryPart { return false } -function getInputOfType(a: InputArgs, b: LineInputsType): InputArg { +function getInputOfType(a: InputArgs, b: LineInputsType | 'radius'): InputArg { return a.find(({ argType }) => argType === b) || a[0] } diff --git a/src/lang/std/stdTypes.ts b/src/lang/std/stdTypes.ts index 60d804b007..1333728014 100644 --- a/src/lang/std/stdTypes.ts +++ b/src/lang/std/stdTypes.ts @@ -34,13 +34,22 @@ interface StraightSegmentInput { to: [number, number] } +/** Inputs for arcs, excluding tangentialArcTo for reasons explain in + * the @straightSegmentInput comment */ +interface ArcSegmentInput { + type: 'arc-segment' + from: [number, number] + center: [number, number] + radius: number +} + /** * SegmentInputs is a union type that can be either a StraightSegmentInput or an ArcSegmentInput. * * - StraightSegmentInput: Represents a straight segment with a starting point (from) and an ending point (to). * - ArcSegmentInput: Represents an arc segment with a starting point (from), a center point, and a radius. */ -export type SegmentInputs = StraightSegmentInput // TODO ArcSegmentInput +export type SegmentInputs = StraightSegmentInput | ArcSegmentInput /** * Interface for adding or replacing a sketch stblib call expression to a sketch. @@ -63,7 +72,14 @@ interface updateArgs extends ModifyAstBase { input: SegmentInputs } -export type InputArgKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag' +export type InputArgKeys = + | 'angle' + | 'offset' + | 'length' + | 'to' + | 'intersectTag' + | 'radius' + | 'center' export interface SingleValueInput { type: 'singleValue' argType: LineInputsType @@ -185,7 +201,12 @@ export type TransformInfo = { export interface ConstrainInfo { stdLibFnName: ToolTip - type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious' + type: + | LineInputsType + | 'vertical' + | 'horizontal' + | 'tangentialWithPrevious' + | 'radius' isConstrained: boolean sourceRange: SourceRange pathToNode: PathToNode diff --git a/src/lib/toolbar.ts b/src/lib/toolbar.ts index bbdc5a9781..5e153fd73e 100644 --- a/src/lib/toolbar.ts +++ b/src/lib/toolbar.ts @@ -2,7 +2,8 @@ import { CustomIconName } from 'components/CustomIcon' import { DEV } from 'env' import { commandBarMachine } from 'machines/commandBarMachine' import { - canRectangleTool, + canRectangleOrCircleTool, + isClosedSketch, isEditingExistingSketch, modelingMachine, } from 'machines/modelingMachine' @@ -301,7 +302,11 @@ export const toolbarConfig: Record = { state.matches('Sketch no face') || state.matches({ Sketch: { 'Rectangle tool': 'Awaiting second corner' }, - }), + }) || + state.matches({ + Sketch: { 'Circle tool': 'Awaiting Radius' }, + }) || + isClosedSketch(state.context), title: 'Line', hotkey: (state) => state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L', @@ -328,7 +333,11 @@ export const toolbarConfig: Record = { !state.matches({ Sketch: 'Tangential arc to' })) || state.matches({ Sketch: { 'Rectangle tool': 'Awaiting second corner' }, - }), + }) || + state.matches({ + Sketch: { 'Circle tool': 'Awaiting Radius' }, + }) || + isClosedSketch(state.context), title: 'Tangential Arc', hotkey: (state) => state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A', @@ -366,10 +375,22 @@ export const toolbarConfig: Record = { [ { id: 'circle-center', - onClick: () => console.error('Center circle not yet implemented'), + onClick: ({ modelingState, modelingSend }) => + modelingSend({ + type: 'change tool', + data: { + tool: !modelingState.matches({ Sketch: 'Circle tool' }) + ? 'circle' + : 'none', + }, + }), icon: 'circle', - status: 'unavailable', + status: 'available', title: 'Center circle', + disabled: (state) => + !canRectangleOrCircleTool(state.context) && + !state.matches({ Sketch: 'Circle tool' }), + isActive: (state) => state.matches({ Sketch: 'Circle tool' }), showTitle: false, description: 'Start drawing a circle from its center', links: [ @@ -385,7 +406,6 @@ export const toolbarConfig: Record = { console.error('Three-point circle not yet implemented'), icon: 'circle', status: 'unavailable', - disabled: () => true, title: 'Three-point circle', showTitle: false, description: 'Draw a circle defined by three points', @@ -407,7 +427,7 @@ export const toolbarConfig: Record = { icon: 'rectangle', status: 'available', disabled: (state) => - !canRectangleTool(state.context) && + !canRectangleOrCircleTool(state.context) && !state.matches({ Sketch: 'Rectangle tool' }), title: 'Corner rectangle', hotkey: (state) => diff --git a/src/machines/modelingMachine.ts b/src/machines/modelingMachine.ts index 7bed9d3c0b..836464cbf0 100644 --- a/src/machines/modelingMachine.ts +++ b/src/machines/modelingMachine.ts @@ -58,6 +58,8 @@ import { deleteSegment } from 'clientSideScene/ClientSideSceneComp' import { executeAst } from 'lang/langHelpers' import toast from 'react-hot-toast' import { ToolbarModeName } from 'lib/toolbar' +import { quaternionFromUpNForward } from 'clientSideScene/helpers' +import { Vector3 } from 'three' export const MODELING_PERSIST_KEY = 'MODELING_PERSIST_KEY' @@ -159,7 +161,12 @@ export interface Store { openPanes: SidebarType[] } -export type SketchTool = 'line' | 'tangentialArc' | 'rectangle' | 'none' +export type SketchTool = + | 'line' + | 'tangentialArc' + | 'rectangle' + | 'circle' + | 'none' export type ModelingMachineEvent = | { @@ -211,6 +218,10 @@ export type ModelingMachineEvent = type: 'Add rectangle origin' data: [x: number, y: number] } + | { + type: 'Add circle origin' + data: [x: number, y: number] + } | { type: 'xstate.done.actor.animate-to-face' output: SketchDetails @@ -244,6 +255,7 @@ export type ModelingMachineEvent = } } | { type: 'Finish rectangle' } + | { type: 'Finish circle' } | { type: 'Artifact graph populated' } | { type: 'Artifact graph emptied' } @@ -464,7 +476,10 @@ export const modelingMachine = setup({ isEditingExistingSketch({ sketchDetails }), 'next is rectangle': ({ context: { sketchDetails, currentTool } }) => - currentTool === 'rectangle' && canRectangleTool({ sketchDetails }), + currentTool === 'rectangle' && + canRectangleOrCircleTool({ sketchDetails }), + 'next is circle': ({ context: { sketchDetails, currentTool } }) => + currentTool === 'circle' && canRectangleOrCircleTool({ sketchDetails }), 'next is line': ({ context }) => context.currentTool === 'line', 'next is none': ({ context }) => context.currentTool === 'none', }, @@ -691,6 +706,42 @@ export const modelingMachine = setup({ }, }) }, + 'listen for circle origin': ({ context: { sketchDetails } }) => { + if (!sketchDetails) return + sceneEntitiesManager.createIntersectionPlane() + const quaternion = quaternionFromUpNForward( + new Vector3(...sketchDetails.yAxis), + new Vector3(...sketchDetails.zAxis) + ) + + // Position the click raycast plane + if (sceneEntitiesManager.intersectionPlane) { + sceneEntitiesManager.intersectionPlane.setRotationFromQuaternion( + quaternion + ) + sceneEntitiesManager.intersectionPlane.position.copy( + new Vector3(...(sketchDetails?.origin || [0, 0, 0])) + ) + } + sceneInfra.setCallbacks({ + onClick: async (args) => { + if (!args) return + if (args.mouseEvent.which !== 1) return + const { intersectionPoint } = args + if (!intersectionPoint?.twoD || !sketchDetails?.sketchPathToNode) + return + const twoD = args.intersectionPoint?.twoD + if (twoD) { + sceneInfra.modelingSend({ + type: 'Add circle origin', + data: [twoD.x, twoD.y], + }) + } else { + console.error('No intersection point found') + } + }, + }) + }, 'set up draft rectangle': ({ context: { sketchDetails }, event }) => { if (event.type !== 'Add rectangle origin') return if (!sketchDetails || !event.data) return @@ -703,6 +754,18 @@ export const modelingMachine = setup({ event.data ) }, + 'set up draft circle': ({ context: { sketchDetails }, event }) => { + if (event.type !== 'Add circle origin') return + if (!sketchDetails || !event.data) return + // eslint-disable-next-line @typescript-eslint/no-floating-promises + sceneEntitiesManager.setupDraftCircle( + sketchDetails.sketchPathToNode, + sketchDetails.zAxis, + sketchDetails.yAxis, + sketchDetails.origin, + event.data + ) + }, 'set up draft line without teardown': ({ context: { sketchDetails } }) => { if (!sketchDetails) return // eslint-disable-next-line @typescript-eslint/no-floating-promises @@ -1802,10 +1865,43 @@ export const modelingMachine = setup({ target: 'Tangential arc to', guard: 'next is tangential arc', }, + { + target: 'Circle tool', + guard: 'next is circle', + }, ], entry: 'assign tool in context', }, + 'Circle tool': { + on: { + 'change tool': 'Change Tool', + }, + + states: { + 'Awaiting origin': { + on: { + 'Add circle origin': { + target: 'Awaiting Radius', + actions: 'set up draft circle', + }, + }, + }, + + 'Awaiting Radius': { + on: { + 'Finish circle': 'Finished Circle', + }, + }, + + 'Finished Circle': { + always: '#Modeling.Sketch.SketchIdle', + }, + }, + + initial: 'Awaiting origin', + entry: 'listen for circle origin', + }, }, initial: 'Init', @@ -1943,10 +2039,13 @@ export function isEditingExistingSketch({ (item) => item.type === 'CallExpression' && item.callee.name === 'startProfileAt' ) - return hasStartProfileAt && pipeExpression.body.length > 2 + const hasCircle = pipeExpression.body.some( + (item) => item.type === 'CallExpression' && item.callee.name === 'circle' + ) + return (hasStartProfileAt && pipeExpression.body.length > 2) || hasCircle } -export function canRectangleTool({ +export function canRectangleOrCircleTool({ sketchDetails, }: { sketchDetails: SketchDetails | null @@ -1961,3 +2060,25 @@ export function canRectangleTool({ if (err(node)) return false return node.node?.declarations?.[0]?.init.type !== 'PipeExpression' } + +/** If the sketch contains `close` or `circle` stdlib functions it must be closed */ +export function isClosedSketch({ + sketchDetails, +}: { + sketchDetails: SketchDetails | null +}): boolean { + const node = getNodeFromPath( + kclManager.ast, + sketchDetails?.sketchPathToNode || [], + 'VariableDeclaration' + ) + // This should not be returning false, and it should be caught + // but we need to simulate old behavior to move on. + if (err(node)) return false + if (node.node?.declarations?.[0]?.init.type !== 'PipeExpression') return false + return node.node.declarations[0].init.body.some( + (yo) => + yo.type === 'CallExpression' && + (yo.callee.name === 'close' || yo.callee.name === 'circle') + ) +} diff --git a/src/wasm-lib/kcl/src/docs.rs b/src/wasm-lib/kcl/src/docs.rs index 760c11f526..d2bb9f7678 100644 --- a/src/wasm-lib/kcl/src/docs.rs +++ b/src/wasm-lib/kcl/src/docs.rs @@ -937,6 +937,9 @@ mod tests { fn get_autocomplete_snippet_circle() { let circle_fn: Box = Box::new(crate::std::shapes::Circle); let snippet = circle_fn.to_autocomplete_snippet().unwrap(); - assert_eq!(snippet, r#"circle([${0:3.14}, ${1:3.14}], ${2:3.14}, ${3:%})${}"#); + assert_eq!( + snippet, + r#"circle({ center: [${0:3.14}, ${1:3.14}], radius: ${2:3.14} }, ${3:%})${}"# + ); } } diff --git a/src/wasm-lib/kcl/src/executor.rs b/src/wasm-lib/kcl/src/executor.rs index fb94b739d3..ddae77ccd7 100644 --- a/src/wasm-lib/kcl/src/executor.rs +++ b/src/wasm-lib/kcl/src/executor.rs @@ -1416,6 +1416,19 @@ pub enum Path { /// arc's direction ccw: bool, }, + /// a complete arc + Circle { + #[serde(flatten)] + base: BasePath, + /// the arc's center + #[ts(type = "[number, number]")] + center: [f64; 2], + /// the arc's radius + radius: f64, + /// arc's direction + // Maybe this one's not needed since it's a full revolution? + ccw: bool, + }, /// A path that is horizontal. Horizontal { #[serde(flatten)] @@ -1448,6 +1461,7 @@ impl Path { Path::Base { base } => base.geo_meta.id, Path::TangentialArcTo { base, .. } => base.geo_meta.id, Path::TangentialArc { base, .. } => base.geo_meta.id, + Path::Circle { base, .. } => base.geo_meta.id, } } @@ -1459,6 +1473,7 @@ impl Path { Path::Base { base } => base.tag.clone(), Path::TangentialArcTo { base, .. } => base.tag.clone(), Path::TangentialArc { base, .. } => base.tag.clone(), + Path::Circle { base, .. } => base.tag.clone(), } } @@ -1470,6 +1485,7 @@ impl Path { Path::Base { base } => base, Path::TangentialArcTo { base, .. } => base, Path::TangentialArc { base, .. } => base, + Path::Circle { base, .. } => base, } } @@ -1481,6 +1497,7 @@ impl Path { Path::Base { base } => Some(base), Path::TangentialArcTo { base, .. } => Some(base), Path::TangentialArc { base, .. } => Some(base), + Path::Circle { base, .. } => Some(base), } } } @@ -2572,7 +2589,7 @@ fn transform = (replicaId) => { fn layer = () => { return startSketchOn("XY") - |> circle([0, 0], 1, %, $tag1) + |> circle({ center: [0, 0], radius: 1 }, %, $tag1) |> extrude(10, %) } @@ -2700,7 +2717,7 @@ fn transform = (replicaId) => { fn layer = () => { return startSketchOn("XY") - |> circle([0, 0], 1, %, $tag1) + |> circle({ center: [0, 0], radius: 1 }, %, $tag1) |> extrude(10, %) } diff --git a/src/wasm-lib/kcl/src/std/args.rs b/src/wasm-lib/kcl/src/std/args.rs index 130d5173f1..7013c09090 100644 --- a/src/wasm-lib/kcl/src/std/args.rs +++ b/src/wasm-lib/kcl/src/std/args.rs @@ -261,8 +261,7 @@ impl Args { &self, ) -> Result< ( - [f64; 2], - f64, + crate::std::shapes::CircleData, crate::std::shapes::SketchSurfaceOrGroup, Option, ), @@ -619,6 +618,7 @@ fn from_user_val(arg: &KclValue) -> Option { impl_from_arg_via_json!(super::sketch::AngledLineData); impl_from_arg_via_json!(super::sketch::AngledLineToData); impl_from_arg_via_json!(super::sketch::AngledLineThatIntersectsData); +impl_from_arg_via_json!(super::shapes::CircleData); impl_from_arg_via_json!(super::sketch::ArcData); impl_from_arg_via_json!(super::sketch::TangentialArcData); impl_from_arg_via_json!(super::sketch::BezierData); diff --git a/src/wasm-lib/kcl/src/std/convert.rs b/src/wasm-lib/kcl/src/std/convert.rs index 8ce353b4fe..5c2e5bfb1d 100644 --- a/src/wasm-lib/kcl/src/std/convert.rs +++ b/src/wasm-lib/kcl/src/std/convert.rs @@ -50,7 +50,7 @@ pub async fn int(args: Args) -> Result { /// /// ```no_run /// const sketch001 = startSketchOn('XZ') -/// |> circle([0, 0], 2, %) +/// |> circle({ center: [0, 0], radius: 2 }, %) /// const extrude001 = extrude(5, sketch001) /// /// const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => { diff --git a/src/wasm-lib/kcl/src/std/extrude.rs b/src/wasm-lib/kcl/src/std/extrude.rs index b62e9075ec..b5a43760d0 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -231,7 +231,7 @@ pub(crate) async fn do_post_extrude( .flat_map(|path| { if let Some(Some(actual_face_id)) = face_id_map.get(&path.get_base().geo_meta.id) { match path { - Path::TangentialArc { .. } | Path::TangentialArcTo { .. } => { + Path::TangentialArc { .. } | Path::TangentialArcTo { .. } | Path::Circle { .. } => { let extrude_surface = ExtrudeSurface::ExtrudeArc(crate::executor::ExtrudeArc { face_id: *actual_face_id, tag: path.get_base().tag.clone(), diff --git a/src/wasm-lib/kcl/src/std/helix.rs b/src/wasm-lib/kcl/src/std/helix.rs index 220bf650b2..b0a9074070 100644 --- a/src/wasm-lib/kcl/src/std/helix.rs +++ b/src/wasm-lib/kcl/src/std/helix.rs @@ -42,7 +42,7 @@ pub async fn helix(args: Args) -> Result { /// /// ```no_run /// const part001 = startSketchOn('XY') -/// |> circle([5, 5], 10, %) +/// |> circle({ center: [5, 5], radius: 10 }, %) /// |> extrude(10, %) /// |> helix({ /// angleStart: 0, diff --git a/src/wasm-lib/kcl/src/std/loft.rs b/src/wasm-lib/kcl/src/std/loft.rs index 57cf8f16db..885e7797f8 100644 --- a/src/wasm-lib/kcl/src/std/loft.rs +++ b/src/wasm-lib/kcl/src/std/loft.rs @@ -91,10 +91,10 @@ pub async fn loft(args: Args) -> Result { /// |> close(%) /// /// const circleSketch0 = startSketchOn(offsetPlane('XY', 75)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// const circleSketch1 = startSketchOn(offsetPlane('XY', 150)) -/// |> circle([0, 100], 20, %) +/// |> circle({ center: [0, 100], radius: 20 }, %) /// /// loft([squareSketch, circleSketch0, circleSketch1]) /// ``` @@ -110,10 +110,10 @@ pub async fn loft(args: Args) -> Result { /// |> close(%) /// /// const circleSketch0 = startSketchOn(offsetPlane('XY', 75)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// const circleSketch1 = startSketchOn(offsetPlane('XY', 150)) -/// |> circle([0, 100], 20, %) +/// |> circle({ center: [0, 100], radius: 20 }, %) /// /// loft([squareSketch, circleSketch0, circleSketch1], { /// // This can be set to override the automatically determined diff --git a/src/wasm-lib/kcl/src/std/math.rs b/src/wasm-lib/kcl/src/std/math.rs index d53307c171..e833c5b09d 100644 --- a/src/wasm-lib/kcl/src/std/math.rs +++ b/src/wasm-lib/kcl/src/std/math.rs @@ -113,7 +113,7 @@ pub async fn pi(args: Args) -> Result { /// const circumference = 70 /// /// const exampleSketch = startSketchOn("XZ") -/// |> circle([0, 0], circumference/ (2 * pi()), %) +/// |> circle({ center: [0, 0], radius: circumference/ (2 * pi()) }, %) /// /// const example = extrude(5, exampleSketch) /// ``` diff --git a/src/wasm-lib/kcl/src/std/mod.rs b/src/wasm-lib/kcl/src/std/mod.rs index 97e6f3c28e..534039c277 100644 --- a/src/wasm-lib/kcl/src/std/mod.rs +++ b/src/wasm-lib/kcl/src/std/mod.rs @@ -37,7 +37,7 @@ use crate::{ ast::types::FunctionExpression, docs::StdLibFn, errors::KclError, - executor::{KclValue, ProgramMemory, SketchGroup, SketchSurface}, + executor::{KclValue, ProgramMemory}, std::kcl_stdlib::KclStdLibFn, }; diff --git a/src/wasm-lib/kcl/src/std/patterns.rs b/src/wasm-lib/kcl/src/std/patterns.rs index 5929e1568f..f5b1c3475e 100644 --- a/src/wasm-lib/kcl/src/std/patterns.rs +++ b/src/wasm-lib/kcl/src/std/patterns.rs @@ -117,7 +117,7 @@ pub async fn pattern_transform(args: Args) -> Result { /// // Each layer is just a pretty thin cylinder. /// fn layer = () => { /// return startSketchOn("XY") // or some other plane idk -/// |> circle([0, 0], 1, %, $tag1) +/// |> circle({ center: [0, 0], radius: 1 }, %, $tag1) /// |> extrude(h, %) /// } /// // The vase is 100 layers tall. @@ -318,7 +318,7 @@ pub async fn pattern_linear_2d(args: Args) -> Result { /// /// ```no_run /// const exampleSketch = startSketchOn('XZ') -/// |> circle([0, 0], 1, %) +/// |> circle({ center: [0, 0], radius: 1 }, %) /// |> patternLinear2d({ /// axis: [1, 0], /// repetitions: 6, @@ -651,7 +651,7 @@ pub async fn pattern_circular_3d(args: Args) -> Result { /// /// ```no_run /// const exampleSketch = startSketchOn('XZ') -/// |> circle([0, 0], 1, %) +/// |> circle({ center: [0, 0], radius: 1 }, %) /// /// const example = extrude(-5, exampleSketch) /// |> patternCircular3d({ diff --git a/src/wasm-lib/kcl/src/std/planes.rs b/src/wasm-lib/kcl/src/std/planes.rs index 47ad5c3795..cb41f7c26e 100644 --- a/src/wasm-lib/kcl/src/std/planes.rs +++ b/src/wasm-lib/kcl/src/std/planes.rs @@ -77,7 +77,7 @@ pub async fn offset_plane(args: Args) -> Result { /// |> close(%) /// /// const circleSketch = startSketchOn(offsetPlane('XY', 150)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// loft([squareSketch, circleSketch]) /// ``` @@ -93,7 +93,7 @@ pub async fn offset_plane(args: Args) -> Result { /// |> close(%) /// /// const circleSketch = startSketchOn(offsetPlane('XZ', 150)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// loft([squareSketch, circleSketch]) /// ``` @@ -109,7 +109,7 @@ pub async fn offset_plane(args: Args) -> Result { /// |> close(%) /// /// const circleSketch = startSketchOn(offsetPlane('YZ', 150)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// loft([squareSketch, circleSketch]) /// ``` @@ -125,7 +125,7 @@ pub async fn offset_plane(args: Args) -> Result { /// |> close(%) /// /// const circleSketch = startSketchOn(offsetPlane('-XZ', -150)) -/// |> circle([0, 100], 50, %) +/// |> circle({ center: [0, 100], radius: 50 }, %) /// /// loft([squareSketch, circleSketch]) /// ``` diff --git a/src/wasm-lib/kcl/src/std/revolve.rs b/src/wasm-lib/kcl/src/std/revolve.rs index ffdffc7a4c..8727838db7 100644 --- a/src/wasm-lib/kcl/src/std/revolve.rs +++ b/src/wasm-lib/kcl/src/std/revolve.rs @@ -133,7 +133,7 @@ pub async fn revolve(args: Args) -> Result { /// ```no_run /// // A donut shape. /// const sketch001 = startSketchOn('XY') -/// |> circle([15, 0], 5, %) +/// |> circle({ center: [15, 0], radius: 5 }, %) /// |> revolve({ /// angle: 360, /// axis: 'y' @@ -185,7 +185,7 @@ pub async fn revolve(args: Args) -> Result { /// |> extrude(20, %) /// /// const sketch001 = startSketchOn(box, "END") -/// |> circle([10,10], 4, %) +/// |> circle({ center: [10,10], radius: 4 }, %) /// |> revolve({ /// angle: -90, /// axis: 'y' @@ -202,7 +202,7 @@ pub async fn revolve(args: Args) -> Result { /// |> extrude(20, %) /// /// const sketch001 = startSketchOn(box, "END") -/// |> circle([10,10], 4, %) +/// |> circle({ center: [10,10], radius: 4 }, %) /// |> revolve({ /// angle: 90, /// axis: getOppositeEdge(revolveAxis) @@ -219,7 +219,7 @@ pub async fn revolve(args: Args) -> Result { /// |> extrude(20, %) /// /// const sketch001 = startSketchOn(box, "END") -/// |> circle([10,10], 4, %) +/// |> circle({ center: [10,10], radius: 4 }, %) /// |> revolve({ /// angle: 90, /// axis: getOppositeEdge(revolveAxis), diff --git a/src/wasm-lib/kcl/src/std/shapes.rs b/src/wasm-lib/kcl/src/std/shapes.rs index 1c80c2d9d4..7807ff378f 100644 --- a/src/wasm-lib/kcl/src/std/shapes.rs +++ b/src/wasm-lib/kcl/src/std/shapes.rs @@ -2,14 +2,15 @@ use anyhow::Result; use derive_docs::stdlib; +use kittycad::types::{Angle, ModelingCmd}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use crate::{ ast::types::TagDeclarator, - errors::KclError, - executor::KclValue, - std::{Args, SketchGroup, SketchSurface}, + errors::{KclError, KclErrorDetails}, + executor::{BasePath, GeoMeta, KclValue, Path, SketchGroup, SketchSurface}, + std::Args, }; /// A sketch surface or a sketch group. @@ -21,12 +22,24 @@ pub enum SketchSurfaceOrGroup { SketchGroup(Box), } +/// Data for drawing an angled line that intersects with a given line. +#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] +#[ts(export)] +#[serde(rename_all = "camelCase")] +// TODO: make sure the docs on the args below are correct. +pub struct CircleData { + /// The center of the circle. + pub center: [f64; 2], + /// The circle radius + pub radius: f64, +} + /// Sketch a circle. pub async fn circle(args: Args) -> Result { - let (center, radius, sketch_surface_or_group, tag): ([f64; 2], f64, SketchSurfaceOrGroup, Option) = + let (data, sketch_surface_or_group, tag): (CircleData, SketchSurfaceOrGroup, Option) = args.get_circle_args()?; - let sketch_group = inner_circle(center, radius, sketch_surface_or_group, tag, args).await?; + let sketch_group = inner_circle(data, sketch_surface_or_group, tag, args).await?; Ok(KclValue::new_user_val(sketch_group.meta.clone(), sketch_group)) } @@ -35,7 +48,7 @@ pub async fn circle(args: Args) -> Result { /// /// ```no_run /// const exampleSketch = startSketchOn("-XZ") -/// |> circle([0, 0], 10, %) +/// |> circle({ center: [0, 0], radius: 10 }, %) /// /// const example = extrude(5, exampleSketch) /// ``` @@ -47,7 +60,7 @@ pub async fn circle(args: Args) -> Result { /// |> line([0, 30], %) /// |> line([-30, 0], %) /// |> close(%) -/// |> hole(circle([0, 15], 5, %), %) +/// |> hole(circle({ center: [0, 15], radius: 5 }, %), %) /// /// const example = extrude(5, exampleSketch) /// ``` @@ -55,8 +68,7 @@ pub async fn circle(args: Args) -> Result { name = "circle", }] async fn inner_circle( - center: [f64; 2], - radius: f64, + data: CircleData, sketch_surface_or_group: SketchSurfaceOrGroup, tag: Option, args: Args, @@ -65,23 +77,70 @@ async fn inner_circle( SketchSurfaceOrGroup::SketchSurface(surface) => surface, SketchSurfaceOrGroup::SketchGroup(group) => group.on, }; - let mut sketch_group = - crate::std::sketch::inner_start_profile_at([center[0] + radius, center[1]], sketch_surface, None, args.clone()) - .await?; - - // Call arc. - sketch_group = crate::std::sketch::inner_arc( - crate::std::sketch::ArcData::AnglesAndRadius { - angle_start: 0.0, - angle_end: 360.0, - radius, - }, - sketch_group, - tag, + let sketch_group = crate::std::sketch::inner_start_profile_at( + [data.center[0] + data.radius, data.center[1]], + sketch_surface, + None, args.clone(), ) .await?; - // Call close. - crate::std::sketch::inner_close(sketch_group, None, args).await + let angle_start = Angle::from_degrees(0.0); + let angle_end = Angle::from_degrees(360.0); + + if angle_start == angle_end { + return Err(KclError::Type(KclErrorDetails { + message: "Arc start and end angles must be different".to_string(), + source_ranges: vec![args.source_range], + })); + } + + let id = uuid::Uuid::new_v4(); + + args.batch_modeling_cmd( + id, + ModelingCmd::ExtendPath { + path: sketch_group.id, + segment: kittycad::types::PathSegment::Arc { + start: angle_start, + end: angle_end, + center: data.center.into(), + radius: data.radius, + relative: false, + }, + }, + ) + .await?; + + let current_path = Path::Circle { + base: BasePath { + from: data.center, + to: data.center, + tag: tag.clone(), + geo_meta: GeoMeta { + id, + metadata: args.source_range.into(), + }, + }, + radius: data.radius, + center: data.center, + ccw: angle_start.degrees() < angle_end.degrees(), + }; + + let mut new_sketch_group = sketch_group.clone(); + if let Some(tag) = &tag { + new_sketch_group.add_tag(tag, ¤t_path); + } + + new_sketch_group.value.push(current_path); + + args.batch_modeling_cmd( + id, + ModelingCmd::ClosePath { + path_id: new_sketch_group.id, + }, + ) + .await?; + + Ok(new_sketch_group) } diff --git a/src/wasm-lib/kcl/src/std/sketch.rs b/src/wasm-lib/kcl/src/std/sketch.rs index e62cc719e2..d65ead8743 100644 --- a/src/wasm-lib/kcl/src/std/sketch.rs +++ b/src/wasm-lib/kcl/src/std/sketch.rs @@ -2059,8 +2059,8 @@ pub async fn hole(args: Args) -> Result { /// |> line([5, 0], %) /// |> line([0, -5], %) /// |> close(%) -/// |> hole(circle([1, 1], .25, %), %) -/// |> hole(circle([1, 4], .25, %), %) +/// |> hole(circle({ center: [1, 1], radius: .25 }, %), %) +/// |> hole(circle({ center: [1, 4], radius: .25 }, %), %) /// /// const example = extrude(1, exampleSketch) /// ``` @@ -2077,7 +2077,7 @@ pub async fn hole(args: Args) -> Result { /// } /// /// const exampleSketch = startSketchOn('-XZ') -/// |> circle([0, 0], 3, %) +/// |> circle({ center: [0, 0], radius: 3 }, %) /// |> hole(squareHoleSketch(), %) /// const example = extrude(1, exampleSketch) /// ``` diff --git a/src/wasm-lib/kcl/src/unparser.rs b/src/wasm-lib/kcl/src/unparser.rs index a401d273ff..c13c9c1d25 100644 --- a/src/wasm-lib/kcl/src/unparser.rs +++ b/src/wasm-lib/kcl/src/unparser.rs @@ -1028,10 +1028,10 @@ const tabs_r = startSketchOn({ |> line([0, -10], %) |> line([-10, -5], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ width / 2 + thk + hole_diam, length / 2 - hole_diam - ], hole_diam / 2, %), %) + ], radius: hole_diam / 2 }, %), %) |> extrude(-thk, %) |> patternLinear3d({ axis: [0, -1, 0], @@ -1052,10 +1052,10 @@ const tabs_l = startSketchOn({ |> line([0, -10], %) |> line([10, -5], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ -width / 2 - thk - hole_diam, length / 2 - hole_diam - ], hole_diam / 2, %), %) + ], radius: hole_diam / 2 }, %), %) |> extrude(-thk, %) |> patternLinear3d({ axis: [0, -1, 0], @@ -1148,10 +1148,10 @@ const tabs_r = startSketchOn({ |> line([0, -10], %) |> line([-10, -5], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ width / 2 + thk + hole_diam, length / 2 - hole_diam - ], hole_diam / 2, %), %) + ], radius: hole_diam / 2 }, %), %) |> extrude(-thk, %) |> patternLinear3d({ axis: [0, -1, 0], @@ -1172,10 +1172,10 @@ const tabs_l = startSketchOn({ |> line([0, -10], %) |> line([10, -5], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ -width / 2 - thk - hole_diam, length / 2 - hole_diam - ], hole_diam / 2, %), %) + ], radius: hole_diam / 2 }, %), %) |> extrude(-thk, %) |> patternLinear3d({ axis: [0, -1, 0], diff --git a/src/wasm-lib/kcl/tests/outputs/serial_test_example_get_next_adjacent_edge0.png b/src/wasm-lib/kcl/tests/outputs/serial_test_example_get_next_adjacent_edge0.png index a61f6b8af8fcd56c6df94dfb0d0afa6dce1eda8c..debdd74470fee53617cffd581d451ac1903d160a 100644 GIT binary patch literal 64161 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 IlP8}4zXpW=0{{R3 literal 65258 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 IlP8}4zXpW=0{{R3 diff --git a/src/wasm-lib/kcl/tests/outputs/serial_test_example_loft1.png b/src/wasm-lib/kcl/tests/outputs/serial_test_example_loft1.png index a44145b368315df03404e97edb16a6595942dab2..25dc6517cce9eced4ed2241dec54bdbf6e8c58d1 100644 GIT binary patch delta 2050 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 diff --git a/src/wasm-lib/kcl/tests/outputs/serial_test_example_loft2.png b/src/wasm-lib/kcl/tests/outputs/serial_test_example_loft2.png index a44145b368315df03404e97edb16a6595942dab2..25dc6517cce9eced4ed2241dec54bdbf6e8c58d1 100644 GIT binary patch delta 2050 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 diff --git a/src/wasm-lib/kcl/tests/outputs/serial_test_example_offset_plane0.png b/src/wasm-lib/kcl/tests/outputs/serial_test_example_offset_plane0.png index 35e17abd94a4473a1da75f123c54981555dbbb6c..37ec6fb46f7011a017b29e7061debefcf442bdd1 100644 GIT binary patch delta 1344 zcmY+EeM}Q~9L8IXRI-guogy$>O^YsDSWVPA?VGv*HU!P$I-Emc>H>~-bs}rAr9XU` zsE8d9Wv0^3HIYS}6_FMv?Lleg|f0OztT6W z-mO=)qbO~|e0W}(D-s9OuG?peUC0p`WAl8tP&&4u%KN3NDL=&@aqvgw1SW@AX=5G#af+=!Mfg0myFZl?nW+oCbV^2HdzpW5DP?}`-Z+Xl zn05WFLN-Y;XC0s4|DvPz!n)k!EkdluG~ol#huEh{C26?x?HTObJejSJr{OkkGXy>x z&5Cmd)2@}sq+@6kH?(@Zv%Q&Xo;2-_BRvr5WYRsPBC95(FoYGWLkdo7PD67}umdGV zP~poZsCNmx%Qfe>DAX;YfD^%;$+l)vVeqC`JS?Iv21pT`s1>^smz{LGpMEKy)03Cs z%R`G^J81cse(1r``jDHXrJy)kRTHfmao}AJJfRSo)FHiKzBmZtPr_lg7pY_u)eUVb zYc$22zf??7JA7(7B%g039ksK+dxErbVdXThLzbIcP80bfn<(nREY09>u%h<_brpc= zCcM*B@PQ|j(NmW(RVT+s$W3TSd$DzKC|sBhJ#^@CW;yiMnV2mb{|8wehMb-WA#7MG z3>Za$T6u#vi4qE%Qa^T|H#uY?h8%b1yuuM+@4pJg)b5RezNF0ZLg(Zj?9 z(6Fgsj$@bc=xD@HLbGQ5<8nroSI!t2jsNx+e)bk=SL&_4wj06cf|q4Y6y6=L;6Mf0 zyo?u2qL-<0Pfy*9n->p+J1T)u;XT4L)B(t{x>dmDW+AwrEn4E4Rucu2mB%fGbk&KmchQ?DyXdJBvH`Y%P5z~4lF+TE?EYJ3Xd`@FDCZEsVryf)E8 z6KxOj3SO*fh=PYTU)(dO=MI!|Mv1SJ_`)+@6L>EQ8S%zKEP6Z9p7-He;ZK1RfTi|- zR08mOJBWVHzRAAmySWi#lp>|!Rf3^1p`;DtOT#Bj_?Uscl3fE5#w<@V7*F2n2EWQ~ zjJ>b7-e+}L+haLQvM$7q+lDKim+uU;<|wh_Yj5(E+AdsG0w#R1aQ6=tug?HUBx!W; rXPV&I#`3S;h@q~irOs}QTmjr*1Bwke4`d%O_ViWght#5hDld delta 540 zcmeyehVArnwh78ilP5K*ZdGNJYp?&Gv2EkodXw8Xp4m^z&G{^U)@<9)^J&tzW4`X6 zuJUiS&11>)3=fXRy*V29`)K^1qw%v>uiN!%)#`P>wkz)d(Lhdp`}NAX*DLiKs^9Hw z|NFN#=kt7^DL_+AZ{IiuG;V#8G>Gv(G$$FzcxFF4Hz#@af1hm|dvpIy-_Xh^HhpUc zqhS3i@n;`COxT=$p6SDl<96S+!i~3Xg4tPKalE|d_<6R9SHbg7{RNrA1vbSRVowT^ zJ%7?S_k!)w0jn?utLO)M4Cc|3K$rCcb^LJ$D*U(kX8kd<@W0(%`ikT98*y{^*Wz_n{)`d@?ekNy{#^y9ym z(Esb6kLs;G|Kuxm{(q_RasNz}b-$+He71jb?my!j&vxeKy#z(qbV*fe*3BQ@D+?H9% O00f?{elF{r5}E+rAVClS diff --git a/src/wasm-lib/tests/executor/inputs/cylinder.kcl b/src/wasm-lib/tests/executor/inputs/cylinder.kcl index 6304c62133..f3800be1c7 100644 --- a/src/wasm-lib/tests/executor/inputs/cylinder.kcl +++ b/src/wasm-lib/tests/executor/inputs/cylinder.kcl @@ -1,3 +1,3 @@ const cylinder = startSketchOn('XY') - |> circle([0,0], 22, %) + |> circle({ center: [0, 0], radius: 22 }, %) |> extrude(14, %) diff --git a/src/wasm-lib/tests/executor/inputs/fillet-and-shell.kcl b/src/wasm-lib/tests/executor/inputs/fillet-and-shell.kcl index cd7dc2c5db..295f959b09 100644 --- a/src/wasm-lib/tests/executor/inputs/fillet-and-shell.kcl +++ b/src/wasm-lib/tests/executor/inputs/fillet-and-shell.kcl @@ -61,8 +61,8 @@ const case = startSketchOn('XY') fn m25Screw = (x, y, height) => { const screw = startSketchOn("XY") |> startProfileAt([0, 0], %) - |> circle([x, y], 2.5, %) - |> hole(circle([x, y], 1.25, %), %) + |> circle({ center: [x, y], radius: 2.5 }, %) + |> hole(circle({ center: [x, y], radius: 1.25 }, %), %) |> extrude(height, %) return screw } diff --git a/src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl b/src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl index c6d2b9a0c3..047127d433 100644 --- a/src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl +++ b/src/wasm-lib/tests/executor/inputs/focusrite_scarlett_mounting_braket.kcl @@ -1,6 +1,7 @@ // A mounting bracket for the Focusrite Scarlett Solo audio interface // This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material + // define constants in mm const radius = 6.0 const width = 144.0 @@ -79,10 +80,13 @@ const tabsR = startSketchOn(tabPlane) |> line([0, -tabLength / 3 * 2], %, $edge12) |> line([-tabWidth, -tabLength / 3], %, $edge13) |> close(%, $edge14) - |> hole(circle([ - width / 2 + thk + tabWidth / 2, - length / 2 + thk - (tabLength / (3 / 2)) - ], holeDiam / 2, %), %) + |> hole(circle({ + center: [ + width / 2 + thk + tabWidth / 2, + length / 2 + thk - (tabLength / (3 / 2)) + ], + radius: holeDiam / 2 + }, %), %) |> extrude(-tabThk, %) |> fillet({ radius: holeDiam / 2, @@ -104,10 +108,13 @@ const tabsL = startSketchOn(tabPlane) |> line([0, -tabLength / 3 * 2], %, $edge22) |> line([tabWidth, -tabLength / 3], %, $edge23) |> close(%, $edge24) - |> hole(circle([ - -width / 2 - thk - (tabWidth / 2), - length / 2 + thk - (tabLength / (3 / 2)) - ], holeDiam / 2, %), %) + |> hole(circle({ + center: [ + -width / 2 - thk - (tabWidth / 2), + length / 2 + thk - (tabLength / (3 / 2)) + ], + radius: holeDiam / 2 + }, %), %) |> extrude(-tabThk, %) |> fillet({ radius: holeDiam / 2, diff --git a/src/wasm-lib/tests/executor/inputs/global-tags.kcl b/src/wasm-lib/tests/executor/inputs/global-tags.kcl index 9781831705..31dcd9105a 100644 --- a/src/wasm-lib/tests/executor/inputs/global-tags.kcl +++ b/src/wasm-lib/tests/executor/inputs/global-tags.kcl @@ -80,10 +80,13 @@ const tabsR = startSketchOn(tabPlane) |> line([0, -tabLength / 3 * 2], %, $edge12) |> line([-tabWidth, -tabLength / 3], %, $edge13) |> close(%, $edge14) - |> hole(circle([ - width / 2 + thk + tabWidth / 2, - length / 2 + thk - (tabLength / (3 / 2)) - ], holeDiam / 2, %), %) + |> hole(circle({ + center: [ + width / 2 + thk + tabWidth / 2, + length / 2 + thk - (tabLength / (3 / 2)) + ], + radius: holeDiam / 2 + }, %), %) |> extrude(-tabThk, %) |> fillet({ radius: holeDiam / 2, @@ -105,10 +108,13 @@ const tabsL = startSketchOn(tabPlane) |> line([0, -tabLength / 3 * 2], %, $edge22) |> line([tabWidth, -tabLength / 3], %, $edge23) |> close(%, $edge24) - |> hole(circle([ - -width / 2 - thk - (tabWidth / 2), - length / 2 + thk - (tabLength / (3 / 2)) - ], holeDiam / 2, %), %) + |> hole(circle({ + center: [ + -width / 2 - thk - (tabWidth / 2), + length / 2 + thk - (tabLength / (3 / 2)) + ], + radius: holeDiam / 2 + }, %), %) |> extrude(-tabThk, %) |> fillet({ radius: holeDiam / 2, diff --git a/src/wasm-lib/tests/executor/inputs/helix_ccw.kcl b/src/wasm-lib/tests/executor/inputs/helix_ccw.kcl index 6975ab7b93..76f31c6df3 100644 --- a/src/wasm-lib/tests/executor/inputs/helix_ccw.kcl +++ b/src/wasm-lib/tests/executor/inputs/helix_ccw.kcl @@ -1,4 +1,4 @@ const part001 = startSketchOn('XY') - |> circle([5, 5], 10, %) + |> circle({ center: [5, 5], radius: 10 }, %) |> extrude(10, %) |> helix({revolutions: 16, angle_start: 0, ccw: true}, %) diff --git a/src/wasm-lib/tests/executor/inputs/helix_defaults.kcl b/src/wasm-lib/tests/executor/inputs/helix_defaults.kcl index e85eab8de5..0c66510377 100644 --- a/src/wasm-lib/tests/executor/inputs/helix_defaults.kcl +++ b/src/wasm-lib/tests/executor/inputs/helix_defaults.kcl @@ -1,4 +1,4 @@ const part001 = startSketchOn('XY') - |> circle([5, 5], 10, %) + |> circle({ center: [5, 5], radius: 10 }, %) |> extrude(10, %) |> helix({revolutions: 16, angle_start: 0}, %) diff --git a/src/wasm-lib/tests/executor/inputs/helix_defaults_negative_extrude.kcl b/src/wasm-lib/tests/executor/inputs/helix_defaults_negative_extrude.kcl index 9a2f3efc1b..ab26043c8e 100644 --- a/src/wasm-lib/tests/executor/inputs/helix_defaults_negative_extrude.kcl +++ b/src/wasm-lib/tests/executor/inputs/helix_defaults_negative_extrude.kcl @@ -1,4 +1,4 @@ const part001 = startSketchOn('XY') - |> circle([5, 5], 10, %) + |> circle({ center: [5, 5], radius: 10 }, %) |> extrude(-10, %) |> helix({revolutions: 16, angle_start: 0}, %) diff --git a/src/wasm-lib/tests/executor/inputs/helix_with_length.kcl b/src/wasm-lib/tests/executor/inputs/helix_with_length.kcl index 1847d4ad05..7818e3c5d8 100644 --- a/src/wasm-lib/tests/executor/inputs/helix_with_length.kcl +++ b/src/wasm-lib/tests/executor/inputs/helix_with_length.kcl @@ -1,4 +1,4 @@ const part001 = startSketchOn('XY') - |> circle([5, 5], 10, %) + |> circle({ center: [5, 5], radius: 10 }, %) |> extrude(10, %) |> helix({revolutions: 16, angle_start: 0, length: 3}, %) diff --git a/src/wasm-lib/tests/executor/inputs/lego.kcl b/src/wasm-lib/tests/executor/inputs/lego.kcl index 26099987e9..88a5b8d799 100644 --- a/src/wasm-lib/tests/executor/inputs/lego.kcl +++ b/src/wasm-lib/tests/executor/inputs/lego.kcl @@ -39,10 +39,10 @@ const shellExtrude = startSketchOn(s, "start") |> extrude(-(height - t), %) const peg = startSketchOn(s, "end") - |> circle([ + |> circle({ center: [ -(total_width / 2 - wSegments), -(total_length / 2 - lSegments) - ], bumpDiam / 2, %) + ], radius: bumpDiam / 2 }, %) |> patternLinear2d({ axis: [1, 0], repetitions: 5, diff --git a/src/wasm-lib/tests/executor/inputs/pattern_vase.kcl b/src/wasm-lib/tests/executor/inputs/pattern_vase.kcl index 24f52bffba..c6edfd9862 100644 --- a/src/wasm-lib/tests/executor/inputs/pattern_vase.kcl +++ b/src/wasm-lib/tests/executor/inputs/pattern_vase.kcl @@ -14,7 +14,7 @@ fn transform = (replicaId) => { // Each layer is just a pretty thin cylinder with a fillet. fn layer = () => { return startSketchOn("XY") // or some other plane idk - |> circle([0, 0], 1, %, $tag1) + |> circle({ center: [0, 0], radius: 1 }, %, $tag1) |> extrude(h, %) // |> fillet({ // radius: h / 2.01, diff --git a/src/wasm-lib/tests/executor/inputs/server-rack-heavy.kcl b/src/wasm-lib/tests/executor/inputs/server-rack-heavy.kcl index b16c9c075d..1170bc2940 100644 --- a/src/wasm-lib/tests/executor/inputs/server-rack-heavy.kcl +++ b/src/wasm-lib/tests/executor/inputs/server-rack-heavy.kcl @@ -30,22 +30,22 @@ fn caster = (originStart) => { |> xLine(-3.543, %) |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ (3.543 - 2.756) / 2, (3.543 - 2.756) / 2 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4}, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2 + 2.756, (3.543 - 2.756) / 2 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4 }, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2, (3.543 - 2.756) / 2 + 2.756 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4 }, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2 + 2.756, (3.543 - 2.756) / 2 + 2.756 - ], 8.8 / 2 / 25.4, %), %) + ], radius: 8.8 / 2 / 25.4 }, %), %) |> extrude(-.25, %) const sketch002c = startSketchOn(sketch001c, 'START') @@ -71,7 +71,7 @@ fn caster = (originStart) => { } } const sketch003c = startSketchOn(plane002c) - |> circle([0, 1.2], 2.48 / 2, %) + |> circle({ center: [0, 1.2], radius 2.48 / 2 }, %) const examplec = extrude(-1 - (3 / 16), sketch003c) return examplec } diff --git a/src/wasm-lib/tests/executor/inputs/server-rack-lite.kcl b/src/wasm-lib/tests/executor/inputs/server-rack-lite.kcl index 46f0ca6509..4a0b4c8640 100644 --- a/src/wasm-lib/tests/executor/inputs/server-rack-lite.kcl +++ b/src/wasm-lib/tests/executor/inputs/server-rack-lite.kcl @@ -28,22 +28,22 @@ fn caster = (originStart) => { |> xLine(-3.543, %) |> lineTo([profileStartX(%), profileStartY(%)], %) |> close(%) - |> hole(circle([ + |> hole(circle({ center: [ (3.543 - 2.756) / 2, (3.543 - 2.756) / 2 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4 }, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2 + 2.756, (3.543 - 2.756) / 2 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4 }, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2, (3.543 - 2.756) / 2 + 2.756 - ], 8.8 / 2 / 25.4, %), %) - |> hole(circle([ + ], radius: 8.8 / 2 / 25.4 }, %), %) + |> hole(circle({ center: [ (3.543 - 2.756) / 2 + 2.756, (3.543 - 2.756) / 2 + 2.756 - ], 8.8 / 2 / 25.4, %), %) + ], radius: 8.8 / 2 / 25.4 }, %), %) |> extrude(-.25, %) const sketch002c = startSketchOn(sketch001c, 'START') @@ -69,7 +69,7 @@ fn caster = (originStart) => { } } const sketch003c = startSketchOn(plane002c) - |> circle([0, 1.2], 2.48 / 2, %) + |> circle({ center: [0, 1.2], radisu: 2.48 / 2 }, %) const examplec = extrude(-1 - (3 / 16), sketch003c) return examplec } diff --git a/src/wasm-lib/tests/executor/inputs/sketch_on_face_circle_tagged.kcl b/src/wasm-lib/tests/executor/inputs/sketch_on_face_circle_tagged.kcl index c4df4848b6..9b37a5d1ef 100644 --- a/src/wasm-lib/tests/executor/inputs/sketch_on_face_circle_tagged.kcl +++ b/src/wasm-lib/tests/executor/inputs/sketch_on_face_circle_tagged.kcl @@ -12,5 +12,5 @@ const part001 = cube([0,0], 20) |> extrude(20, %) const part002 = startSketchOn(part001, "end") - |> circle([0, 0], 5, %, $myCircle) + |> circle({ center: [0, 0], radisu: 5 }, %, $myCircle) |> extrude(5, %) diff --git a/src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl b/src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl index a8aae57f25..8c1b291562 100644 --- a/src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl +++ b/src/wasm-lib/tests/executor/inputs/slow_lego.kcl.tmpl @@ -62,10 +62,10 @@ fn tr = (i) => { // Create the pegs on the top of the base const totalBumps = (wbumps * lbumps)-1 const peg = startSketchOn(s, 'end') - |> circle([ + |> circle({ center: [ -(pitch*(wbumps-1)/2), -(pitch*(lbumps-1)/2) - ], bumpDiam / 2, %) + ], radius: bumpDiam / 2 }, %) |> patternLinear2d({ axis: [1, 0], repetitions: wbumps-1, diff --git a/src/wasm-lib/tests/executor/main.rs b/src/wasm-lib/tests/executor/main.rs index 66f3c18e41..8fa6025839 100644 --- a/src/wasm-lib/tests/executor/main.rs +++ b/src/wasm-lib/tests/executor/main.rs @@ -301,8 +301,8 @@ async fn kcl_test_holes() { |> line([10, 0], %) |> line([0, -10], %) |> close(%) - |> hole(circle([2, 2], .5, %), %) - |> hole(circle([2, 8], .5, %), %) + |> hole(circle({ center: [2, 2], radius: .5 }, %), %) + |> hole(circle({ center: [2, 8], radius: .5 }, %), %) |> extrude(2, %) "#; @@ -354,10 +354,10 @@ const holeRadius = 1 const holeIndex = 6 const part = roundedRectangle([0, 0], 20, 20, 4) - |> hole(circle([-holeIndex, holeIndex], holeRadius, %), %) - |> hole(circle([holeIndex, holeIndex], holeRadius, %), %) - |> hole(circle([-holeIndex, -holeIndex], holeRadius, %), %) - |> hole(circle([holeIndex, -holeIndex], holeRadius, %), %) + |> hole(circle({ center: [-holeIndex, holeIndex], radius: holeRadius }, %), %) + |> hole(circle({ center: [holeIndex, holeIndex], radius: holeRadius }, %), %) + |> hole(circle({ center: [-holeIndex, -holeIndex], radius: holeRadius }, %), %) + |> hole(circle({ center: [holeIndex, -holeIndex], radius: holeRadius }, %), %) |> extrude(2, %) "#; @@ -367,7 +367,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4) #[tokio::test(flavor = "multi_thread")] async fn kcl_test_top_level_expression() { - let code = r#"startSketchOn('XY') |> circle([0,0], 22, %) |> extrude(14, %)"#; + let code = r#"startSketchOn('XY') |> circle({ center: [0,0], radius: 22 }, %) |> extrude(14, %)"#; let result = execute_and_snapshot(code, UnitLength::Mm).await.unwrap(); assert_out("top_level_expression", &result); @@ -378,7 +378,7 @@ async fn kcl_test_patterns_linear_basic_with_math() { let code = r#"const num = 12 const distance = 5 const part = startSketchOn('XY') - |> circle([0,0], 2, %) + |> circle({ center: [0,0], radius: 2 }, %) |> patternLinear2d({axis: [0,1], repetitions: num -1, distance: distance - 1}, %) |> extrude(1, %) "#; @@ -390,7 +390,7 @@ const part = startSketchOn('XY') #[tokio::test(flavor = "multi_thread")] async fn kcl_test_patterns_linear_basic() { let code = r#"const part = startSketchOn('XY') - |> circle([0,0], 2, %) + |> circle({ center: [0,0], radius: 2 }, %) |> patternLinear2d({axis: [0,1], repetitions: 12, distance: 4}, %) |> extrude(1, %) "#; @@ -418,7 +418,7 @@ async fn kcl_test_patterns_linear_basic_3d() { #[tokio::test(flavor = "multi_thread")] async fn kcl_test_patterns_linear_basic_negative_distance() { let code = r#"const part = startSketchOn('XY') - |> circle([0,0], 2, %) + |> circle({ center: [0,0], radius: 2 }, %) |> patternLinear2d({axis: [0,1], repetitions: 12, distance: -2}, %) |> extrude(1, %) "#; @@ -430,7 +430,7 @@ async fn kcl_test_patterns_linear_basic_negative_distance() { #[tokio::test(flavor = "multi_thread")] async fn kcl_test_patterns_linear_basic_negative_axis() { let code = r#"const part = startSketchOn('XY') - |> circle([0,0], 2, %) + |> circle({ center: [0,0], radius: 2 }, %) |> patternLinear2d({axis: [0,-1], repetitions: 12, distance: 2}, %) |> extrude(1, %) "#; @@ -442,7 +442,7 @@ async fn kcl_test_patterns_linear_basic_negative_axis() { #[tokio::test(flavor = "multi_thread")] async fn kcl_test_patterns_linear_basic_holes() { let code = r#"const circles = startSketchOn('XY') - |> circle([5, 5], 1, %) + |> circle({ center: [5, 5], radius: 1 }, %) |> patternLinear2d({axis: [1,1], repetitions: 12, distance: 3}, %) const rectangle = startSketchOn('XY') @@ -463,7 +463,7 @@ const rectangle = startSketchOn('XY') #[tokio::test(flavor = "multi_thread")] async fn kcl_test_patterns_circular_basic_2d() { let code = r#"const part = startSketchOn('XY') - |> circle([0,0], 2, %) + |> circle({ center: [0,0], radius: 2 }, %) |> patternCircular2d({center: [20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %) |> extrude(1, %) "#; @@ -787,8 +787,8 @@ async fn kcl_test_stdlib_kcl_error_right_code_path() { |> line([10, 0], %) |> line([0, -10], %) |> close(%) - |> hole(circle([2, 2], .5), %) - |> hole(circle([2, 8], .5, %), %) + |> hole(circle({ center: [2, 2], radius: .5 }), %) + |> hole(circle({ center: [2, 8], radius: .5 }, %), %) |> extrude(2, %) "#; @@ -816,7 +816,7 @@ const part001 = cube([0,0], 20) |> extrude(20, %) const part002 = startSketchOn(part001, "end") - |> circle([0, 0], 5, %) + |> circle({ center: [0, 0], radius: 5 }, %) |> extrude(5, %) "#; @@ -1085,7 +1085,7 @@ async fn kcl_test_revolve_on_face_circle_edge() { |> extrude(20, %) const sketch001 = startSketchOn(box, "END") - |> circle([10,10], 4, %) + |> circle({ center: [10,10], radius: 4 }, %) |> revolve({ angle: 90, axis: getOppositeEdge(revolveAxis) @@ -1107,7 +1107,7 @@ async fn kcl_test_revolve_on_face_circle() { |> extrude(20, %) const sketch001 = startSketchOn(box, "END") - |> circle([10,10], 4, %) + |> circle({ center: [10,10], radius: 4 }, %) |> revolve({ angle: -90, axis: 'y' @@ -1147,7 +1147,7 @@ const sketch001 = startSketchOn(box, "end") #[tokio::test(flavor = "multi_thread")] async fn kcl_test_basic_revolve_circle() { let code = r#"const sketch001 = startSketchOn('XY') - |> circle([15, 0], 5, %) + |> circle({ center: [15, 0], radius: 5 }, %) |> revolve({ angle: 360, axis: 'y' @@ -1271,10 +1271,10 @@ async fn kcl_test_member_expression_in_params() { z_axis: { x: 0, y: 1, z: 0 } } }) - |> circle([0, 0], capDia / 2, %) + |> circle({ center: [0, 0], radius: capDia / 2 }, %) |> extrude(capHeadLength, %) const screw = startSketchOn(screwHead, "start") - |> circle([0, 0], dia / 2, %) + |> circle({ center: [0, 0], radius: dia / 2 }, %) |> extrude(length, %) return screw } @@ -1343,7 +1343,7 @@ async fn kcl_test_error_empty_start_sketch_on_string() { |> extrude(100, %) const secondSketch = startSketchOn(part001, '') - |> circle([-20, 50], 40, %) + |> circle({ center: [-20, 50], radius: 40 }, %) |> extrude(20, %) "#; @@ -1373,7 +1373,7 @@ fn squareHole = (l, w) => { } const extrusion = startSketchOn('XY') - |> circle([0, 0], dia/2, %) + |> circle({ center: [0, 0], radius: dia/2 }, %) |> hole(squareHole(length, width, height), %) |> extrude(height, %) "#;