Compare commits

...

12 Commits

Author SHA1 Message Date
cc145a267c WIP: Do we need to derive JsonSchema? 2024-09-24 12:30:50 -05:00
66e60f2ddb Bump clap from 4.5.17 to 4.5.18 in /src/wasm-lib (#3949)
Bumps [clap](https://github.com/clap-rs/clap) from 4.5.17 to 4.5.18.
- [Release notes](https://github.com/clap-rs/clap/releases)
- [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md)
- [Commits](https://github.com/clap-rs/clap/compare/clap_complete-v4.5.17...clap_complete-v4.5.18)

---
updated-dependencies:
- dependency-name: clap
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 12:42:27 -07:00
5f51a0f569 Bump thiserror from 1.0.63 to 1.0.64 in /src/wasm-lib (#3948)
Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.63 to 1.0.64.
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/1.0.63...1.0.64)

---
updated-dependencies:
- dependency-name: thiserror
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-23 12:42:17 -07:00
aee1d66e56 Add ability to open KCL samples in-app (#3912)
* Add a shell script to get the list of KCL samples into the app

* Add support for overwriting current file with sample

* Move these KCL commands down into FileMachineProvider

* Add support for creating a new file on desktop

* Make it so these files aren't set to "renaming mode" right away

* Add support for initializing default values that are functions

* Add E2E tests

* Add a code menu item to load a sample

* Fix tsc issues

* Remove `yarn fetch:samples` from `yarn postinstall`

* Remove change to arg initialization logic, I was holding it wrong

* Switch to use new manifest file from kcl-samples repo

* Update tests now that we use proper sample titles

* Remove double-load from units menu test

* @jtran feedback

* Don't encode `https://` that's silly

* fmt

* Update e2e/playwright/testing-samples-loading.spec.ts

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>

* Test feedback

* Add a test step to actually check the file contents were written to (@Irev-Dev feedback)

---------

Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-09-23 14:35:38 -04:00
1d1bb8cee0 Cut release v0.25.4 (#3952) 2024-09-23 13:10:12 -04:00
c7dd89e720 bump kcl-lib 2024-09-23 11:37:11 -04:00
9860294eb1 Release kcl kcl-0.2.18 2024-09-23 11:35:42 -04:00
1c393bfa84 Add E2E regression test that the gizmo remains at far zooms (#3923)
* Add E2E regression test that the gizmo remains at far zooms

* Tweak test's plane color, it's slightly different in CI
2024-09-23 10:33:47 -04:00
95ea1427bc Show device token while signing in (#3935)
* Show user code while logging in

* fmt

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* @jtran feedback

Co-authored-by: Jonathan Tran <jonnytran@gmail.com>

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)"

This reverts commit 5ba9e4351a.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2024-09-23 10:17:56 -04:00
f1b0e40388 Circle function and UI tool (#3860)
* circle

* fix another example

* fix bad comment

* toPoint fix

* cargo fmt

* resolve most of the tests

* fix last test

* missed circle in bracket

* remove console error

* fmt

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* trigger ci

* remove three dot menu for circle

* make sure circle can be extruded

* fix up after merge

* add extrude test for circle

* clean up

* typo

* A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: windows-latest)"

This reverts commit 03f8eeb542.

* update docs again

* cmd bar test serialisation improvements

* tiny clean up

* fix after: Replace kittycad crate with kittycad-modeling-cmds

* fmt

* rename fix

* Update src/lib/toolbar.ts

Co-authored-by: Frank Noirot <frank@zoo.dev>

* add another error to list

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)

* image updates

* Revert "A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu-latest)"

This reverts commit 505bb20bea.

* update markdown

* skip un reproducable windows test failure

* rust review

* leave issue todo comment

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
2024-09-23 07:42:51 -05:00
max
7848d63177 add multiple selections support for focusPath (#3944) 2024-09-23 08:07:31 +02:00
619b059ae1 Bump vite from 5.4.3 to 5.4.6 (#3911) 2024-09-22 02:06:09 +00:00
152 changed files with 23580 additions and 648 deletions

View File

@ -263,7 +263,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
os: [ubuntu-latest, windows-latest, macos-14] os: [ubuntu-latest, windows-latest, macos-14]
timeout-minutes: 40 timeout-minutes: 60
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
needs: check-rust-changes needs: check-rust-changes
steps: steps:

View File

@ -270,6 +270,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -479,6 +499,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -274,6 +274,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -483,6 +503,26 @@ const extrusion = extrude(5, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -189,6 +189,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -398,6 +418,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -609,6 +649,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -818,6 +878,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -188,6 +188,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -397,6 +417,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -608,6 +648,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -817,6 +877,26 @@ const extrusion = extrude(10, sketch001)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -190,6 +190,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -399,6 +419,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -610,6 +650,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -819,6 +879,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -282,6 +282,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -491,6 +511,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -702,6 +742,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -911,6 +971,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -187,6 +187,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -396,6 +416,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -607,6 +647,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -816,6 +876,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -200,6 +200,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -409,6 +429,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -620,6 +660,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -829,6 +889,26 @@ const exampleSketch = startSketchOn('XZ')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -182,6 +182,26 @@ decagon(5.0)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -391,6 +411,26 @@ decagon(5.0)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -594,6 +634,26 @@ decagon(5.0)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -803,6 +863,26 @@ decagon(5.0)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -193,6 +193,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -402,6 +422,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -613,6 +653,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -822,6 +882,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -448,6 +448,26 @@ const sketch001 = startSketchOn(part001, chamfer1)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -852,6 +872,26 @@ const sketch001 = startSketchOn(part001, chamfer1)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -9,14 +9,14 @@ Construct a 2-dimensional circle, of the specified radius, centered at
the provided (x, y) origin point. the provided (x, y) origin point.
```js ```js
circle(center: [number], radius: number, sketch_surface_or_group: SketchSurfaceOrGroup, tag?: TagDeclarator) -> SketchGroup circle(data: CircleData, sketch_surface_or_group: SketchSurfaceOrGroup, tag?: TagDeclarator) -> SketchGroup
``` ```
### Examples ### Examples
```js ```js
const exampleSketch = startSketchOn("-XZ") const exampleSketch = startSketchOn("-XZ")
|> circle([0, 0], 10, %) |> circle({ center: [0, 0], radius: 10 }, %)
const example = extrude(5, exampleSketch) const example = extrude(5, exampleSketch)
``` ```
@ -30,7 +30,7 @@ const exampleSketch = startSketchOn("XZ")
|> line([0, 30], %) |> line([0, 30], %)
|> line([-30, 0], %) |> line([-30, 0], %)
|> close(%) |> close(%)
|> hole(circle([0, 15], 5, %), %) |> hole(circle({ center: [0, 15], radius: 5 }, %), %)
const example = extrude(5, exampleSketch) const example = extrude(5, exampleSketch)
``` ```
@ -39,8 +39,15 @@ const example = extrude(5, exampleSketch)
### Arguments ### Arguments
* `center`: `[number]` (REQUIRED) * `data`: `CircleData` - Data for drawing an circle (REQUIRED)
* `radius`: `number` (REQUIRED) ```js
{
// The center of the circle.
center: [number, number],
// The circle radius
radius: number,
}
```
* `sketch_surface_or_group`: `SketchSurfaceOrGroup` - A sketch surface or a sketch group. (REQUIRED) * `sketch_surface_or_group`: `SketchSurfaceOrGroup` - A sketch surface or a sketch group. (REQUIRED)
```js ```js
{ {
@ -186,6 +193,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -562,6 +589,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -773,6 +820,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -982,6 +1049,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -188,6 +188,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -397,6 +417,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -608,6 +648,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -817,6 +877,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -213,6 +213,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -423,6 +443,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -753,6 +793,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -445,6 +445,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -849,6 +869,26 @@ const mountingPlate = extrude(thickness, mountingPlateSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@ helix(data: HelixData, extrude_group: ExtrudeGroup) -> ExtrudeGroup
```js ```js
const part001 = startSketchOn('XY') const part001 = startSketchOn('XY')
|> circle([5, 5], 10, %) |> circle({ center: [5, 5], radius: 10 }, %)
|> extrude(10, %) |> extrude(10, %)
|> helix({ |> helix({
angleStart: 0, angleStart: 0,
@ -316,6 +316,26 @@ const part001 = startSketchOn('XY')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -711,6 +731,26 @@ const part001 = startSketchOn('XY')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -21,8 +21,8 @@ const exampleSketch = startSketchOn('XY')
|> line([5, 0], %) |> line([5, 0], %)
|> line([0, -5], %) |> line([0, -5], %)
|> close(%) |> close(%)
|> hole(circle([1, 1], .25, %), %) |> hole(circle({ center: [1, 1], radius: .25 }, %), %)
|> hole(circle([1, 4], .25, %), %) |> hole(circle({ center: [1, 4], radius: .25 }, %), %)
const example = extrude(1, exampleSketch) const example = extrude(1, exampleSketch)
``` ```
@ -41,7 +41,7 @@ fn squareHoleSketch = () => {
} }
const exampleSketch = startSketchOn('-XZ') const exampleSketch = startSketchOn('-XZ')
|> circle([0, 0], 3, %) |> circle({ center: [0, 0], radius: 3 }, %)
|> hole(squareHoleSketch(), %) |> hole(squareHoleSketch(), %)
const example = extrude(1, exampleSketch) const example = extrude(1, exampleSketch)
``` ```
@ -199,6 +199,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -409,6 +429,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -611,6 +651,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -820,6 +880,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -1022,6 +1102,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -1231,6 +1331,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -54,11 +54,17 @@ const case = startSketchOn('-XZ')
|> extrude(65, %) |> extrude(65, %)
const thing1 = startSketchOn(case, 'end') const thing1 = startSketchOn(case, 'end')
|> circle([-size / 2, -size / 2], 25, %) |> circle({
center: [-size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
const thing2 = startSketchOn(case, 'end') const thing2 = startSketchOn(case, 'end')
|> circle([size / 2, -size / 2], 25, %) |> circle({
center: [size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
hollow(0.5, case) hollow(0.5, case)
@ -343,6 +349,26 @@ hollow(0.5, case)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -738,6 +764,26 @@ hollow(0.5, case)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -21,7 +21,7 @@ int(num: number) -> i64
```js ```js
const sketch001 = startSketchOn('XZ') const sketch001 = startSketchOn('XZ')
|> circle([0, 0], 2, %) |> circle({ center: [0, 0], radius: 2 }, %)
const extrude001 = extrude(5, sketch001) const extrude001 = extrude(5, sketch001)
const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => { const pattern01 = patternTransform(int(ceil(5 / 2)), (id) => {

View File

@ -179,6 +179,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -388,6 +408,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -179,6 +179,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -388,6 +408,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -192,6 +192,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -401,6 +421,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -612,6 +652,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -821,6 +881,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -179,6 +179,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -388,6 +408,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -599,6 +639,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -808,6 +868,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -197,6 +197,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -407,6 +427,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -16,7 +16,7 @@ patternCircular3d(data: CircularPattern3dData, extrude_group_set: ExtrudeGroupSe
```js ```js
const exampleSketch = startSketchOn('XZ') const exampleSketch = startSketchOn('XZ')
|> circle([0, 0], 1, %) |> circle({ center: [0, 0], radius: 1 }, %)
const example = extrude(-5, exampleSketch) const example = extrude(-5, exampleSketch)
|> patternCircular3d({ |> patternCircular3d({
@ -321,6 +321,26 @@ const example = extrude(-5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -16,7 +16,7 @@ patternLinear2d(data: LinearPattern2dData, sketch_group_set: SketchGroupSet) ->
```js ```js
const exampleSketch = startSketchOn('XZ') const exampleSketch = startSketchOn('XZ')
|> circle([0, 0], 1, %) |> circle({ center: [0, 0], radius: 1 }, %)
|> patternLinear2d({ |> patternLinear2d({
axis: [1, 0], axis: [1, 0],
repetitions: 6, repetitions: 6,
@ -190,6 +190,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -400,6 +420,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -319,6 +319,26 @@ const example = extrude(1, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -32,7 +32,7 @@ fn transform = (replicaId) => {
fn layer = () => { fn layer = () => {
return startSketchOn("XY") return startSketchOn("XY")
// or some other plane idk // or some other plane idk
|> circle([0, 0], 1, %, $tag1) |> circle({ center: [0, 0], radius: 1 }, %, $tag1)
|> extrude(h, %) |> extrude(h, %)
} }
// The vase is 100 layers tall. // The vase is 100 layers tall.
@ -321,6 +321,26 @@ let vase = layer()
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -22,7 +22,10 @@ pi() -> number
const circumference = 70 const circumference = 70
const exampleSketch = startSketchOn("XZ") const exampleSketch = startSketchOn("XZ")
|> circle([0, 0], circumference / (2 * pi()), %) |> circle({
center: [0, 0],
radius: circumference / (2 * pi())
}, %)
const example = extrude(5, exampleSketch) const example = extrude(5, exampleSketch)
``` ```

View File

@ -180,6 +180,26 @@ const sketch001 = startSketchOn('XY')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -389,6 +409,26 @@ const sketch001 = startSketchOn('XY')
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -93,11 +93,17 @@ const case = startSketchOn('-XZ')
|> extrude(65, %) |> extrude(65, %)
const thing1 = startSketchOn(case, 'end') const thing1 = startSketchOn(case, 'end')
|> circle([-size / 2, -size / 2], 25, %) |> circle({
center: [-size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
const thing2 = startSketchOn(case, 'end') const thing2 = startSketchOn(case, 'end')
|> circle([size / 2, -size / 2], 25, %) |> circle({
center: [size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
// We put "case" in the shell function to shell the entire object. // We put "case" in the shell function to shell the entire object.
@ -118,11 +124,17 @@ const case = startSketchOn('XY')
|> extrude(65, %) |> extrude(65, %)
const thing1 = startSketchOn(case, 'end') const thing1 = startSketchOn(case, 'end')
|> circle([-size / 2, -size / 2], 25, %) |> circle({
center: [-size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
const thing2 = startSketchOn(case, 'end') const thing2 = startSketchOn(case, 'end')
|> circle([size / 2, -size / 2], 25, %) |> circle({
center: [size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
// We put "thing1" in the shell function to shell the end face of the object. // We put "thing1" in the shell function to shell the end face of the object.
@ -146,11 +158,17 @@ const case = startSketchOn('XY')
|> extrude(65, %) |> extrude(65, %)
const thing1 = startSketchOn(case, 'end') const thing1 = startSketchOn(case, 'end')
|> circle([-size / 2, -size / 2], 25, %) |> circle({
center: [-size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
const thing2 = startSketchOn(case, 'end') const thing2 = startSketchOn(case, 'end')
|> circle([size / 2, -size / 2], 25, %) |> circle({
center: [size / 2, -size / 2],
radius: 25
}, %)
|> extrude(50, %) |> extrude(50, %)
// We put "thing1" and "thing2" in the shell function to shell the end face of the object. // We put "thing1" and "thing2" in the shell function to shell the end face of the object.
@ -533,6 +551,26 @@ shell({ faces: ['end'], thickness: 5 }, [thing1, thing2])
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -932,6 +970,26 @@ shell({ faces: ['end'], thickness: 5 }, [thing1, thing2])
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -258,6 +258,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -561,6 +581,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -770,6 +810,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -205,6 +205,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -414,6 +434,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -327,6 +327,26 @@ const a1 = startSketchOn({
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -743,6 +763,26 @@ const a1 = startSketchOn({
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

File diff suppressed because it is too large Load Diff

View File

@ -188,6 +188,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -397,6 +417,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -608,6 +648,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -817,6 +877,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -179,6 +179,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -388,6 +408,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -599,6 +639,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -808,6 +868,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -179,6 +179,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -388,6 +408,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -599,6 +639,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -808,6 +868,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -182,6 +182,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -391,6 +411,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -602,6 +642,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -811,6 +871,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -182,6 +182,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -391,6 +411,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -602,6 +642,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -811,6 +871,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -180,6 +180,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -389,6 +409,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -600,6 +640,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -809,6 +869,26 @@ const example = extrude(10, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -178,6 +178,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -387,6 +407,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -598,6 +638,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],
@ -807,6 +867,26 @@ const example = extrude(5, exampleSketch)
to: [number, number], to: [number, number],
type: "TangentialArc", 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. // The from point.
from: [number, number], from: [number, number],

View File

@ -0,0 +1,216 @@
import type { Page, Locator } from '@playwright/test'
import { expect, test as base } from '@playwright/test'
import { getUtils, setup, tearDown } from './test-utils'
import fsp from 'fs/promises'
import { join } from 'path'
type CmdBarSerilised =
| {
stage: 'commandBarClosed'
// TODO no more properties needed but needs to be implemented in _serialiseCmdBar
}
| {
stage: 'pickCommand'
// TODO this will need more properties when implemented in _serialiseCmdBar
}
| {
stage: 'arguments'
currentArgKey: string
currentArgValue: string
headerArguments: Record<string, string>
highlightedHeaderArg: string
commandName: string
}
| {
stage: 'review'
headerArguments: Record<string, string>
commandName: string
}
export class AuthenticatedApp {
private readonly codeContent: Locator
private readonly extrudeButton: Locator
constructor(public readonly page: Page) {
this.codeContent = page.locator('.cm-content')
this.extrudeButton = page.getByTestId('extrude')
}
async initialise(code = '') {
const u = await getUtils(this.page)
await this.page.addInitScript(async (code) => {
localStorage.setItem('persistCode', code)
;(window as any).playwrightSkipFilePicker = true
}, code)
await this.page.setViewportSize({ width: 1000, height: 500 })
await u.waitForAuthSkipAppStart()
}
getInputFile = (fileName: string) => {
return fsp.readFile(
join('src', 'wasm-lib', 'tests', 'executor', 'inputs', fileName),
'utf-8'
)
}
makeMouseHelpers = (x: number, y: number) => [
() => this.page.mouse.click(x, y),
() => this.page.mouse.move(x, y),
]
/** Likely no where, there's a chance it will click something in the scene, depending what you have in the scene.
*
* Expects the viewPort to be 1000x500 */
clickNoWhere = () => this.page.mouse.click(998, 60)
// Toolbars
expectExtrudeButtonToBeDisabled = async () =>
await expect(this.extrudeButton).toBeDisabled()
expectExtrudeButtonToBeEnabled = async () =>
await expect(this.extrudeButton).not.toBeDisabled()
clickExtrudeButton = async () => await this.extrudeButton.click()
private _serialiseCmdBar = async (): Promise<CmdBarSerilised> => {
const reviewForm = await this.page.locator('#review-form')
const getHeaderArgs = async () => {
const inputs = await this.page.getByTestId('cmd-bar-input-tab').all()
const entries = await Promise.all(
inputs.map((input) => {
const key = input
.locator('[data-test-name="arg-name"]')
.innerText()
.then((a) => a.trim())
const value = input
.getByTestId('header-arg-value')
.innerText()
.then((a) => a.trim())
return Promise.all([key, value])
})
)
return Object.fromEntries(entries)
}
const getCommandName = () =>
this.page.getByTestId('command-name').textContent()
if (await reviewForm.isVisible()) {
const [headerArguments, commandName] = await Promise.all([
getHeaderArgs(),
getCommandName(),
])
return {
stage: 'review',
headerArguments,
commandName: commandName || '',
}
}
const [
currentArgKey,
currentArgValue,
headerArguments,
highlightedHeaderArg,
commandName,
] = await Promise.all([
this.page.getByTestId('cmd-bar-arg-name').textContent(),
this.page.getByTestId('cmd-bar-arg-value').textContent(),
getHeaderArgs(),
this.page
.locator('[data-is-current-arg="true"]')
.locator('[data-test-name="arg-name"]')
.textContent(),
getCommandName(),
])
return {
stage: 'arguments',
currentArgKey: currentArgKey || '',
currentArgValue: currentArgValue || '',
headerArguments,
highlightedHeaderArg: highlightedHeaderArg || '',
commandName: commandName || '',
}
}
expectCmdBarToBe = async (expected: CmdBarSerilised) => {
return expect.poll(() => this._serialiseCmdBar()).toEqual(expected)
}
progressCmdBar = async () => {
if (Math.random() > 0.5) {
const arrowButton = this.page.getByRole('button', {
name: 'arrow right Continue',
})
if (await arrowButton.isVisible()) {
await arrowButton.click()
} else {
await this.page
.getByRole('button', { name: 'checkmark Submit command' })
.click()
}
} else {
await this.page.keyboard.press('Enter')
}
}
expectCodeHighlightedToBe = async (code: string) =>
await expect
.poll(async () => {
const texts = (
await this.page.getByTestId('hover-highlight').allInnerTexts()
).map((s) => s.replace(/\s+/g, '').trim())
return texts.join('')
})
.toBe(code.replace(/\s+/g, '').trim())
expectActiveLinesToBe = async (lines: Array<string>) => {
await expect
.poll(async () => {
return (await this.page.locator('.cm-activeLine').allInnerTexts()).map(
(l) => l.trim()
)
})
.toEqual(lines.map((l) => l.trim()))
}
private _expectEditorToContain =
(not = false) =>
(
code: string,
{
shouldNormalise = false,
timeout = 5_000,
}: { shouldNormalise?: boolean; timeout?: number } = {}
) => {
if (!shouldNormalise) {
const expectStart = expect(this.codeContent)
if (not) {
return expectStart.not.toContainText(code, { timeout })
}
return expectStart.toContainText(code, { timeout })
}
const normalisedCode = code.replaceAll(/\s+/g, ' ').trim()
const expectStart = expect.poll(() => this.codeContent.textContent(), {
timeout,
})
if (not) {
return expectStart.not.toContain(normalisedCode)
}
return expectStart.toContain(normalisedCode)
}
expectEditor = {
toContain: this._expectEditorToContain(),
not: { toContain: this._expectEditorToContain(true) },
}
}
export const test = base.extend<{
app: AuthenticatedApp
}>({
app: async ({ page }, use) => {
const authenticatedApp = new AuthenticatedApp(page)
await use(authenticatedApp)
},
})
test.beforeEach(async ({ context, page }, testInfo) => {
await setup(context, page, testInfo)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
export { expect } from '@playwright/test'

View File

@ -558,7 +558,7 @@ test.describe('Editor tests', () => {
await page.keyboard.press('ArrowDown') await page.keyboard.press('ArrowDown')
await page.keyboard.press('Enter') await page.keyboard.press('Enter')
await page.keyboard.type(`const extrusion = startSketchOn('XY') 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), %) |> hole(squareHole(length, width, height), %)
|> extrude(height, %)`) |> extrude(height, %)`)

View File

@ -0,0 +1,55 @@
import { test } from './authenticatedAppFixture'
// test file is for testing point an click code gen functionality that's not sketch mode related
test('verify extruding circle works', async ({ app }) => {
test.skip(
process.platform === 'win32',
'Fails on windows in CI, can not be replicated locally on windows.'
)
const file = await app.getInputFile('test-circle-extrude.kcl')
await app.initialise(file)
const [clickCircle, moveToCircle] = app.makeMouseHelpers(582, 217)
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
await app.clickNoWhere()
await app.expectExtrudeButtonToBeEnabled()
})
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
await moveToCircle()
const circleSnippet =
'circle({ center: [318.33, 168.1], radius: 182.8 }, %)'
await app.expectCodeHighlightedToBe(circleSnippet)
await clickCircle()
await app.expectActiveLinesToBe([circleSnippet.slice(-5)])
await app.expectExtrudeButtonToBeEnabled()
})
await test.step('do extrude flow and check extrude code is added to editor', async () => {
await app.clickExtrudeButton()
await app.expectCmdBarToBe({
stage: 'arguments',
currentArgKey: 'distance',
currentArgValue: '5',
headerArguments: { Selection: '1 face', Distance: '' },
highlightedHeaderArg: 'distance',
commandName: 'Extrude',
})
await app.progressCmdBar()
const expectString = 'const extrude001 = extrude(5, sketch001)'
await app.expectEditor.not.toContain(expectString)
await app.expectCmdBarToBe({
stage: 'review',
headerArguments: { Selection: '1 face', Distance: '5' },
commandName: 'Extrude',
})
await app.progressCmdBar()
await app.expectEditor.toContain(expectString)
})
})

View File

@ -537,6 +537,61 @@ const sketch001 = startSketchAt([-0, -0])
await electronApp.close() await electronApp.close()
} }
) )
test(`View gizmo stays visible even when zoomed out all the way`, async ({
page,
}) => {
const u = await getUtils(page)
// Constants and locators
const planeColor: [number, number, number] = [170, 220, 170]
const bgColor: [number, number, number] = [27, 27, 27]
const middlePixelIsColor = async (color: [number, number, number]) => {
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
}
const gizmo = page.locator('[aria-label*=gizmo]')
await test.step(`Load an empty file`, async () => {
await page.addInitScript(async () => {
localStorage.setItem('persistCode', '')
})
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
await u.closeKclCodePanel()
})
await test.step(`Zoom out until you can't see the default planes`, async () => {
await expect
.poll(async () => middlePixelIsColor(planeColor), {
timeout: 5000,
message: 'Plane color is visible',
})
.toBeLessThan(15)
let maxZoomOuts = 10
let middlePixelIsBackgroundColor =
(await middlePixelIsColor(bgColor)) < 10
while (!middlePixelIsBackgroundColor && maxZoomOuts > 0) {
await page.keyboard.down('Control')
await page.mouse.move(600, 460)
await page.mouse.down({ button: 'right' })
await page.mouse.move(600, 50, { steps: 20 })
await page.mouse.up({ button: 'right' })
await page.keyboard.up('Control')
await page.waitForTimeout(100)
maxZoomOuts--
middlePixelIsBackgroundColor = (await middlePixelIsColor(bgColor)) < 10
}
expect(middlePixelIsBackgroundColor, {
message: 'We no longer the default planes',
}).toBeTruthy()
})
await test.step(`Check that the gizmo is still visible`, async () => {
await expect(gizmo).toBeVisible()
})
})
}) })
async function clickExportButton(page: Page) { async function clickExportButton(page: Page) {

View File

@ -149,14 +149,16 @@ test.describe('Sketch tests', () => {
await page.getByRole('button', { name: 'line Line', exact: true }).click() await page.getByRole('button', { name: 'line Line', exact: true }).click()
await page.waitForTimeout(100) await page.waitForTimeout(100)
await page.mouse.click(700, 200) await expect(async () => {
await page.mouse.click(700, 200)
await expect.poll(u.normalisedEditorCode) await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
.toBe(`const sketch001 = startSketchOn('XZ') .toBe(`const sketch001 = startSketchOn('XZ')
|> startProfileAt([12.34, -12.34], %) |> startProfileAt([12.34, -12.34], %)
|> line([-12.34, 12.34], %) |> line([-12.34, 12.34], %)
`) `)
}).toPass({ timeout: 40_000, intervals: [1_000] })
}) })
test('Can exit selection of face', async ({ page }) => { test('Can exit selection of face', async ({ page }) => {
// Load the app with the code panes // 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 ({ test('Can edit a sketch that has been extruded in the same pipe', async ({
page, page,
}) => { }) => {

View File

@ -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( test.describe(
'Client side scene scale should match engine scale', 'Client side scene scale should match engine scale',

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View File

@ -365,10 +365,10 @@ const box = startSketchOn('XY')
svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness) svg(startSketchOn(keychain, 'end'), [-33, 32], -thickness)
startSketchOn(keychain, 'end') startSketchOn(keychain, 'end')
|> circle([ |> circle({ center: [
width / 2, width / 2,
height - (keychainHoleSize + 1.5) height - (keychainHoleSize + 1.5)
], keychainHoleSize, %) ], radius: keychainHoleSize }, %)
|> extrude(-thickness, %)` |> extrude(-thickness, %)`
export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `const thing = 1` export const TEST_CODE_TRIGGER_ENGINE_EXPORT_ERROR = `const thing = 1`

View File

@ -0,0 +1,195 @@
import { test, expect } from '@playwright/test'
import { getUtils, setup, setupElectron, tearDown } from './test-utils'
import { bracket } from 'lib/exampleKcl'
import * as fsp from 'fs/promises'
import { join } from 'path'
import { FILE_EXT } from 'lib/constants'
test.beforeEach(async ({ context, page }, testInfo) => {
await setup(context, page, testInfo)
})
test.afterEach(async ({ page }, testInfo) => {
await tearDown(page, testInfo)
})
test.describe('Testing in-app sample loading', () => {
/**
* Note this test implicitly depends on the KCL sample "flange-with-patterns.kcl"
* and its title. https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl
*/
test('Web: should overwrite current code, cannot create new file', async ({
page,
}) => {
const u = await getUtils(page)
await test.step(`Test setup`, async () => {
await page.addInitScript((code) => {
window.localStorage.setItem('persistCode', code)
}, bracket)
await page.setViewportSize({ width: 1200, height: 500 })
await u.waitForAuthSkipAppStart()
})
// Locators and constants
const newSample = {
file: 'flange-with-patterns' + FILE_EXT,
title: 'Flange',
}
const commandBarButton = page.getByRole('button', { name: 'Commands' })
const samplesCommandOption = page.getByRole('option', {
name: 'Open Sample',
})
const commandSampleOption = page.getByRole('option', {
name: newSample.title,
exact: true,
})
const commandMethodArgButton = page.getByRole('button', {
name: 'Method',
})
const commandMethodOption = (name: 'Overwrite' | 'Create new file') =>
page.getByRole('option', {
name,
})
const warningText = page.getByText('Overwrite current file?')
const confirmButton = page.getByRole('button', { name: 'Submit command' })
const codeLocator = page.locator('.cm-content')
await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel()
await expect(codeLocator).toContainText(bracket.split('\n')[0])
})
await test.step(`Load a KCL sample with the command palette`, async () => {
await commandBarButton.click()
await samplesCommandOption.click()
await commandSampleOption.click()
await commandMethodArgButton.click()
await expect(commandMethodOption('Create new file')).not.toBeVisible()
await commandMethodOption('Overwrite').click()
await expect(warningText).toBeVisible()
await confirmButton.click()
await expect(codeLocator).toContainText('// ' + newSample.title)
})
})
/**
* Note this test implicitly depends on the KCL samples:
* "flange-with-patterns.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/flange-with-patterns/flange-with-patterns.kcl
* "gear-rack.kcl": https://github.com/KittyCAD/kcl-samples/blob/main/gear-rack/gear-rack.kcl
*/
test(
'Desktop: should create new file by default, optionally overwrite',
{ tag: '@electron' },
async ({ browserName: _ }, testInfo) => {
const { electronApp, page, dir } = await setupElectron({
testInfo,
folderSetupFn: async (dir) => {
const bracketDir = join(dir, 'bracket')
await fsp.mkdir(bracketDir, { recursive: true })
await fsp.writeFile(join(bracketDir, 'main.kcl'), bracket, {
encoding: 'utf-8',
})
},
})
const u = await getUtils(page)
// Locators and constants
const sampleOne = {
file: 'flange-with-patterns' + FILE_EXT,
title: 'Flange',
}
const sampleTwo = {
file: 'gear-rack' + FILE_EXT,
title: '100mm Gear Rack',
}
const projectCard = page.getByRole('link', { name: 'bracket' })
const commandBarButton = page.getByRole('button', { name: 'Commands' })
const commandOption = page.getByRole('option', { name: 'Open Sample' })
const commandSampleOption = (name: string) =>
page.getByRole('option', {
name,
exact: true,
})
const commandMethodArgButton = page.getByRole('button', {
name: 'Method',
})
const commandMethodOption = page.getByRole('option', {
name: 'Overwrite',
})
const newFileWarning = page.getByText(
'Create a new file with the example code?'
)
const overwriteWarning = page.getByText('Overwrite current file?')
const confirmButton = page.getByRole('button', { name: 'Submit command' })
const projectMenuButton = page.getByTestId('project-sidebar-toggle')
const newlyCreatedFile = (name: string) =>
page.getByRole('listitem').filter({
has: page.getByRole('button', { name }),
})
const codeLocator = page.locator('.cm-content')
await test.step(`Test setup`, async () => {
await page.setViewportSize({ width: 1200, height: 500 })
await projectCard.click()
await u.waitForPageLoad()
})
await test.step(`Precondition: check the initial code`, async () => {
await u.openKclCodePanel()
await expect(codeLocator).toContainText(bracket.split('\n')[0])
await u.openFilePanel()
await expect(projectMenuButton).toContainText('main.kcl')
await expect(newlyCreatedFile(sampleOne.file)).not.toBeVisible()
})
await test.step(`Load a KCL sample with the command palette`, async () => {
await commandBarButton.click()
await commandOption.click()
await commandSampleOption(sampleOne.title).click()
await expect(overwriteWarning).not.toBeVisible()
await expect(newFileWarning).toBeVisible()
await confirmButton.click()
})
await test.step(`Ensure we made and opened a new file`, async () => {
await expect(codeLocator).toContainText('// ' + sampleOne.title)
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
await expect(projectMenuButton).toContainText(sampleOne.file)
})
await test.step(`Now overwrite the current file`, async () => {
await commandBarButton.click()
await commandOption.click()
await commandSampleOption(sampleTwo.title).click()
await commandMethodArgButton.click()
await commandMethodOption.click()
await expect(commandMethodArgButton).toContainText('overwrite')
await expect(newFileWarning).not.toBeVisible()
await expect(overwriteWarning).toBeVisible()
await confirmButton.click()
})
await test.step(`Ensure we overwrote the current file without navigating`, async () => {
await expect(codeLocator).toContainText('// ' + sampleTwo.title)
await test.step(`Check actual file contents`, async () => {
await expect
.poll(async () => {
return await fsp.readFile(
join(dir, 'bracket', sampleOne.file),
'utf-8'
)
})
.toContain('// ' + sampleTwo.title)
})
await expect(newlyCreatedFile(sampleOne.file)).toBeVisible()
await expect(newlyCreatedFile(sampleTwo.file)).not.toBeVisible()
await expect(projectMenuButton).toContainText(sampleOne.file)
})
await electronApp.close()
}
)
})

View File

@ -774,6 +774,80 @@ const part001 = startSketchOn('XZ')
locator: '[data-overlay-toolbar-index="12"]', 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', () => { test.describe('Testing deleting a segment', () => {
const _deleteSegmentSequence = const _deleteSegmentSequence =

View File

@ -13,9 +13,6 @@ test.afterEach(async ({ page }, testInfo) => {
test('Units menu', async ({ page }) => { test('Units menu', async ({ page }) => {
const u = await getUtils(page) const u = await getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
await page.goto('/')
await page.waitForURL('**/file/**', { waitUntil: 'domcontentloaded' })
await u.waitForAuthSkipAppStart() await u.waitForAuthSkipAppStart()
const unitsMenuButton = page.getByRole('button', { const unitsMenuButton = page.getByRole('button', {

5
interface.d.ts vendored
View File

@ -10,7 +10,10 @@ export interface IElectronAPI {
save: typeof dialog.showSaveDialog save: typeof dialog.showSaveDialog
openExternal: typeof shell.openExternal openExternal: typeof shell.openExternal
showInFolder: typeof shell.showItemInFolder showInFolder: typeof shell.showItemInFolder
login: (host: string) => Promise<string> /** Require to be called first before {@link loginWithDeviceFlow} */
startDeviceFlow: (host: string) => Promise<string>
/** Registered by first calling {@link startDeviceFlow}, which sets up the device flow handle */
loginWithDeviceFlow: () => Promise<string>
platform: typeof process.env.platform platform: typeof process.env.platform
arch: typeof process.env.arch arch: typeof process.env.arch
version: typeof process.env.version version: typeof process.env.version

View File

@ -1,6 +1,6 @@
{ {
"name": "zoo-modeling-app", "name": "zoo-modeling-app",
"version": "0.25.3", "version": "0.25.4",
"private": true, "private": true,
"productName": "Zoo Modeling App", "productName": "Zoo Modeling App",
"author": { "author": {
@ -83,6 +83,7 @@
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages", "fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages", "fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
"fetch:wasm": "./get-latest-wasm-bundle.sh", "fetch:wasm": "./get-latest-wasm-bundle.sh",
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifest.json",
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)", "isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt", "build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt", "build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
@ -90,7 +91,7 @@
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings", "wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
"lint": "eslint --fix src e2e packages/codemirror-lsp-client", "lint": "eslint --fix src e2e packages/codemirror-lsp-client",
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json", "bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
"postinstall": "yarn xstate:typegen && ./node_modules/.bin/electron-rebuild", "postinstall": "yarn fetch:samples && yarn xstate:typegen && ./node_modules/.bin/electron-rebuild",
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"", "xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\"",
"make:dev": "make dev", "make:dev": "make dev",
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts", "generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
@ -183,7 +184,7 @@
"tailwindcss": "^3.4.1", "tailwindcss": "^3.4.1",
"ts-node": "^10.0.0", "ts-node": "^10.0.0",
"typescript": "^5.0.0", "typescript": "^5.0.0",
"vite": "^5.4.3", "vite": "^5.4.6",
"vite-plugin-eslint": "^1.8.1", "vite-plugin-eslint": "^1.8.1",
"vite-plugin-package-version": "^1.1.0", "vite-plugin-package-version": "^1.1.0",
"vite-tsconfig-paths": "^4.3.2", "vite-tsconfig-paths": "^4.3.2",

View File

@ -0,0 +1,152 @@
[
{
"file": "80-20-rail.kcl",
"title": "80/20 Rail",
"description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position"
},
{
"file": "a-parametric-bearing-pillow-block.kcl",
"title": "A Parametric Bearing Pillow Block",
"description": "A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads."
},
{
"file": "ball-bearing.kcl",
"title": "Ball Bearing",
"description": "A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads."
},
{
"file": "bracket.kcl",
"title": "Shelf Bracket",
"description": "This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided."
},
{
"file": "brake-caliper.kcl",
"title": "Brake Caliper",
"description": "Brake calipers are used to squeeze the brake pads against the rotor, causing larger and larger amounts of friction depending on how hard the brakes are pressed."
},
{
"file": "car-wheel.kcl",
"title": "Car Wheel",
"description": "A sports car wheel with a circular lug pattern and spokes."
},
{
"file": "car-wheel-assembly.kcl",
"title": "Car Wheel Assembly",
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
},
{
"file": "enclosure.kcl",
"title": "Enclosure",
"description": "An enclosure body and sealing lid for storing items"
},
{
"file": "flange-with-patterns.kcl",
"title": "Flange",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
},
{
"file": "flange-xy.kcl",
"title": "Flange with XY coordinates",
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
},
{
"file": "focusrite-scarlett-mounting-bracket.kcl",
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
"description": "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"
},
{
"file": "french-press.kcl",
"title": "French Press",
"description": "A french press immersion coffee maker"
},
{
"file": "gear.kcl",
"title": "Gear",
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
},
{
"file": "gear-rack.kcl",
"title": "100mm Gear Rack",
"description": "A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate."
},
{
"file": "hex-nut.kcl",
"title": "Hex nut",
"description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware."
},
{
"file": "kitt.kcl",
"title": "Kitt",
"description": "The beloved KittyCAD mascot in a voxelized style."
},
{
"file": "lego.kcl",
"title": "Lego Brick",
"description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code."
},
{
"file": "lug-nut.kcl",
"title": "Lug Nut",
"description": "lug Nuts are essential components used to create secure connections, whether for electrical purposes, like terminating wires or grounding, or for mechanical purposes, such as providing mounting points or reinforcing structural joints."
},
{
"file": "mounting-plate.kcl",
"title": "Mounting Plate",
"description": "A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components."
},
{
"file": "multi-axis-robot.kcl",
"title": "Robot Arm",
"description": "A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes"
},
{
"file": "pipe.kcl",
"title": "Pipe",
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
},
{
"file": "pipe-flange-assembly.kcl",
"title": "Pipe and Flange Assembly",
"description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint."
},
{
"file": "poopy-shoe.kcl",
"title": "Poopy Shoe",
"description": "poop shute for bambu labs printer - optimized for printing."
},
{
"file": "router-template-cross-bar.kcl",
"title": "Router template for a cross bar",
"description": "A guide for routing a notch into a cross bar."
},
{
"file": "router-template-slate.kcl",
"title": "Router template for a slate",
"description": "A guide for routing a slate for a cross bar."
},
{
"file": "sheet-metal-bracket.kcl",
"title": "Sheet Metal Bracket",
"description": "A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly."
},
{
"file": "socket-head-cap-screw.kcl",
"title": "Socket Head Cap Screw",
"description": "This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key."
},
{
"file": "tire.kcl",
"title": "Tire",
"description": "A tire is a critical component of a vehicle that provides the necessary traction and grip between the car and the road. It supports the vehicle's weight and absorbs shocks from road irregularities."
},
{
"file": "washer.kcl",
"title": "Washer",
"description": "A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time."
},
{
"file": "wheel-rotor.kcl",
"title": "Wheel rotor",
"description": "A component of a disc brake system. It provides a surface for brake pads to press against, generating the friction needed to slow or stop the vehicle."
}
]

View File

@ -96,15 +96,29 @@ export const ClientSideScene = ({
canvas.appendChild(sceneInfra.renderer.domElement) canvas.appendChild(sceneInfra.renderer.domElement)
canvas.appendChild(sceneInfra.labelRenderer.domElement) canvas.appendChild(sceneInfra.labelRenderer.domElement)
sceneInfra.animate() sceneInfra.animate()
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false) canvas.addEventListener(
'mousemove',
toSync(sceneInfra.onMouseMove, reportRejection),
false
)
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false) canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false) canvas.addEventListener(
'mouseup',
toSync(sceneInfra.onMouseUp, reportRejection),
false
)
sceneInfra.setSend(send) sceneInfra.setSend(send)
engineCommandManager.modelingSend = send engineCommandManager.modelingSend = send
return () => { return () => {
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove) canvas?.removeEventListener(
'mousemove',
toSync(sceneInfra.onMouseMove, reportRejection)
)
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown) canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
canvas?.removeEventListener('mouseup', sceneInfra.onMouseUp) canvas?.removeEventListener(
'mouseup',
toSync(sceneInfra.onMouseUp, reportRejection)
)
} }
}, []) }, [])
@ -124,7 +138,8 @@ export const ClientSideScene = ({
} else if ( } else if (
state.matches({ Sketch: 'Line tool' }) || state.matches({ Sketch: 'Line tool' }) ||
state.matches({ Sketch: 'Tangential arc to' }) || state.matches({ Sketch: 'Tangential arc to' }) ||
state.matches({ Sketch: 'Rectangle tool' }) state.matches({ Sketch: 'Rectangle tool' }) ||
state.matches({ Sketch: 'Circle tool' })
) { ) {
cursor = 'crosshair' cursor = 'crosshair'
} else { } else {
@ -269,15 +284,22 @@ const Overlay = ({
} }
/> />
))} ))}
<SegmentMenu {/* delete circle is complicated by the fact it's the only segment in the
verticalPosition={ pipe expression. Maybe it should delete the entire pipeExpression, however
overlay.windowCoords[1] > window.innerHeight / 2 this will likely change soon when we implement multi-profile so we'll leave it for now
? 'top' issue: https://github.com/KittyCAD/modeling-app/issues/3910
: 'bottom' */}
} {callExpression?.callee?.name !== 'circle' && (
pathToNode={overlay.pathToNode} <SegmentMenu
stdLibFnName={constraints[0]?.stdLibFnName} verticalPosition={
/> overlay.windowCoords[1] > window.innerHeight / 2
? 'top'
: 'bottom'
}
pathToNode={overlay.pathToNode}
stdLibFnName={constraints[0]?.stdLibFnName}
/>
)}
</div> </div>
)} )}
</div> </div>
@ -512,6 +534,11 @@ const ConstraintSymbol = ({
displayName: 'Intersection Offset', displayName: 'Intersection Offset',
iconName: 'intersection-offset', iconName: 'intersection-offset',
}, },
radius: {
varName: 'radius',
displayName: 'Radius',
iconName: 'dimension',
},
// implicit constraints // implicit constraints
vertical: { vertical: {

View File

@ -72,6 +72,7 @@ import {
createArrayExpression, createArrayExpression,
createCallExpressionStdLib, createCallExpressionStdLib,
createLiteral, createLiteral,
createObjectExpression,
createPipeExpression, createPipeExpression,
createPipeSubstitution, createPipeSubstitution,
findUniqueName, findUniqueName,
@ -90,6 +91,7 @@ import { getThemeColorForThreeJs, Themes } from 'lib/theme'
import { err, reportRejection, trap } from 'lib/trap' import { err, reportRejection, trap } from 'lib/trap'
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d' import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
import { SegmentInputs } from 'lang/std/stdTypes'
type DraftSegment = 'line' | 'tangentialArcTo' type DraftSegment = 'line' | 'tangentialArcTo'
@ -103,10 +105,18 @@ export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
'tangential-arc-to-segment-body-dashed' 'tangential-arc-to-segment-body-dashed'
export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment' export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body' 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 SEGMENT_WIDTH_PX = 1.6
export const HIDE_SEGMENT_LENGTH = 75 // in pixels export const HIDE_SEGMENT_LENGTH = 75 // in pixels
export const HIDE_HOVER_SEGMENT_LENGTH = 60 // 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 = [ export const SEGMENT_BODIES_PLUS_PROFILE_START = [
...SEGMENT_BODIES, ...SEGMENT_BODIES,
PROFILE_START, PROFILE_START,
@ -144,11 +154,11 @@ export class SceneEntities {
? orthoFactor ? orthoFactor
: perspScale(sceneInfra.camControls.camera, segment)) / : perspScale(sceneInfra.camControls.camera, segment)) /
sceneInfra._baseUnitMultiplier sceneInfra._baseUnitMultiplier
const input = { let input: SegmentInputs = {
type: 'straight-segment', type: 'straight-segment',
from: segment.userData.from, from: segment.userData.from,
to: segment.userData.to, to: segment.userData.to,
} as const }
let update: SegmentUtils['update'] | null = null let update: SegmentUtils['update'] | null = null
if ( if (
segment.userData.from && segment.userData.from &&
@ -165,6 +175,21 @@ export class SceneEntities {
) { ) {
update = segmentUtils.tangentialArcTo.update update = segmentUtils.tangentialArcTo.update
} }
if (
segment.userData.from &&
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?.({ const callBack = update?.({
prevSegment: segment.userData.prevSegment, prevSegment: segment.userData.prevSegment,
input, input,
@ -311,7 +336,6 @@ export class SceneEntities {
) )
} }
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick: async (args) => { onClick: async (args) => {
if (!args) return if (!args) return
if (args.mouseEvent.which !== 1) return if (args.mouseEvent.which !== 1) return
@ -409,19 +433,21 @@ export class SceneEntities {
maybeModdedAst, maybeModdedAst,
sketchGroup.start.__geoMeta.sourceRange sketchGroup.start.__geoMeta.sourceRange
) )
const _profileStart = createProfileStartHandle({ if (sketchGroup?.value?.[0]?.type !== 'Circle') {
from: sketchGroup.start.from, const _profileStart = createProfileStartHandle({
id: sketchGroup.start.__geoMeta.id, from: sketchGroup.start.from,
pathToNode: segPathToNode, id: sketchGroup.start.__geoMeta.id,
scale: factor, pathToNode: segPathToNode,
theme: sceneInfra._theme, scale: factor,
}) theme: sceneInfra._theme,
_profileStart.layers.set(SKETCH_LAYER) })
_profileStart.traverse((child) => { _profileStart.layers.set(SKETCH_LAYER)
child.layers.set(SKETCH_LAYER) _profileStart.traverse((child) => {
}) child.layers.set(SKETCH_LAYER)
group.add(_profileStart) })
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart group.add(_profileStart)
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
}
const callbacks: (() => SegmentOverlayPayload | null)[] = [] const callbacks: (() => SegmentOverlayPayload | null)[] = []
sketchGroup.value.forEach((segment, index) => { sketchGroup.value.forEach((segment, index) => {
let segPathToNode = getNodePathFromSourceRange( let segPathToNode = getNodePathFromSourceRange(
@ -467,15 +493,26 @@ export class SceneEntities {
const initSegment = const initSegment =
segment.type === 'TangentialArcTo' segment.type === 'TangentialArcTo'
? segmentUtils.tangentialArcTo.init ? segmentUtils.tangentialArcTo.init
: segment.type === 'Circle'
? segmentUtils.circle.init
: segmentUtils.straight.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({ const result = initSegment({
prevSegment: sketchGroup.value[index - 1], prevSegment: sketchGroup.value[index - 1],
callExpName, callExpName,
input: { input,
type: 'straight-segment',
from: segment.from,
to: segment.to,
},
id: segment.__geoMeta.id, id: segment.__geoMeta.id,
pathToNode: segPathToNode, pathToNode: segPathToNode,
isDraftSegment, isDraftSegment,
@ -575,7 +612,6 @@ export class SceneEntities {
const lastSeg = sg?.value?.slice(-1)[0] || sg.start 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 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({ const mod = addNewSketchLn({
node: _ast, node: _ast,
programMemory: kclManager.programMemory, programMemory: kclManager.programMemory,
@ -606,7 +642,6 @@ export class SceneEntities {
draftExpressionsIndices, draftExpressionsIndices,
}) })
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick: async (args) => { onClick: async (args) => {
if (!args) return if (!args) return
if (args.mouseEvent.which !== 1) return if (args.mouseEvent.which !== 1) return
@ -747,7 +782,6 @@ export class SceneEntities {
}) })
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onMove: async (args) => { onMove: async (args) => {
// Update the width and height of the draft rectangle // Update the width and height of the draft rectangle
const pathToNodeTwo = structuredClone(sketchPathToNode) const pathToNodeTwo = structuredClone(sketchPathToNode)
@ -779,7 +813,7 @@ export class SceneEntities {
programMemory.get(variableDeclarationName), programMemory.get(variableDeclarationName),
variableDeclarationName variableDeclarationName
) )
if (err(sketchGroup)) return sketchGroup if (err(sketchGroup)) return Promise.reject(sketchGroup)
const sgPaths = sketchGroup.value const sgPaths = sketchGroup.value
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
@ -795,7 +829,6 @@ export class SceneEntities {
this.updateSegment(seg, index, 0, _ast, orthoFactor, sketchGroup) this.updateSegment(seg, index, 0, _ast, orthoFactor, sketchGroup)
) )
}, },
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onClick: async (args) => { onClick: async (args) => {
// Commit the rectangle to the full AST/code and return to sketch.idle // Commit the rectangle to the full AST/code and return to sketch.idle
const cornerPoint = args.intersectionPoint?.twoD const cornerPoint = args.intersectionPoint?.twoD
@ -857,6 +890,173 @@ 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<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node1)) return Promise.reject(_node1)
const variableDeclarationName =
_node1.node?.declarations?.[0]?.id?.name || ''
const startSketchOn = _node1.node?.declarations
const startSketchOnInit = startSketchOn?.[0]?.init
startSketchOn[0].init = createPipeExpression([
startSketchOnInit,
createCallExpressionStdLib('circle', [
createObjectExpression({
center: createArrayExpression([
createLiteral(roundOff(circleCenter[0])),
createLiteral(roundOff(circleCenter[1])),
]),
radius: createLiteral(1),
}),
createPipeSubstitution(),
]),
])
let _recastAst = parse(recast(_ast))
if (trap(_recastAst)) return Promise.reject(_recastAst)
_ast = _recastAst
// do a quick mock execution to get the program memory up-to-date
await kclManager.executeAstMock(_ast)
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
sketchPathToNode,
forward,
up,
position: sketchOrigin,
maybeModdedAst: _ast,
draftExpressionsIndices: { start: 0, end: 0 },
})
sceneInfra.setCallbacks({
onMove: async (args) => {
const pathToNodeTwo = structuredClone(sketchPathToNode)
pathToNodeTwo[1][0] = 0
const _node = getNodeFromPath<VariableDeclaration>(
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 = changeSketchArguments(
modded,
kclManager.programMemory,
{
type: 'path',
pathToNode: [
..._node.deepPath,
['body', 'PipeExpression'],
[1, 'index'],
],
},
{
type: 'arc-segment',
center: circleCenter,
radius: Math.sqrt(x ** 2 + y ** 2),
from: circleCenter,
}
)
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)
)
},
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<VariableDeclaration>(
_ast,
sketchPathToNode || [],
'VariableDeclaration'
)
if (trap(_node)) return
const sketchInit = _node.node?.declarations?.[0]?.init
let modded = structuredClone(_ast)
if (sketchInit.type === 'PipeExpression') {
const moddedResult = changeSketchArguments(
modded,
kclManager.programMemory,
{
type: 'path',
pathToNode: [
..._node.deepPath,
['body', 'PipeExpression'],
[1, 'index'],
],
},
{
type: 'arc-segment',
center: circleCenter,
radius: Math.sqrt(x ** 2 + y ** 2),
from: circleCenter,
}
)
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 = ({ setupSketchIdleCallbacks = ({
pathToNode, pathToNode,
up, up,
@ -870,7 +1070,6 @@ export class SceneEntities {
}) => { }) => {
let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing' let addingNewSegmentStatus: 'nothing' | 'pending' | 'added' = 'nothing'
sceneInfra.setCallbacks({ sceneInfra.setCallbacks({
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onDragEnd: async () => { onDragEnd: async () => {
if (addingNewSegmentStatus !== 'nothing') { if (addingNewSegmentStatus !== 'nothing') {
await this.tearDownSketch({ removeAxis: false }) await this.tearDownSketch({ removeAxis: false })
@ -891,7 +1090,6 @@ export class SceneEntities {
}) })
} }
}, },
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onDrag: async ({ onDrag: async ({
selected, selected,
intersectionPoint, intersectionPoint,
@ -1028,11 +1226,8 @@ export class SceneEntities {
? new Vector2(profileStart.position.x, profileStart.position.y) ? new Vector2(profileStart.position.x, profileStart.position.y)
: _intersection2d : _intersection2d
const group = getParentGroup(object, [ const group = getParentGroup(object, SEGMENT_BODIES_PLUS_PROFILE_START)
STRAIGHT_SEGMENT, const subGroup = getParentGroup(object, [ARROWHEAD, CIRCLE_CENTER_HANDLE])
TANGENTIAL_ARC_TO_SEGMENT,
PROFILE_START,
])
if (!group) return if (!group) return
const pathToNode: PathToNode = structuredClone(group.userData.pathToNode) const pathToNode: PathToNode = structuredClone(group.userData.pathToNode)
const varDecIndex = pathToNode[1][0] const varDecIndex = pathToNode[1][0]
@ -1069,6 +1264,43 @@ export class SceneEntities {
pathToNode: PathToNode pathToNode: PathToNode
} }
| Error | 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) { if (group.name === PROFILE_START) {
modded = updateStartProfileAtArgs({ modded = updateStartProfileAtArgs({
node: modifiedAst, node: modifiedAst,
@ -1084,12 +1316,11 @@ export class SceneEntities {
modded = changeSketchArguments( modded = changeSketchArguments(
modifiedAst, modifiedAst,
kclManager.programMemory, kclManager.programMemory,
[node.start, node.end],
{ {
type: 'straight-segment', type: 'sourceRange',
from, sourceRange: [node.start, node.end],
to: dragTo, },
} getChangeSketchInput()
) )
} }
if (trap(modded)) return if (trap(modded)) return
@ -1192,16 +1423,28 @@ export class SceneEntities {
? orthoFactor ? orthoFactor
: perspScale(sceneInfra.camControls.camera, group)) / : perspScale(sceneInfra.camControls.camera, group)) /
sceneInfra._baseUnitMultiplier sceneInfra._baseUnitMultiplier
const input = { let input: SegmentInputs = {
type: 'straight-segment', type: 'straight-segment',
from: segment.from, from: segment.from,
to: segment.to, to: segment.to,
} as const }
let update: SegmentUtils['update'] | null = null let update: SegmentUtils['update'] | null = null
if (type === TANGENTIAL_ARC_TO_SEGMENT) { if (type === TANGENTIAL_ARC_TO_SEGMENT) {
update = segmentUtils.tangentialArcTo.update update = segmentUtils.tangentialArcTo.update
} else if (type === STRAIGHT_SEGMENT) { } else if (type === STRAIGHT_SEGMENT) {
update = segmentUtils.straight.update 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 = const callBack =
update && update &&
@ -1276,7 +1519,7 @@ export class SceneEntities {
this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis }) this._tearDownSketch(callDepth + 1, resolve, reject, { removeAxis })
}, delay) }, delay)
} else { } else {
reject() resolve(true)
} }
} }
sceneInfra.camControls.enableRotate = true sceneInfra.camControls.enableRotate = true
@ -1306,11 +1549,10 @@ export class SceneEntities {
mat.color.set(obj.userData.baseColor) mat.color.set(obj.userData.baseColor)
mat.color.offsetHSL(0, 0, 0.5) mat.color.offsetHSL(0, 0, 0.5)
} }
const parent = getParentGroup(selected, [ const parent = getParentGroup(
STRAIGHT_SEGMENT, selected,
TANGENTIAL_ARC_TO_SEGMENT, SEGMENT_BODIES_PLUS_PROFILE_START
PROFILE_START, )
])
if (parent?.userData?.pathToNode) { if (parent?.userData?.pathToNode) {
const updatedAst = parse(recast(kclManager.ast)) const updatedAst = parse(recast(kclManager.ast))
if (trap(updatedAst)) return if (trap(updatedAst)) return
@ -1334,11 +1576,11 @@ export class SceneEntities {
} }
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
const input = { let input: SegmentInputs = {
type: 'straight-segment', type: 'straight-segment',
from: parent.userData.from, from: parent.userData.from,
to: parent.userData.to, to: parent.userData.to,
} as const }
const factor = const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera (sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor ? orthoFactor
@ -1349,6 +1591,12 @@ export class SceneEntities {
update = segmentUtils.straight.update update = segmentUtils.straight.update
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) { } else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
update = segmentUtils.tangentialArcTo.update update = segmentUtils.tangentialArcTo.update
input = {
type: 'arc-segment',
from: parent.userData.from,
radius: parent.userData.radius,
center: parent.userData.center,
}
} }
update && update &&
update({ update({
@ -1364,19 +1612,18 @@ export class SceneEntities {
}, },
onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => { onMouseLeave: ({ selected, ...rest }: OnMouseEnterLeaveArgs) => {
editorManager.setHighlightRange([[0, 0]]) editorManager.setHighlightRange([[0, 0]])
const parent = getParentGroup(selected, [ const parent = getParentGroup(
STRAIGHT_SEGMENT, selected,
TANGENTIAL_ARC_TO_SEGMENT, SEGMENT_BODIES_PLUS_PROFILE_START
PROFILE_START, )
])
if (parent) { if (parent) {
const orthoFactor = orthoScale(sceneInfra.camControls.camera) const orthoFactor = orthoScale(sceneInfra.camControls.camera)
const input = { let input: SegmentInputs = {
type: 'straight-segment', type: 'straight-segment',
from: parent.userData.from, from: parent.userData.from,
to: parent.userData.to, to: parent.userData.to,
} as const }
const factor = const factor =
(sceneInfra.camControls.camera instanceof OrthographicCamera (sceneInfra.camControls.camera instanceof OrthographicCamera
? orthoFactor ? orthoFactor
@ -1387,6 +1634,12 @@ export class SceneEntities {
update = segmentUtils.straight.update update = segmentUtils.straight.update
} else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) { } else if (parent.name === TANGENTIAL_ARC_TO_SEGMENT) {
update = segmentUtils.tangentialArcTo.update update = segmentUtils.tangentialArcTo.update
input = {
type: 'arc-segment',
from: parent.userData.from,
radius: parent.userData.radius,
center: parent.userData.center,
}
} }
update && update &&
update({ update({
@ -1557,7 +1810,7 @@ function prepareTruncatedMemoryAndAst(
export function getParentGroup( export function getParentGroup(
object: any, object: any,
stopAt: string[] = [STRAIGHT_SEGMENT, TANGENTIAL_ARC_TO_SEGMENT] stopAt: string[] = SEGMENT_BODIES
): Group | null { ): Group | null {
if (stopAt.includes(object?.userData?.type)) { if (stopAt.includes(object?.userData?.type)) {
return object return object
@ -1604,10 +1857,7 @@ function colorSegment(object: any, color: number) {
}) })
return return
} }
const straightSegmentBody = getParentGroup(object, [ const straightSegmentBody = getParentGroup(object, SEGMENT_BODIES)
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
])
if (straightSegmentBody) { if (straightSegmentBody) {
straightSegmentBody.traverse((child) => { straightSegmentBody.traverse((child) => {
if (child instanceof Mesh && !child.userData.ignoreColorChange) { if (child instanceof Mesh && !child.userData.ignoreColorChange) {

View File

@ -92,6 +92,8 @@ interface OnMoveCallbackArgs {
// This singleton class is responsible for all of the under the hood setup for the client side scene. // This singleton class is responsible for all of the under the hood setup for the client side scene.
// That is the cameras and switching between them, raycasters for click mouse events and their abstractions (onClick etc), setting up controls. // That is the cameras and switching between them, raycasters for click mouse events and their abstractions (onClick etc), setting up controls.
// Anything that added the the scene for the user to interact with is probably in SceneEntities.ts // Anything that added the the scene for the user to interact with is probably in SceneEntities.ts
type Voidish = void | Promise<void>
export class SceneInfra { export class SceneInfra {
static instance: SceneInfra static instance: SceneInfra
scene: Scene scene: Scene
@ -107,21 +109,21 @@ export class SceneInfra {
_theme: Themes = Themes.System _theme: Themes = Themes.System
extraSegmentTexture: Texture extraSegmentTexture: Texture
lastMouseState: MouseState = { type: 'idle' } lastMouseState: MouseState = { type: 'idle' }
onDragStartCallback: (arg: OnDragCallbackArgs) => void = () => {} onDragStartCallback: (arg: OnDragCallbackArgs) => Voidish = () => {}
onDragEndCallback: (arg: OnDragCallbackArgs) => void = () => {} onDragEndCallback: (arg: OnDragCallbackArgs) => Voidish = () => {}
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {} onDragCallback: (arg: OnDragCallbackArgs) => Voidish = () => {}
onMoveCallback: (arg: OnMoveCallbackArgs) => void = () => {} onMoveCallback: (arg: OnMoveCallbackArgs) => Voidish = () => {}
onClickCallback: (arg: OnClickCallbackArgs) => void = () => {} onClickCallback: (arg: OnClickCallbackArgs) => Voidish = () => {}
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => void = () => {} onMouseEnter: (arg: OnMouseEnterLeaveArgs) => Voidish = () => {}
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => void = () => {} onMouseLeave: (arg: OnMouseEnterLeaveArgs) => Voidish = () => {}
setCallbacks = (callbacks: { setCallbacks = (callbacks: {
onDragStart?: (arg: OnDragCallbackArgs) => void onDragStart?: (arg: OnDragCallbackArgs) => Voidish
onDragEnd?: (arg: OnDragCallbackArgs) => void onDragEnd?: (arg: OnDragCallbackArgs) => Voidish
onDrag?: (arg: OnDragCallbackArgs) => void onDrag?: (arg: OnDragCallbackArgs) => Voidish
onMove?: (arg: OnMoveCallbackArgs) => void onMove?: (arg: OnMoveCallbackArgs) => Voidish
onClick?: (arg: OnClickCallbackArgs) => void onClick?: (arg: OnClickCallbackArgs) => Voidish
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => void onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => Voidish
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => void onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => Voidish
}) => { }) => {
this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback this.onDragStartCallback = callbacks.onDragStart || this.onDragStartCallback
this.onDragEndCallback = callbacks.onDragEnd || this.onDragEndCallback this.onDragEndCallback = callbacks.onDragEnd || this.onDragEndCallback
@ -389,7 +391,7 @@ export class SceneInfra {
intersection: planeIntersects[0], intersection: planeIntersects[0],
} }
} }
onMouseMove = (mouseEvent: MouseEvent) => { onMouseMove = async (mouseEvent: MouseEvent) => {
this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1 this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1
this.currentMouseVector.y = this.currentMouseVector.y =
-(mouseEvent.clientY / window.innerHeight) * 2 + 1 -(mouseEvent.clientY / window.innerHeight) * 2 + 1
@ -414,7 +416,7 @@ export class SceneInfra {
planeIntersectPoint.twoD && planeIntersectPoint.twoD &&
planeIntersectPoint.threeD planeIntersectPoint.threeD
) { ) {
this.onDragCallback({ await this.onDragCallback({
mouseEvent, mouseEvent,
intersectionPoint: { intersectionPoint: {
twoD: planeIntersectPoint.twoD, twoD: planeIntersectPoint.twoD,
@ -433,7 +435,7 @@ export class SceneInfra {
planeIntersectPoint.twoD && planeIntersectPoint.twoD &&
planeIntersectPoint.threeD planeIntersectPoint.threeD
) { ) {
this.onMoveCallback({ await this.onMoveCallback({
mouseEvent, mouseEvent,
intersectionPoint: { intersectionPoint: {
twoD: planeIntersectPoint.twoD, twoD: planeIntersectPoint.twoD,
@ -448,12 +450,12 @@ export class SceneInfra {
if (this.hoveredObject !== firstIntersectObject) { if (this.hoveredObject !== firstIntersectObject) {
const hoveredObj = this.hoveredObject const hoveredObj = this.hoveredObject
this.hoveredObject = null this.hoveredObject = null
this.onMouseLeave({ await this.onMouseLeave({
selected: hoveredObj, selected: hoveredObj,
mouseEvent: mouseEvent, mouseEvent: mouseEvent,
}) })
this.hoveredObject = firstIntersectObject this.hoveredObject = firstIntersectObject
this.onMouseEnter({ await this.onMouseEnter({
selected: this.hoveredObject, selected: this.hoveredObject,
dragSelected: this.selected?.object, dragSelected: this.selected?.object,
mouseEvent: mouseEvent, mouseEvent: mouseEvent,
@ -468,7 +470,7 @@ export class SceneInfra {
if (this.hoveredObject) { if (this.hoveredObject) {
const hoveredObj = this.hoveredObject const hoveredObj = this.hoveredObject
this.hoveredObject = null this.hoveredObject = null
this.onMouseLeave({ await this.onMouseLeave({
selected: hoveredObj, selected: hoveredObj,
dragSelected: this.selected?.object, dragSelected: this.selected?.object,
mouseEvent: mouseEvent, mouseEvent: mouseEvent,
@ -555,7 +557,7 @@ export class SceneInfra {
} }
} }
onMouseUp = (mouseEvent: MouseEvent) => { onMouseUp = async (mouseEvent: MouseEvent) => {
this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1 this.currentMouseVector.x = (mouseEvent.clientX / window.innerWidth) * 2 - 1
this.currentMouseVector.y = this.currentMouseVector.y =
-(mouseEvent.clientY / window.innerHeight) * 2 + 1 -(mouseEvent.clientY / window.innerHeight) * 2 + 1
@ -565,7 +567,7 @@ export class SceneInfra {
if (this.selected) { if (this.selected) {
if (this.selected.hasBeenDragged) { if (this.selected.hasBeenDragged) {
// TODO do the types properly here // TODO do the types properly here
this.onDragEndCallback({ await this.onDragEndCallback({
intersectionPoint: { intersectionPoint: {
twoD: planeIntersectPoint?.twoD as any, twoD: planeIntersectPoint?.twoD as any,
threeD: planeIntersectPoint?.threeD as any, threeD: planeIntersectPoint?.threeD as any,
@ -586,7 +588,7 @@ export class SceneInfra {
} }
} else if (planeIntersectPoint?.twoD && planeIntersectPoint?.threeD) { } else if (planeIntersectPoint?.twoD && planeIntersectPoint?.threeD) {
// fire onClick event as there was no drags // fire onClick event as there was no drags
this.onClickCallback({ await this.onClickCallback({
mouseEvent, mouseEvent,
intersectionPoint: { intersectionPoint: {
twoD: planeIntersectPoint.twoD, twoD: planeIntersectPoint.twoD,
@ -596,17 +598,17 @@ export class SceneInfra {
selected: this.selected.object, selected: this.selected.object,
}) })
} else if (planeIntersectPoint) { } else if (planeIntersectPoint) {
this.onClickCallback({ await this.onClickCallback({
mouseEvent, mouseEvent,
intersects, intersects,
}) })
} else { } else {
this.onClickCallback({ mouseEvent, intersects }) await this.onClickCallback({ mouseEvent, intersects })
} }
// Clear the selected state whether it was dragged or not // Clear the selected state whether it was dragged or not
this.selected = null this.selected = null
} else if (planeIntersectPoint?.twoD && planeIntersectPoint?.threeD) { } else if (planeIntersectPoint?.twoD && planeIntersectPoint?.threeD) {
this.onClickCallback({ await this.onClickCallback({
mouseEvent, mouseEvent,
intersectionPoint: { intersectionPoint: {
twoD: planeIntersectPoint.twoD, twoD: planeIntersectPoint.twoD,
@ -615,7 +617,7 @@ export class SceneInfra {
intersects, intersects,
}) })
} else { } else {
this.onClickCallback({ mouseEvent, intersects }) await this.onClickCallback({ mouseEvent, intersects })
} }
} }
updateOtherSelectionColors = (otherSelections: Axis[]) => { updateOtherSelectionColors = (otherSelections: Axis[]) => {

View File

@ -24,6 +24,10 @@ import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js
import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer' import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm' import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
import { import {
CIRCLE_CENTER_HANDLE,
CIRCLE_SEGMENT,
CIRCLE_SEGMENT_BODY,
CIRCLE_SEGMENT_DASH,
EXTRA_SEGMENT_HANDLE, EXTRA_SEGMENT_HANDLE,
EXTRA_SEGMENT_OFFSET_PX, EXTRA_SEGMENT_OFFSET_PX,
HIDE_HOVER_SEGMENT_LENGTH, HIDE_HOVER_SEGMENT_LENGTH,
@ -477,6 +481,169 @@ 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,
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({ export function createProfileStartHandle({
from, from,
id, id,
@ -535,6 +702,28 @@ function createArrowhead(scale = 1, theme: Themes, color?: number): Group {
arrowGroup.scale.set(scale, scale, scale) arrowGroup.scale.set(scale, scale, scale)
return arrowGroup 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( function createExtraSegmentHandle(
scale: number, scale: number,
@ -788,4 +977,5 @@ export function dashedStraight(
export const segmentUtils = { export const segmentUtils = {
straight: new StraightSegment(), straight: new StraightSegment(),
tangentialArcTo: new TangentialArcToSegment(), tangentialArcTo: new TangentialArcToSegment(),
circle: new CircleSegment(),
} as const } as const

View File

@ -6,8 +6,8 @@ import { CommandArgument, CommandArgumentOption } from 'lib/commandTypes'
import { useEffect, useMemo, useRef, useState } from 'react' import { useEffect, useMemo, useRef, useState } from 'react'
import { AnyStateMachine, StateFrom } from 'xstate' import { AnyStateMachine, StateFrom } from 'xstate'
const contextSelector = (snapshot: StateFrom<AnyStateMachine>) => const contextSelector = (snapshot: StateFrom<AnyStateMachine> | undefined) =>
snapshot.context snapshot?.context
function CommandArgOptionInput({ function CommandArgOptionInput({
arg, arg,

View File

@ -33,11 +33,15 @@ function CommandBarBasicInput({
return ( return (
<form id="arg-form" onSubmit={handleSubmit}> <form id="arg-form" onSubmit={handleSubmit}>
<label className="flex items-center mx-4 my-4"> <label
data-testid="cmd-bar-arg-name"
className="flex items-center mx-4 my-4"
>
<span className="capitalize px-2 py-1 rounded-l bg-chalkboard-100 dark:bg-chalkboard-80 text-chalkboard-10 border-b border-b-chalkboard-100 dark:border-b-chalkboard-80"> <span className="capitalize px-2 py-1 rounded-l bg-chalkboard-100 dark:bg-chalkboard-80 text-chalkboard-10 border-b border-b-chalkboard-100 dark:border-b-chalkboard-80">
{arg.name} {arg.name}
</span> </span>
<input <input
data-testid="cmd-bar-arg-value"
id="arg-form" id="arg-form"
name={arg.inputType} name={arg.inputType}
ref={inputRef} ref={inputRef}

View File

@ -74,7 +74,9 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
selectedCommand.icon && ( selectedCommand.icon && (
<CustomIcon name={selectedCommand.icon} className="w-5 h-5" /> <CustomIcon name={selectedCommand.icon} className="w-5 h-5" />
)} )}
{selectedCommand.displayName || selectedCommand.name} <span data-testid="command-name">
{selectedCommand.displayName || selectedCommand.name}
</span>
</p> </p>
{Object.entries(selectedCommand?.args || {}) {Object.entries(selectedCommand?.args || {})
.filter(([_, argConfig]) => .filter(([_, argConfig]) =>
@ -92,6 +94,10 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
return ( return (
<button <button
data-testid="cmd-bar-input-tab"
data-is-current-arg={
argName === currentArgument?.name ? 'true' : 'false'
}
disabled={!isReviewing && currentArgument?.name === argName} disabled={!isReviewing && currentArgument?.name === argName}
onClick={() => { onClick={() => {
commandBarSend({ commandBarSend({
@ -110,29 +116,38 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
> >
<span <span
data-testid={`arg-name-${argName.toLowerCase()}`} data-testid={`arg-name-${argName.toLowerCase()}`}
data-test-name="arg-name"
className="capitalize" className="capitalize"
> >
{argName} {argName}
</span> </span>
<span className="sr-only">:&nbsp;</span> <span className="sr-only">:&nbsp;</span>
{argValue ? ( <span data-testid="header-arg-value">
arg.inputType === 'selection' ? ( {argValue ? (
getSelectionTypeDisplayText(argValue as Selections) arg.inputType === 'selection' ? (
) : arg.inputType === 'kcl' ? ( getSelectionTypeDisplayText(argValue as Selections)
roundOff( ) : arg.inputType === 'kcl' ? (
Number((argValue as KclCommandValue).valueCalculated), roundOff(
4 Number(
) (argValue as KclCommandValue).valueCalculated
) : typeof argValue === 'object' ? ( ),
arg.valueSummary ? ( 4
arg.valueSummary(argValue) )
) : typeof argValue === 'object' ? (
arg.valueSummary ? (
arg.valueSummary(argValue)
) : (
JSON.stringify(argValue)
)
) : ( ) : (
JSON.stringify(argValue) <em>
{arg.valueSummary
? arg.valueSummary(argValue)
: argValue}
</em>
) )
) : ( ) : null}
<em>{argValue}</em> </span>
)
) : null}
{showShortcuts && ( {showShortcuts && (
<small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-primary dark:text-chalkboard-100"> <small className="absolute -top-[1px] right-full translate-x-1/2 px-0.5 rounded-sm bg-chalkboard-80 text-chalkboard-10 dark:bg-primary dark:text-chalkboard-100">
<span className="sr-only">Hotkey: </span> <span className="sr-only">Hotkey: </span>

View File

@ -139,10 +139,17 @@ function CommandBarKclInput({
return ( return (
<form id="arg-form" onSubmit={handleSubmit} data-can-submit={canSubmit}> <form id="arg-form" onSubmit={handleSubmit} data-can-submit={canSubmit}>
<label className="flex gap-4 items-center mx-4 my-4 border-solid border-b border-chalkboard-50"> <label className="flex gap-4 items-center mx-4 my-4 border-solid border-b border-chalkboard-50">
<span className="capitalize text-chalkboard-80 dark:text-chalkboard-20"> <span
data-testid="cmd-bar-arg-name"
className="capitalize text-chalkboard-80 dark:text-chalkboard-20"
>
{arg.name} {arg.name}
</span> </span>
<div ref={editorRef} className={styles.editor} /> <div
data-testid="cmd-bar-arg-value"
ref={editorRef}
className={styles.editor}
/>
<CustomIcon <CustomIcon
name="equal" name="equal"
className="w-5 h-5 text-chalkboard-70 dark:text-chalkboard-40" className="w-5 h-5 text-chalkboard-70 dark:text-chalkboard-40"

View File

@ -58,7 +58,17 @@ function CommandBarReview({ stepBack }: { stepBack: () => void }) {
return ( return (
<CommandBarHeader> <CommandBarHeader>
<p className="px-4">Confirm {selectedCommand?.name}</p> <p className="px-4">
{selectedCommand?.reviewMessage ? (
selectedCommand.reviewMessage instanceof Function ? (
selectedCommand.reviewMessage(commandBarState.context)
) : (
selectedCommand.reviewMessage
)
) : (
<>Confirm {selectedCommand?.name}</>
)}
</p>
<form <form
id="review-form" id="review-form"
className="absolute opacity-0 inset-0 pointer-events-none" className="absolute opacity-0 inset-0 pointer-events-none"

View File

@ -31,8 +31,8 @@ function getSemanticSelectionType(selectionType: Array<Selection['type']>) {
return Array.from(semanticSelectionType) return Array.from(semanticSelectionType)
} }
const selectionSelector = (snapshot: StateFrom<typeof modelingMachine>) => const selectionSelector = (snapshot?: StateFrom<typeof modelingMachine>) =>
snapshot.context.selectionRanges snapshot?.context.selectionRanges
function CommandBarSelectionInput({ function CommandBarSelectionInput({
arg, arg,
@ -49,7 +49,7 @@ function CommandBarSelectionInput({
const [hasSubmitted, setHasSubmitted] = useState(false) const [hasSubmitted, setHasSubmitted] = useState(false)
const selection = useSelector(arg.machineActor, selectionSelector) const selection = useSelector(arg.machineActor, selectionSelector)
const selectionsByType = useMemo(() => { const selectionsByType = useMemo(() => {
const selectionRangeEnd = selection.codeBasedSelections[0]?.range[1] const selectionRangeEnd = selection?.codeBasedSelections[0]?.range[1]
return !selectionRangeEnd || selectionRangeEnd === code.length return !selectionRangeEnd || selectionRangeEnd === code.length
? 'none' ? 'none'
: getSelectionType(selection) : getSelectionType(selection)

View File

@ -36,10 +36,14 @@ function CommandBarTextareaInput({
return ( return (
<form id="arg-form" onSubmit={handleSubmit} ref={formRef}> <form id="arg-form" onSubmit={handleSubmit} ref={formRef}>
<label className="flex items-start rounded mx-4 my-4 border border-chalkboard-100 dark:border-chalkboard-80"> <label className="flex items-start rounded mx-4 my-4 border border-chalkboard-100 dark:border-chalkboard-80">
<span className="capitalize px-2 py-1 rounded-br bg-chalkboard-100 dark:bg-chalkboard-80 text-chalkboard-10 border-b border-b-chalkboard-100 dark:border-b-chalkboard-80"> <span
data-testid="cmd-bar-arg-name"
className="capitalize px-2 py-1 rounded-br bg-chalkboard-100 dark:bg-chalkboard-80 text-chalkboard-10 border-b border-b-chalkboard-100 dark:border-b-chalkboard-80"
>
{arg.name} {arg.name}
</span> </span>
<textarea <textarea
data-testid="cmd-bar-arg-value"
id="arg-form" id="arg-form"
name={arg.inputType} name={arg.inputType}
ref={inputRef} ref={inputRef}

View File

@ -0,0 +1,16 @@
interface CommandBarOverwriteWarningProps {
heading?: string
message?: string
}
export function CommandBarOverwriteWarning({
heading = 'Overwrite current file?',
message = 'This will permanently replace the current code in the editor.',
}: CommandBarOverwriteWarningProps) {
return (
<>
<p className="font-bold text-destroy-60">{heading}</p>
<p>{message}</p>
</>
)
}

View File

@ -2,7 +2,7 @@ import { useMachine } from '@xstate/react'
import { useNavigate, useRouteLoaderData } from 'react-router-dom' import { useNavigate, useRouteLoaderData } from 'react-router-dom'
import { type IndexLoaderData } from 'lib/types' import { type IndexLoaderData } from 'lib/types'
import { PATHS } from 'lib/paths' import { PATHS } from 'lib/paths'
import React, { createContext } from 'react' import React, { createContext, useEffect, useMemo } from 'react'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { import {
Actor, Actor,
@ -22,6 +22,12 @@ import {
} from 'lib/constants' } from 'lib/constants'
import { getProjectInfo } from 'lib/desktop' import { getProjectInfo } from 'lib/desktop'
import { getNextDirName, getNextFileName } from 'lib/desktopFS' import { getNextDirName, getNextFileName } from 'lib/desktopFS'
import { kclCommands } from 'lib/kclCommands'
import { codeManager, kclManager } from 'lib/singletons'
import {
getKclSamplesManifest,
KclSamplesManifestItem,
} from 'lib/getKclSamplesManifest'
type MachineContext<T extends AnyStateMachine> = { type MachineContext<T extends AnyStateMachine> = {
state: StateFrom<T> state: StateFrom<T>
@ -41,6 +47,16 @@ export const FileMachineProvider = ({
const navigate = useNavigate() const navigate = useNavigate()
const { commandBarSend } = useCommandsContext() const { commandBarSend } = useCommandsContext()
const { project, file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData const { project, file } = useRouteLoaderData(PATHS.FILE) as IndexLoaderData
const [kclSamples, setKclSamples] = React.useState<KclSamplesManifestItem[]>(
[]
)
useEffect(() => {
async function fetchKclSamples() {
setKclSamples(await getKclSamplesManifest())
}
fetchKclSamples().catch(reportError)
}, [])
const [state, send] = useMachine( const [state, send] = useMachine(
fileMachine.provide({ fileMachine.provide({
@ -121,6 +137,7 @@ export const FileMachineProvider = ({
return { return {
message: `Successfully created "${createdName}"`, message: `Successfully created "${createdName}"`,
path: createdPath, path: createdPath,
shouldSetToRename: input.shouldSetToRename,
} }
}), }),
createFile: fromPromise(async ({ input }) => { createFile: fromPromise(async ({ input }) => {
@ -271,6 +288,46 @@ export const FileMachineProvider = ({
} }
) )
const kclCommandMemo = useMemo(
() =>
kclCommands(
async (data) => {
if (data.method === 'overwrite') {
codeManager.updateCodeStateEditor(data.code)
await kclManager.executeCode(true)
await codeManager.writeToFile()
} else if (data.method === 'newFile' && isDesktop()) {
send({
type: 'Create file',
data: {
name: data.sampleName,
content: data.code,
makeDir: false,
},
})
}
},
kclSamples.map((sample) => ({
value: sample.file,
name: sample.title,
}))
).filter(
(command) => kclSamples.length || command.name !== 'open-kcl-example'
),
[codeManager, kclManager, send, kclSamples]
)
useEffect(() => {
commandBarSend({ type: 'Add commands', data: { commands: kclCommandMemo } })
return () => {
commandBarSend({
type: 'Remove commands',
data: { commands: kclCommandMemo },
})
}
}, [commandBarSend, kclCommandMemo])
return ( return (
<FileContext.Provider <FileContext.Provider
value={{ value={{

View File

@ -393,14 +393,14 @@ export const FileTreeMenu = () => {
function createFile() { function createFile() {
send({ send({
type: 'Create file', type: 'Create file',
data: { name: '', makeDir: false }, data: { name: '', makeDir: false, shouldSetToRename: true },
}) })
} }
function createFolder() { function createFolder() {
send({ send({
type: 'Create file', type: 'Create file',
data: { name: '', makeDir: true }, data: { name: '', makeDir: true, shouldSetToRename: true },
}) })
} }

View File

@ -41,7 +41,7 @@ import {
canSweepSelection, canSweepSelection,
handleSelectionBatch, handleSelectionBatch,
isSelectionLastLine, isSelectionLastLine,
isRangeInbetweenCharacters, isRangeBetweenCharacters,
isSketchPipe, isSketchPipe,
updateSelections, updateSelections,
} from 'lib/selections' } from 'lib/selections'
@ -50,8 +50,7 @@ import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
import useStateMachineCommands from 'hooks/useStateMachineCommands' import useStateMachineCommands from 'hooks/useStateMachineCommands'
import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig' import { modelingMachineCommandConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
import { import {
STRAIGHT_SEGMENT, SEGMENT_BODIES,
TANGENTIAL_ARC_TO_SEGMENT,
getParentGroup, getParentGroup,
getSketchOrientationDetails, getSketchOrientationDetails,
} from 'clientSideScene/sceneEntities' } from 'clientSideScene/sceneEntities'
@ -168,10 +167,7 @@ export const ModelingMachineProvider = ({
if (event.type !== 'Set mouse state') return {} if (event.type !== 'Set mouse state') return {}
const nextSegmentHoverMap = () => { const nextSegmentHoverMap = () => {
if (event.data.type === 'isHovering') { if (event.data.type === 'isHovering') {
const parent = getParentGroup(event.data.on, [ const parent = getParentGroup(event.data.on, SEGMENT_BODIES)
STRAIGHT_SEGMENT,
TANGENTIAL_ARC_TO_SEGMENT,
])
const pathToNode = parent?.userData?.pathToNode const pathToNode = parent?.userData?.pathToNode
const pathToNodeString = JSON.stringify(pathToNode) const pathToNodeString = JSON.stringify(pathToNode)
if (!parent || !pathToNode) return context.segmentHoverMap if (!parent || !pathToNode) return context.segmentHoverMap
@ -187,10 +183,10 @@ export const ModelingMachineProvider = ({
event.data.type === 'idle' && event.data.type === 'idle' &&
context.mouseState.type === 'isHovering' context.mouseState.type === 'isHovering'
) { ) {
const mouseOnParent = getParentGroup(context.mouseState.on, [ const mouseOnParent = getParentGroup(
STRAIGHT_SEGMENT, context.mouseState.on,
TANGENTIAL_ARC_TO_SEGMENT, SEGMENT_BODIES
]) )
if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode) if (!mouseOnParent || !mouseOnParent?.userData?.pathToNode)
return context.segmentHoverMap return context.segmentHoverMap
const pathToNodeString = JSON.stringify( const pathToNodeString = JSON.stringify(
@ -204,8 +200,8 @@ export const ModelingMachineProvider = ({
pathToNodeString, pathToNodeString,
}, },
}) })
// overlay timeout // overlay timeout is 1s
}, 800) as unknown as number }, 1000) as unknown as number
return { return {
...context.segmentHoverMap, ...context.segmentHoverMap,
[pathToNodeString]: timeoutId, [pathToNodeString]: timeoutId,
@ -495,43 +491,21 @@ export const ModelingMachineProvider = ({
}, },
}, },
guards: { guards: {
'has valid extrude selection': ({ context: { selectionRanges } }) => { 'has valid sweep selection': ({ context: { selectionRanges } }) => {
// A user can begin extruding if they either have 1+ faces selected or nothing selected // A user can begin extruding if they either have 1+ faces selected or nothing selected
// TODO: I believe this guard only allows for extruding a single face at a time // TODO: I believe this guard only allows for extruding a single face at a time
const isPipe = isSketchPipe(selectionRanges) const hasNoSelection =
if (
selectionRanges.codeBasedSelections.length === 0 || selectionRanges.codeBasedSelections.length === 0 ||
isRangeInbetweenCharacters(selectionRanges) || isRangeBetweenCharacters(selectionRanges) ||
isSelectionLastLine(selectionRanges, codeManager.code) isSelectionLastLine(selectionRanges, codeManager.code)
) {
if (hasNoSelection) {
// they have no selection, we should enable the button // they have no selection, we should enable the button
// so they can select the face through the cmdbar // so they can select the face through the cmdbar
// BUT only if there's extrudable geometry // BUT only if there's extrudable geometry
if (doesSceneHaveSweepableSketch(kclManager.ast)) return true return doesSceneHaveSweepableSketch(kclManager.ast)
return false
} }
if (!isPipe) return false if (!isSketchPipe(selectionRanges)) return false
return canSweepSelection(selectionRanges)
},
'has valid revolve selection': ({ context: { selectionRanges } }) => {
// A user can begin extruding if they either have 1+ faces selected or nothing selected
// TODO: I believe this guard only allows for extruding a single face at a time
const isPipe = isSketchPipe(selectionRanges)
if (
selectionRanges.codeBasedSelections.length === 0 ||
isRangeInbetweenCharacters(selectionRanges) ||
isSelectionLastLine(selectionRanges, codeManager.code)
) {
// they have no selection, we should enable the button
// so they can select the face through the cmdbar
// BUT only if there's extrudable geometry
if (doesSceneHaveSweepableSketch(kclManager.ast)) return true
return false
}
if (!isPipe) return false
return canSweepSelection(selectionRanges) return canSweepSelection(selectionRanges)
}, },

View File

@ -9,10 +9,12 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { kclManager } from 'lib/singletons' import { kclManager } from 'lib/singletons'
import { openExternalBrowserIfDesktop } from 'lib/openWindow' import { openExternalBrowserIfDesktop } from 'lib/openWindow'
import { reportRejection } from 'lib/trap' import { reportRejection } from 'lib/trap'
import { useCommandsContext } from 'hooks/useCommandsContext'
export const KclEditorMenu = ({ children }: PropsWithChildren) => { export const KclEditorMenu = ({ children }: PropsWithChildren) => {
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } = const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
useConvertToVariable() useConvertToVariable()
const { commandBarSend } = useCommandsContext()
return ( return (
<Menu> <Menu>
@ -77,6 +79,22 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
</small> </small>
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item>
<button
onClick={() => {
commandBarSend({
type: 'Find and select command',
data: {
groupId: 'code',
name: 'open-kcl-example',
},
})
}}
className={styles.button}
>
<span>Load a sample model</span>
</button>
</Menu.Item>
<Menu.Item> <Menu.Item>
<a <a
className={styles.button} className={styles.button}
@ -85,7 +103,7 @@ export const KclEditorMenu = ({ children }: PropsWithChildren) => {
rel="noopener noreferrer" rel="noopener noreferrer"
onClick={openExternalBrowserIfDesktop()} onClick={openExternalBrowserIfDesktop()}
> >
<span>KCL samples</span> <span>View all samples</span>
<small> <small>
zoo.dev zoo.dev
<FontAwesomeIcon <FontAwesomeIcon

View File

@ -3,8 +3,6 @@ import { createContext, useContext, useEffect, useState } from 'react'
import { type IndexLoaderData } from 'lib/types' import { type IndexLoaderData } from 'lib/types'
import { useLoaderData } from 'react-router-dom' import { useLoaderData } from 'react-router-dom'
import { codeManager, kclManager } from 'lib/singletons' import { codeManager, kclManager } from 'lib/singletons'
import { useCommandsContext } from 'hooks/useCommandsContext'
import { Command } from 'lib/commandTypes'
const KclContext = createContext({ const KclContext = createContext({
code: codeManager?.code || '', code: codeManager?.code || '',
@ -37,7 +35,6 @@ export function KclContextProvider({
const [errors, setErrors] = useState<KCLError[]>([]) const [errors, setErrors] = useState<KCLError[]>([])
const [logs, setLogs] = useState<string[]>([]) const [logs, setLogs] = useState<string[]>([])
const [wasmInitFailed, setWasmInitFailed] = useState(false) const [wasmInitFailed, setWasmInitFailed] = useState(false)
const { commandBarSend } = useCommandsContext()
useEffect(() => { useEffect(() => {
codeManager.registerCallBacks({ codeManager.registerCallBacks({
@ -53,28 +50,6 @@ export function KclContextProvider({
}) })
}, []) }, [])
// Add format code to command palette.
useEffect(() => {
const commands: Command[] = [
{
name: 'format-code',
displayName: 'Format Code',
description: 'Nicely formats the KCL code in the editor.',
needsReview: false,
groupId: 'code',
icon: 'code',
onSubmit: (data) => {
kclManager.format()
},
},
]
commandBarSend({ type: 'Add commands', data: { commands } })
return () => {
commandBarSend({ type: 'Remove commands', data: { commands } })
}
}, [kclManager, commandBarSend])
return ( return (
<KclContext.Provider <KclContext.Provider
value={{ value={{

View File

@ -416,7 +416,7 @@ export class KclManager {
ast: Program, ast: Program,
execute: boolean, execute: boolean,
optionalParams?: { optionalParams?: {
focusPath?: PathToNode focusPath?: Array<PathToNode>
zoomToFit?: boolean zoomToFit?: boolean
zoomOnRangeAndType?: { zoomOnRangeAndType?: {
range: SourceRange range: SourceRange
@ -435,27 +435,34 @@ export class KclManager {
let returnVal: Selections | undefined = undefined let returnVal: Selections | undefined = undefined
if (optionalParams?.focusPath) { if (optionalParams?.focusPath) {
const _node1 = getNodeFromPath<any>(
astWithUpdatedSource,
optionalParams?.focusPath
)
if (err(_node1)) return Promise.reject(_node1)
const { node } = _node1
const { start, end } = node
if (!start || !end)
return {
selections: undefined,
newAst: astWithUpdatedSource,
}
returnVal = { returnVal = {
codeBasedSelections: [ codeBasedSelections: [],
{ otherSelections: [],
}
for (const path of optionalParams.focusPath) {
const getNodeFromPathResult = getNodeFromPath<any>(
astWithUpdatedSource,
path
)
if (err(getNodeFromPathResult))
return Promise.reject(getNodeFromPathResult)
const { node } = getNodeFromPathResult
const { start, end } = node
if (!start || !end)
return {
selections: undefined,
newAst: astWithUpdatedSource,
}
if (start && end) {
returnVal.codeBasedSelections.push({
type: 'default', type: 'default',
range: [start, end], range: [start, end],
}, })
], }
otherSelections: [],
} }
} }

View File

@ -24,8 +24,9 @@ export type ToolTip =
| 'yLineTo' | 'yLineTo'
| 'angledLineThatIntersects' | 'angledLineThatIntersects'
| 'tangentialArcTo' | 'tangentialArcTo'
| 'circle'
export const toolTips = [ export const toolTips: Array<ToolTip> = [
'line', 'line',
'lineTo', 'lineTo',
'angledLine', 'angledLine',

View File

@ -65,7 +65,7 @@ export function modifyAstWithFilletAndTag(
ast: Program, ast: Program,
selection: Selections, selection: Selections,
radius: KclCommandValue radius: KclCommandValue
): { modifiedAst: Program; pathToFilletNode: PathToNode } | Error { ): { modifiedAst: Program; pathToFilletNode: Array<PathToNode> } | Error {
const astResult = insertRadiusIntoAst(ast, radius) const astResult = insertRadiusIntoAst(ast, radius)
if (err(astResult)) return astResult if (err(astResult)) return astResult
@ -73,7 +73,8 @@ export function modifyAstWithFilletAndTag(
const artifactGraph = engineCommandManager.artifactGraph const artifactGraph = engineCommandManager.artifactGraph
let clonedAst = structuredClone(ast) let clonedAst = structuredClone(ast)
let lastPathToFilletNode: PathToNode = [] const clonedAstForGetExtrude = structuredClone(ast)
let pathToFilletNodes: Array<PathToNode> = []
for (const selectionRange of selection.codeBasedSelections) { for (const selectionRange of selection.codeBasedSelections) {
const singleSelection = { const singleSelection = {
@ -82,7 +83,7 @@ export function modifyAstWithFilletAndTag(
} }
const getPathToExtrudeForSegmentSelectionResult = const getPathToExtrudeForSegmentSelectionResult =
getPathToExtrudeForSegmentSelection( getPathToExtrudeForSegmentSelection(
clonedAst, clonedAstForGetExtrude,
singleSelection, singleSelection,
programMemory, programMemory,
artifactGraph artifactGraph
@ -101,9 +102,9 @@ export function modifyAstWithFilletAndTag(
if (trap(addFilletResult)) return addFilletResult if (trap(addFilletResult)) return addFilletResult
const { modifiedAst, pathToFilletNode } = addFilletResult const { modifiedAst, pathToFilletNode } = addFilletResult
clonedAst = modifiedAst clonedAst = modifiedAst
lastPathToFilletNode = pathToFilletNode pathToFilletNodes.push(pathToFilletNode)
} }
return { modifiedAst: clonedAst, pathToFilletNode: lastPathToFilletNode } return { modifiedAst: clonedAst, pathToFilletNode: pathToFilletNodes }
} }
function insertRadiusIntoAst( function insertRadiusIntoAst(
@ -166,7 +167,7 @@ export function getPathToExtrudeForSegmentSelection(
async function updateAstAndFocus( async function updateAstAndFocus(
modifiedAst: Program, modifiedAst: Program,
pathToFilletNode: PathToNode pathToFilletNode: Array<PathToNode>
) { ) {
const updatedAst = await kclManager.updateAst(modifiedAst, true, { const updatedAst = await kclManager.updateAst(modifiedAst, true, {
focusPath: pathToFilletNode, focusPath: pathToFilletNode,

View File

@ -903,6 +903,7 @@ export function doesSceneHaveSweepableSketch(ast: Program) {
let hasStartProfileAt = false let hasStartProfileAt = false
let hasStartSketchOn = false let hasStartSketchOn = false
let hasClose = false let hasClose = false
let hasCircle = false
for (const pipe of node.init.body) { for (const pipe of node.init.body) {
if ( if (
pipe.type === 'CallExpression' && pipe.type === 'CallExpression' &&
@ -919,8 +920,15 @@ export function doesSceneHaveSweepableSketch(ast: Program) {
if (pipe.type === 'CallExpression' && pipe.callee.name === 'close') { if (pipe.type === 'CallExpression' && pipe.callee.name === 'close') {
hasClose = true hasClose = true
} }
if (pipe.type === 'CallExpression' && pipe.callee.name === 'circle') {
hasCircle = true
}
} }
if (hasStartProfileAt && hasStartSketchOn && hasClose) { if (
(hasStartProfileAt || hasCircle) &&
hasStartSketchOn &&
(hasClose || hasCircle)
) {
theMap[node.id.name] = true theMap[node.id.name] = true
} }
} else if ( } else if (

View File

@ -122,7 +122,10 @@ describe('testing changeSketchArguments', () => {
const changeSketchArgsRetVal = changeSketchArguments( const changeSketchArgsRetVal = changeSketchArguments(
ast, ast,
programMemory, programMemory,
[sourceStart, sourceStart + lineToChange.length], {
type: 'sourceRange',
sourceRange: [sourceStart, sourceStart + lineToChange.length],
},
{ {
type: 'straight-segment', type: 'straight-segment',
from: [0, 0], from: [0, 0],

View File

@ -17,6 +17,7 @@ import {
getNodeFromPath, getNodeFromPath,
getNodeFromPathCurry, getNodeFromPathCurry,
getNodePathFromSourceRange, getNodePathFromSourceRange,
getObjExprProperty,
} from 'lang/queryAst' } from 'lang/queryAst'
import { import {
isLiteralArrayOrStatic, isLiteralArrayOrStatic,
@ -57,6 +58,7 @@ import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
const STRAIGHT_SEGMENT_ERR = new Error( const STRAIGHT_SEGMENT_ERR = new Error(
'Invalid input, expected "straight-segment"' 'Invalid input, expected "straight-segment"'
) )
const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"')
export type Coords2d = [number, number] 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<PipeExpression>(
_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<CallExpression>(_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(),
addTag: addTag(),
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 = { export const angledLine: SketchLineHelper = {
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => { add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
@ -1688,16 +1861,28 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
angledLineToY, angledLineToY,
angledLineThatIntersects, angledLineThatIntersects,
tangentialArcTo, tangentialArcTo,
circle,
} as const } as const
export function changeSketchArguments( export function changeSketchArguments(
node: Program, node: Program,
programMemory: ProgramMemory, programMemory: ProgramMemory,
sourceRange: SourceRange, sourceRangeOrPath:
| {
type: 'sourceRange'
sourceRange: SourceRange
}
| {
type: 'path'
pathToNode: PathToNode
},
input: SegmentInputs input: SegmentInputs
): { modifiedAst: Program; pathToNode: PathToNode } | Error { ): { modifiedAst: Program; pathToNode: PathToNode } | Error {
const _node = { ...node } const _node = { ...node }
const thePath = getNodePathFromSourceRange(_node, sourceRange) const thePath =
sourceRangeOrPath.type === 'sourceRange'
? getNodePathFromSourceRange(_node, sourceRangeOrPath.sourceRange)
: sourceRangeOrPath.pathToNode
const nodeMeta = getNodeFromPath<CallExpression>(_node, thePath) const nodeMeta = getNodeFromPath<CallExpression>(_node, thePath)
if (err(nodeMeta)) return nodeMeta if (err(nodeMeta)) return nodeMeta
@ -1875,7 +2060,7 @@ export function replaceSketchLine({
pathToNode: PathToNode pathToNode: PathToNode
} }
| Error { | Error {
if (![...toolTips, 'intersect'].includes(fnName)) { if (![...toolTips, 'intersect', 'circle'].includes(fnName)) {
return new Error(`The following function name is not tooltip: ${fnName}`) return new Error(`The following function name is not tooltip: ${fnName}`)
} }
const _node = { ...node } const _node = { ...node }
@ -2065,6 +2250,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 = ( const getAngledLineThatIntersects = (
callExp: CallExpression callExp: CallExpression
): ):
@ -2124,5 +2335,8 @@ export function getFirstArg(callExp: CallExpression):
// TODO probably needs it's own implementation // TODO probably needs it's own implementation
return getFirstArgValuesForXYFns(callExp) return getFirstArgValuesForXYFns(callExp)
} }
if (name === 'circle') {
return getCircle(callExp)
}
return new Error('unexpected call expression: ' + name) return new Error('unexpected call expression: ' + name)
} }

View File

@ -59,6 +59,7 @@ export type LineInputsType =
| 'length' | 'length'
| 'intersectionOffset' | 'intersectionOffset'
| 'intersectionTag' | 'intersectionTag'
| 'radius'
export type ConstraintType = export type ConstraintType =
| 'equalLength' | 'equalLength'
@ -89,7 +90,10 @@ function createCallWrapper(
tag?: Expr, tag?: Expr,
valueUsedInTransform?: number valueUsedInTransform?: number
): CreatedSketchExprResult { ): CreatedSketchExprResult {
const args = [createFirstArg(tooltip, val), createPipeSubstitution()] const args =
tooltip === 'circle'
? []
: [createFirstArg(tooltip, val), createPipeSubstitution()]
if (tag) { if (tag) {
args.push(tag) args.push(tag)
} }
@ -1735,11 +1739,20 @@ export function transformAstSketchLines({
pathToNode: _pathToNode, pathToNode: _pathToNode,
referencedSegment, referencedSegment,
fnName: transformTo || (callExp.node.callee.name as ToolTip), fnName: transformTo || (callExp.node.callee.name as ToolTip),
segmentInput: { segmentInput:
type: 'straight-segment', seg.type === 'Circle'
to, ? {
from, type: 'arc-segment',
}, center: seg.center,
radius: seg.radius,
from,
}
: {
type: 'straight-segment',
to,
from,
},
replaceExistingCallback: (rawArgs) => replaceExistingCallback: (rawArgs) =>
callBack({ callBack({
referenceSegName: _referencedSegmentName, referenceSegName: _referencedSegmentName,
@ -1888,6 +1901,6 @@ export function isExprBinaryPart(expr: Expr): expr is BinaryPart {
return false 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] return a.find(({ argType }) => argType === b) || a[0]
} }

View File

@ -35,13 +35,22 @@ interface StraightSegmentInput {
to: [number, number] 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. * 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). * - 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. * - 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. * Interface for adding or replacing a sketch stblib call expression to a sketch.
@ -66,7 +75,14 @@ interface updateArgs extends ModifyAstBase {
input: SegmentInputs input: SegmentInputs
} }
export type InputArgKeys = 'angle' | 'offset' | 'length' | 'to' | 'intersectTag' export type InputArgKeys =
| 'angle'
| 'offset'
| 'length'
| 'to'
| 'intersectTag'
| 'radius'
| 'center'
export interface SingleValueInput<T> { export interface SingleValueInput<T> {
type: 'singleValue' type: 'singleValue'
argType: LineInputsType argType: LineInputsType
@ -182,7 +198,12 @@ export type TransformInfo = {
export interface ConstrainInfo { export interface ConstrainInfo {
stdLibFnName: ToolTip stdLibFnName: ToolTip
type: LineInputsType | 'vertical' | 'horizontal' | 'tangentialWithPrevious' type:
| LineInputsType
| 'vertical'
| 'horizontal'
| 'tangentialWithPrevious'
| 'radius'
isConstrained: boolean isConstrained: boolean
sourceRange: SourceRange sourceRange: SourceRange
pathToNode: PathToNode pathToNode: PathToNode

View File

@ -4,6 +4,7 @@ import { Actor, AnyStateMachine, ContextFrom, EventFrom } from 'xstate'
import { Selection } from './selections' import { Selection } from './selections'
import { Identifier, Expr, VariableDeclaration } from 'lang/wasm' import { Identifier, Expr, VariableDeclaration } from 'lang/wasm'
import { commandBarMachine } from 'machines/commandBarMachine' import { commandBarMachine } from 'machines/commandBarMachine'
import { ReactNode } from 'react'
type Icon = CustomIconName type Icon = CustomIconName
const PLATFORMS = ['both', 'web', 'desktop'] as const const PLATFORMS = ['both', 'web', 'desktop'] as const
@ -67,6 +68,12 @@ export type Command<
name: CommandName name: CommandName
groupId: T['id'] groupId: T['id']
needsReview: boolean needsReview: boolean
reviewMessage?:
| string
| ReactNode
| ((
commandBarContext: { argumentsToSubmit: Record<string, unknown> } // Should be the commandbarMachine's context, but it creates a circular dependency
) => string | ReactNode)
onSubmit: (data?: CommandSchema) => void onSubmit: (data?: CommandSchema) => void
onCancel?: () => void onCancel?: () => void
args?: { args?: {
@ -181,7 +188,7 @@ export type CommandArgument<
machineContext?: ContextFrom<T> machineContext?: ContextFrom<T>
) => boolean) ) => boolean)
skip?: boolean skip?: boolean
machineActor: Actor<T> machineActor?: Actor<T>
/** For showing a summary display of the current value, such as in /** For showing a summary display of the current value, such as in
* the command bar's header * the command bar's header
*/ */

View File

@ -95,3 +95,10 @@ export const MAKE_TOAST_MESSAGES = {
ERROR_STARTING_PRINT: 'Error while starting print', ERROR_STARTING_PRINT: 'Error while starting print',
SUCCESS: 'Started print successfully', SUCCESS: 'Started print successfully',
} }
/** The URL for the KCL samples manifest files */
export const KCL_SAMPLES_MANIFEST_URLS = {
remote:
'https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifst.json',
localFallback: '/kcl-samples-manifest-fallback.json',
} as const

View File

@ -1,6 +1,7 @@
export const bracket = `// Shelf Bracket export const bracket = `// Shelf Bracket
// This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided. // This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided.
// Define constants // Define constants
const sigmaAllow = 35000 // psi (6061-T6 aluminum) const sigmaAllow = 35000 // psi (6061-T6 aluminum)
const width = 6 // inch const width = 6 // inch
@ -11,50 +12,65 @@ const wallMountL = 2 // inches
const shelfDepth = 12 // Shelf is 12 inches in depth from the wall const shelfDepth = 12 // Shelf is 12 inches in depth from the wall
const moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in) const moment = shelfDepth * p // assume the force is applied at the end of the shelf to be conservative (lb-in)
const filletRadius = .375 // inches const filletRadius = .375 // inches
const extFilletRadius = .25 // inches const extFilletRadius = .25 // inches
const mountingHoleDiameter = 0.5 // inches const mountingHoleDiameter = 0.5 // inches
// Calculate required thickness of bracket // Calculate required thickness of bracket
const thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches) const thickness = sqrt(moment * factorOfSafety * 6 / (sigmaAllow * width)) // this is the calculation of two brackets holding up the shelf (inches)
// Sketch the bracket body and fillet the inner and outer edges of the bend // Sketch the bracket body and fillet the inner and outer edges of the bend
const bracketLeg1Sketch = startSketchOn('XY') const bracketLeg1Sketch = startSketchOn('XY')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([shelfMountL-filletRadius, 0], %, $fillet1) |> line([shelfMountL - filletRadius, 0], %, $fillet1)
|> line([0, width], %, $fillet2) |> line([0, width], %, $fillet2)
|> line([-shelfMountL + filletRadius, 0], %) |> line([-shelfMountL + filletRadius, 0], %)
|> close(%) |> close(%)
|> hole(circle([1, 1], mountingHoleDiameter/2, %), %) |> hole(circle({
|> hole(circle([shelfMountL-1.5, width-1], mountingHoleDiameter/2, %), %) center: [1, 1],
|> hole(circle([1, width-1], mountingHoleDiameter/2, %), %) radius: mountingHoleDiameter / 2
|> hole(circle([shelfMountL-1.5, 1], mountingHoleDiameter/2, %), %) }, %), %)
|> hole(circle({
center: [shelfMountL - 1.5, width - 1],
radius: mountingHoleDiameter / 2
}, %), %)
|> hole(circle({
center: [1, width - 1],
radius: mountingHoleDiameter / 2
}, %), %)
|> hole(circle({
center: [shelfMountL - 1.5, 1],
radius: mountingHoleDiameter / 2
}, %), %)
// Extrude the leg 2 bracket sketch // Extrude the leg 2 bracket sketch
const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch) const bracketLeg1Extrude = extrude(thickness, bracketLeg1Sketch)
|> fillet({ |> fillet({
radius: extFilletRadius, radius: extFilletRadius,
tags: [ tags: [
getNextAdjacentEdge(fillet1), getNextAdjacentEdge(fillet1),
getNextAdjacentEdge(fillet2) getNextAdjacentEdge(fillet2)
], ]
}, %) }, %)
// Sketch the fillet arc // Sketch the fillet arc
const filletSketch = startSketchOn('XZ') const filletSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %) |> startProfileAt([0, 0], %)
|> line([0, thickness], %) |> line([0, thickness], %)
|> arc({ |> arc({
angleEnd: 180, angleEnd: 180,
angleStart: 90, angleStart: 90,
radius: filletRadius + thickness, radius: filletRadius + thickness
}, %) }, %)
|> line([thickness, 0], %) |> line([thickness, 0], %)
|> arc({ |> arc({
angleEnd: 90, angleEnd: 90,
angleStart: 180, angleStart: 180,
radius: filletRadius, radius: filletRadius
}, %) }, %)
// Sketch the bend // Sketch the bend
const filletExtrude = extrude(-width, filletSketch) const filletExtrude = extrude(-width, filletSketch)
@ -76,18 +92,25 @@ const bracketLeg2Sketch = startSketchOn(customPlane)
|> line([0, -wallMountL], %, $fillet3) |> line([0, -wallMountL], %, $fillet3)
|> line([-width, 0], %, $fillet4) |> line([-width, 0], %, $fillet4)
|> close(%) |> close(%)
|> hole(circle([1, -1.5], mountingHoleDiameter / 2, %), %) |> hole(circle({
|> hole(circle([5, -1.5], mountingHoleDiameter / 2, %), %) center: [1, -1.5],
radius: mountingHoleDiameter / 2
}, %), %)
|> hole(circle({
center: [5, -1.5],
radius: mountingHoleDiameter / 2
}, %), %)
// Extrude the second leg // Extrude the second leg
const bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch) const bracketLeg2Extrude = extrude(-thickness, bracketLeg2Sketch)
|> fillet({ |> fillet({
radius: extFilletRadius, radius: extFilletRadius,
tags: [ tags: [
getNextAdjacentEdge(fillet3), getNextAdjacentEdge(fillet3),
getNextAdjacentEdge(fillet4) getNextAdjacentEdge(fillet4)
], ]
}, %)` }, %)
`
/** /**
* @throws Error if the search text is not found in the example code. * @throws Error if the search text is not found in the example code.

View File

@ -0,0 +1,31 @@
import { KCL_SAMPLES_MANIFEST_URLS } from './constants'
import { isDesktop } from './isDesktop'
export type KclSamplesManifestItem = {
file: string
title: string
description: string
}
export async function getKclSamplesManifest() {
let response = await fetch(KCL_SAMPLES_MANIFEST_URLS.remote)
if (!response.ok) {
console.warn(
'Failed to fetch latest remote KCL samples manifest, falling back to local:',
response.statusText
)
response = await fetch(
(isDesktop() ? '.' : '') + KCL_SAMPLES_MANIFEST_URLS.localFallback
)
if (!response.ok) {
console.error(
'Failed to fetch fallback KCL samples manifest:',
response.statusText
)
return []
}
}
return response.json().then((manifest) => {
return manifest as KclSamplesManifestItem[]
})
}

110
src/lib/kclCommands.ts Normal file
View File

@ -0,0 +1,110 @@
import { CommandBarOverwriteWarning } from 'components/CommandBarOverwriteWarning'
import { Command, CommandArgumentOption } from './commandTypes'
import { kclManager } from './singletons'
import { isDesktop } from './isDesktop'
import { FILE_EXT } from './constants'
interface OnSubmitProps {
sampleName: string
code: string
method: 'overwrite' | 'newFile'
}
export function kclCommands(
onSubmit: (p: OnSubmitProps) => Promise<void>,
providedOptions: CommandArgumentOption<string>[]
): Command[] {
return [
{
name: 'format-code',
displayName: 'Format Code',
description: 'Nicely formats the KCL code in the editor.',
needsReview: false,
groupId: 'code',
icon: 'code',
onSubmit: () => {
kclManager.format()
},
},
{
name: 'open-kcl-example',
displayName: 'Open sample',
description: 'Imports an example KCL program into the editor.',
needsReview: true,
icon: 'code',
reviewMessage: ({ argumentsToSubmit }) =>
argumentsToSubmit.method === 'newFile'
? 'Create a new file with the example code?'
: CommandBarOverwriteWarning({}),
groupId: 'code',
onSubmit(data) {
if (!data?.sample) {
return
}
const sampleCodeUrl = `https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/${encodeURIComponent(
data.sample.replace(FILE_EXT, '')
)}/${encodeURIComponent(data.sample)}`
fetch(sampleCodeUrl)
.then(async (response) => {
if (!response.ok) {
console.error('Failed to fetch sample code:', response.statusText)
return
}
const code = await response.text()
return {
sampleName: data.sample,
code,
method: data.method,
}
})
.then((props) => {
if (props?.code) {
onSubmit(props).catch(reportError)
}
})
.catch(reportError)
},
args: {
method: {
inputType: 'options',
required: true,
skip: true,
defaultValue: isDesktop() ? 'newFile' : 'overwrite',
options() {
return [
{
value: 'overwrite',
name: 'Overwrite current code',
isCurrent: !isDesktop(),
},
...(isDesktop()
? [
{
value: 'newFile',
name: 'Create a new file',
isCurrent: true,
},
]
: []),
]
},
},
sample: {
inputType: 'options',
required: true,
valueSummary(value) {
const MAX_LENGTH = 12
if (typeof value === 'string') {
return value.length > MAX_LENGTH
? value.substring(0, MAX_LENGTH) + '...'
: value
}
return value
},
options: providedOptions,
},
},
},
]
}

View File

@ -0,0 +1,34 @@
[
"80-20-rail",
"a-parametric-bearing-pillow-block",
"ball-bearing",
"bracket",
"brake-caliper",
"car-wheel-assembly",
"car-wheel",
"enclosure",
"flange-with-patterns",
"flange-xy",
"focusrite-scarlett-mounting-bracket",
"french-press",
"gear-rack",
"gear",
"hex-nut",
"kitt",
"lego",
"lug-nut",
"mounting-plate",
"multi-axis-robot",
"pipe-flange-assembly",
"pipe",
"poopy-shoe",
"router-template-cross-bar",
"router-template-slate",
"screenshots",
"sheet-metal-bracket",
"socket-head-cap-screw",
"step",
"tire",
"washer",
"wheel-rotor"
]

View File

@ -374,7 +374,7 @@ export function isSelectionLastLine(
return selectionRanges.codeBasedSelections[i].range[1] === code.length return selectionRanges.codeBasedSelections[i].range[1] === code.length
} }
export function isRangeInbetweenCharacters(selectionRanges: Selections) { export function isRangeBetweenCharacters(selectionRanges: Selections) {
return ( return (
selectionRanges.codeBasedSelections.length === 1 && selectionRanges.codeBasedSelections.length === 1 &&
selectionRanges.codeBasedSelections[0].range[0] === 0 && selectionRanges.codeBasedSelections[0].range[0] === 0 &&
@ -413,6 +413,12 @@ function nodeHasClose(node: CommonASTNode) {
...node, ...node,
}) })
} }
function nodeHasCircle(node: CommonASTNode) {
return doesPipeHaveCallExp({
calleeName: 'circle',
...node,
})
}
export function canSweepSelection(selection: Selections) { export function canSweepSelection(selection: Selections) {
const commonNodes = selection.codeBasedSelections.map((_, i) => const commonNodes = selection.codeBasedSelections.map((_, i) =>
@ -421,7 +427,8 @@ export function canSweepSelection(selection: Selections) {
return ( return (
!!isSketchPipe(selection) && !!isSketchPipe(selection) &&
commonNodes.every((n) => !hasSketchPipeBeenExtruded(n.selection, n.ast)) && commonNodes.every((n) => !hasSketchPipeBeenExtruded(n.selection, n.ast)) &&
commonNodes.every((n) => nodeHasClose(n)) && (commonNodes.every((n) => nodeHasClose(n)) ||
commonNodes.every((n) => nodeHasCircle(n))) &&
commonNodes.every((n) => !nodeHasExtrude(n)) commonNodes.every((n) => !nodeHasExtrude(n))
) )
} }
@ -446,7 +453,7 @@ function canExtrudeSelectionItem(selection: Selections, i: number) {
return ( return (
!!isSketchPipe(isolatedSelection) && !!isSketchPipe(isolatedSelection) &&
nodeHasClose(commonNode) && (nodeHasClose(commonNode) || nodeHasCircle(commonNode)) &&
!nodeHasExtrude(commonNode) !nodeHasExtrude(commonNode)
) )
} }
@ -462,8 +469,9 @@ export type ResolvedSelectionType = [Selection['type'] | 'other', number]
* @returns * @returns
*/ */
export function getSelectionType( export function getSelectionType(
selection: Selections selection?: Selections
): ResolvedSelectionType[] { ): ResolvedSelectionType[] {
if (!selection) return []
const extrudableCount = selection.codeBasedSelections.filter((_, i) => { const extrudableCount = selection.codeBasedSelections.filter((_, i) => {
const singleSelection = { const singleSelection = {
...selection, ...selection,
@ -478,7 +486,7 @@ export function getSelectionType(
} }
export function getSelectionTypeDisplayText( export function getSelectionTypeDisplayText(
selection: Selections selection?: Selections
): string | null { ): string | null {
const selectionsByType = getSelectionType(selection) const selectionsByType = getSelectionType(selection)

View File

@ -2,9 +2,11 @@ import { CustomIconName } from 'components/CustomIcon'
import { DEV } from 'env' import { DEV } from 'env'
import { commandBarMachine } from 'machines/commandBarMachine' import { commandBarMachine } from 'machines/commandBarMachine'
import { import {
canRectangleTool, canRectangleOrCircleTool,
isClosedSketch,
isEditingExistingSketch, isEditingExistingSketch,
modelingMachine, modelingMachine,
pipeHasCircle,
} from 'machines/modelingMachine' } from 'machines/modelingMachine'
import { EventFrom, StateFrom } from 'xstate' import { EventFrom, StateFrom } from 'xstate'
@ -308,7 +310,11 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
state.matches('Sketch no face') || state.matches('Sketch no face') ||
state.matches({ state.matches({
Sketch: { 'Rectangle tool': 'Awaiting second corner' }, Sketch: { 'Rectangle tool': 'Awaiting second corner' },
}), }) ||
state.matches({
Sketch: { 'Circle tool': 'Awaiting Radius' },
}) ||
isClosedSketch(state.context),
title: 'Line', title: 'Line',
hotkey: (state) => hotkey: (state) =>
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L', state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
@ -331,8 +337,9 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
icon: 'arc', icon: 'arc',
status: 'available', status: 'available',
disabled: (state) => disabled: (state) =>
!isEditingExistingSketch(state.context) && (!isEditingExistingSketch(state.context) &&
!state.matches({ Sketch: 'Tangential arc to' }), !state.matches({ Sketch: 'Tangential arc to' })) ||
pipeHasCircle(state.context),
title: 'Tangential Arc', title: 'Tangential Arc',
hotkey: (state) => hotkey: (state) =>
state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A', state.matches({ Sketch: 'Tangential arc to' }) ? ['Esc', 'A'] : 'A',
@ -370,10 +377,24 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
[ [
{ {
id: 'circle-center', 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', icon: 'circle',
status: 'unavailable', status: 'available',
title: 'Center circle', title: 'Center circle',
disabled: (state) =>
!canRectangleOrCircleTool(state.context) &&
!state.matches({ Sketch: 'Circle tool' }),
isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
hotkey: (state) =>
state.matches({ Sketch: 'Circle tool' }) ? ['Esc', 'C'] : 'C',
showTitle: false, showTitle: false,
description: 'Start drawing a circle from its center', description: 'Start drawing a circle from its center',
links: [ links: [
@ -389,7 +410,6 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
console.error('Three-point circle not yet implemented'), console.error('Three-point circle not yet implemented'),
icon: 'circle', icon: 'circle',
status: 'unavailable', status: 'unavailable',
disabled: () => true,
title: 'Three-point circle', title: 'Three-point circle',
showTitle: false, showTitle: false,
description: 'Draw a circle defined by three points', description: 'Draw a circle defined by three points',
@ -411,7 +431,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
icon: 'rectangle', icon: 'rectangle',
status: 'available', status: 'available',
disabled: (state) => disabled: (state) =>
!canRectangleTool(state.context) && !canRectangleOrCircleTool(state.context) &&
!state.matches({ Sketch: 'Rectangle tool' }), !state.matches({ Sketch: 'Rectangle tool' }),
title: 'Corner rectangle', title: 'Corner rectangle',
hotkey: (state) => hotkey: (state) =>

View File

@ -283,7 +283,7 @@ export const commandBarMachine = setup({
typeof argConfig.options === 'function' typeof argConfig.options === 'function'
? argConfig.options( ? argConfig.options(
input, input,
argConfig.machineActor.getSnapshot().context argConfig.machineActor?.getSnapshot().context
) )
: argConfig.options : argConfig.options
).some((o) => o.value === argValue) ).some((o) => o.value === argValue)

Some files were not shown because too many files have changed in this diff Show More