From 2a468c9269650f381a01cc3bdf3437822ec0fe2d 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 -> 42321 bytes ...hould-look-right-1-Google-Chrome-win32.png | Bin 0 -> 38822 bytes e2e/playwright/storageStates.ts | 4 +- .../testing-segment-overlays.spec.ts | 74 ++++ src/clientSideScene/ClientSideSceneComp.tsx | 8 +- src/clientSideScene/sceneEntities.ts | 349 +++++++++++++++--- src/clientSideScene/segments.ts | 191 ++++++++++ src/components/ModelingMachineProvider.tsx | 20 +- src/lang/langHelpers.ts | 3 +- src/lang/std/sketch.ts | 240 +++++++++++- src/lang/std/sketchcombos.ts | 27 +- src/lang/std/stdTypes.ts | 27 +- src/lib/toolbar.ts | 28 +- 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, 2139 insertions(+), 229 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 2d90b1fcc0..6afd3298b8 100644 --- a/docs/kcl/chamfer.md +++ b/docs/kcl/chamfer.md @@ -448,6 +448,26 @@ const sketch001 = startSketchOn(part001, chamfer1) 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], @@ -852,6 +872,26 @@ const sketch001 = startSketchOn(part001, chamfer1) 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..3629772c1a 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 * 2, 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.44 }, %) +`) + }) 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..6850f61d954f20a7826de8238f3b5a74bcfff31f GIT binary patch literal 42321 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 YirYn9!7+BE3H^E68dom8Q?m;CA0YmNYybcN 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..b4e6a15ab2566c8205ae4acd9bcee50c7369348d GIT binary patch literal 38822 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 HE~Neq0~2|F literal 0 HcmV?d00001 diff --git a/e2e/playwright/storageStates.ts b/e2e/playwright/storageStates.ts index 6cc85bfdce..61259fbcbb 100644 --- a/e2e/playwright/storageStates.ts +++ b/e2e/playwright/storageStates.ts @@ -365,10 +365,10 @@ const box = startSketchOn('XY') svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness) startSketchOn(keychain, 'end') - |> 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 7a1c66e753..ab34321607 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 1262297717..1f8d3e55e7 100644 --- a/src/clientSideScene/sceneEntities.ts +++ b/src/clientSideScene/sceneEntities.ts @@ -63,6 +63,7 @@ import { addCallExpressionsToPipe, addCloseToPipe, addNewSketchLn, + changeCircleArguments, changeSketchArguments, updateStartProfileAtArgs, } from 'lang/std/sketch' @@ -72,6 +73,7 @@ import { createArrayExpression, createCallExpressionStdLib, createLiteral, + createObjectExpression, createPipeExpression, createPipeSubstitution, findUniqueName, @@ -90,6 +92,7 @@ import { getThemeColorForThreeJs, Themes } from 'lib/theme' import { err, reportRejection, trap } from 'lib/trap' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { Point3d } from 'wasm-lib/kcl/bindings/Point3d' +import { SegmentInputs } from 'lang/std/stdTypes' type DraftSegment = 'line' | 'tangentialArcTo' @@ -103,10 +106,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, @@ -144,11 +155,11 @@ export class SceneEntities { ? orthoFactor : perspScale(sceneInfra.camControls.camera, segment)) / sceneInfra._baseUnitMultiplier - const input = { + let input: SegmentInputs = { type: 'straight-segment', from: segment.userData.from, to: segment.userData.to, - } as const + } let update: SegmentUtils['update'] | null = null if ( segment.userData.from && @@ -165,6 +176,22 @@ export class SceneEntities { ) { update = segmentUtils.tangentialArcTo.update } + if ( + segment.userData.from && + segment.userData.to && + segment.userData.center && + segment.userData.radius && + segment.userData.type === CIRCLE_SEGMENT + ) { + update = segmentUtils.circle.update + input = { + type: 'arc-segment', + from: segment.userData.from, + center: segment.userData.center, + radius: segment.userData.radius, + } + } + const callBack = update?.({ prevSegment: segment.userData.prevSegment, input, @@ -409,19 +436,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( @@ -467,15 +496,26 @@ export class SceneEntities { const initSegment = segment.type === 'TangentialArcTo' ? segmentUtils.tangentialArcTo.init + : segment.type === 'Circle' + ? segmentUtils.circle.init : segmentUtils.straight.init + const input: SegmentInputs = + segment.type === 'Circle' + ? { + type: 'arc-segment', + from: segment.from, + center: segment.center, + radius: segment.radius, + } + : { + type: 'straight-segment', + from: segment.from, + to: segment.to, + } const result = initSegment({ prevSegment: sketchGroup.value[index - 1], callExpName, - input: { - type: 'straight-segment', - from: segment.from, - to: segment.to, - }, + input, id: segment.__geoMeta.id, pathToNode: segPathToNode, isDraftSegment, @@ -575,7 +615,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, @@ -779,7 +818,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) @@ -857,6 +896,153 @@ 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({ + // eslint-disable-next-line @typescript-eslint/no-misused-promises + 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 + 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 + 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 + 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) + ) + }, + // eslint-disable-next-line @typescript-eslint/no-misused-promises + 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 + 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 + 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, @@ -1028,11 +1214,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] @@ -1069,6 +1252,43 @@ export class SceneEntities { pathToNode: PathToNode } | Error + + const getChangeSketchInput = (): SegmentInputs => { + 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) + ) + return { + type: 'arc-segment', + from, + center: group.userData.center, + // distance between the center and the drag point + radius: Math.sqrt( + (group.userData.center[0] - dragTo[0]) ** 2 + + (group.userData.center[1] - dragTo[1]) ** 2 + ), + } + if ( + group.name === CIRCLE_SEGMENT && + subGroup?.name === CIRCLE_CENTER_HANDLE + ) + return { + type: 'arc-segment', + from, + center: dragTo, + radius: group.userData.radius, + } + + // straight segment is the default + return { + type: 'straight-segment', + from, + to: dragTo, + } + } + if (group.name === PROFILE_START) { modded = updateStartProfileAtArgs({ node: modifiedAst, @@ -1085,11 +1305,7 @@ export class SceneEntities { modifiedAst, kclManager.programMemory, [node.start, node.end], - { - type: 'straight-segment', - from, - to: dragTo, - } + getChangeSketchInput() ) } if (trap(modded)) return @@ -1192,16 +1408,28 @@ export class SceneEntities { ? orthoFactor : perspScale(sceneInfra.camControls.camera, group)) / sceneInfra._baseUnitMultiplier - const input = { + let input: SegmentInputs = { type: 'straight-segment', from: segment.from, to: segment.to, - } as const + } let update: SegmentUtils['update'] | null = null if (type === TANGENTIAL_ARC_TO_SEGMENT) { update = segmentUtils.tangentialArcTo.update } else if (type === STRAIGHT_SEGMENT) { update = segmentUtils.straight.update + } else if ( + type === CIRCLE_SEGMENT && + 'type' in segment && + segment.type === 'Circle' + ) { + update = segmentUtils.circle.update + input = { + type: 'arc-segment', + from: segment.from, + center: segment.center, + radius: segment.radius, + } } const callBack = update && @@ -1246,7 +1474,7 @@ export class SceneEntities { } private _tearDownSketch( callDepth = 0, - resolve: (val: unknown) => void, + resolve: any, reject: () => void, { removeAxis = true }: { removeAxis?: boolean } ) { @@ -1276,7 +1504,7 @@ export class SceneEntities { this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis }) }, delay) } else { - reject() + resolve(true) } } sceneInfra.camControls.enableRotate = true @@ -1288,7 +1516,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 @@ -1306,11 +1534,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 @@ -1334,11 +1561,11 @@ export class SceneEntities { } const orthoFactor = orthoScale(sceneInfra.camControls.camera) - const input = { + let input: SegmentInputs = { type: 'straight-segment', from: parent.userData.from, to: parent.userData.to, - } as const + } const factor = (sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor @@ -1349,6 +1576,12 @@ export class SceneEntities { update = segmentUtils.straight.update } else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) { update = segmentUtils.tangentialArcTo.update + input = { + type: 'arc-segment', + from: parent.userData.from, + radius: parent.userData.radius, + center: parent.userData.center, + } } update && update({ @@ -1364,19 +1597,18 @@ 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) - const input = { + let input: SegmentInputs = { type: 'straight-segment', from: parent.userData.from, to: parent.userData.to, - } as const + } const factor = (sceneInfra.camControls.camera instanceof OrthographicCamera ? orthoFactor @@ -1387,6 +1619,12 @@ export class SceneEntities { update = segmentUtils.straight.update } else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) { update = segmentUtils.tangentialArcTo.update + input = { + type: 'arc-segment', + from: parent.userData.from, + radius: parent.userData.radius, + center: parent.userData.center, + } } update && update({ @@ -1557,7 +1795,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 @@ -1604,10 +1842,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 bf1c8e636b..3232dd9ba6 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_HOVER_SEGMENT_LENGTH, @@ -477,6 +481,170 @@ class TangentialArcToSegment implements SegmentUtils { } } +class CircleSegment implements SegmentUtils { + init: SegmentUtils['init'] = ({ + prevSegment, + input, + id, + pathToNode, + isDraftSegment, + scale = 1, + theme, + isSelected, + sceneInfra, + }) => { + if (input.type !== 'arc-segment') { + return new Error('Invalid segment type') + } + const { from, center, radius } = input + const baseColor = getThemeColorForThreeJs(theme) + const color = isSelected ? 0x0000ff : baseColor + + const group = new Group() + const geometry = createArcGeometry({ + center, + radius, + startAngle: 0, + endAngle: Math.PI * 2, + ccw: true, + isDashed: isDraftSegment, + scale, + }) + const mat = new MeshBasicMaterial({ color }) + const arcMesh = new Mesh(geometry, mat) + const meshType = isDraftSegment ? CIRCLE_SEGMENT_DASH : CIRCLE_SEGMENT_BODY + const arrowGroup = createArrowhead(scale, theme, color) + const circleCenterGroup = createCircleCenterHandle(scale, theme, color) + + arcMesh.userData.type = meshType + arcMesh.name = meshType + group.userData = { + type: CIRCLE_SEGMENT, + id, + from, + // to, + radius, + center, + ccw: true, + prevSegment, + pathToNode, + isSelected, + baseColor, + } + group.name = CIRCLE_SEGMENT + + group.add(arcMesh, arrowGroup, circleCenterGroup) + const updateOverlaysCallback = this.update({ + prevSegment, + input, + group, + scale, + sceneInfra, + }) + if (err(updateOverlaysCallback)) return updateOverlaysCallback + + return { + group, + updateOverlaysCallback, + } + } + update: SegmentUtils['update'] = ({ + prevSegment, + input, + group, + scale = 1, + sceneInfra, + }) => { + if (input.type !== 'arc-segment') { + return new Error('Invalid segment type') + } + const { from, center, radius } = input + 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.getObjectByName(CIRCLE_SEGMENT_DASH) + if (circleSegmentBodyDashed instanceof Mesh) { + // consider throttling the whole updateTangentialArcToSegment + // if there are more perf considerations going forward + circleSegmentBodyDashed.geometry = createArcGeometry({ + center, + radius, + ccw: true, + // make the start end where the handle is + startAngle: Math.PI * 0.25, + endAngle: Math.PI * 2.25, + isDashed: true, + scale, + }) + } + return () => + sceneInfra.updateOverlayDetails({ + arrowGroup, + group, + isHandlesVisible, + from: from, + to: [center[0], center[1]], + angle: Math.PI / 4, + }) + } +} + export function createProfileStartHandle({ from, id, @@ -535,6 +703,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, @@ -788,4 +978,5 @@ export function dashedStraight( export const segmentUtils = { straight: new StraightSegment(), tangentialArcTo: new TangentialArcToSegment(), + circle: new CircleSegment(), } as const 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 c212c279dd..f923e00f7c 100644 --- a/src/lang/std/sketch.ts +++ b/src/lang/std/sketch.ts @@ -17,6 +17,7 @@ import { getNodeFromPath, getNodeFromPathCurry, getNodePathFromSourceRange, + getObjExprProperty, } 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] @@ -925,6 +927,177 @@ 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 result = 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, + }, + ]) + if (err(result)) return result + const { callExp, valueUsedInTransform } = result + + 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 = getObjExprProperty(firstArg, 'center') + const radiusDetails = getObjExprProperty(firstArg, 'radius') + if (!centerDetails || !radiusDetails) return [] + if (centerDetails.expr.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.expr), + code.slice(radiusDetails.expr.start, radiusDetails.expr.end), + 'circle', + 'radius', + [radiusDetails.expr.start, radiusDetails.expr.end], + pathToRadiusLiteral + ), + { + stdLibFnName: 'circle', + type: 'xAbsolute', + isConstrained: isNotLiteralArrayOrStatic( + centerDetails.expr.elements[0] + ), + sourceRange: [ + centerDetails.expr.elements[0].start, + centerDetails.expr.elements[0].end, + ], + pathToNode: pathToXArg, + value: code.slice( + centerDetails.expr.elements[0].start, + centerDetails.expr.elements[0].end + ), + argPosition: { + type: 'arrayInObject', + index: 0, + key: 'center', + }, + }, + { + stdLibFnName: 'circle', + type: 'yAbsolute', + isConstrained: isNotLiteralArrayOrStatic( + centerDetails.expr.elements[1] + ), + sourceRange: [ + centerDetails.expr.elements[1].start, + centerDetails.expr.elements[1].end, + ], + pathToNode: pathToYArg, + value: code.slice( + centerDetails.expr.elements[1].start, + centerDetails.expr.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 @@ -1688,8 +1861,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, @@ -1875,7 +2084,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 } @@ -2065,6 +2274,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 = getObjExprProperty(firstArg, 'center') + const radiusDetails = getObjExprProperty(firstArg, 'radius') + const tag = callExp.arguments[2] + if (centerDetails?.expr?.type === 'ArrayExpression' && radiusDetails) { + return { + val: [ + centerDetails?.expr.elements[0], + centerDetails?.expr.elements[1], + radiusDetails.expr, + ], + tag, + } + } + } + return new Error('expected ArrayExpression or ObjectExpression') +} const getAngledLineThatIntersects = ( callExp: CallExpression ): @@ -2124,5 +2359,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 49d573562a..e4c5247b68 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' @@ -89,7 +90,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) } @@ -1735,11 +1739,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, @@ -1888,6 +1901,6 @@ export 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 c432ba33c0..fb9dcbb971 100644 --- a/src/lang/std/stdTypes.ts +++ b/src/lang/std/stdTypes.ts @@ -35,13 +35,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. @@ -66,7 +75,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 @@ -182,7 +198,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 c791ffe1eb..a22ae5ac71 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', @@ -363,10 +368,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: [ @@ -382,7 +399,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', @@ -404,7 +420,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 fd6eb57fef..3c8f198db7 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' @@ -156,7 +158,12 @@ export interface Store { openPanes: SidebarType[] } -export type SketchTool = 'line' | 'tangentialArc' | 'rectangle' | 'none' +export type SketchTool = + | 'line' + | 'tangentialArc' + | 'rectangle' + | 'circle' + | 'none' export type ModelingMachineEvent = | { @@ -208,6 +215,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 @@ -241,6 +252,7 @@ export type ModelingMachineEvent = } } | { type: 'Finish rectangle' } + | { type: 'Finish circle' } | { type: 'Artifact graph populated' } | { type: 'Artifact graph emptied' } @@ -458,7 +470,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', }, @@ -699,6 +714,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: (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 @@ -711,6 +762,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 @@ -1813,10 +1876,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', @@ -1954,10 +2050,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 @@ -1972,3 +2071,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 4af84bfa72..1fd1a2ae26 100644 --- a/src/wasm-lib/kcl/src/std/extrude.rs +++ b/src/wasm-lib/kcl/src/std/extrude.rs @@ -230,7 +230,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 34539435ef..fc0aba5bb6 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. @@ -320,7 +320,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, @@ -653,7 +653,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 zcmeFadwi7D)iyjK0xDt(3Q>_nC5rVFTPi^bj8-kAR`XbksUk@gYaYuXAYu{%8I&p_ zYB)SfH9}gcrIkXYJQ$FK0YOnQq)IU$hY1NJa!f)9InB)dUf0_DK21XVe&6r={?nf% zlMpidUV9y`b*;76p7&o5jOu;i6&HFup57^=@A;+2(;Y9n{qy`D_*60Lz{4I-cumSZ zcl|0S>SR^af`pVs2`PQ&zyH?8QTN>Q)KgDAJO=+|^S_oa|Nr>!S^ny@G``DIPyOs? z_xyfi;aeA0{Nky!?CIIrsTF+7Upz7EpL622jY%6feRt;%&Az^GtX%!kvG=?9VBBy+ z*Z*kxZez@^&hd}lH&*`jv98_;-!sx4+|cDay}vSC{O&VdERVk^lyUEou9k=IwfkTB zqVqG!yEps#eg9w%iGIIhE~Wfl6&LG&cp5azjq!k1^M22eD6HIM;(fSG51Vazp*5j>!)4$s0Gr z&7~Qc@{2M~e3E`*9e>>)?cb)pnLF~i?ChvL2M!#`UvkU1ZdH5Mj8AUe5>xxl*sLR$ zSLF0CV)xd*Z~Qtj^mSr+W6k!qFDrsyR<_OcwayLH+CjtHKk|)ZBi}gI*BI+N8Jo8z zHg8hgZ?dzOWG2^Ke&W=rXO4b-Etin<>5yex{D*H2oW42tZ%bzUF#l|(?@fPOq5sf^ zy|p)dP;PYpu&tosYb>O+d944omIK*=Rq>~CWS&Ew@!Z*7Td%2M&zGTed9?v<W&dC0QHi8z$2AuC8S@9M^n!YF&O(&s=Y5v3a?nW&Hv`4 z@Y>MM`ryT~_v*LhF0IV1545iev`=r41(8a*j6;U zuhIX*w)$)4(HQp6IKE2W6vp~VdWXFAEa>@-j`tkte9xX6MHQh8uNO6oLYA3tnFKk* zxURV%C3!)@vO?MBk~{K)!#pJio*S?zCx2p-Z(c&UY+{r5#f0#>iB0iGsSr60bK<|0 zt!p7~{aCivx}!6(XcXnNM&w`21hiZD)sg@12h+1^pJz8*a5 z>(ruQQ>)+e9bJ+?uc+; z6d$PwpQu=mFd~0(LCWZ#_EkS)y$mNU;=i%JHea?vc_V%_p|O6=qpLikM76cGjS_3b zO^tB4seMjwk#*Xky$4!Cb9~23^5;{9e%9Bs2>$8R;CVe4ZEG4dFBT@VFz(DBdqy=- zT*hc@=o5uT?}Wyz#|w>=gvNK_pgZ>YLu1pAuS@SW``;>2vr~y;l^JoH1I_RnPZWz^ z7suh@_JtCrdbJ%fE{sj7&i^oX^7hvUMpu3P-hIK;Yv&o~D`UoP7Twvm;v~Oa9tieM zZ2o0p_s!x3zsbz6%?uB9Y~hUa9ACV6un~5&cGJRpW2to zJQ~{cQ(Bd|7t}Cr+_pJ!XRh)fn7DQZ%J;^h#g`$zJMhcsbvu8b}i*wu`?CW=9CLF&I2mAHKzt zS+VTMiM?xM@)0JNwm4-s*D1S2Vi-`j`0zLtr-*YtXRL_6>H<%SXLPqaX}ZdJO@_CI zcEerne($~|tpPX*2ZJ*vYcw`^F2!{z$!{EyoQyr*o&M=zas;CQo znicGEy!nbQd;zj{I^d$cNsw4nLMK^CVUkz zhOhVRgal|Tx1c&-`9M)$W%_)8@ba>4_m!SkF!S)BwvGOd;md-D2RC^acCl8`^c9^w zcqbK}>TjOWd-L+R=Ie(hCEcE!oE)Ynj0x}ZhISPlvL7VlXU=>>1;4EJzhup5|73n_ z8=U#Ya6jkGhTnEQ0G&7@5knwi-@|;WW%2uhS%c^KZrm44y=I>8=6%6zxZ24ZPY(6O zb?n)G`n5poYo&$I;xY$!W7z& za^6u_&Fg^6=6ebnm<&hR3&;q&Xv8HNrxPpIRi^LA2!4_=B`~X{F&MGC&d9jU7}`m1 zq2j6Pw^@Hx?cFh>tVh*dF*ANpSXf9M-k#CeFQajb54fOxh;4#pQ=Bp2V&oLXO$XJ( z;OPTZc=~-&Sb!%1%fbVBuyivT@mUriGU7q1y6(T4=BYI)qS4q~hc5U=#W&^{XJ8@ad+H$rk?6;BSBy=3O+OU|pJi~8WTll9?tfYTO-W_EYNvaALb&Q+Q=DR6cYcY*fA1$oRB zDXmZ_wW1&){SV`}zdm)x%_UP#?D>&r@}{<@ULQF8Q!J*nM3w^>>i>Fte8;ahH4gAE zL{y5&JqJu(<^@t2@v=ivUqdL1F;**xnAP!NRt-|@?$FT)30Zz$99D7(SA^BbV&c6! zckaBnWdAe0j77VD*M0Nv);&>{9{s_it8)I>JfnP!F=Ap<(Uze`-+jTny|vxfl=~0A zG+=Vowt1HXnaA<{H%}=0W-;D8ZF-G{ zC7R>jOy^P;B%I2|GSgv2SI$oJHK)~ee`M9ee}DK!=JxBKJ=)hp=N^E`EreTLIEgt| z#*U$hH|z`EH?b*kO?j{Jg|VLwO?-9IsqDt;d(%U^GnQ^2d3H?@NI05k{9MSm)B)wn zULMJx--w03R2goJH>NHNpIIRmBId969Z3cT5{j1kB;Ah z74)Ftyc*xJCcb9r!J|t@9v|Xu8sc-r<2q;G(7i5&MS{I5Idn926b$78!~q4V#H}h{ z1{Aoz9$ZZ+<3M6#-^496nLqCS-6zPgSMGjAGaO4sSHHNXeA?n|SY+bM3E?5YT-S(p zWNfAEORt~U^zoir{EQNR>a1fAZ8-GOfSgs8Gj1k6iK<$scp`)L0t*fs7Wd8nX6(gWF0NSNj zeVBVy3e{u3pnFCRr0CwaCoy3ye!MUidvNWCxsRLA5PEaRwb=)z$0qHc5{*2(xLmSt z#(*DhKE%_ZKzQVPc6(d^}4M3Qf@6ejeyJNt$t+(n|GxF8q0e`(MC7ZZ- z%UqRH7dS~sF8af;Y{{Nr>vp=_v8x{5KfQ9C7Jm5JML7?qRA=1R*XWbbXgohDJZO)$ zR%(pXT0N6X8-GTte+O%pm_75exidfOM9nRG&4GZMrb^l_kp1{5-ez|44%UUOUeoQ_ow-$)ySXW5X`cnt}LmnXr8tBVmEIf%>hUCYk?1Tp>H1ph7A~K+i=k9Nh)anZz^kc;ll@g6C@}DJ-b4eeC zemyWtJI9z%`ssuQ@*>oUju9uHPOVwJ@zfLR9&Nny!wsMZf3OcmYfCmMrSz{bx6#!G z95*;hu!QNq`Y`uFrPboUq1c|s)=zo9SL`iQYQMZ3IQNR3%SLomDT*XM^i_Q5N}JD_ z{S2(h{_<3d;sbt(%(*^vTSn7uqp!KfL|_Wh5e?BI%W5ZVx-P4+f7X^k@ol4Pn{LNW zGPm@%&i7Zrk^ExU?@4=!BHvBS*;&v@X&wu+|;X^@sB+CJp@zdYSdSP@b{8Q~)t zJM;W4dEVC8{6n#uUb2Gu4S#fj&>J+@P|*5vLF=jdwq^B?XJ^Nz=!G-))L4|rBmMNX z8H=)W=4MwdESQ6wJ1_PP#79tS zDSeGJnA{?V$)#T^E?S(%fZ2H5`;!iAn-d@W4Y`ha6veP`rtfT~cUn%)`W@yfn~lre z*{UMBPS)3ZTS;nTxqbSWXqGY-FUMd$@Zh0=Ipviz?{}yKn7CS~1PHL42W7p!n}x^m zzmgDM3D)@v!_r zaQik5$cy+V+CXOpKT&Yiyp^kgIhKfkBNgIB?$5Kk?w>e$^646HxTchBQX^tna~4c6 z&nA+MwEqF@w%~lonC@`M8#f)_h=lgqYa&$Q>I-I*i=Vl<=S&Y{kSFJhhn9T|5+Gw5 z756f8ss5LgF5KVOh*i3vW&5H-w!OxSE_}Xt@a?nOf3m2RiF-|P!-d70i^4~X!WUWC zw9`_XQ;N={c&CFb9@*OBGg{`Ie@(X(%2uMC2gXLk%WPy&aogL)7i@lJ!(n1?7RwM> z#3fidkT<&8zbGNR7CHb}0sQRJfs7}JNh?l%{9>=V4h`5o;?z{hbnX+5w*H+F zd1oKC{FhO1Jyp<5m)nh_LW_5d$!H$qZO3L5zI^^QFT4!YFS5mAf$WN#A1(fbOa@&c zAdJ+aR{Gm-{L9lUR`~Dfn|K3CBBOOYp1QjqDq+7VWX2rn!(RBsu5C}OscJu7T=(5~ zFEgb*Tz>FGd0bEmRaa)du{f(+`#DxACWAR>OdpvxUvV+IV1Pyv_d2Ib=0aS+v zdoL&6UGv%`goiz!4yApMA3!jPkBkKPi0?{tkZgOcB@(ka@WIBOL~XEBO4eQgT6pz#iHrMMw<%KDA~ma zJN!n+J?Hhg>z=FTQTXEF8d++HY5!wPHCQpg_VDRMW6_G_BR_Ed1aM(QB%UfTu^!W^PB1_L^Mc}CXO$}`K#$*aE(X8yz44OAgS?(fP5Iev4eooH(M^WYH|%w}emyi2EKwj1%es}x3J zd}E*Z#&N#3CBC*QTF(Fc(&@M9{m6|sva0JzZu>m7mI{^&x)bF=znsBm%rgQ zi>5G&?BYW>gVma)Et`NP(^)v!_qTQAafmXYAaSP`%xy@oZ%+46H&`}>KW=zoPmenZ zhftP?MRZ8eAF&Jq&>IALQb7<7!EgHkJ?T?^Tj=X8xt2;7v43&j1>2gg95}FX&-Ri1 zQ|LstsN^Z4s%vEYmos)Z8{1Lp865f-9a_8}X|+?QQGrXJOMUB6h2I`LSRaZ5CPGGX z@743bxTpULuo&ewaPP}USHGXRw%m8=J{CQh3tlMeLy$($tEl+N{SYYff$b$2aYuDQ_`3ow>-V%~ z_#b6taR2mU+r)tZ8Hkw_TMo{5p7L~-CE_vSAzgl_zR7-{eW?^KQV zo*mD)Jo#*R?zJrA-5qWT86Y6SITi|`Dtpc9OB~QOi ziVkq7mJJNxyKnE3C~o(^?%04sLV$GG8{kP{JUzrkMGf#iKd*r!M)I*8IU*}j(ShkkxFuo6&6@|i!y9GMkpnI#X! z()DK`y)FG_DnbSwWbfL{0;vo) zf{yowF1G>4j`Qu;yHrhBC9#`hf?$h@iRR56dFD^0^&OiMJtcws;j*l0YoU|ZnyRf7 z+*0#jsSrYhVTyQ&7Q933UB3jBQoF*~r2f6$HS5=lc_E8o8m?CnTGqO4aJL3H!#AaU zKv5s;fVkDx_}@loeDlcqrjZ%V3+5&te$U^r(O;-FUUne_@8?+`I=iopNXksL5l~EB zkAbc)-e&YfJ_szud=P}B3E&e53e2x9O;v+|I z5NbO2!T8|%;@bZ?_cW@i`JUv)d8vns!^es}a02I)zv*U(i>S9P4*jO97>{~)g!F@> zG`3P|zlQ!2y9-6qdn_iPgf0?MH5Nv_Qb*r`9m+HAb$j;v0lJIMJS2S))Sy2+e%bZ! z{h*JMb-)NkwNlQ@&i?NTHh8Fv&rZ~T zFGt`hl4ke}=SLH;V%wEt&(ad7T6$q2rjXl8T+&qv?QklTtBM#+cc#v)z9cH=#Q6AL zQXJEbNHzkA?PI?ltg0nZ!DGeY8&J%-JHGwD4jQ3A(ItZ)n8K>Alwz&lO?aka5LJgU z!vt*FP?;B+YnH$F6C+xV68yEPQk9}oDhRoSLIDW^YzhLOOvNe?VJ}bbSeH>~bJ_E4 z410|O&%Wctv);DF-a}}FxaYjVsml&WkF1LJ49h-sB}xOnPnxhOl|iT{QBS2mcHw@O z|KGYF=hNy3Z!368OTbJy5YqVmlCJQhh(V$-f&8HhQax2`3TE9@ai-JeRsO)rGIk9MlBIPFUbacQqJD<+Vzu{Vy z3IRmxRU2@WkCA(C?qAWk7&%4>a?>b9)YxnO{={Kv7tBO`Uu=yJPdQlC`=Dobprb5MgU#oPtx$z*RykIA61@(MpCR2m zc|5&!K5gyXwkA^-+r-Tw)bN|?qcTQ*r040q8CfH{aHBv&=Jdq21_mhaX2^p;th zV?3yTSSzXPQRq>W`{c*+X~XgMbvu{VeEHT8HXch-sibZDfmnwF8>}2ybnuJf;Dv0Z z{+RIxMB$E0mNlG5|00`gesnyGMKd&65wqOaU#Xk5cGIdHyVUt5Dv}y5n!idB7^tn9 zDU!pJiW#lnKH}op!k`a8+MsfH2UvF(6pRCE$5^2Xu8TT_e);r6{nDdE;l^J%iba@3 zY|GRX8>xmkR0=?|E?cbVhOI-5hs~;tN|VLm9^JJ9jrtdj9XpnSZY7I5vK7!U)n^w9 zd_!zk5;vFK`wtbh|4Nq*0RVlyL0Tf=LG9(ighnsAjoI;s;swh_$m?|t zR|xmqyXZqe1Ose8K4HTAbMx$D9q?(wIO(q!7AODo;-ZT-Z;d;9D}8^H*O=C|IL&}+ znISe;%PyF03Q?=1!=(UnUu^!IP*7?oiWx$*eQWzb7*r7IbJDM#$ebpEwe;QSmTq?Y zpHtiaT-1&SXzIyE#viwIEcdn#!b7o(8F@Mz0YR|ehQ3$GI1$Gb7Hu;!k;Ob&=u1?o zq_kB?-u{pBVXvbY@XXfUNmtEdW;Ifp{eocFk5*6!21yi3Puz^m)ppxK;4DF(rXfzxGI0m;Sh3c}a3*Dzd#CXGOQoM|ZU8oD^K@oSSLsOfBtj>C z=@1pzgVM94@XGAIV`Hcz8sC9~3iW3mA=s;6b)wr8lvSQVNI#V+9*6s-gMh>RcM4_3 zLLH{Kvth5pjM@q`qEMk2nS~%<#&T6BkdM$Q6#qUe3hgb5QW#bkTxpQCW7j@M>{{u> zuuYzVy!|yf!W}{T6v#F|szgV`I#TkcZFu{PN|4B$TVP-T4t`B$$?| zJP#!Z9c63%iz1(*bBuND4Sok`C)3{vfHF76)pplg^sXLuA|S;`+gdc%p+yr^&*Cgpi+i&Ye&J@SU?cpTN(mh~n{rF0{@RM<${E^> z&n#1ghP@(t6LdN^QpZdb^zR>r#ZE2AyV8U5Z~bP8!P5WmiB+BRceg8b{*P5rS?c!> zt$1MS>BA`($HZ(viAxcu7Nx9yzqwkoypv^S{}r8p=Z_=6n{m0;8XX={7Zq1P3@c(+ z&IXA$K>b4xYw2H5+f{ZUzCi-38#31h zG*1I1zQI)D|Df(zv?G|a&~H9hocspVX57!Eu$Met%(L)GIEUl)U~?KSmd@fxJUzf& zp7HKe9qIE~06dG1=-khj%uG48v3{hKDp1X585f8MhkzJ)SSCXE?)(I(Iw}X(NoyVp zTsTNl391YyqZYs^NDmF#aRC$&fwqf%MfMUKiyg?QMz`VCX69d)b^5xjZkzcW4R`XH z#j;5cDjG|DMBJu_K?D+Q#gB!TyCi2gBw1*TR^rz*g)6N`axV(fwnSPtJyOR^g76x; zM3+ByCmD8PF66n8j z6X+FZ6oC$ScZyZHjJFFXe>9INEo<#)UXyB}#+qz=1!E3@UzhS&a~4eS7pe=vV$NBV z&Hr@jlW3B_d7wVC$r29}xY>y_AMD_9M`<2cP`vQC=||s4zd-(Z)${a}07#tzh6$8R zZi6jH+Ona4z{5JmK{ggRv0RyD^0vQukw=<)DghGeIxw(x`lQ+0$JLT#G|k*LYUZ|V zgpAPMIT0(^abTMVoq#?Oy)c8~8qfEH!}Z3p4#@03I0SYr zD51oE?tlpI*Et9`MRGGY7Dp_pfFmao@ik2YK<_55l>HY_kXHM6OepoIc9<kD>Hess5M75Pd47z=T;9JAB(#)eH4C3aucfbAGU72JB*sy zrL#$_9paeF{8P^+vc}ctMdfovoIPir-qg7SRkQ zvlEtgxPPY|GQC(YWG)V->}Xe}^N73Hf!vhx8UPrNs63Wt`}>I+wfo6OBn-({O9mxd%k zxFXZ&ZXY&GwXm_lgvJ(^P zwBLpggUrQ^FI<`atR-=_?>lgYDNb24c2yp|Wg}bl@aNyM%I7)4_4x{yLDe(LC68@j zrk|W-Z4wt_3Y1<60`1Q=SsG&jUL|J3VllAgCbek_7_}Ki#tCSW*tBflp+i@r8PONB znF#PSI~!DTG1WY`8cMy>RZ{g8tBz-9 zR;n%ZcBs=RiA;V_G&saRfaRcm;=I5j)r&&h$rdEWT!#DL4<0Qx5F9U3#EH1N%F$Hp@b9E3D0}lOmTHO7yeB zbalI;&Sw$bHCRDEjo9|NwQyf_Ch3xY!5x&ib*ns-pGbi{}nb;%s8dO zs`~hOYW|N4a<&cfw90u;ir0*4iWJhxF?LoIc7!RKuS|Kb!I{H+Q`qR&BP*rBq;{`b zL4y-y>V_EfYYAu?YwA>5-|Z%Fi<=eZ+u;xHh{-=tIcpp-A0;K~8MJc15_B|zz%7Gz zqTzfTjw>yl6y69!LO*wz4g2;!g<{X@va^qV_~D1GsMzNoB(UBRF`dGA!=abIRA=+m zN1k@*Q;>6>2d9;M+t`q-QZWfCPFvOuXoVOF!GJ9Zph?$hH`wG?e<7yNpss-iPFX2u zp!{wx9cJV$PPP2+iV?jvy$F7*I3y{G?GiKQxn1HNs`nP+^9QRcXC0_K@~^)=B)b4# z7H?)I=mlkF!U+~~K4xV&`vb;1heIs>ZDC#Jm7_>B9sNsT4(dK1vsdyolgHu^+G~4d z*zPdH;SLjdni)fqWT6B5C?LlMUqvHYbXc;AM}ZZ;fQ>E$ z^t4&H18K?rLtcH^62ML9*PH}kv5u<9D!~mlX7IfwAg#E#{r%$VH;{9BT5)vrZW7K7dVa-~UfjXEA#7-|M~- z=FpM~n!ufe9Ta~eC@Xq91 ziXy81Zm~^6%!iq_+qlTw8hN20vjBZMc|q_^`*~0wWqGD}$1(UQ*F6BEz2awX?7K{1 zUo{kNer)3Dmq#VPK=VZtmH!B+CjeeHs%zQ=&Zz($vp+{P^dcJGh-m0^@1?iSojdoj zyp!wL_b_qNq^SZtK1rMTNlzo9)R}p;ZD|i_mh{uinLQN!&r^70N$r4dA${T2(X30k zSkM<$SIY-XoVL4{4L`y+svndJ8b}@`Wdh81TUpT6IE}NiR5W%syZZXOaoV)5a9aDn zR-8st(es?-b+f7mqpKB1n!GsHh9-L76)8a+VKSapsRBrldVmKKcBp7_<^>7~a}vN# zZ>nLCdoCE!zTl0vp+ix%G$Sc%5erAx$QPdE=D(#LVQJ9q$oY-2%1k9G#g}HS7bmS; z{>Z`-fW&srXiJp=G+UQ})Gv{AoRi|j<$*aP_*a~WP|nVo#LVk{H{u>(rtC)7-#3;28;DQ`IPKm8V6RWWo!Yn7Lh-HW{$L{Izn=40QY6`>S zFci$VoY&c1cUj~i!LJ^6e(_Mwk+l5ZdRSQQVFrF>n8iZV2ny2~sEI8K zjhjqo`B-yCg}^j8LiJsRCZtxV+#_@B=ffC>*B{fJPFB`#>Nr)S@gVW5-C^gw!i`+l zpp-tcdLvn!Gd6PNp>=kAa5Mt6kE0Q6?Hht~b*yS2P4LBH^YQ6Cl_q?F<0Yyo0j?HO zYkM)H^~D;iI{Lz45rzsAZTUv5)P22(^gv)EVToS zt9I08II8PuD!{8BrJvIqjLhvzc26iJ^Ke4vIzl|-uwBB};j6q~J zqZliNRu@IN4;H0j^XhgOuh>je5t%gk8G)QaqkKXZ2Ll*-$;`G!o|;sS*Jg1>IRePt z&MpLUWuu^!S6+E#!~d*Eez61oPk}yGyfwC!u6)ZSN`k;jip~`FVAQJ0ZDGU{`CX~P zaVkIwR1>FOrq!3VJi|X=8xyA5GYjle>!_>j0C%^pZ|_c<)z1;&g4|{S%_^vgp(uh; z2Y2PHon_YA_kF`nA>zoi1VO|;AtUflWqlqZQ@Di-6$geew>**qbFS_{1kuQfcEyMm z5gyL~=_E9Z!6c7MRva_T64Ajb_*jK9wZYE5ePg%dMl@3OU+fi| zb9hkv=ObG7Z{=BSOe2Bg(JUAp&%MIw2pJX-wL{O2w36ik`;htNIY1zVt>1jpe{-XF z;f9FPslT7?bm~>zA;crkr5I0nxd$u8Ofx(`1DPVcT#1Ubc35m+g-P6lTi7U8%epxB z8>M^6Y#90PZRUVe509=~CJb|W1I!aB2EBA4%KZo8Psk#xFIISbCPD0#={a%gkmzg@{G4M`ZfyP z!gIML3f}+LBswKTDlt?Q%Hc4tI!XvECR(2cw1R}%0=ALLe@D{1e55q;4WBYJc@5@^+LbRQNfo&w|aWpxx zp)0Y?c+gIY6aV4Bk&{vsh~#)2>7e4V8&M3EdqZ{#6G+GiT|SB2!Q7*|Vqks0th(1WQn{_X6D zGUL8liOS9`SE&y+=`be|WQ;M@+)@O;LgT+Jh$9U@1A$G{hT-heB_uyTk>L;aVU78( zSoMq<3!U)hK9p2)&cg$Vb6^ElXUQ&PBea}q53(&&xp8Hgk2RO-1)^a`l4BMGS-|8( zXzl1l|Klveoh3`PQTRlt@977S*L6b$X%P1H^hU(m0=tnA4~MrUsPUfKcsY4-(r7h;fPEV(8A_QfJ-ZVd}P3s8AU(X7}v9>#)*$ zRe~oaj?|)&<>36+<787V+tnEpDLu8=hh=T#;1qDcggknAlVNBp(j5dQdQkEVrbUp; z`0HRQSqTolf?(18An>vGW+DLoA>ujv?S$LALOj>nqV<2LL@O@Y674H>;5e)WOFru4 z8%5)24;IY!FNMvhh($x`@?|)Qmc>pDxMzhbiPTz#qjJs6(8I%0qw)`r%KyEfu$!zT z!FPcx&?oLup;mn)^x;(kJoOC9y2mmXZ}Y{%y?~bBEFjJ%N8O?&Jq0$9ylo7j*Mi7Kf$O$Y&W zTQa|+Jc^uMU3G zin<02@=MhuDSd@zO$imxg$a#C`1FQk=c~KHyda&4kj@hf52b&7>G9v75?7eD>GIj< zJhxKun(bEOob^sP`Xy=kN78y!aT0CrWO`LIk5z3rioj5K$0UdQiDWp-(Ft-g?1BX6 z`db!-Zxb|U99Oo4n<%C#aL6k;)C?LsVAr{s+G#V2Ft($(ofCAH%9*4~&QzYd4`o;i`GY4#*ADNHK2DkDX%3#(=y2TW{q+1I=UjP7n4y!=qWB zw7CZj&$!ACkGJa+>N{c8d0s+Oj!yG%s?6thjHKh#(VEn`)+H%bS{;~RL;tNODmS`| zHLhjwcu+BX1svnwqlz2~hwmwOuDCnP^kVLDvD<5BslGs3>YNOWkSQQOBoidV(^%mJ z=|eybDN$ws)W3ktuA8?qjN!MEae(!~jIR+Bhg=j5>)Zd#j+;5uOHyjg3U7-%8h>h( z)%i7IhqE1!J;+G^F4)Wi!l&(MqjG5ojk+O$ra(nwbu1#7ZH5(45^eHRB*&y<5xW@o z;f4T0N%UsERTRejz13-6e7ueDZ_=ksLI(on9Eudx!22~k47Lb*s^LxcPQl6osL{r0rWI@rYOOPIs(SC^a$;m=0>gF|R^Cw@~$vwTZ6Bp&|CvhPpV= zBBU7j=YvpW;oDQ zu`$(39x7*igM;EYCwg(j+mAVVlz&*l;U6ZCG5K&%jg?lXpxri{BkjWNs6qT3^UV%k zG#ev|0!N5S6=D`|z${OWL%I+ZHb_nAX)Cg+U#e7^=}R$6oxT&k>mgDgmc&TA$nh_c zn0@t*Gdnbj%EV8R>1`WT%?*L%mBG?n4ERNL5~YTBGZd5C?(QEU+HrFoOFV&*Lb zvth6%RY+8cmw`KagDNtfbEK%?QDjs_f-3Kve6(nGHHR@^rZBWl+9z!K7wz6V6#xP zQU)qiu`!R@ld}Q<$+{pQY*?CYtZ=6F@ok@t>ehgfYx7W~E5yVxx;s-gRBsKOWDJ#R zbXRlTngR+~5>Jk#PwLJJoQf%DTUtH5jl$2~GREF2yQlo|z7dSen!aKjCGP*q>A#f}CTujY7Kg&SYA+C55VfjutK& zTD@5!hJO}uUib`?(QS?y=m!zQ2wAwEt+RY8W)u>_&Q5z_|QJlu>A=&Xl z2aORxLuZN1bn`?At_XjTKrShlGM1jFd*Sd3yAFszWp(NJjCvNL}k+*m9yN9&GS zj0A88rq?wyl{U9Y(Zvc%dX0=(<;JrXKV3WpwI%*>j`}vsdWP3|jxUsvEtIsR^m*gQ zN6%(=^br^YE8L_F*ov*#19W8jdZYx?@D)h}1MIK3SGp9RiC-UiXn@*yE)VclE6!*ubca0d?jYd9dM8E00v z2P%dla~D*%jvU-8`a(0;FuicrAhSAY)Tr*HoWE>5_0fF(5oRe*E1{rCd0U_g>LNXv zaQ%uA<7`d%RwPV7x{3QWw-xQUjm;v7jG>msc~421p)j+ZxJX8vfLYOg5=w9DnW`^} z@78$H4r96n1PNl`2ye?;9}x3D%X7KEv5f8(`(00nCe6>BfoX%Ead(ln($dsJit*7E zSz=}aA>H0)6?ix|dBZU@W-TetkM zvU2IXQ7Euh#>N(6QaW7^5-!}+lZL8A0-<$zJqGJd^-JH@z^)$kn3%MFRL1H4ogPyH zk{S-78+rh>@ly(xtE39;EZcEAACAfG+p;Ap71KSUKnZ_2$K5Px6xxybuM(LjE<0Lb z1S=v#4x1YE-imBVIkW!XRxG(D&f?L9J=R>bb!b|sL&^~?>T)bAz+}Da82aAsuT`!~ zI==^0@jZDA8x!-TSqRXcJws^2V)cgurt#I{gz$|i)xN%P6ZL@jKL-x_@5_34R{O&< z&<9fx%>vZcdNYqVO6>4@mqNqqLngrkb$%!+pu?4kCvML+@s=|mCD2+809vq+|7M_55ad8>p%>Hzv3_`1 z)#;Kp4tEh^MzsHwg`RBpYd^0I58~DRhuuH$`ztOfYQ0AlgmI!mpou)Nxvi?w3(|wz z?0R5PT^}OP;E&7%$~|2yTyIq*j6sgBi|8QAW!DLbVgf~ROC--!!5!rTl^jZ1A&non zQ-cx5fQUnNcfc3@Fg5o=9;Pgh=$|kK*w*>7;_YW^Uyp;Bd6Xf2AZML8@)-Ml@mD|L zI?ygCn5?ac(-qM19va2+$(PvK-s)~ebGk{h&+1qV#jC(3(~K^wl4~xDoZipj61V^v z)*uJlK~Ej{+{cj~bhC&FuFzN$-wbwnj2EN>{9 ze7RebIe+1OA7&N!nt1(%hv+3n&DpfPVYVH|k0>d4IKuUr%5mAa^QN<_kjHerE7KmV zB9o$JKaueeq?0zTxp*r07TH>qxS;D;MX3H>%OB5IJ~K*YxZ;i!UL5Plup$kI(M6t+ zRb$0Vi~&4C9UUoo2JWF?pBP8!&z8Vtp%q`Eg&Q)C>Gn(}Gy|&Gog3i@Nx5l1EFh_i zN9~T?^kKXQ-0^D?j(l_Mv@T+betTe^S*8dt!JW~dD7s;W>axuUH;q=bsM(Z* z+ASVc-k*=UoLUYf$TX_0=u7VD3+H;)!H#DIIkT}}6|2*^SJ|5>-k`px}y>e=(xiY$8koWYUBJ&hZsNPwQX!d_~=+L24 zn2b}@h#OyC$tuGY1skT-?Lis)MRZo^ROV7-H24bj&`~&Q%1!E`ZScieD@>drhg zMd65521|kJcq1%eEVu<2uFZ!@v+ z$2x1E##219@+k(@Em*SY=wAxsO7YKlj+vL9o3$78G_eGuTANsj`EMEjXIz9K-6+pI zt$K1dVgVl|EnNa=Gj0o8iNUbFwSaY#xV=hitX@DR*oJKcNd5;^!_v{6+YLiZJ>G z(ant(rs56Yp>xp9H_I~;Gf|qRvx;b63TK3zE%xEW^~`p!sQPWj+23Y#d^#ZOFlm@J zoF?^A*^n@IsdH@j!QTyq&O!W>a9QA63E{yxuT|#s{;;H^O&X%Y>=`dM+oHl*&ik0! z{hYp4!EZpB-+aods}Z_200o$QM6*l(+pWY=3DtI~8I!EoVp+%#)NJ z*(AW0A`CIbrs6jQmm7`+KwzTs>_4!h^vfMn#MpW7ZoOsffuA~RkIlZ%&uQ#DRr;XX zX~!pQxO?BxgdV^*_u&E%;BGbGZq6>s|LPgtMI2vLZRc)s~?NvzN@; zyJXf$9#NH3Jpo)N$9KkW2rvYuB9_Xs2Dr*~?_XnU18%-g+3j#@Q^Z^3JT~du!Pvn1M^G*_OiY zK^%a3H-}|+AdWjco7``{(PePOxWaiq1aM+VI$Kg#Wk2^E2X5!((QWEt6P$?FeTNCy zX)2Fxpb+K7y#XvramOCv8>`S(5&W`ZCTH6;7hAvFs8@~I5ieytPuRDmD4VNt7ryX{*mFm8%`pP2mv7~#nFklBqf z^P9|B-yFw`f>Rcc*kbdDm$U#ruVRtc80jg<{c`;J#!m+*Oa=5Xh~b6=KM)grj_VT& zpW&oMxB-M32n4`V<;@9G{p@&NlKa+@s48A!no&orX5JXvZanWid2Dca>%j{h0UrmK zUwizroXN`m&Ucximc0I^{xll0N2z8R)E_>X*y>@7*)82Ywyd9rm6!J0-M8cz@h&e= z#1mD{>CekUhV9OsY|zj;+!vk_xuNZx000`fq+M2hH!KWtBaiC$QU9EGLrR0%4e zT$1L;2unU-l1)k*wu#tx?{ol7bxG4qME_8Sl@ocLhn_;dXIYnyN=LjuyDkg&g7T>T zk1mR)Pjke3hY{c{@D*CoD2X!oe>i@eYa!-QfdZjt2@Z5NzM0kbriWc`pJf6>7-w6~ zwS?(dLMAVl$vuh#eMh&jZQ+8{oNgSaLeq1)Z2c=HSoIddR$)5kqj^9 zas(mXo^uHh5j{S{pwHtIjixwmWpup6t=%Fs_=R98dZ~XB<7?f@ z8}25DS}{&&iG_`PcA{R#lb&O}$JdTLKHb9*u~3u()iv!Cgx@l8i2|siC*W$3J^rGv zu~e@By-RYl3}lmryXCoL;L@HS5gsyDMZ~FC=UZBh@!?~Ol5jsO!*ney2;2X$BhV@$;};w>I2YJQ|R zx`9dJ{+ycE)YJk-3a5Y29Dq6?Bz>vu+xFu<-@;8pMmaS^M~?JM;q3fM<_zLV_sd>-UM3FOHcQiWnh3=}WR5+D7@!}%{>&4vkUe=2 ztHHl7y_J#Uld+}URjw3)AIC&=?oDeKRlr^MV1jTc_}F{#yt{{_gz2TU+a9F6w|hg> z+!laFv2n%@K~l@PCH)uW+(o8r?y6OCv-Y79miuiaxpLV6mugfTCx|Y@8TPc9$1#H| zF0gm+A>Py~je~Y1LkQER!L_I{jR1T^`*5C=%1;0#kj$tB3y?VWUivGzC+rY=tV;5O z2qQa8f0{PJYf=`|cQ;LUD#mxCyl*p^QPizlF~DO;a|t@R>eU%@pTKHdYmB>DAv;Lm zQCxK|{nWe}DZ0Ux#(*QTN)Tfb|0&T;>C9&4&TJW#gqi%uAh5Nd`0$Bl$PPdfBEvDk zyN=!((IoIhUS46+jO9t)s@OF?vO@x~eZaZ9Ggg0eth=Drc46wl&V08d|GT^6zhJku z4GI)Q&~4*{MQ}t~+6WJnc7I4);u(6VV;=y1Y@s>cCO7AtYtPd!bDxe`FL$*n_>Q6B zxij$`GR#74o~?U2R&fQ|3e*aqdwOgFz2pH$#IRoSFpYT1#8)_$_3%f>SJx2FHDi2K zO@_&5d~__sF-&m2Gjzl?{b73pl;H{wp+e+_$ad(1i&?)dG@x+s)QlM0eCkSdh7QP1 zF3c8}wU*jJm(@!~M*(a^MQX>p&J-VOdk%L6Bh%TNsa+?|-cn9DeipI<1SNpL0c6K* zvu#4lXq^Nt`WXC`w@_{$4`!QjEt8wK3aA?~KWBbPPOmO0kCm>oC@18u&kp&Rmzg=Y zB)72S(DN<_s`x%y35Ey6@E`>bI;4$I3&bE`I?=z$Ua@Y-C0>Agn0c5oO2ba^P$m(- zdd7opSNg8#a;?IgxEp%A$$m^jQlUJk{-m)+77jyDg`g7RrDWU}29{#qpj}|YD2OCP zdC2u%Xm!bgZ38wP8t@FcNAV7g7>1Q-(h=bA@3LG;Xr*#6lC5}>YkyR5i0eX=y8;<` zW<3N!g*~8XR5b>&;(QT2J#j@MuWMwy#}G31Kazj)w7PIN1}8hg7gVE;=t~ko4ge`( zSjWXO>|4dqD2CK?xo#LR9?p+vhiViC~h$QTl?Vt&j zUM2IDapqF7uqr(=3BfTJ%5EiC09m7=uhOOZJ2Amrx!U0`3>{3|E-%tx~=W z-UQ6Bz9=W3*EcdSb#s?l#x=*k=p;VgH!dTL6rBbne0aa*BfVWK`aa;FeGexHzj zWK8;t&}o@Tn3sPjHviB{lP<(Axx-cbaR+4=nAv2L^-u&jYExo(c3>_pQ5h5^=3JPm zL9#?BwaN{}&iI3l)q?JZSWEw!4t}hNq7?6a5~EiWa|f6mP4653ZKGfx+tMvMe>>tB zLB};_N0sV|K`f^-v41Eqm;2$Q6H&#neGy%9y_($DV#@~_g)LO_fl+Y_c^ju(|E%|uNSw(u!5Z= z=Dv}Z+plZ16CUDhlN{GvRHKRhJj6Cd^dq(%B9PFL1jqvDO2cCvU2lW;#)Z^xB%ll@ z%abo;L@O1ZzH(73O;0KO)fdcWCa|JDj|V^xujPD}4#%aSYa`ZFcP@=mf4kKd}OiUk*r1{kav=E7b(?Ibsk%Fe&JA^_q$dd z-4sXA!*mJClZHO2H+kd9VqSc%HV&V$Q+VGYLUFhaZY{->^I++=`%2I2GN$cyvM#IJ zE2g_4b5>T?G+b7R!vJk1uo_J~!MYeR5d7uM2?o{WXmV>3q=HM?A)@Pf4;4(EH;Usk zxJ)cYdjtu!))%Ar-Nqn$NYwcORWmL*IOBMR*?yK5%5?JhA06`sR+SuVuk<9gJd@b+ zZ3H7gWTN(v>$dl>w|PAl7p&K4E%>jDDKtv_QxWNKbH(t%boKe%#neDcR~#d_P%HXwhamPfURKnH_7 z;F5c#l!7G~$s4@I&3ACPUSO_uJDWek+24jSo!JJ%=yRI`Iw75`NGYpMP!!kMBVWgd&FWtv$5l&WBz|R zvw3zuOa?VfET@C852enOiA@Gvl)Sa5m~x+BweA&F(i%i%_b3gLB8QFO-f(MsF?nE6 z(Wj22pK?*rjCbPv9P!Q_e&LN|C9O+jk8oyOEsJiHdnC>cj>=S^bG#HJ_4|ZVEi;N+ zW}KN-A#R@3b(Sy2meXx+XMfyLz!ZGWGde6?5OC6wDz1x)uTnHPQmn}~PEhSMP9Dp> zr?39vT__YR6;BNq#no2(U$M~f!>X$Bz&pdXbWz@#zT4L`VucJxVd+#=sW<9d&D$qU zt3+Xg3Ac8wQa7@KwQ{Yvdz*d(>yV|f2SlyYv{rhOo7D<^*NA*`9xTHNwqdO3dRqTp z(f;>JOgP_vMQG41!X&uF0BqMdv3k8!66al zsNBB5)Oh*=&L+4P+ok_T&05mS~Ra3rDI<;hl zKVZ-O#m4{*U94Zke!`CaMmYH2cg39>7g)U>?;Cw0bB!tJa#AyqT2MaeiFRn5@6yV? z#FX2|rgL#_GAar`R(d}f{j|MFh+iB@og0_Bh-;`X!k{`dsf(AbB{N=3 z%jqtso(`4x1R&bU#$Y$k>r+^Nsm-bPT;H^FIL;B8?p2ehScH|ncM1lVahbGJbKTl> z1#3{w3a6ogLjyJx4>^wuU1)PT5&uAY1Y_pD%SVExgw%^SbAsDcXj3;Ow*V^l{m74v zkYErgG+DLyid1H=5TJ)+bU3S^M<*@1Cqj#G|Kj>y4V+j|JZsneNsTPpH_zxjTV-L8 zi>*Rrf^7V5>M{I*TP}b%Khsu+P>P_AEn&XXOkVfnpcaZI zI1elLA))06h(BAPw3gvrFnFv+gsCW4f_-`cRHNtdG&K+QMOQ^&QQsXl=<}7L3uI)C z{6xv5tK)7bCQ;0>yx-LAFxC8WTlEUA+fB)ewVZ1Nejk2<(chbS)n;I6a zk{_&|lG9Z6IgJsE#?y3%bA>Ugr*TOdPb|<(jH59_F@bR%d$ymh>goMMmyz#gN94P$ zQ&otaCOt7T5mOA(Sd`l!6qoC*yAY%bf!ysMVs!u3@$ntMj_e=^Cq=mOf~{M(9uOT< zr4HT{6*`)lS(eVj-h}wh%#dlkg<%NnwaLLjId>QZf(T(bcbd=Q@)~q!YI|d!96x>z zS&E*@DC%UA_S;sP=DWmU8cSu&@hSCj>C%DX)9P^XFlc~`CqZvFrRZgV8>COmhiK|X z!5(ElMN#c-BWjf-TRhznjCnB&Mbav36^hyeFS*&uS<3(vjAlhu;-c1q`V%J_==Ra5 z#v~Ech#ReH!bXILAWbg@Z9*)>vMkAAoe=4=cv-l^kzjQ||FTB^R<>|sXw2nGf5Ip{ z{?_JklkEPCaM~SgXB??oR)oiOXjlfK;>FS`2F?Ow_0j5Dh-?QoST&A~~9#y4OWANb@k( z^xei&Zje#`94@a5mv`1U3}%QZRKOuyERGFPyHqzNOubOIFd0b9jsIP7;}ykQ&*m3i)$JK*qe3|xZBlE~dsh9ZAon7+a(;%+e6+-1ibamJhQg_lAy>MOorS!h zeD=65OPK1zDAYv+Tr@ zTeb5U99k*mGy!nRa^jz+tHU%EvlmnXn%!GrbaRKYEPV{X&W$;RjNPD&V7joVSPTV_ zR3z3|QlTTr7UG6%p`OV}rc%P^bY8Q=fDoH+EX4@qzPLwL`mC}HNn31ylyR4%cOLpW zFtU(^yhW}Y!YLor9*T9fMx3unTEI>%;09kU+pgE8)(lIx*Sw#u9A>Pgccl+n}Cuk&WPTq3cFuP+?{@Mx2N?>lZPK`8Yrx#TC~N{#9Uuv)Cl-f=+q!w3xP|-0GpOY! z0d#EuqYvS0rw>0R7_JNWnDf?>+{=N1Te~>;vbx>>vOA_1?!>J}uQEAEoYhpGp@3DL&;1bj;9tpn^Y&yu?YSEbmwl;)>a5rTC3E~dkO>fvEPb2*6AnZD zdip@stj{Ye8g8>4S7n6Y0}&+^CmOY)L@Y2U*NzPk##Fci{Rl>8mTbJt+JnFD}nEpBchs$-kG7Vc{ zb&c=dfhDWP-^~2l%uPF`oM-QS)T3$g8{QYA|iWZZOU}Ga0<4=MUxwG z*g2Nm6D$VoKtxWZod$*u{!Y6utlyG6uV?&e<@By;PM`C{9Z*A}1ZH*WOo}!;cU~-a zbzv9?>&pt<{FPa*rWtIa%tB*YXBFHQ_n@3a|8`JQu!g?iSG;p( zSXcTRZ?~Tti8!x>KIB^H%5jBTa9Z@d&79xOnKur+haBFyx$s_)0k%|`Rt&nF5$P-$ z0W?VR3}-5Yjk0hIu}nU_u;9Edjxv38%oyZGuRbQAtIR?)?~Oof`mc*CQHnzzu5vzQ zP`d191ybh5P)*CAB2*JeHogNo2mVy;>YdO?^)t`^=|5Q|o5?Hbo9I$?2o8G{5Q&NFl(qP6AxAcvFSNl;LU^bAbQrhEEQY70Y z?m>e^wFg*|dJkoYo|7YTd*{Q%fJ`WrdEi}nQHZbSG9Dx+re6igdZUiWhCOVC8nU&iL5#=`oFPCwQS|A@mLG!Aw{8N@$i-6To}9YOe%`g zRPTxlH_V-ea+5j^hL*CNP$QB-(TO9 zgNwJubX}KUJ>vlvIX13&e?;!qid%l=F2Zv>0X(w{u$#%Hoo@W+<=a}}dx(PbUG@kWcL6dORlYvPd|eR`#0+IljQt-Q7w#zj;Ccr}2_cKAk%L zvL32sL0kfoBJ{A>wG656M7n1yTeD)cTa3WiKy>oGQ#fvpak-1uaIm)(C;%-UfAOs5za!6D#1VV-8bh<>s z4g@bkXc3Ule;KCXV|sean0ALpOGf0 zs2p^{+#47AZWPAD;KoQq3=mHDq8E6y6r9yc;2^|A%=I`IsN|Usj};Ph;RtIe1*yKi zh%=jG60dNfGaNl~=qPVL)F-eAfdR+R8w%yrdAh~iV^KC~>&}V0iVjMGqNVCF@IxH{ zLJo05dnJ%5&4{|Ss3KQxQq9Lj?PsQ`%7;}i##wqdn%F)6n5yP?ar&|X>!#MP! zGf@Ut1?f924JIWx7|Lv^K)z*W)CtEEDMhhu&oVkZ>XI3MTk^!3UpK_CizehgF*$wr z{0LMRzD=kf)h0DE_w?aLbWE_y*956|^&sLhw1Qh8*?JJXaJyJj=*mnxZH@WY%A=sA zmQD|rut@HVXEUCF2G`Jn>6mSIhYgp<7(W-hjFZSk&36c1&S59t7-O28(Ti}mqTyYw zM*Ffz(Q3B_T;X|V*wh!;8qgV76x&y6jBCzP9XooE0uPZ)MBnsoKkg}0om9wx)c>$K z9N5DG6%PzgDyj+*;$7WeiD+!Y`)_P8$77lE4cJ_X_pq7`i$}St6gbo+-P1ByH~(MD za;OaM-_>JjS5W|VoE0i(;%>QI(^qvZQKh!l-$huVI1-cx*`a$*6daxu z0onq`A`dy^v>p#RBgpCkOJwyAdTRR+H_?Ojn+d5}rnq6cy?jPB^_6LPP+$EB=6Bpa zv7_wE9ipbEQ{8P6JL74ka1AC$+w4rXOaf2K2@IJQo4I0O(X7 zVKc~95$nV~Kuz!sb3I)san2tv8T`PM>TMT9_D8I4Z*ezByzeVmzI^$8j?+k-f}%z( z;B9Q-YQn;q^Q+LM3m8#S2n^C{@c^dd}bH~gAoU3f_yuyetn)y(eL5wV zrz1e2O2;w%*b%56prD`)aru;>^*@tdi`N&7-_ z&QscDDL&_gApPIS@^SWXYJDMVN~x&>g=B^d+G+ROEIgc=5aR)fl-cNvb8_?*&bxLC zx~&NIRiT8JP)5CoOy{q56g9k%G`Bq7+>7%?R`+w`G~S)bRKI67Mgb|qdj!`RNs7nh z>R?^LW(iUk7ROLFh@k%RFGoH=<1mokf|p@0w{kuzrd`IG+F3ODN;u3ypv5?Nxi+~8 z1#ii9D};;51?2tFSsdGU)X7c|`>4L!6r_^Y06}>1sU+V~S%tR6mGF6)m`M*Vj7$k$_Zy2}Z zj|{BwErpTpXOAwuprf(dSGS@@@`R+!QMdMzJIsQd)%SxA=xv%(u8pe$+g9+p=}@8K z>7E)2Ee6o1j~ne=U#RCIjyXN1a-~rn(*J#lO2HN>mrdunUo&Tp%$6&S{3oJK*Qt+4 zN~|iy2R{#GCwGWxK?;lZ=r|iqyW7)~2TS5XA z?maVTTch`Fqr+5szG_|=RW7cF!g7MI^ z5zM_a8=$g;bxf;EFdlj~g6HAPMlc@1cq|9+-ieYz*dkkSW?n*|X@h4%Nr<4`BtbA9 k!FYsZj*3n_nC5rVFTPi^bj8-kAR`XbksUk@gYaYuXAYu{%8I&p_ zYB)SfH9}gcrIkXYJQ$FK0YOnQq)IU$hY1NJa!f)9InB)dUf0_DK21XVe&6r={?nf% zlMpidUV9y`b*;76p7&o5jOu;i6&HFup57^=@A;+2(;Y9n{qy`D_*60Lz{4I-cumSZ zcl|0S>SR^af`pVs2`PQ&zyH?8QTN>Q)KgDAJO=+|^S_oa|Nr>!S^ny@G``DIPyOs? z_xyfi;aeA0{Nky!?CIIrsTF+7Upz7EpL622jY%6feRt;%&Az^GtX%!kvG=?9VBBy+ z*Z*kxZez@^&hd}lH&*`jv98_;-!sx4+|cDay}vSC{O&VdERVk^lyUEou9k=IwfkTB zqVqG!yEps#eg9w%iGIIhE~Wfl6&LG&cp5azjq!k1^M22eD6HIM;(fSG51Vazp*5j>!)4$s0Gr z&7~Qc@{2M~e3E`*9e>>)?cb)pnLF~i?ChvL2M!#`UvkU1ZdH5Mj8AUe5>xxl*sLR$ zSLF0CV)xd*Z~Qtj^mSr+W6k!qFDrsyR<_OcwayLH+CjtHKk|)ZBi}gI*BI+N8Jo8z zHg8hgZ?dzOWG2^Ke&W=rXO4b-Etin<>5yex{D*H2oW42tZ%bzUF#l|(?@fPOq5sf^ zy|p)dP;PYpu&tosYb>O+d944omIK*=Rq>~CWS&Ew@!Z*7Td%2M&zGTed9?v<W&dC0QHi8z$2AuC8S@9M^n!YF&O(&s=Y5v3a?nW&Hv`4 z@Y>MM`ryT~_v*LhF0IV1545iev`=r41(8a*j6;U zuhIX*w)$)4(HQp6IKE2W6vp~VdWXFAEa>@-j`tkte9xX6MHQh8uNO6oLYA3tnFKk* zxURV%C3!)@vO?MBk~{K)!#pJio*S?zCx2p-Z(c&UY+{r5#f0#>iB0iGsSr60bK<|0 zt!p7~{aCivx}!6(XcXnNM&w`21hiZD)sg@12h+1^pJz8*a5 z>(ruQQ>)+e9bJ+?uc+; z6d$PwpQu=mFd~0(LCWZ#_EkS)y$mNU;=i%JHea?vc_V%_p|O6=qpLikM76cGjS_3b zO^tB4seMjwk#*Xky$4!Cb9~23^5;{9e%9Bs2>$8R;CVe4ZEG4dFBT@VFz(DBdqy=- zT*hc@=o5uT?}Wyz#|w>=gvNK_pgZ>YLu1pAuS@SW``;>2vr~y;l^JoH1I_RnPZWz^ z7suh@_JtCrdbJ%fE{sj7&i^oX^7hvUMpu3P-hIK;Yv&o~D`UoP7Twvm;v~Oa9tieM zZ2o0p_s!x3zsbz6%?uB9Y~hUa9ACV6un~5&cGJRpW2to zJQ~{cQ(Bd|7t}Cr+_pJ!XRh)fn7DQZ%J;^h#g`$zJMhcsbvu8b}i*wu`?CW=9CLF&I2mAHKzt zS+VTMiM?xM@)0JNwm4-s*D1S2Vi-`j`0zLtr-*YtXRL_6>H<%SXLPqaX}ZdJO@_CI zcEerne($~|tpPX*2ZJ*vYcw`^F2!{z$!{EyoQyr*o&M=zas;CQo znicGEy!nbQd;zj{I^d$cNsw4nLMK^CVUkz zhOhVRgal|Tx1c&-`9M)$W%_)8@ba>4_m!SkF!S)BwvGOd;md-D2RC^acCl8`^c9^w zcqbK}>TjOWd-L+R=Ie(hCEcE!oE)Ynj0x}ZhISPlvL7VlXU=>>1;4EJzhup5|73n_ z8=U#Ya6jkGhTnEQ0G&7@5knwi-@|;WW%2uhS%c^KZrm44y=I>8=6%6zxZ24ZPY(6O zb?n)G`n5poYo&$I;xY$!W7z& za^6u_&Fg^6=6ebnm<&hR3&;q&Xv8HNrxPpIRi^LA2!4_=B`~X{F&MGC&d9jU7}`m1 zq2j6Pw^@Hx?cFh>tVh*dF*ANpSXf9M-k#CeFQajb54fOxh;4#pQ=Bp2V&oLXO$XJ( z;OPTZc=~-&Sb!%1%fbVBuyivT@mUriGU7q1y6(T4=BYI)qS4q~hc5U=#W&^{XJ8@ad+H$rk?6;BSBy=3O+OU|pJi~8WTll9?tfYTO-W_EYNvaALb&Q+Q=DR6cYcY*fA1$oRB zDXmZ_wW1&){SV`}zdm)x%_UP#?D>&r@}{<@ULQF8Q!J*nM3w^>>i>Fte8;ahH4gAE zL{y5&JqJu(<^@t2@v=ivUqdL1F;**xnAP!NRt-|@?$FT)30Zz$99D7(SA^BbV&c6! zckaBnWdAe0j77VD*M0Nv);&>{9{s_it8)I>JfnP!F=Ap<(Uze`-+jTny|vxfl=~0A zG+=Vowt1HXnaA<{H%}=0W-;D8ZF-G{ zC7R>jOy^P;B%I2|GSgv2SI$oJHK)~ee`M9ee}DK!=JxBKJ=)hp=N^E`EreTLIEgt| z#*U$hH|z`EH?b*kO?j{Jg|VLwO?-9IsqDt;d(%U^GnQ^2d3H?@NI05k{9MSm)B)wn zULMJx--w03R2goJH>NHNpIIRmBId969Z3cT5{j1kB;Ah z74)Ftyc*xJCcb9r!J|t@9v|Xu8sc-r<2q;G(7i5&MS{I5Idn926b$78!~q4V#H}h{ z1{Aoz9$ZZ+<3M6#-^496nLqCS-6zPgSMGjAGaO4sSHHNXeA?n|SY+bM3E?5YT-S(p zWNfAEORt~U^zoir{EQNR>a1fAZ8-GOfSgs8Gj1k6iK<$scp`)L0t*fs7Wd8nX6(gWF0NSNj zeVBVy3e{u3pnFCRr0CwaCoy3ye!MUidvNWCxsRLA5PEaRwb=)z$0qHc5{*2(xLmSt z#(*DhKE%_ZKzQVPc6(d^}4M3Qf@6ejeyJNt$t+(n|GxF8q0e`(MC7ZZ- z%UqRH7dS~sF8af;Y{{Nr>vp=_v8x{5KfQ9C7Jm5JML7?qRA=1R*XWbbXgohDJZO)$ zR%(pXT0N6X8-GTte+O%pm_75exidfOM9nRG&4GZMrb^l_kp1{5-ez|44%UUOUeoQ_ow-$)ySXW5X`cnt}LmnXr8tBVmEIf%>hUCYk?1Tp>H1ph7A~K+i=k9Nh)anZz^kc;ll@g6C@}DJ-b4eeC zemyWtJI9z%`ssuQ@*>oUju9uHPOVwJ@zfLR9&Nny!wsMZf3OcmYfCmMrSz{bx6#!G z95*;hu!QNq`Y`uFrPboUq1c|s)=zo9SL`iQYQMZ3IQNR3%SLomDT*XM^i_Q5N}JD_ z{S2(h{_<3d;sbt(%(*^vTSn7uqp!KfL|_Wh5e?BI%W5ZVx-P4+f7X^k@ol4Pn{LNW zGPm@%&i7Zrk^ExU?@4=!BHvBS*;&v@X&wu+|;X^@sB+CJp@zdYSdSP@b{8Q~)t zJM;W4dEVC8{6n#uUb2Gu4S#fj&>J+@P|*5vLF=jdwq^B?XJ^Nz=!G-))L4|rBmMNX z8H=)W=4MwdESQ6wJ1_PP#79tS zDSeGJnA{?V$)#T^E?S(%fZ2H5`;!iAn-d@W4Y`ha6veP`rtfT~cUn%)`W@yfn~lre z*{UMBPS)3ZTS;nTxqbSWXqGY-FUMd$@Zh0=Ipviz?{}yKn7CS~1PHL42W7p!n}x^m zzmgDM3D)@v!_r zaQik5$cy+V+CXOpKT&Yiyp^kgIhKfkBNgIB?$5Kk?w>e$^646HxTchBQX^tna~4c6 z&nA+MwEqF@w%~lonC@`M8#f)_h=lgqYa&$Q>I-I*i=Vl<=S&Y{kSFJhhn9T|5+Gw5 z756f8ss5LgF5KVOh*i3vW&5H-w!OxSE_}Xt@a?nOf3m2RiF-|P!-d70i^4~X!WUWC zw9`_XQ;N={c&CFb9@*OBGg{`Ie@(X(%2uMC2gXLk%WPy&aogL)7i@lJ!(n1?7RwM> z#3fidkT<&8zbGNR7CHb}0sQRJfs7}JNh?l%{9>=V4h`5o;?z{hbnX+5w*H+F zd1oKC{FhO1Jyp<5m)nh_LW_5d$!H$qZO3L5zI^^QFT4!YFS5mAf$WN#A1(fbOa@&c zAdJ+aR{Gm-{L9lUR`~Dfn|K3CBBOOYp1QjqDq+7VWX2rn!(RBsu5C}OscJu7T=(5~ zFEgb*Tz>FGd0bEmRaa)du{f(+`#DxACWAR>OdpvxUvV+IV1Pyv_d2Ib=0aS+v zdoL&6UGv%`goiz!4yApMA3!jPkBkKPi0?{tkZgOcB@(ka@WIBOL~XEBO4eQgT6pz#iHrMMw<%KDA~ma zJN!n+J?Hhg>z=FTQTXEF8d++HY5!wPHCQpg_VDRMW6_G_BR_Ed1aM(QB%UfTu^!W^PB1_L^Mc}CXO$}`K#$*aE(X8yz44OAgS?(fP5Iev4eooH(M^WYH|%w}emyi2EKwj1%es}x3J zd}E*Z#&N#3CBC*QTF(Fc(&@M9{m6|sva0JzZu>m7mI{^&x)bF=znsBm%rgQ zi>5G&?BYW>gVma)Et`NP(^)v!_qTQAafmXYAaSP`%xy@oZ%+46H&`}>KW=zoPmenZ zhftP?MRZ8eAF&Jq&>IALQb7<7!EgHkJ?T?^Tj=X8xt2;7v43&j1>2gg95}FX&-Ri1 zQ|LstsN^Z4s%vEYmos)Z8{1Lp865f-9a_8}X|+?QQGrXJOMUB6h2I`LSRaZ5CPGGX z@743bxTpULuo&ewaPP}USHGXRw%m8=J{CQh3tlMeLy$($tEl+N{SYYff$b$2aYuDQ_`3ow>-V%~ z_#b6taR2mU+r)tZ8Hkw_TMo{5p7L~-CE_vSAzgl_zR7-{eW?^KQV zo*mD)Jo#*R?zJrA-5qWT86Y6SITi|`Dtpc9OB~QOi ziVkq7mJJNxyKnE3C~o(^?%04sLV$GG8{kP{JUzrkMGf#iKd*r!M)I*8IU*}j(ShkkxFuo6&6@|i!y9GMkpnI#X! z()DK`y)FG_DnbSwWbfL{0;vo) zf{yowF1G>4j`Qu;yHrhBC9#`hf?$h@iRR56dFD^0^&OiMJtcws;j*l0YoU|ZnyRf7 z+*0#jsSrYhVTyQ&7Q933UB3jBQoF*~r2f6$HS5=lc_E8o8m?CnTGqO4aJL3H!#AaU zKv5s;fVkDx_}@loeDlcqrjZ%V3+5&te$U^r(O;-FUUne_@8?+`I=iopNXksL5l~EB zkAbc)-e&YfJ_szud=P}B3E&e53e2x9O;v+|I z5NbO2!T8|%;@bZ?_cW@i`JUv)d8vns!^es}a02I)zv*U(i>S9P4*jO97>{~)g!F@> zG`3P|zlQ!2y9-6qdn_iPgf0?MH5Nv_Qb*r`9m+HAb$j;v0lJIMJS2S))Sy2+e%bZ! z{h*JMb-)NkwNlQ@&i?NTHh8Fv&rZ~T zFGt`hl4ke}=SLH;V%wEt&(ad7T6$q2rjXl8T+&qv?QklTtBM#+cc#v)z9cH=#Q6AL zQXJEbNHzkA?PI?ltg0nZ!DGeY8&J%-JHGwD4jQ3A(ItZ)n8K>Alwz&lO?aka5LJgU z!vt*FP?;B+YnH$F6C+xV68yEPQk9}oDhRoSLIDW^YzhLOOvNe?VJ}bbSeH>~bJ_E4 z410|O&%Wctv);DF-a}}FxaYjVsml&WkF1LJ49h-sB}xOnPnxhOl|iT{QBS2mcHw@O z|KGYF=hNy3Z!368OTbJy5YqVmlCJQhh(V$-f&8HhQax2`3TE9@ai-JeRsO)rGIk9MlBIPFUbacQqJD<+Vzu{Vy z3IRmxRU2@WkCA(C?qAWk7&%4>a?>b9)YxnO{={Kv7tBO`Uu=yJPdQlC`=Dobprb5MgU#oPtx$z*RykIA61@(MpCR2m zc|5&!K5gyXwkA^-+r-Tw)bN|?qcTQ*r040q8CfH{aHBv&=Jdq21_mhaX2^p;th zV?3yTSSzXPQRq>W`{c*+X~XgMbvu{VeEHT8HXch-sibZDfmnwF8>}2ybnuJf;Dv0Z z{+RIxMB$E0mNlG5|00`gesnyGMKd&65wqOaU#Xk5cGIdHyVUt5Dv}y5n!idB7^tn9 zDU!pJiW#lnKH}op!k`a8+MsfH2UvF(6pRCE$5^2Xu8TT_e);r6{nDdE;l^J%iba@3 zY|GRX8>xmkR0=?|E?cbVhOI-5hs~;tN|VLm9^JJ9jrtdj9XpnSZY7I5vK7!U)n^w9 zd_!zk5;vFK`wtbh|4Nq*0RVlyL0Tf=LG9(ighnsAjoI;s;swh_$m?|t zR|xmqyXZqe1Ose8K4HTAbMx$D9q?(wIO(q!7AODo;-ZT-Z;d;9D}8^H*O=C|IL&}+ znISe;%PyF03Q?=1!=(UnUu^!IP*7?oiWx$*eQWzb7*r7IbJDM#$ebpEwe;QSmTq?Y zpHtiaT-1&SXzIyE#viwIEcdn#!b7o(8F@Mz0YR|ehQ3$GI1$Gb7Hu;!k;Ob&=u1?o zq_kB?-u{pBVXvbY@XXfUNmtEdW;Ifp{eocFk5*6!21yi3Puz^m)ppxK;4DF(rXfzxGI0m;Sh3c}a3*Dzd#CXGOQoM|ZU8oD^K@oSSLsOfBtj>C z=@1pzgVM94@XGAIV`Hcz8sC9~3iW3mA=s;6b)wr8lvSQVNI#V+9*6s-gMh>RcM4_3 zLLH{Kvth5pjM@q`qEMk2nS~%<#&T6BkdM$Q6#qUe3hgb5QW#bkTxpQCW7j@M>{{u> zuuYzVy!|yf!W}{T6v#F|szgV`I#TkcZFu{PN|4B$TVP-T4t`B$$?| zJP#!Z9c63%iz1(*bBuND4Sok`C)3{vfHF76)pplg^sXLuA|S;`+gdc%p+yr^&*Cgpi+i&Ye&J@SU?cpTN(mh~n{rF0{@RM<${E^> z&n#1ghP@(t6LdN^QpZdb^zR>r#ZE2AyV8U5Z~bP8!P5WmiB+BRceg8b{*P5rS?c!> zt$1MS>BA`($HZ(viAxcu7Nx9yzqwkoypv^S{}r8p=Z_=6n{m0;8XX={7Zq1P3@c(+ z&IXA$K>b4xYw2H5+f{ZUzCi-38#31h zG*1I1zQI)D|Df(zv?G|a&~H9hocspVX57!Eu$Met%(L)GIEUl)U~?KSmd@fxJUzf& zp7HKe9qIE~06dG1=-khj%uG48v3{hKDp1X585f8MhkzJ)SSCXE?)(I(Iw}X(NoyVp zTsTNl391YyqZYs^NDmF#aRC$&fwqf%MfMUKiyg?QMz`VCX69d)b^5xjZkzcW4R`XH z#j;5cDjG|DMBJu_K?D+Q#gB!TyCi2gBw1*TR^rz*g)6N`axV(fwnSPtJyOR^g76x; zM3+ByCmD8PF66n8j z6X+FZ6oC$ScZyZHjJFFXe>9INEo<#)UXyB}#+qz=1!E3@UzhS&a~4eS7pe=vV$NBV z&Hr@jlW3B_d7wVC$r29}xY>y_AMD_9M`<2cP`vQC=||s4zd-(Z)${a}07#tzh6$8R zZi6jH+Ona4z{5JmK{ggRv0RyD^0vQukw=<)DghGeIxw(x`lQ+0$JLT#G|k*LYUZ|V zgpAPMIT0(^abTMVoq#?Oy)c8~8qfEH!}Z3p4#@03I0SYr zD51oE?tlpI*Et9`MRGGY7Dp_pfFmao@ik2YK<_55l>HY_kXHM6OepoIc9<kD>Hess5M75Pd47z=T;9JAB(#)eH4C3aucfbAGU72JB*sy zrL#$_9paeF{8P^+vc}ctMdfovoIPir-qg7SRkQ zvlEtgxPPY|GQC(YWG)V->}Xe}^N73Hf!vhx8UPrNs63Wt`}>I+wfo6OBn-({O9mxd%k zxFXZ&ZXY&GwXm_lgvJ(^P zwBLpggUrQ^FI<`atR-=_?>lgYDNb24c2yp|Wg}bl@aNyM%I7)4_4x{yLDe(LC68@j zrk|W-Z4wt_3Y1<60`1Q=SsG&jUL|J3VllAgCbek_7_}Ki#tCSW*tBflp+i@r8PONB znF#PSI~!DTG1WY`8cMy>RZ{g8tBz-9 zR;n%ZcBs=RiA;V_G&saRfaRcm;=I5j)r&&h$rdEWT!#DL4<0Qx5F9U3#EH1N%F$Hp@b9E3D0}lOmTHO7yeB zbalI;&Sw$bHCRDEjo9|NwQyf_Ch3xY!5x&ib*ns-pGbi{}nb;%s8dO zs`~hOYW|N4a<&cfw90u;ir0*4iWJhxF?LoIc7!RKuS|Kb!I{H+Q`qR&BP*rBq;{`b zL4y-y>V_EfYYAu?YwA>5-|Z%Fi<=eZ+u;xHh{-=tIcpp-A0;K~8MJc15_B|zz%7Gz zqTzfTjw>yl6y69!LO*wz4g2;!g<{X@va^qV_~D1GsMzNoB(UBRF`dGA!=abIRA=+m zN1k@*Q;>6>2d9;M+t`q-QZWfCPFvOuXoVOF!GJ9Zph?$hH`wG?e<7yNpss-iPFX2u zp!{wx9cJV$PPP2+iV?jvy$F7*I3y{G?GiKQxn1HNs`nP+^9QRcXC0_K@~^)=B)b4# z7H?)I=mlkF!U+~~K4xV&`vb;1heIs>ZDC#Jm7_>B9sNsT4(dK1vsdyolgHu^+G~4d z*zPdH;SLjdni)fqWT6B5C?LlMUqvHYbXc;AM}ZZ;fQ>E$ z^t4&H18K?rLtcH^62ML9*PH}kv5u<9D!~mlX7IfwAg#E#{r%$VH;{9BT5)vrZW7K7dVa-~UfjXEA#7-|M~- z=FpM~n!ufe9Ta~eC@Xq91 ziXy81Zm~^6%!iq_+qlTw8hN20vjBZMc|q_^`*~0wWqGD}$1(UQ*F6BEz2awX?7K{1 zUo{kNer)3Dmq#VPK=VZtmH!B+CjeeHs%zQ=&Zz($vp+{P^dcJGh-m0^@1?iSojdoj zyp!wL_b_qNq^SZtK1rMTNlzo9)R}p;ZD|i_mh{uinLQN!&r^70N$r4dA${T2(X30k zSkM<$SIY-XoVL4{4L`y+svndJ8b}@`Wdh81TUpT6IE}NiR5W%syZZXOaoV)5a9aDn zR-8st(es?-b+f7mqpKB1n!GsHh9-L76)8a+VKSapsRBrldVmKKcBp7_<^>7~a}vN# zZ>nLCdoCE!zTl0vp+ix%G$Sc%5erAx$QPdE=D(#LVQJ9q$oY-2%1k9G#g}HS7bmS; z{>Z`-fW&srXiJp=G+UQ})Gv{AoRi|j<$*aP_*a~WP|nVo#LVk{H{u>(rtC)7-#3;28;DQ`IPKm8V6RWWo!Yn7Lh-HW{$L{Izn=40QY6`>S zFci$VoY&c1cUj~i!LJ^6e(_Mwk+l5ZdRSQQVFrF>n8iZV2ny2~sEI8K zjhjqo`B-yCg}^j8LiJsRCZtxV+#_@B=ffC>*B{fJPFB`#>Nr)S@gVW5-C^gw!i`+l zpp-tcdLvn!Gd6PNp>=kAa5Mt6kE0Q6?Hht~b*yS2P4LBH^YQ6Cl_q?F<0Yyo0j?HO zYkM)H^~D;iI{Lz45rzsAZTUv5)P22(^gv)EVToS zt9I08II8PuD!{8BrJvIqjLhvzc26iJ^Ke4vIzl|-uwBB};j6q~J zqZliNRu@IN4;H0j^XhgOuh>je5t%gk8G)QaqkKXZ2Ll*-$;`G!o|;sS*Jg1>IRePt z&MpLUWuu^!S6+E#!~d*Eez61oPk}yGyfwC!u6)ZSN`k;jip~`FVAQJ0ZDGU{`CX~P zaVkIwR1>FOrq!3VJi|X=8xyA5GYjle>!_>j0C%^pZ|_c<)z1;&g4|{S%_^vgp(uh; z2Y2PHon_YA_kF`nA>zoi1VO|;AtUflWqlqZQ@Di-6$geew>**qbFS_{1kuQfcEyMm z5gyL~=_E9Z!6c7MRva_T64Ajb_*jK9wZYE5ePg%dMl@3OU+fi| zb9hkv=ObG7Z{=BSOe2Bg(JUAp&%MIw2pJX-wL{O2w36ik`;htNIY1zVt>1jpe{-XF z;f9FPslT7?bm~>zA;crkr5I0nxd$u8Ofx(`1DPVcT#1Ubc35m+g-P6lTi7U8%epxB z8>M^6Y#90PZRUVe509=~CJb|W1I!aB2EBA4%KZo8Psk#xFIISbCPD0#={a%gkmzg@{G4M`ZfyP z!gIML3f}+LBswKTDlt?Q%Hc4tI!XvECR(2cw1R}%0=ALLe@D{1e55q;4WBYJc@5@^+LbRQNfo&w|aWpxx zp)0Y?c+gIY6aV4Bk&{vsh~#)2>7e4V8&M3EdqZ{#6G+GiT|SB2!Q7*|Vqks0th(1WQn{_X6D zGUL8liOS9`SE&y+=`be|WQ;M@+)@O;LgT+Jh$9U@1A$G{hT-heB_uyTk>L;aVU78( zSoMq<3!U)hK9p2)&cg$Vb6^ElXUQ&PBea}q53(&&xp8Hgk2RO-1)^a`l4BMGS-|8( zXzl1l|Klveoh3`PQTRlt@977S*L6b$X%P1H^hU(m0=tnA4~MrUsPUfKcsY4-(r7h;fPEV(8A_QfJ-ZVd}P3s8AU(X7}v9>#)*$ zRe~oaj?|)&<>36+<787V+tnEpDLu8=hh=T#;1qDcggknAlVNBp(j5dQdQkEVrbUp; z`0HRQSqTolf?(18An>vGW+DLoA>ujv?S$LALOj>nqV<2LL@O@Y674H>;5e)WOFru4 z8%5)24;IY!FNMvhh($x`@?|)Qmc>pDxMzhbiPTz#qjJs6(8I%0qw)`r%KyEfu$!zT z!FPcx&?oLup;mn)^x;(kJoOC9y2mmXZ}Y{%y?~bBEFjJ%N8O?&Jq0$9ylo7j*Mi7Kf$O$Y&W zTQa|+Jc^uMU3G zin<02@=MhuDSd@zO$imxg$a#C`1FQk=c~KHyda&4kj@hf52b&7>G9v75?7eD>GIj< zJhxKun(bEOob^sP`Xy=kN78y!aT0CrWO`LIk5z3rioj5K$0UdQiDWp-(Ft-g?1BX6 z`db!-Zxb|U99Oo4n<%C#aL6k;)C?LsVAr{s+G#V2Ft($(ofCAH%9*4~&QzYd4`o;i`GY4#*ADNHK2DkDX%3#(=y2TW{q+1I=UjP7n4y!=qWB zw7CZj&$!ACkGJa+>N{c8d0s+Oj!yG%s?6thjHKh#(VEn`)+H%bS{;~RL;tNODmS`| zHLhjwcu+BX1svnwqlz2~hwmwOuDCnP^kVLDvD<5BslGs3>YNOWkSQQOBoidV(^%mJ z=|eybDN$ws)W3ktuA8?qjN!MEae(!~jIR+Bhg=j5>)Zd#j+;5uOHyjg3U7-%8h>h( z)%i7IhqE1!J;+G^F4)Wi!l&(MqjG5ojk+O$ra(nwbu1#7ZH5(45^eHRB*&y<5xW@o z;f4T0N%UsERTRejz13-6e7ueDZ_=ksLI(on9Eudx!22~k47Lb*s^LxcPQl6osL{r0rWI@rYOOPIs(SC^a$;m=0>gF|R^Cw@~$vwTZ6Bp&|CvhPpV= zBBU7j=YvpW;oDQ zu`$(39x7*igM;EYCwg(j+mAVVlz&*l;U6ZCG5K&%jg?lXpxri{BkjWNs6qT3^UV%k zG#ev|0!N5S6=D`|z${OWL%I+ZHb_nAX)Cg+U#e7^=}R$6oxT&k>mgDgmc&TA$nh_c zn0@t*Gdnbj%EV8R>1`WT%?*L%mBG?n4ERNL5~YTBGZd5C?(QEU+HrFoOFV&*Lb zvth6%RY+8cmw`KagDNtfbEK%?QDjs_f-3Kve6(nGHHR@^rZBWl+9z!K7wz6V6#xP zQU)qiu`!R@ld}Q<$+{pQY*?CYtZ=6F@ok@t>ehgfYx7W~E5yVxx;s-gRBsKOWDJ#R zbXRlTngR+~5>Jk#PwLJJoQf%DTUtH5jl$2~GREF2yQlo|z7dSen!aKjCGP*q>A#f}CTujY7Kg&SYA+C55VfjutK& zTD@5!hJO}uUib`?(QS?y=m!zQ2wAwEt+RY8W)u>_&Q5z_|QJlu>A=&Xl z2aORxLuZN1bn`?At_XjTKrShlGM1jFd*Sd3yAFszWp(NJjCvNL}k+*m9yN9&GS zj0A88rq?wyl{U9Y(Zvc%dX0=(<;JrXKV3WpwI%*>j`}vsdWP3|jxUsvEtIsR^m*gQ zN6%(=^br^YE8L_F*ov*#19W8jdZYx?@D)h}1MIK3SGp9RiC-UiXn@*yE)VclE6!*ubca0d?jYd9dM8E00v z2P%dla~D*%jvU-8`a(0;FuicrAhSAY)Tr*HoWE>5_0fF(5oRe*E1{rCd0U_g>LNXv zaQ%uA<7`d%RwPV7x{3QWw-xQUjm;v7jG>msc~421p)j+ZxJX8vfLYOg5=w9DnW`^} z@78$H4r96n1PNl`2ye?;9}x3D%X7KEv5f8(`(00nCe6>BfoX%Ead(ln($dsJit*7E zSz=}aA>H0)6?ix|dBZU@W-TetkM zvU2IXQ7Euh#>N(6QaW7^5-!}+lZL8A0-<$zJqGJd^-JH@z^)$kn3%MFRL1H4ogPyH zk{S-78+rh>@ly(xtE39;EZcEAACAfG+p;Ap71KSUKnZ_2$K5Px6xxybuM(LjE<0Lb z1S=v#4x1YE-imBVIkW!XRxG(D&f?L9J=R>bb!b|sL&^~?>T)bAz+}Da82aAsuT`!~ zI==^0@jZDA8x!-TSqRXcJws^2V)cgurt#I{gz$|i)xN%P6ZL@jKL-x_@5_34R{O&< z&<9fx%>vZcdNYqVO6>4@mqNqqLngrkb$%!+pu?4kCvML+@s=|mCD2+809vq+|7M_55ad8>p%>Hzv3_`1 z)#;Kp4tEh^MzsHwg`RBpYd^0I58~DRhuuH$`ztOfYQ0AlgmI!mpou)Nxvi?w3(|wz z?0R5PT^}OP;E&7%$~|2yTyIq*j6sgBi|8QAW!DLbVgf~ROC--!!5!rTl^jZ1A&non zQ-cx5fQUnNcfc3@Fg5o=9;Pgh=$|kK*w*>7;_YW^Uyp;Bd6Xf2AZML8@)-Ml@mD|L zI?ygCn5?ac(-qM19va2+$(PvK-s)~ebGk{h&+1qV#jC(3(~K^wl4~xDoZipj61V^v z)*uJlK~Ej{+{cj~bhC&FuFzN$-wbwnj2EN>{9 ze7RebIe+1OA7&N!nt1(%hv+3n&DpfPVYVH|k0>d4IKuUr%5mAa^QN<_kjHerE7KmV zB9o$JKaueeq?0zTxp*r07TH>qxS;D;MX3H>%OB5IJ~K*YxZ;i!UL5Plup$kI(M6t+ zRb$0Vi~&4C9UUoo2JWF?pBP8!&z8Vtp%q`Eg&Q)C>Gn(}Gy|&Gog3i@Nx5l1EFh_i zN9~T?^kKXQ-0^D?j(l_Mv@T+betTe^S*8dt!JW~dD7s;W>axuUH;q=bsM(Z* z+ASVc-k*=UoLUYf$TX_0=u7VD3+H;)!H#DIIkT}}6|2*^SJ|5>-k`px}y>e=(xiY$8koWYUBJ&hZsNPwQX!d_~=+L24 zn2b}@h#OyC$tuGY1skT-?Lis)MRZo^ROV7-H24bj&`~&Q%1!E`ZScieD@>drhg zMd65521|kJcq1%eEVu<2uFZ!@v+ z$2x1E##219@+k(@Em*SY=wAxsO7YKlj+vL9o3$78G_eGuTANsj`EMEjXIz9K-6+pI zt$K1dVgVl|EnNa=Gj0o8iNUbFwSaY#xV=hitX@DR*oJKcNd5;^!_v{6+YLiZJ>G z(ant(rs56Yp>xp9H_I~;Gf|qRvx;b63TK3zE%xEW^~`p!sQPWj+23Y#d^#ZOFlm@J zoF?^A*^n@IsdH@j!QTyq&O!W>a9QA63E{yxuT|#s{;;H^O&X%Y>=`dM+oHl*&ik0! z{hYp4!EZpB-+aods}Z_200o$QM6*l(+pWY=3DtI~8I!EoVp+%#)NJ z*(AW0A`CIbrs6jQmm7`+KwzTs>_4!h^vfMn#MpW7ZoOsffuA~RkIlZ%&uQ#DRr;XX zX~!pQxO?BxgdV^*_u&E%;BGbGZq6>s|LPgtMI2vLZRc)s~?NvzN@; zyJXf$9#NH3Jpo)N$9KkW2rvYuB9_Xs2Dr*~?_XnU18%-g+3j#@Q^Z^3JT~du!Pvn1M^G*_OiY zK^%a3H-}|+AdWjco7``{(PePOxWaiq1aM+VI$Kg#Wk2^E2X5!((QWEt6P$?FeTNCy zX)2Fxpb+K7y#XvramOCv8>`S(5&W`ZCTH6;7hAvFs8@~I5ieytPuRDmD4VNt7ryX{*mFm8%`pP2mv7~#nFklBqf z^P9|B-yFw`f>Rcc*kbdDm$U#ruVRtc80jg<{c`;J#!m+*Oa=5Xh~b6=KM)grj_VT& zpW&oMxB-M32n4`V<;@9G{p@&NlKa+@s48A!no&orX5JXvZanWid2Dca>%j{h0UrmK zUwizroXN`m&Ucximc0I^{xll0N2z8R)E_>X*y>@7*)82Ywyd9rm6!J0-M8cz@h&e= z#1mD{>CekUhV9OsY|zj;+!vk_xuNZx000`fq+M2hH!KWtBaiC$QU9EGLrR0%4e zT$1L;2unU-l1)k*wu#tx?{ol7bxG4qME_8Sl@ocLhn_;dXIYnyN=LjuyDkg&g7T>T zk1mR)Pjke3hY{c{@D*CoD2X!oe>i@eYa!-QfdZjt2@Z5NzM0kbriWc`pJf6>7-w6~ zwS?(dLMAVl$vuh#eMh&jZQ+8{oNgSaLeq1)Z2c=HSoIddR$)5kqj^9 zas(mXo^uHh5j{S{pwHtIjixwmWpup6t=%Fs_=R98dZ~XB<7?f@ z8}25DS}{&&iG_`PcA{R#lb&O}$JdTLKHb9*u~3u()iv!Cgx@l8i2|siC*W$3J^rGv zu~e@By-RYl3}lmryXCoL;L@HS5gsyDMZ~FC=UZBh@!?~Ol5jsO!*ney2;2X$BhV@$;};w>I2YJQ|R zx`9dJ{+ycE)YJk-3a5Y29Dq6?Bz>vu+xFu<-@;8pMmaS^M~?JM;q3fM<_zLV_sd>-UM3FOHcQiWnh3=}WR5+D7@!}%{>&4vkUe=2 ztHHl7y_J#Uld+}URjw3)AIC&=?oDeKRlr^MV1jTc_}F{#yt{{_gz2TU+a9F6w|hg> z+!laFv2n%@K~l@PCH)uW+(o8r?y6OCv-Y79miuiaxpLV6mugfTCx|Y@8TPc9$1#H| zF0gm+A>Py~je~Y1LkQER!L_I{jR1T^`*5C=%1;0#kj$tB3y?VWUivGzC+rY=tV;5O z2qQa8f0{PJYf=`|cQ;LUD#mxCyl*p^QPizlF~DO;a|t@R>eU%@pTKHdYmB>DAv;Lm zQCxK|{nWe}DZ0Ux#(*QTN)Tfb|0&T;>C9&4&TJW#gqi%uAh5Nd`0$Bl$PPdfBEvDk zyN=!((IoIhUS46+jO9t)s@OF?vO@x~eZaZ9Ggg0eth=Drc46wl&V08d|GT^6zhJku z4GI)Q&~4*{MQ}t~+6WJnc7I4);u(6VV;=y1Y@s>cCO7AtYtPd!bDxe`FL$*n_>Q6B zxij$`GR#74o~?U2R&fQ|3e*aqdwOgFz2pH$#IRoSFpYT1#8)_$_3%f>SJx2FHDi2K zO@_&5d~__sF-&m2Gjzl?{b73pl;H{wp+e+_$ad(1i&?)dG@x+s)QlM0eCkSdh7QP1 zF3c8}wU*jJm(@!~M*(a^MQX>p&J-VOdk%L6Bh%TNsa+?|-cn9DeipI<1SNpL0c6K* zvu#4lXq^Nt`WXC`w@_{$4`!QjEt8wK3aA?~KWBbPPOmO0kCm>oC@18u&kp&Rmzg=Y zB)72S(DN<_s`x%y35Ey6@E`>bI;4$I3&bE`I?=z$Ua@Y-C0>Agn0c5oO2ba^P$m(- zdd7opSNg8#a;?IgxEp%A$$m^jQlUJk{-m)+77jyDg`g7RrDWU}29{#qpj}|YD2OCP zdC2u%Xm!bgZ38wP8t@FcNAV7g7>1Q-(h=bA@3LG;Xr*#6lC5}>YkyR5i0eX=y8;<` zW<3N!g*~8XR5b>&;(QT2J#j@MuWMwy#}G31Kazj)w7PIN1}8hg7gVE;=t~ko4ge`( zSjWXO>|4dqD2CK?xo#LR9?p+vhiViC~h$QTl?Vt&j zUM2IDapqF7uqr(=3BfTJ%5EiC09m7=uhOOZJ2Amrx!U0`3>{3|E-%tx~=W z-UQ6Bz9=W3*EcdSb#s?l#x=*k=p;VgH!dTL6rBbne0aa*BfVWK`aa;FeGexHzj zWK8;t&}o@Tn3sPjHviB{lP<(Axx-cbaR+4=nAv2L^-u&jYExo(c3>_pQ5h5^=3JPm zL9#?BwaN{}&iI3l)q?JZSWEw!4t}hNq7?6a5~EiWa|f6mP4653ZKGfx+tMvMe>>tB zLB};_N0sV|K`f^-v41Eqm;2$Q6H&#neGy%9y_($DV#@~_g)LO_fl+Y_c^ju(|E%|uNSw(u!5Z= z=Dv}Z+plZ16CUDhlN{GvRHKRhJj6Cd^dq(%B9PFL1jqvDO2cCvU2lW;#)Z^xB%ll@ z%abo;L@O1ZzH(73O;0KO)fdcWCa|JDj|V^xujPD}4#%aSYa`ZFcP@=mf4kKd}OiUk*r1{kav=E7b(?Ibsk%Fe&JA^_q$dd z-4sXA!*mJClZHO2H+kd9VqSc%HV&V$Q+VGYLUFhaZY{->^I++=`%2I2GN$cyvM#IJ zE2g_4b5>T?G+b7R!vJk1uo_J~!MYeR5d7uM2?o{WXmV>3q=HM?A)@Pf4;4(EH;Usk zxJ)cYdjtu!))%Ar-Nqn$NYwcORWmL*IOBMR*?yK5%5?JhA06`sR+SuVuk<9gJd@b+ zZ3H7gWTN(v>$dl>w|PAl7p&K4E%>jDDKtv_QxWNKbH(t%boKe%#neDcR~#d_P%HXwhamPfURKnH_7 z;F5c#l!7G~$s4@I&3ACPUSO_uJDWek+24jSo!JJ%=yRI`Iw75`NGYpMP!!kMBVWgd&FWtv$5l&WBz|R zvw3zuOa?VfET@C852enOiA@Gvl)Sa5m~x+BweA&F(i%i%_b3gLB8QFO-f(MsF?nE6 z(Wj22pK?*rjCbPv9P!Q_e&LN|C9O+jk8oyOEsJiHdnC>cj>=S^bG#HJ_4|ZVEi;N+ zW}KN-A#R@3b(Sy2meXx+XMfyLz!ZGWGde6?5OC6wDz1x)uTnHPQmn}~PEhSMP9Dp> zr?39vT__YR6;BNq#no2(U$M~f!>X$Bz&pdXbWz@#zT4L`VucJxVd+#=sW<9d&D$qU zt3+Xg3Ac8wQa7@KwQ{Yvdz*d(>yV|f2SlyYv{rhOo7D<^*NA*`9xTHNwqdO3dRqTp z(f;>JOgP_vMQG41!X&uF0BqMdv3k8!66al zsNBB5)Oh*=&L+4P+ok_T&05mS~Ra3rDI<;hl zKVZ-O#m4{*U94Zke!`CaMmYH2cg39>7g)U>?;Cw0bB!tJa#AyqT2MaeiFRn5@6yV? z#FX2|rgL#_GAar`R(d}f{j|MFh+iB@og0_Bh-;`X!k{`dsf(AbB{N=3 z%jqtso(`4x1R&bU#$Y$k>r+^Nsm-bPT;H^FIL;B8?p2ehScH|ncM1lVahbGJbKTl> z1#3{w3a6ogLjyJx4>^wuU1)PT5&uAY1Y_pD%SVExgw%^SbAsDcXj3;Ow*V^l{m74v zkYErgG+DLyid1H=5TJ)+bU3S^M<*@1Cqj#G|Kj>y4V+j|JZsneNsTPpH_zxjTV-L8 zi>*Rrf^7V5>M{I*TP}b%Khsu+P>P_AEn&XXOkVfnpcaZI zI1elLA))06h(BAPw3gvrFnFv+gsCW4f_-`cRHNtdG&K+QMOQ^&QQsXl=<}7L3uI)C z{6xv5tK)7bCQ;0>yx-LAFxC8WTlEUA+fB)ewVZ1Nejk2<(chbS)n;I6a zk{_&|lG9Z6IgJsE#?y3%bA>Ugr*TOdPb|<(jH59_F@bR%d$ymh>goMMmyz#gN94P$ zQ&otaCOt7T5mOA(Sd`l!6qoC*yAY%bf!ysMVs!u3@$ntMj_e=^Cq=mOf~{M(9uOT< zr4HT{6*`)lS(eVj-h}wh%#dlkg<%NnwaLLjId>QZf(T(bcbd=Q@)~q!YI|d!96x>z zS&E*@DC%UA_S;sP=DWmU8cSu&@hSCj>C%DX)9P^XFlc~`CqZvFrRZgV8>COmhiK|X z!5(ElMN#c-BWjf-TRhznjCnB&Mbav36^hyeFS*&uS<3(vjAlhu;-c1q`V%J_==Ra5 z#v~Ech#ReH!bXILAWbg@Z9*)>vMkAAoe=4=cv-l^kzjQ||FTB^R<>|sXw2nGf5Ip{ z{?_JklkEPCaM~SgXB??oR)oiOXjlfK;>FS`2F?Ow_0j5Dh-?QoST&A~~9#y4OWANb@k( z^xei&Zje#`94@a5mv`1U3}%QZRKOuyERGFPyHqzNOubOIFd0b9jsIP7;}ykQ&*m3i)$JK*qe3|xZBlE~dsh9ZAon7+a(;%+e6+-1ibamJhQg_lAy>MOorS!h zeD=65OPK1zDAYv+Tr@ zTeb5U99k*mGy!nRa^jz+tHU%EvlmnXn%!GrbaRKYEPV{X&W$;RjNPD&V7joVSPTV_ zR3z3|QlTTr7UG6%p`OV}rc%P^bY8Q=fDoH+EX4@qzPLwL`mC}HNn31ylyR4%cOLpW zFtU(^yhW}Y!YLor9*T9fMx3unTEI>%;09kU+pgE8)(lIx*Sw#u9A>Pgccl+n}Cuk&WPTq3cFuP+?{@Mx2N?>lZPK`8Yrx#TC~N{#9Uuv)Cl-f=+q!w3xP|-0GpOY! z0d#EuqYvS0rw>0R7_JNWnDf?>+{=N1Te~>;vbx>>vOA_1?!>J}uQEAEoYhpGp@3DL&;1bj;9tpn^Y&yu?YSEbmwl;)>a5rTC3E~dkO>fvEPb2*6AnZD zdip@stj{Ye8g8>4S7n6Y0}&+^CmOY)L@Y2U*NzPk##Fci{Rl>8mTbJt+JnFD}nEpBchs$-kG7Vc{ zb&c=dfhDWP-^~2l%uPF`oM-QS)T3$g8{QYA|iWZZOU}Ga0<4=MUxwG z*g2Nm6D$VoKtxWZod$*u{!Y6utlyG6uV?&e<@By;PM`C{9Z*A}1ZH*WOo}!;cU~-a zbzv9?>&pt<{FPa*rWtIa%tB*YXBFHQ_n@3a|8`JQu!g?iSG;p( zSXcTRZ?~Tti8!x>KIB^H%5jBTa9Z@d&79xOnKur+haBFyx$s_)0k%|`Rt&nF5$P-$ z0W?VR3}-5Yjk0hIu}nU_u;9Edjxv38%oyZGuRbQAtIR?)?~Oof`mc*CQHnzzu5vzQ zP`d191ybh5P)*CAB2*JeHogNo2mVy;>YdO?^)t`^=|5Q|o5?Hbo9I$?2o8G{5Q&NFl(qP6AxAcvFSNl;LU^bAbQrhEEQY70Y z?m>e^wFg*|dJkoYo|7YTd*{Q%fJ`WrdEi}nQHZbSG9Dx+re6igdZUiWhCOVC8nU&iL5#=`oFPCwQS|A@mLG!Aw{8N@$i-6To}9YOe%`g zRPTxlH_V-ea+5j^hL*CNP$QB-(TO9 zgNwJubX}KUJ>vlvIX13&e?;!qid%l=F2Zv>0X(w{u$#%Hoo@W+<=a}}dx(PbUG@kWcL6dORlYvPd|eR`#0+IljQt-Q7w#zj;Ccr}2_cKAk%L zvL32sL0kfoBJ{A>wG656M7n1yTeD)cTa3WiKy>oGQ#fvpak-1uaIm)(C;%-UfAOs5za!6D#1VV-8bh<>s z4g@bkXc3Ule;KCXV|sean0ALpOGf0 zs2p^{+#47AZWPAD;KoQq3=mHDq8E6y6r9yc;2^|A%=I`IsN|Usj};Ph;RtIe1*yKi zh%=jG60dNfGaNl~=qPVL)F-eAfdR+R8w%yrdAh~iV^KC~>&}V0iVjMGqNVCF@IxH{ zLJo05dnJ%5&4{|Ss3KQxQq9Lj?PsQ`%7;}i##wqdn%F)6n5yP?ar&|X>!#MP! zGf@Ut1?f924JIWx7|Lv^K)z*W)CtEEDMhhu&oVkZ>XI3MTk^!3UpK_CizehgF*$wr z{0LMRzD=kf)h0DE_w?aLbWE_y*956|^&sLhw1Qh8*?JJXaJyJj=*mnxZH@WY%A=sA zmQD|rut@HVXEUCF2G`Jn>6mSIhYgp<7(W-hjFZSk&36c1&S59t7-O28(Ti}mqTyYw zM*Ffz(Q3B_T;X|V*wh!;8qgV76x&y6jBCzP9XooE0uPZ)MBnsoKkg}0om9wx)c>$K z9N5DG6%PzgDyj+*;$7WeiD+!Y`)_P8$77lE4cJ_X_pq7`i$}St6gbo+-P1ByH~(MD za;OaM-_>JjS5W|VoE0i(;%>QI(^qvZQKh!l-$huVI1-cx*`a$*6daxu z0onq`A`dy^v>p#RBgpCkOJwyAdTRR+H_?Ojn+d5}rnq6cy?jPB^_6LPP+$EB=6Bpa zv7_wE9ipbEQ{8P6JL74ka1AC$+w4rXOaf2K2@IJQo4I0O(X7 zVKc~95$nV~Kuz!sb3I)san2tv8T`PM>TMT9_D8I4Z*ezByzeVmzI^$8j?+k-f}%z( z;B9Q-YQn;q^Q+LM3m8#S2n^C{@c^dd}bH~gAoU3f_yuyetn)y(eL5wV zrz1e2O2;w%*b%56prD`)aru;>^*@tdi`N&7-_ z&QscDDL&_gApPIS@^SWXYJDMVN~x&>g=B^d+G+ROEIgc=5aR)fl-cNvb8_?*&bxLC zx~&NIRiT8JP)5CoOy{q56g9k%G`Bq7+>7%?R`+w`G~S)bRKI67Mgb|qdj!`RNs7nh z>R?^LW(iUk7ROLFh@k%RFGoH=<1mokf|p@0w{kuzrd`IG+F3ODN;u3ypv5?Nxi+~8 z1#ii9D};;51?2tFSsdGU)X7c|`>4L!6r_^Y06}>1sU+V~S%tR6mGF6)m`M*Vj7$k$_Zy2}Z zj|{BwErpTpXOAwuprf(dSGS@@@`R+!QMdMzJIsQd)%SxA=xv%(u8pe$+g9+p=}@8K z>7E)2Ee6o1j~ne=U#RCIjyXN1a-~rn(*J#lO2HN>mrdunUo&Tp%$6&S{3oJK*Qt+4 zN~|iy2R{#GCwGWxK?;lZ=r|iqyW7)~2TS5XA z?maVTTch`Fqr+5szG_|=RW7cF!g7MI^ z5zM_a8=$g;bxf;EFdlj~g6HAPMlc@1cq|9+-ieYz*dkkSW?n*|X@h4%Nr<4`BtbA9 k!FYsZj*3nUny?4RCJv6rW<^3=Bcs#wwkGt>xcs$+lvdgF6>Vf}N=6&^; z$J73s@%P>HXm-q*>X=3G#4_k`fxV-X#r&6+JXJsW<(jy=EW!|Ur2JM)TGI{pC&R?2+eO_C=cJs+q zQNCEW-WvUvX795mJbIB|T4k;N>!fH;!h2TA!<(W$Y1Qg*>Dy0786JI5C~eH~Xv4#M z?f#cO?mQ=cdb6+ZrHL65y|iL3rM#qyhHhN4F_&(nOuhsjZs5NJ9xesB1Rj^b;}Y|5 z5x`4a(Z!)Jkzg0kzr^lc0nDYa#T{*40*_1JaVa%mCNeJNJWOf)KZ(b}OUw2zEZ;xX z*Sf&hnl%dyw*22pnjbG|9?|kuR$z^jvkL9HH+Iga^ETQZtiABUh|;DJ^`Rqi;Uj_W z4I}+cBPTWc=1pJQUfBG3Lg@2>-u$;JgP&AByPQ9Ky2FgZQ~smpsITuaK74G)UozVN zvNA2R;*Qwx@<7}5`7K$#WvjOhPkC;7IN|RVzCSF>Ke4QKO`v_vruwZNGeV1E+ZHXD zX%p_|xP{e@Gx6>}Rkr6;S<4+&lbW8G>kl3DA73>z{=1irANP|!OBVehKHAG2*3HrX z$$F&oZxxqJ%qZ_Wck0?rweLX&rA>#8x$L^p5K`=gSL<6}E$QBn=xa)xwE3BD9D^EJ zjWz7zBenL9k1~k@Yle)z;QnQ|H&*#VRi3h=Bexz+rxleHXM~78HANKYC=VRm)2dmu00 z_OoYix6(Sx-?e}R)*9FMwur+kJFj(c>Y1}=pFXu_Lzf0#n4K3J#tm4u`qsyj_69;< z1w?gHLI=NbDB;A;&l^|c>k_$`o>tOu50vN4o{rt=Rhnli@`oeK_m0dPzHL}>;}yk? zFQ+#giaynEez(=?yd*Lu>yaqh)EF4wr!xD4VH=yjm>84&PQToDsCJ@vsWJynx3E9M0nuMae? zGi#RTpzK{NV|1v```7EAV2YUhw zjP(ngz)-LEE=+GP4|I(B=0(y&fpxc9Kf8!}$Q`E7jWL?2(r24Amj z1c235Im@eZ5)Aas`u^a*ENqdI(I`Qa1mvj4)!WDSV=6x-0_8~ux;j(CT4UngM|_zJ)J zYBJC$zo!6r{rQLL&p&Pe_`$1;Zw8dJfAsU$rU!iROA9(Zt7cVQ^8a?lk_S8EC(W;E zYdh!W6AYU%tWRn1 z^v)W*q_w1z?+uUoeO6Y^2g8bEwt+`2Y-BcMcoqL3bKO1{uS-8!e&M5~Lt=FGlmNH3Y{nke{V=~akuJvD_pLT682t1J$G1-cKR)wn%XLSHVbY!d?CZSc5YSvnX1~;2iC=gK_D9HYaBm1=3c{q zZ=AC-YtG8b@bR?puLp%27IbYO+zV#hY=35iSnM$uSiPeC(+=y$Q3iTDy*Fdanc<#6 z9s75kqliYLBLi68Hf&?gRnKzR6!TV6P9Fi5RlYZ(=#+j|Fe3u=$#QmB@%rD@u*2&J z(#iF8t$sJH68M)4JUoyf@n1JLw^w zY6UK;iBSWN#=wJa?cCx2A`m_k=rXhRlkW^_*|@8b{a0Sa9aTwxbX-KgSB=I5e>Fz9 z-QTPFl|bt&F+^HQFZ{0b1FPqHK#?_L{ieVh!_tX<{*XR!R4Y@dQ>XmlQ-KA>N4{)) zWUpEDz*bP1*@rt;c-vRBoh%Li&hzT^BR_a&$}R%6aBk9tcM6XW@HY-NCFV2TQUa z9#=EwCL#jCu}gMXKV9Ee_*9Yawu5DVoz|3q0qhu_@bdK3>kgLv0FuSPbDC3pvsuqc zu|TZvP^2S7-}?>f(eyTj@{Y*s>IRRl8NBrr8SkLr%^BxzhW;8aPqh_?Pge{IPV_fV3~YBIjwU0-o-o@dym+Ug*OUPV_t(yU=auPr%U6J}^!=D8@%4k@>+yDY-2r*Q|DsPXU*UgU z%_5Bk`vHlcM*1?z!zC*c+JL#opZ;ia)>qv@u`Wz)B19C(Gx7?qIqWtG4ry8I_P8sz z1*}jY(`$={W&+XZHPJKC@_bk?KvTW5EG&7w8uns+!@ISCk9j>=UbpZM)~YG z#o{=gKZe6gwmxS@a^A^DVzN(9iDQZs7mTFhnWpyLE_ni=blPr0m97o{Tsiu+x?OIi zW4&OdYkHs7jO~#Xs;ShOW7zMkoYi0SP3tJ>mlhO_-%$rzwXtKz215aBl&8Mo%KB}E zR%>BXBoTFudaig2PGGG}E~z~d>$}sFob&M`7=Q?bRL-^;XZUza79sT-8Q*||!Kr$^ z$dvG}Z6zPtlIQO?sA-j%%OEzWrlIYV4YgzP#+lDS?*RXvJEIZQIm3-eqi448)6JzF zBT^gtrd>7HZ*}0FLmm-d3<89!TXi+B;3mHLb~C>yf!ZD`34OWL4?cgAE{U< zDi^*A%}D7Q4FKK%GCuh9reohr&bizZ*Zv=IAM=7mQrvY0r5v@_V7QO0e5kQu zf%h$lXnEG*kD3U51Q6)s4K0(2UIZE1{GZsXWG|s>K_QS3ae|`F z&pUaY%SGk7+zB24et+!!`MnP1)Ga?$Nrp{6z*!EBtn6uu`pMXXQ?;Ho#qG2gLBFT0 zC}K|ZZmoBTo~Kq?a-aq<*eLFdqoOIZZh6+zN8azg=dHW8bBqc$b@n}34`;PvoPert z12Q5Hm4xmpX}YWI)RwJ7J<0j!mYPnK0f7{Gn#MD|V3*s*bcbt6w5wG{qyI5Me!guP z&AyW-r6;h>e3YF3BTt5PA)}H_B^tT3Mt{ScV-ib|z!uMn6SKj`ISc#ypz@PFe zjEkmMp^w&iAfg=VBEwXu_HOwt=tNlNd+yAqqy~%4e_4NPq{_<-Q4`&a~ zIy9>%C6h_gX&`_F@!^|53dh$FDST{6YvIiZ(J-HnhFg!V2X!Hfg;HR6&@6yi?`@Fx zv5%HQ-u=fDdEV2Ql084AI@vm(?0dl(%uvTASZjeju`x9kcyH~$cnD>2; zl#-DCOStf*ZJ1Wt!>`79_h|ClZ0#NU?2fU|R+WZNmX2@z zNW$VBD9=z-@Yndp((e}dZbL|bpdU9(U1DxSM(B%-yjVLrvBo+fr**#!RKE60NIk|v&i;I9@eam7Cd3R| z3>1nvXhgmkf2g&(*eZ)(4o+0C2s#$YA;UizMIBTfrAQO zcPwL-6t=!t*t#|?v@b2Mfqd-DbH~P?-FWm${yRkJE5pQ18Yb$$TVUM|&HYu8b>;fD z76dV`YnT}GB=I$jFX_`CKR9jLw7;)gx4pLZW=^scl^-Y?)w&`7)P}82{Aw9VQww=| z{E?jLnT7ol&cVwMoa=8Z@*iP3qaac!m>|E6|B8-j&0}aT`6iGE53^|-g^7&653{h1 zPgaQRu#2`FtZ%n%Q{p;)UVNgHbp1|pBpjf*EN^N5vNZII!^a*x+->fVQ76W`JfnmF z?C;r)*}B1N4cG4L<Sf?jr~hP zTEyim7RHM&VKbow^WyQzbNVP=9NJ$Pk@{`6F0+YBv-Lp~S_}ju<8|UXjXVA z;?7ENf_^LUFjq6TUYP2CJuzo#VoWv7UQ+@y=ChxMG1!6BcEtjUtZ&(_iA2ikXie+% z9so60>zrSEx*2@Z#v$Vj@7!Fz*)5^33zg+5&wo~96ObYnr1mmQOq zmm(DrcH=@fr=Nm?ikVq!G6P)&w@)17B9^8 zamU$}u5cypto58iUb`Ua*mo1kC*7X&(Xwa9?^!-_*VQGh_r*89y=ce6pHK#qXv5n} zcKGMVhs)y|(;n<&?VHw=Rzd8hZ3Da=Nbxl`DV{$a8bW~(#Q5aYsSDSoR}S(~QT(W{ zeWkB`n8Rb1Iq|Huk_!-mRAIL~J+$n|Nf{aKnra zPfTBbJYzXBrV0_s*bmhgBtLs2SAW_NAWoNrV=47-*p-1$+E4Gw58mlerOEY8qwAX{ z#O8c>Z|a;WQ|1lGC|i6m!@qpxs58GUyFt>m9C@TZx}>!V;$9FRUNOGL>UuDk4k5qh zCTbLev^{?NvbSJVsPx=WaB(@O7;;XJl(S?=C{Igy9M)1vyP02UaT%EHTw!2$WVjI? zVn8IlY>InNe#XWX&Z`KzxE2&HnaHb$Qf#{^sB7YQE~4a(-A2b-8;?5rlbHAUki5^6 zLv1Bi+q|@se@=agMT)7ANL5HDs=M%KeXRaVS~o1&;k#!1q~_9e!bjF!hpGLVoU7-9 zS-+ySnXxfO1sBnI?-+ykjyHKP<;=2oraU{wLk4+H2Xh)q#o4%Aj+5APprFZm!&Xa# z8!E$5n?~?1NhO1m3A8Y`={cw7>XL6U8{>8&Rn2gGzyHOjgRQe1tj28vpb0Vc5N|I~Oz3b`QrCz`9_F97QaeHSoax z{ne;%Jdv5XG%dI@?O3#|eY4N$U}*O3krKFko!_fpHs^`2E^g>X)e5ma!2v|WoFhCf$F5VuaoBI>z9z#dKBgSJLM~wgQNnuo zq#o78euKOH-Gu#8=UF8CEj3tsvdMn8BFQK0_x-Djw`G!~3~m@@S!_rS=9-SW*6MF~ zJxz0E2?c(II`dave)f2oBeTv9gvhvJx3%N!(P-?}Z?Idh$!_;1<~`yG*Cd3WXNpEj zCEirUkPsz@sw+|x$%qj(W*#`ABi2g!wgcknGUL26Rr$A}aG23$ z+tD~Hdk_^2SV#hH3gT2`$Apz=2OD>7K$Bijz*@`Zfi$LAQ8Tw@k$aawN)P&#=^k2& z;L*6)Z6nLJk8Fs#6i)U!R3goyvpI`L$v@OP!NI6ifYXx%@GKRUy;5nN1G4EXWnWzQ zRAl|l^*vT-tn#YCiQ<8|qM%#=#bAbfD$g9+l=;@ktXIJYW@2PBFPuKm)9>#34W->} zD3nU;QcFSEM@40qd3@nI->YmEhJR7)*)C>Cs@5r|UBnc$r5Fq1dKpaD%BMdHC1CJy zPKHOY1Qba~sb&@C4wQ;kV*b&@b8HTFUAoy9p{Ty>z5QBtU#|NjPSPzOYXi0134F#)Q;;8%&%uIro%zf&J5KCf zc~@rb6%!{;q$)@2(5*E?N4G|#AAQ4|^rNqw>$s;Y+yASw=0OAs9@4Z*flu~r+N(Hm zijpyWE8C^br6l69I6JO1gOhk-3F^m@(ZkP~_)lC`l>P2PU+^dwGj|tKFdZ#(?u$s| zwzM0W^P-}nL)k}P!PH+L0zsDDqUA^gOVd_#Irl-1@gEgdJlYO^Swv<2sRxL^Ai&1J znw}qc{q?ij&;OQ65MO+8ZF*(#s4xv>7qYn4`E)17+They!&OErZ-|w)X6RPXaLh=7 zIs0ZItNN<~3ti;4i@-(J?a?4lX>C(Lkyld4i7G-?a^aDN}` zjrd0JL&UkYpyAfF$ddnqjFUwB*J;CE9ZX9!b9?V`H5<_NTj(btMaEtOjF@1k+0kl! zR1&<5dRe};4LwG8O45Ycy1_{zn2Awd^IhXOcD8n_$n8C?#R}oX3yH~#OJQLWU~+xq zEcN(YIaktH(!j%22dCcP8|+#`H@r`j0_DKoV{e!9^Smat_vcJ$wwl00(*x`wZn~uCNVE>^*$Hw~3kIlgj zgr=U>7a5^1S{xM3a2nG>rDo)khZ37Kr_oH{n@EE8B$0$-N2H@oDVkUKy4eEE+_!L# zm_I4uSXJ}k%q&qt!wImiCwxK+#u|8I0bD@Y1t!zOod|Te5wwhuE@x`Qs4UugqNqy) z2Kg$b%8-`PWWtD3B+8Zu1RRDD1}keXC)rZit04(i)K>T_fqNZRi~FMlo2Umo{#)n0T}?^*Uk)?$(rH~4!gV(5!s*AA{1J_hZjIE}s|}y< z8mIP4OWWQoJ@R1S{DU#A>%1MqI**=W5TQCcDUSk!j!+DGW=Q@UUKUxu(~}lPt{2DA zMK(SjN9IVl+)89)Je=#xKfNpe<%pg`>p>&LxkF0iQejD6K4K^LUCk(OevI8Zw^xYz zL-B=Y>(l_ujz?XjV*$)CbDzIh(<3WY|GTaJSn5aVO!_GM9RrA>Y0tUrsaWTq zTpK{$)lmHTl5;c^pBl+!6=AAey^$=uihFg1LDy0-uas|4aT$2jAFw7U-$O+@vLR(s z&b&-@`qTF9_tMU@n7LWd|LOr-j|`AD54$P3JuuguO*tUh?o^~ag6~r2;r&~W-T!nT zuceW|wL@y$M3WZn@WTVGfd{&$kM(%`tbKm4ZspG(V}Fk5hB_jdClQxN{Qw~fJW&$! zq}VW|wzuSn?vHLSdD$uI#K~5Sj$z)WVJIQwT7MEFQ_Xe^i>zKse@q_f}^ z<3%XeQzs6`W z$$d&y74iwiawPz{0>_59DIorn2sF17M@{~(bXU54e`(1nX=z)9)}L9}oQpq)p4t0g zQs_i-9aWrKmhSMsVCVhNLk#ZeMc!}N_?lHdm|!|2-pyuB{kc=4NpVkbSPzv=wl}6c zyE7#zjI{qsuidTN?7hk<*m*yfjGvV4N`fDiH2kde{LlE;yT*b+5H)1qHG*2F zM50+Gzw6a0JQoeoP-c4&)_qyZPpHh}X&?boU}Ln4I6Km;f?IYK#??6%r@EfWLk~tRFq-}p|xP@6}yK~98e_z?~;@G4vbIFjZI}2NuHg(AFm%ngClDXyhd%Orh%3J;i^*kURNy{ZLg)} zNK3}ae||3r&$AIG$Qo# zHTH{6?30b$Tj4nll%*uW#J=&xv7Uw&pVdNLe*X#(bjq#B8fOSKWz-Pc6YB;prP6wZ zD97cfzuIO_>Us1H>;j1C1i zGkH&QQZD;T;KL~@9>zj;PYn%=DCY+He|Ag#)}G}Yp*WSmWFvE^+83HBNG&^2wDo%) zUvQTX8!?zwIWE~XLFzqZzGB^?$2qCFgt|+Ocf+G-^|*7_G`foS-ismm#Y&QyHo-(D znO)Dvw-tJ;j6@q5=9 zGp#CRH#dPVeYK1JD!(1A5+Jak{4o}hpvo`Z6gEo5CdEs*YQrA1Q*VBSYH=+ zwjx($C&4T}pXfdQz2qaWrJi_gBM!4@r4PbR8TKEjx!)#@sNa&_$iKxm26hg&u0Bxf zoi`o5=IDE(Vg*#PHtlJU9c=O5PJfYyKLECv58_Dd??{%DQyXa{f6ho<%HO`)|0eCB z>sehNK3ktnnRbWTsQkHfg()8Hq$G>1%M5Y-i546#fD3OmCi4u)rD@l%1#I;f}$B-Yai8yqnHynB^4@fy5d19H9ivh4pb% z#e|1I9$=cJC}XL&kj4R>xhpB@*q2jFk0XqH5;e>Yn053#ufLbWWdbL8m_oZHo;+J0 zewAo9S-1X5TBGb#7gd}>g&CU_Q6kzFXNCHwwibc{?sjw8`q`FUKoSq-9k0W#96xt< zmtWu`PaNWK`@bLERp=m7ORJID9WY=3Q@}I7O+HHf4cMje3ouX-lso_TlL^y!V%kY0u++O~PVQ)s`(F#IBIi<4b-oWSB~YL}8B-AL9|wqQzCGKGB$@hiknhECccqMhR9L=E!#U^ysT z@FNb@|28?dKX)80-T}X;wtzb1&)>%liL+H>M;j#g9%7Ty-G6&zC9eXoY&7?C$lg#@ zQ@AR=v1B~5;q^~YIge{L0*DB;Dl6}N6+G_gy$>cfE?SWEhq%yZamVux*X5;l^vgfe zZ>!UF@4wuE!?ap~L(YPZypb?DW*hC{L^+7^VBq39cfdWQV$XJOy|K3?CDdZe&if`C zBzMM=h$I)y`&xg{ayU0(!GZRyUu0iW!s89=@6IafOI2QRnLdK6y4C%QIXEBXDM`SEw z`(!A^Xuh~#a@~9i)@YO*3n9ci@4T~`bfFShCnM!gCWPYLwg}Mf|1|fhf~jvh$=sg@ z-c%xRDFDhGp3Ih$Vzp^U59ih&r@!A+$?t0jw2vWBsVGp*ggQJmm&K~v)CmvbP(xw3 z?y(3DrIeChZ3-;{5qk$qrK}+7Rh7ivfNUOyPFbIM^SaVb-xw~?2VtzYaG~RXqXg!d zpOJ8b+axPdilpWzXc7J8nu-uhPpuU^`}F9j72 z$th^$7@g+T0q9HSHBswvC@YW`J13&GyVBTWv@%!rL9Qxozx6Pj>GiU4fKw-WMG!t| ztr15j9p@U$@%#zaFf}Wb#A!VY`Y};0XGLZQomwK?x72Dct>eVH!+pIGd@V;+3V2JM zhSsy?Eva+}#OuhDQZe2kYl9Ka1Kfuq^<-tQZ!5(dxG56LPC@3=XWp%P8i~@s zzx(b^#12_O+I$ihj7U9|%y+V+$<4gM687IbK}BM8!dO?L_tz@ywU-@lK*jX3tF{eS zn`y;_gg-)0L}z~qjlObv$%YYO2)Jba8nKF(w+ikcUF+?{Hwl+K?JH$2={ zP&8w6!OGFif8#y|+CwGjIUF}Y9Snsx7Fgd$k_qPkU^9=eKevxkt}d4;lhyy|omyBo z6fmxTDzwr4fCD3^mr~}sF*;A8KVb#FO?DiyS5V^!7Pqp zJ6lPGFFvZ}*c&<3htS4a5+;}8V3}=5G2ZJTFa0tnyV%jl(Fl%T^mcVTQb~>Bkvh<} z<4J1%TQY+jJ0k@ncnHuGTELsP)OKEqw$$ocr1vI98OGffkbhFfQV7@27)4e34S(;w z4fs#H$*F%N0ljE@sN2;m=TjKw(s|6MXq#is@s#{udnh4@3Xbqm8nW6>p$(b#rWx>W zx2a8PPIEE_{BBg>f6h3Fg;MWIs_s;ix4$FRxU+`f9jR7V&+Sl=GP7uv9Ws0rL?hiz z6oP3P)~@pL{(H8!p^P|%$TA5avRTfbUR**M2|h=GEP_z}Yjp~o)9b#%pmIkf$PKp^ zT7MjyRCARl_+DHvh7VCt)I-XwRL|A?O?K|tb&#esS1!y7*nUbUisVNt{LA=5Bxab; zM|iQ0UNM(y@?S9bHll=hhn{=?Y0`7hHBT!z>HyX9&t4w3?t z%eHF$((le~uAfAyNb2?IuMlwPPgJmH&z>9&SDybek_bL* z@RlF2_m`@5&R@K(^Ss-LSplq6vTq`(@nfuz>;M!`$FtJyO8hG8fJTSM=>`7``3uQB zlVmx{D%(4<{AwIt;@`jlKPww*N`f^4fqVH=zU6>E@F8W;Nmk`(<#)%>15DCWwp$1*2B>+5EW zY=?7#U_Ij37(T|7cDzWal0h5tG?6zLg=!P2^*iPAO(*SaGVQL$P{V^s(5UpYU#E%9 zB{s))POe(m9G}AJu(_*@mHf6a(4@ViS`KZ0r1;!5yoF5>9LS`N8dFV_tPOkn=17g&iFwQ2T%)DUNC|mb z4rRvT@L1m~It|HXtVOhqA~<&G4qtcdrtlTg8979TDIHml9j_GTR87vh9+uS`ZIYf{ zY+dg~HlaP{B>w#exdND2d7jUz#Tp3prrbjmcWgTj!@Fr@qqb7+*(U$Z5_pt^-Bpg` zEf$>nTZ-fUX22 zY0yYEmc<@^7b)RQXE&dsI@6*Z7pJ5dMsZ*Z!NcD4rU!Tz32h1I%#-q&A`6LpVI}1F zu}hvUXEobD7Xwy@k19n8^SkUD#C=k+v3;JmwQDERggj@?4{R**cXzydh8*f#F>Dm2 zsEc#Y9-*3|jX)aZWmKH=uy;^EAT%P;&P^xVobRxIj4=}L2&segCLX8-WguQjtt3>H ztD_ErK|%L1D9xt4iX?~L_yDOW$280`*rQ>tqzK}2h=-9*eMb$)TCT^%o~Fo}YJ4x2 z#%_uilN~yM?NB`xV}-hNLq*gwvF|nqE(6DeaI$!}5h7h^X^Av=T)} z9skaV-(yhf48MeV(&hmcGRPb;0s?%+q@H?}kRr=qaw2xCTGcqW27wuN zn-bckHICk8Z$zn0p$}tbV=RGSMZm*52`CMHE%$DIWjpAETOD&qJI?0NtC|cQr>-zp zR1RG zrxS3_VyW0lPifa6DZGLYXW7hXP<24rxjrvJvu&T@1RB2@-PNR43QHhKNWCfJUBAEb zk&^aD4UM918NU+&j&Ynq`G>NQp0haSo{wRnn34n~`5|Fhv81(>mY~!KJx3qr5MJpF zhJA(_7poSlyfId$A5_%mPO4?pU(cmYsI6`;0C4H|FpO*m7J# zkJi=x_Myc^SWb$oWZ23r(^$X7W0lhn>;nQStlt|Wy~s-em2hi(~! zXK`HHRgr|}CU*^Nw`9t>3Pq*oU~`&v2Q{1JVp9l;GO0+Z+)!YR!`=`aO>_H_jVFCW zs0EQ!6MmI6uJR0?inIA%omyIct!MnM=hCSK$mba5^9E+UZMu!QeiUA7s4?6v8OQGB+NstP0pnSc~Q5fba1IM}3dv37zeU2rrwVivfdtF-) zdbL~?*LYPxb~2$fP)qr{HtMchCRHdjD1zspQ{L)}^MvQ4W?G7_n%kk%I28$-Xz7ue zfPvXnlXtyamC8rt@t%X4c@cX#isKw8m_MhJmIzuK2qk za9>d1cs||3!b#G?{jRm@^PPIO9c{~M2#&j35OwB!>yTnLHB z7L?YZG%Jv;T@oK&hYI_=_;3P(1lS3V%d#$)vJ!ESf^BVc4sJroZ4vIAREl zFJc$fb53wqcgn~z4X94A&Lv2O{xQsnWFlunva+Ch1u(9aQjy)pV{rqz8}e*v5lH(H z_d`C40B|hRC^u(NO`ZCjjr5_DVRQ{rIXF6=4Cx0+hBzZ62+ZXrGe$@@jCCes=Ic^u zp>Wz*;jgIih?2Iz7TTuKle-{VIfUcG@a+(8(2I6fd(Kwz7BRBXRmG_~rlj~c!k&Z? z*ZS@3W=g1%cYsjeDxbn7uo61Escw*w$co%t5;B%b&OGnl@Sj6Q6@ZV~`YUZXyjCQM zv7Ec)1Mez)*RpqSt5=zeO(*@xc0dw1P^l2d)`}#aBQAzxE%Q{H4b>qdxLCduQ#Q#} z6+N;^N4pY2IxE4vP*#FM1Ig55OhT*e%rh-XiNFu&hvR5s4<>mjG{zd!X@~T8e}2UN z?^`_%+}&jq^783GEZn72LFY8OQ6Gj5LZ;{^73bK$k#3eyf>J9ADgCK@0A%#@4 zC3Pdu<-sPJ(h4dvoTIj%fPM6QcPF7gYJu!Cn2T;T*}lf!IhQ_^c{s5vjWOg~RPxA$ zF-3qp9#a$O5ABzvv&4`9c>hplp?>dSqdr<0KRY2>`5+q~g&8C21~!a(gjM6geZ0wUQ` z;d@t3%%Zhwr6W(=X9idS$NYCI1R}nQaF;~Dp$1V@_(fom(0k6?O2$&c-Czn+RWy}Y zG_PT_?hgyGTa$Zs(*pWSTTE!@)De^>avNNfzS-($N3XLF@IPIByeem160 zt+B4@bJ#4TCYVLq&0fGy$2+GX{#-26FARn9iqY9kyFh4j$-sjV_ko10`|Na<)Czku z`r`LE2P$$Wsg^6l)_iI)2P*m)9CXgm7_bC&SPLYO=%fY@2SgqUrodw)MWv5>w!BW6 zMW{o}A#iB!+Na(3=8D$?^lhUtqZB)~L+s1r`=jNAo%ei7l z_?^g}62D?1Sa&iJ1tgrwRe`0X?Som$0q?I}Wlf@WzXR)zbY3D(g-WxU)4I}rddiE& zMoiywh&{nwY>)kcKpi!v$%AP&0_hT)-9mP#YJ;4PhbBHku!XnRwsV&Y?L0=8ETwR* z#pLfOf|7)tZ0e+DGhIMxw(S1Hkhg`)!@Tg9*^<|5PNTrX6pqp=tI7#Zabesnk#l3A z`Mt#dV-x&J-cM6pLczL+=103ax^kA?Zm6&lw8w3>4qdrH-r4lB-(19x4{CF}dP(mM`%*{!OT06^QZ zI%|M#S~v6hK}ulJx?O#s*4jcY3^}gL=kf*sgAZ#m+HO6knP50BmwQ8c@cEn6JI}yF zSmZJV(Ma;~3WXSio4D_hAMGrXK!bGxnu7-9tAJ2PC?AZ!F%;maa|ZS9xUkPzmR-_H z=Yczr4t`BCILvhuK@$;`L--$gts-lU4D1nXv7Ci~l#cL>;VnG^Ozyq-R*M5%r|avv z<53X89vd-J$)59>Kusm&8weU`xi+1&ZaxTij+mU3g8s^FWO8|q=H^z1?HDbJc>Co} z7t+PdT)P88Wvq&e5c5I3F3$i|ZUH9YvB{{htl`W-zEn23gsE%a)>pD-w+F(o3dPIie$@*$`zZMEy zp=ttt6u`rTXoduT60}w_6C&Jg3P@>vZ95v$0LWBGwUkrNJ_EMhZ*z%o z32#KaVkXp?4y$w&RQM;Hit0L${JYdQ&@IeTD4M6W+`@N+!X*(c#|LW3@QG zpUx6dJqY$}R*)PQ(`WvMMYWE;pFrOu7ekux_PPoyi}K7BU1@Dd71+r}9M~GpuscF+ zeLr&s2tc7Cn-($3PRgfO3c^hxVo6b&JU4g%EGHZ7gNcY4Bd!ItHaL@7Moz(21eTjZ zC|MRDHRo6u7`cEEOvc2(p@zbyL0_t9S~yrPCF->5ZU3;km}=JbzZOhlr4a0uOtexv z$~e-e`-MvV7Lr^Y&iH$UbstTI{(2&m;Ce?1KJDz0Y59elQAdOW_UWXG9DML%;W-eY zj~7XJHw*?n%_wdIVOM z5(=QgFzw>gNpPlfl4jEPiK$bu=PQ>iG@2f32o=0mvuP_qO%}W*VSL#w2rMWqcEvVV z1-RL7htMJKhVt^>bJ;F2UdCsp4H__=ER`{%p)@06(JAG>LD67*A8UXFkHQV)t8Fn4 zX^UyL{)z2s8OPg5!4x?$2m>SO!?YwoQm&X%=PkE4;9=kesf*wYc8Mx3#SH<<5oX_TofpsZNBl1@Y_Wu!(^9@uz>YEtJD4Dh4X?3rp= z{bco}*~g(*(I3Jt4ax@SkHd*w8>urT+?Kx@IBb(oqM3i-F=|$gA3rb7o!E~pa-Rfm*@x32jf9~?uh{? z$*g+?t5vnQ+gw}}zUU8NS@Eb>U6IiAqlB*8uuUo>uZ1?C2kj8r(fP^E8BZx31$Pup zK^PAHV>=?^uC#}hqr%cB;1s4~KPH-hs>tMo20q_axeI7plAkglh0Sv1d~y?e535<}?)^}41eZ2PAh8wSnA zipI4K)cnim)!?xi*JvonP)ft|BBNuaLAHsWf#gF882ABVbg$qN(Ev(-Rg*yLp^jxe zJU9-cq-})E(w?84c5k-!Q?bGtzU`I7!x{=T9Vg6?N3k`#rw5pqXD*KqCj#BOU3|%W zc7t@tdx5et{;oLhEYZUu1`ei;wtB=?%~^hja(vTp+aeY4#$rDi`nqn5pXH}R*N?QH zD&_8@{0`Ukg6)30(<2-=5;sw;@r)cfGHHs z`h(8kPU6;5Y!ARzV%i70v$-m*a(CG$yFCqb$Vq7?mB01viJ^dV{K4ekAAKe999>|i zA4MC)ggdjW9R?+q!i{igxIO3oc-)O~`I6Sa*LS2Iy~87Drtbwm;I_!`9w=}9}3o3)FfVKOE=LbIaa0! zmR3@BG!U<*!n+GtRj3U89W1SmD-eUANgxeZjt0|lSPCkiIM3(p>=VoKf4g?e*%6+t zxMCulXD0%-I6?U)+I8^-WszIYoH;X{C;O<5vTTmUDipD(lsa3x8JB^#R>pH3I-Ob! zv)@>j^BvFVCY<*(8=t&&qK&4`LvN)6Gr}8U=x`CpM^$l=pj$Puj37|HQId+&kH|+6@3sf^99G{II2&+MV(TRDUoa;~1wq z#Sa66*b*cD2X@>R7}qCdR1sj6S}Fv(L!>XMwZBg7F}E`KN#(Q4Nk1Ob_-*yHr+M-p z0_}5gKKXOb0!C38HD4eesWLtaJ7#@9mY$a1L7sB81Z~js3eY(zL>uDPZa$5)-i9u8 zCM97Ge^h6|+6ts5jfgA-O)}kanKm8IM7xrHu_XJIs_fo;4AqehzGGwakFB$HG|e`Q zRl8YxEuz@aRA7L|WrX?xKytOb+C<4~Q);V zv39kn`TNu;bM7urVa_L0xXWI=K0dFL@;Cqp=Z{_(@b+66O&6Y|YfAr|*8b-hs?oQ% z*qq8|bE?(F$5~%G_$!ifMRa0)PYK^$m?Qe>_%llRSqXGd7@0J7IyuSGH0kVK)jSS&Q3J07W8PH2TZK`;%=ypZqMpevxU<&iIhMw>m~ zCX79i9_lIL3zDPx(zS-F?GlP<6V1Z>^DCrfMg%SfqZFJL9*GwOUz~^O72oKmTjdL^ z@w^&zoD!W$fZg^K1@){JHD>(KdCSe0&()Fr+BsYE$;dN3biKr4Z1>h=#a43ns-xqt ztpr^}2k4Qv(IatdGUmUAbnlgAbN;rhhOCPbmrk`yvmbiWW}dV6H8r!#5tmdBXRsnJ z^c(H$R6NQ0ZRgGGIU}7G3+dzO56}hNPqFlI)&utT=l1!zAHexQ2`F+U~JUw1v7y@|`=V zT}^7tU6fMO@owby3{Zqkafl(-tY#WOI8h`gG@O!>7sz@dl5`nTiIJE#+dFar{FZ9t@+M)t9E5iq*x*5%DoIDI%p)?;?12 zO8E^Ae)zwJ5|16-lkc(sTGOX;Cd365Hgb)`2Iab&u!^}*oL}PS6X&c*>{892A&{@V z?B&xwdy=nOn z->c*Rz6>|*O)39@Gr5JwnA)VLr!Ux*RQtnD9$qUf+$ud2(nM`sdSPco+)pnUfp$M8 zQ2ln&S(#5mz_TczXy@3%#>VCZXXdH#4B!$gdrDj8l=ei_ZuoXW36-#?YBCm_(&d1` z*3z5BDfwRlOlgz`WTVD8{Lqv@ah=LaP6j+o2aVPQ%t{13z0Eog$>pX&DPvX#191NM+~8A8zO!SVS1h z&_WxXec#dKca^WV19xd>gs_qum<_dM_}PSTjDj_#Ccs*O9%|Cg`aAf<4*@shfv@ON z#q+R!IBVx99q!bb-fupcw$OE=t8e-C?c2Yam{H!GA)N&qHf*>n>+Haejt<%mMt$K> z3;d6Ig30P-3qGlZ9Na~RC*iiusW|!-(do4`fNL-SYs7dPjebRR9KE((E`sVC=gg?1x6!K0lj$*X_pOha1a!-o`5=C^E#$ky#@=AC>UW1LYjNocs@Fajt`3T7NN+!gIU?x z8<7|~kY0vk_iH9rJ~fOkR71g&PSbJ>hBg*Z%QU3s;D=Ha=um$-{Ebhk2PgTBh>0#E z=CRRk1pMYs^ib25v@5r9XtRf%x6W(M-skV>J`!m3hQo7CrFAVkw7dM!)Bu7yY}^U< z#{F)3@i06!E(h1hZhMOYg9_1A8FGp-22X^Aw0JMRIF?2CG7y7_#t=^iBFxKA$3-87 zPJMVkAh#lxILwJ;-v~7*4YndIXHaBSGANwt&#&ZM;KxWR%K&!;)^q`07$oAPviAm* z#Z;x{-X>`04j3j_u06!2N1}L;FZ06j)Er*Yeh2to;cpqgE?SL*^dtJ&93_Mk7<4nB zT}j^9ysz&i_Y;w2$FXxiac#ozy-YB}vLN~xm;>&RS-0mM)U4^|GMvp+k9jJH!5hl+ zKs}(y5Jo@f5X_72=zD>$IM_he1o8|OPa8s>2zb0?V*vXzcGFFtT_xWa>3!nnyTH%7 zGpfRnavF$-wNb-ShzEtaOEqxz4 zP5a}UNBJ9i`5X4xp2=G5-ltqR`yzmp2KzB2hPf+W4c$-@)7R|Lz_bSO5HU04^Yj35 zSF-aBbP7iI2h4|d$W)Rjg)GypGG)E(9UZC8=z=+`2~~;KBR4g4L}>kvxwMIzzMx4S|#V> z2`ti`J?c_Q+)xQ~;J4X*kd(;uMBx{8+eY4DZ_6lVQ3vSs+0ZCQqash)xtNP1-BlU| z$42a?lVgKM%@T60tC^R!`YE-ADx&Zb<} zH1`I%x)Di9hj7!;>0zY3jcXhZ#7-UnkZOA}q`f+xWM z5r5h?M(ou2hQgX9+4<=3>Ys|^61%J1D3P&JojhC}UiYwT?|w-=?t2MMox3b|)3V$% zlwG5{AhF$u-0a2331ghocmI z9p;IGZW@(Fch*&AJ5dImI!3n~lCo=%w-rp$XMuLPs*3Jg!hP=QVph3)iHFB(kKrwa zRB6bV2TG77R}XneW$WB2s^+u^um97)&zw0?nFW@y&jk@aOu)d7nJH-LH* zPO$SeZ~|888s1;YCrQ*nhDp#`Gp3m~EhnA-kJ#?j>qehims;1*Fmu^zt$nT&1i9Ct zSus4yZP0m_6a*?K>_x5_7fe}lOYJT2`&d4KPjKuT8OW6eLD{=R%dW4oe&*Kpsu5h* zOK5pIq2)_@UUKf2d#`)pId;DceulpjA!zrj@5>FM!3JxDfP-+#StABFh3cf-7kSAs z%1`>V%Ia~e7e}4P88#BUr*kstF31+8$_PT(jQN-RQbQic}3Nsm)oDax9rbNWqxUoQMywWp_2^S6!YRw1zgnVRV{dr_77!ia+u-Z{2 z2roR?ds&7HynpD4?jD!BU3PsW;i3&8jfK7>p@SFkjNqgWRw<)EBqS?{*pb8tr4v2F zDk3hmM=Fvv;C2U9AJ%Q!bP@OI-m4nUy&fwbrm(hNf-wME^Da*7NCr{`U;s0?EdevS zVMP45qjCMP^ekU84jHoW513RhA&WbFJ=KeayaR;*{(W}fbSSgm+3#z0a}sdYw<2SW zjmWN`8Y^e@VM}Is2wFr%Q~+O!AhEJfYH%?Z8G8Q)>wVG&+Mz_7X?sRyt9_Tday#L4 zI#_*;$3Qw?``*V(t0b$V$k$MVe7RhtLJA1S8Q1#;crZ01p2)fM_3PJXZ%=&oUQhmc z%%)iC);wuen9NUJwF4okmq&AWq(+}59_o%uQr*v^jEq3BhV^QFdr1GiP!L_6z zAO+mQ`a#kzj<2F$!HMPX4=wAW(qLi%4mDircH7=vpMCb(pScLVT*x#ovLDoQW7D3I zXj0-Z)#bG9n^Od!3{<_24Haw11K-hWFkvm0m3;>#%+%=873s(H=s<-Z(%I%SIi1=V zXEi1`=~Sm1N7z5L2;bgJzvKuQ;`W75flXgtrvdD6^bp~#f)0Bpb!aB7Tb z1j#O0;`$|Y{K^&7ve#o<1W~o_bNkFn+_JN=R|B6$l1HZm6Noj>={=WI9>kU@M8tDk z@s|ndSU&<`L2m0;DK3cjf>-WFU{&H{e2O(jAlfCcV z@W)+AFL0Tlk`t`;C<@S}189N)LS!LygpU>(-a|BuoFfAj)R{RT_z=3?>eU!!no3Uo-^n2>Jowb7>d6}NbW>jFE7(RS>_L{0W zw|Rt}OKX9FJ1g8*&1~i9f^S&!wu5k z7%hEknb+Cf!Ob4tQwRcPdj>WAaTn`Y&wtQv&|nI60&L>=-}PvWDN>NmYJZPzlbsUR z@$-n@tJUGeh%1E(-TAh5Ty#Ro?DX=~rn~J}7M%uBNP!{&WO8tQ<_ZUCg)bZ`v`D&%8KtIUQrtepbGszk)HX4o4)QMwj+0B$1lNt6@+Y z4w#k?tE(Fed*tD;DqF`Cg92DVn}GQkmMuAq6Cy2+Rt#t(^4fn4`Ww@UVhxS3#E8^Ma>?k5y(QsizBqLU0g40llG&o{ZFH{) zrAG&s7-D6imQX&4?k9clhP=Sw34GE2#KD~lPYUXc%;t9sDk>gC?I<-?#l-9)jl0g) zI89mBK4EKoSHc!@RtGoaO~me6ijLqy>0Fm86xtHrq7|k30IZ-1yyiA!z)AhYVljAO z8^FM86s}p*hl!9r6Bnq4_U$wq+QkZ+8tZX!%I zP`5D2k?4j0U1?+g()V!lH-~c}93D z^la!H<0;&`(tWHfuAIJbCMrW^(RCclJI?7J2r=!3BcKQV#|%R)1HH&mG9FK znZSwV#Qm0_4hJ~IM6Y&Q5w|2|uq*3deLVH-@zf6LvZR7nw(|Wd4F#O(EqRuR0mEv5 zHW(Lt2xJQlNJ`X73C!s{X2XEsn}_nXkGK{mS_^SXDD56s5y!foPS5>-@2=e1R$CNT z1_AFazVKrzm8$@gB+NcjCx%Q$2dw60SL2c~0}GuoA0jA+8H?C$n)OW@QbQMw-39gF zSQDW`ob@9o%vt8__On)Y%7V~^RIT-JEw}OYQgq+cP%sE&Xg)&#Jw~akIXpu0Hn1E) za8#Hnl;F5bRMlSv6Qa>{Qr0TBvifk5Z=|x)jaH(p8Av=TW#ytIr65&ORC8b@N`WG! zuSGO2lHCl3r9mDKesRS%s!7~a-{g~xI}-1OU%Vp$6xq+ zWFJ_Tcc9AED50ETZ(e=Lic5}$)YD-SP)`g_{Grm#n;8}!WKqlT6EVU~$mlqr51e8S z=Yo5o8r&HU4%{jsHO}v!U+Tb?|3$Y?FX9{`1qTV7L(E2x1*OmF=rdb^b-EWpoiJVq z-kyT;-0YXMc|}ZtTA#J#Sy6-+6W3J1Rbxgo^(DMDI`2)1+!ETBB;*Xx8RAveYQs6L zV-`S%(}c@om_-t}VM_)Dc%|6v_yAoRh+|CH$Kz52V4KodXn|G)+pM$>5LJg%;h||% z8;`#}ks(&PaSZ!$S08*;lqN9hxDcSK<081zS}Onmh_$^FB&nDXlUoN#9ep(hOX3qG zP(H)u{E+|$9@w(Hd*wsRYpCn@VoLn7TL%s*B}VyVlcU6g#&HN=#UZ?^=u)wXQx|sB zO#H;<8nk%Tf^QlR&9(nPg&%~2LoXTI9aJCO3ZaKWik+*so?QL;?CZH0{1q32?{EZP z6(RTl`!znX56vmHJxw-hdkKnj9_1{lN^wilK|t3=3dIP-QHaI z8HLXt8+_O7lh<@Y+cY=YTH8CLt)d3`I&lNZQOqmJTvg$)?VdcVa6QMZ!tTH643Z82p9HLCQgNJ63*Iyo4J%N z?;@k_iF69=fJIJ9n~4k^>{G!P#&1iW`WSvvHJk6HsN=iYvh-HRl#tI|0I(9+F{1%$p_`ew1uke`@K2_j0h--rXgvAfPRe;Jf990FnE zkK@#Y)({pv%J*GG)89CI`_=?20h{CUHstfZQmNj6CU(PdoY$h+*>|m>nDD%&N&>wg z11zbSRxFzR;puD{0m(Rcb|?Y8!mTk$=N1=sQ}mU%{%dxiZVh&ae?C_&@vCR9riJ}E zW!Q~K1(s{qO-+N6*j*SEl+C4@2(s}G(mB9WS=GCtk@7u}1D*>2m6enNFmbJ^i)dm0 za%x2MTD9MOL%3DSo=m#4a5s1?UqR*z9;JP`$mFWxoLw*ns7lV3A}vllP~-;yix@oC zK*ldq(Bmg(j6bBP>?JT{+c{wToVIu^t?e~R1xj;SM8VM-=9a!i!MAMw$U*h5>toQo%8QzRjxW@)|Z;s~qBgxy4^2U-WrF=J-XZDp9U z48{z|eF$+Q%Pl{>d+YZX?tDHf;I3Z#Cs&&K*143v}kgQNVcwi-i3;Ax$NxV3#_n>gYf=rZ~o{mzb zn!RtGgV-sfwng?f@vc-awnX2|G`(sGp&jNx8wdd_&|j>FXtkEAjLnv>jd~cE1VMb13fy0KvKr}atrHxotC$CBEea7C zq?=69trKj6q7b%NwJ^!+ke+Bt0RaE+pMO7lUrOEtRk(;~D_T3}Ay;y2a`T~x*v;<$ zxX28E%oKatgk1-hDm-Q&7Fg5EPws{F*&e>56}IohEJVlgKPDKo#pe6h_TsZ7HgK3` zZ;4*Lc1~*KEbJbU4Ji7c*Nl5aJXwcxhUjJBegbD1XcC+|lfKOJVYh1Gv$*T=LWVEa z_Iwi(uCuq~qI=LrK@^GLVT4#&$j#$ z#qH?(q$W4N?+P$694WmSWm|3};WSPZi86ACy$eEsIh6RMyV<%*`hDEj%gJd}oJLK4$+hH#5UYK&YK8O>;{{k)aEazG-O^ zB(bI0Dv+;yaiZwqG6n> zd`*ygw2A1FuoL;)vyc~VZ=r=QW*TK8wubSVK)V!MqFyRG)*)Iy)dXvVLzgGuEKWKp zrKI`slI9W2m$N^aGHCk*D-&?bp`NKFtl`l{qvRPWT8+xs4)oTYv!18cfX>9CbgbP? zf#ZavV~0C$yEaf>XIcjK=lcuwb`3;8@&`7DO{y?_xG`V`kd##wCWxoHzZ+NMGQ8hl zf&Y=v@sFry0N^~i(y^{8#RThG-qpcJYUclQ#h~WTX_t!Z=|kNWoE?bkpLE68DU zH4ruGqiN1H>8pGroo1I+z6IPX!AE~g=Nw8BdQWP7U1U-w-scoy0O^Ew;44fD z3=Y1I;Q?xbH}rU-C~@|m&<#GbX2)fb{Si2h$lV~Z%2&8z#ftm6pd|%Y)NOL8N$Cey z;4zMH39Ti%uw#D7078@yvB*@!3QS;a7KITsaE;qg4onClSGq5Ds8mMK4vbP-uH?KF z4uzcR_3Ch*LCL~hB?V{UcB(J3C^0-dqiOi0<}yBJ!FrrI3^oD@p=V*@*6(_0c3Tsu>f|N0^OhuS#z4gclWRLDDj67Up_CDW0HP1 zG6N`k_Rw8?+%!kPNRBv7O)fmMOKUn4XnKgdX(*jUtEHz$?w(J4%ni(hwfFd>E;BWJ zJZ(_)Mho257{Mrf|H(aQF;wtA`)&pu*F+aSb8{Jx83sp_u^ADp4T>w)))~+uNhP^h zUis)_kMb@dGt>xL!uM4_J6fHK=05u4d)3+|4! zIZkB}W<`YAZlJZ*n{pF|dfT~fL&I&z)!teA5Cb_PCEDd~q%gtFa8B1{9#V`Yoc=4g0>%PS6~<-AUyoB#IJ-EIA6!Phsg< zv%wsr%pm$?Wc;%-(s_9tA|-1K$TTrReZU)(oU6 z@Rl{;p;s{?%J!|lsG36(DjL*n#;UqNOb)G6-xLknC?08ndiAxA>+$1YQHDuF zN4jl$XUZmPOmPfkIyU3d>1-8snI7r3i|ACwNn`hzpZ+Q5PAWN<&G)qO1%7|TiDW)G z)}@Y*BXZ80S!Ip*`kvT1-*&Ps);>8rfB*XUM$dXQ>&=~hf1lVV4x(1@|M#~z%Cif? zDWR%+W9NK2&u#*B^qgOXli#-YeNCRNj_wsXgwn(u;QzqI+Jb?dE;h~2NO36DmM%>LF|{a0r??#Edv_Tjj8ol!kATzY9@wg@k+7-=o5 zuKIFG6-{ZNZS0bb847X$_;37}hwfYZ 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 c13798bbc6..a5e095f160 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, %) "#;