Compare commits
69 Commits
kcl_prelud
...
v0.17.0
Author | SHA1 | Date | |
---|---|---|---|
d8cc57b843 | |||
e76db4a621 | |||
027e947bd5 | |||
0983dcca22 | |||
846fc99bbc | |||
c258ede25c | |||
4cc1b3d5ed | |||
be0dd1512d | |||
a5156c3f5d | |||
8038b5d7a3 | |||
54b234360e | |||
465d933d53 | |||
ccd0c619a6 | |||
7b570bf525 | |||
44d1c29801 | |||
0e916cfd5b | |||
e773e932b0 | |||
2d39fd32ce | |||
5a585a6c2d | |||
c09d6ee6bd | |||
09b55259ab | |||
68b61c9832 | |||
469ca94437 | |||
1d3850b46a | |||
0358343285 | |||
38b0603fa2 | |||
e48a8b6c5d | |||
73e573b251 | |||
793409d53d | |||
3e9ab16c4b | |||
ab226bc86f | |||
97677e4474 | |||
37fbc8c9ab | |||
29d61da552 | |||
ad2f669ec6 | |||
d66aad8b5d | |||
d8b8710a0d | |||
8f8ba2dca5 | |||
970b0abb54 | |||
06b464816f | |||
08f7bb2811 | |||
197df9f25d | |||
4d387dfaf7 | |||
912b97bea5 | |||
3e4ce44dc9 | |||
c2058a05fa | |||
7a57965690 | |||
c5b115ba97 | |||
90057c2dda | |||
f3e59690d6 | |||
9642a44a02 | |||
252c7651ac | |||
79edcf3826 | |||
b05ac3a05f | |||
1d01ba454b | |||
bf1d6963fe | |||
176ee63cb9 | |||
1ae8059c2b | |||
93f406d005 | |||
e97833f0ed | |||
35417dd8a6 | |||
cf0560dcfb | |||
3659946653 | |||
156c51484a | |||
dc8dd4bc72 | |||
335add67bd | |||
231794a69d | |||
8e5a6bc6fc | |||
4f82121105 |
4
.github/workflows/check-exampleKcl.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Comment on PR
|
- name: Comment on PR
|
||||||
uses: actions/github-script@v6
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
script: |
|
script: |
|
||||||
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
|
const message = '`src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
|
||||||
|
4
.github/workflows/playwright.yml
vendored
@ -9,6 +9,10 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
@ -69,6 +69,8 @@ const part001 = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -69,6 +69,8 @@ const part001 = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -76,6 +76,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -244,6 +246,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -76,6 +76,8 @@ startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -244,6 +246,8 @@ startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -77,6 +77,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -245,6 +247,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -82,6 +82,8 @@ const part001 = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -250,6 +252,8 @@ const part001 = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -76,6 +76,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -244,6 +246,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -75,6 +75,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -243,6 +245,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -86,6 +86,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -254,6 +256,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -79,6 +79,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -247,6 +249,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -71,6 +71,8 @@ const rectangle = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -135,6 +137,8 @@ const rectangle = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -302,6 +306,8 @@ const rectangle = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -72,6 +72,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -240,6 +242,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -67,6 +67,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -68,6 +68,8 @@ const square = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -236,6 +238,8 @@ const square = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -403,6 +407,8 @@ const square = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn("YZ")
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn("YZ")
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn('-XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -234,6 +236,8 @@ startSketchOn('-XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -72,6 +72,8 @@ const part = rectShape([0, 0], 20, 20)
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -240,6 +242,8 @@ const part = rectShape([0, 0], 20, 20)
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -80,6 +80,8 @@ const part = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -77,6 +77,8 @@ const part = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -69,6 +69,8 @@ const part001 = startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -67,6 +67,8 @@ startSketchOn("YZ")
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -67,6 +67,8 @@ startSketchOn("YZ")
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -67,6 +67,8 @@ startSketchOn("YZ")
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -57,6 +57,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -128,6 +130,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -65,6 +65,8 @@ startSketchAt([0, 0])
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -254,6 +254,8 @@ string
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -75,6 +75,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -243,6 +245,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -65,6 +65,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -233,6 +235,8 @@ startSketchOn('-YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -234,6 +236,8 @@ startSketchOn('YZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -234,6 +236,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -66,6 +66,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -234,6 +236,8 @@ startSketchOn('XY')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
@ -67,6 +67,8 @@ startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
@ -235,6 +237,8 @@ startSketchOn('XZ')
|
|||||||
},
|
},
|
||||||
} |
|
} |
|
||||||
{
|
{
|
||||||
|
// the face id the sketch is on
|
||||||
|
faceId: uuid,
|
||||||
// The id of the face.
|
// The id of the face.
|
||||||
id: uuid,
|
id: uuid,
|
||||||
// The original sketch group id of the object we are sketching on.
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 220 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 221 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 163 KiB |
Before Width: | Height: | Size: 221 KiB After Width: | Height: | Size: 163 KiB |
@ -20,6 +20,8 @@ const commonPoints = {
|
|||||||
startAt: '[9.06, -12.22]',
|
startAt: '[9.06, -12.22]',
|
||||||
num1: 9.14,
|
num1: 9.14,
|
||||||
num2: 18.2,
|
num2: 18.2,
|
||||||
|
// num1: 9.64,
|
||||||
|
// num2: 19.19,
|
||||||
}
|
}
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
@ -76,6 +78,7 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`const part001 = startSketchOn('-XZ')`
|
`const part001 = startSketchOn('-XZ')`
|
||||||
)
|
)
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
@ -86,7 +89,6 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -472,8 +474,18 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
// const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const lspStartPromise = page.waitForEvent('console', async (message) => {
|
||||||
|
// it would be better to wait for a message that the kcl lsp has started by looking for the message message.text().includes('[lsp] [window/logMessage]')
|
||||||
|
// but that doesn't seem to make it to the console for macos/safari :(
|
||||||
|
if (message.text().includes('start kcl lsp')) {
|
||||||
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
await page.goto('/')
|
await page.goto('/')
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await lspStartPromise
|
||||||
|
|
||||||
// this test might be brittle as we add and remove functions
|
// this test might be brittle as we add and remove functions
|
||||||
// but should also be easy to update.
|
// but should also be easy to update.
|
||||||
@ -625,7 +637,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
const emptySpaceClick = () =>
|
const emptySpaceClick = () =>
|
||||||
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
||||||
const topHorzSegmentClick = () =>
|
const topHorzSegmentClick = () =>
|
||||||
page.mouse.click(709, 289).then(() => page.waitForTimeout(100))
|
page.mouse.click(709, 290).then(() => page.waitForTimeout(100))
|
||||||
const bottomHorzSegmentClick = () =>
|
const bottomHorzSegmentClick = () =>
|
||||||
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
|
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
|
||||||
|
|
||||||
@ -640,13 +652,12 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await page.waitForTimeout(700) // wait for animation
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
@ -727,13 +738,18 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
|||||||
await emptySpaceClick()
|
await emptySpaceClick()
|
||||||
|
|
||||||
// select segment in editor than another segment in scene and check there are two cursors
|
// select segment in editor than another segment in scene and check there are two cursors
|
||||||
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
// TODO change this back to shift click in the scene, not cmd click in the editor
|
||||||
await page.waitForTimeout(300)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
|
||||||
await bottomHorzSegmentClick()
|
await bottomHorzSegmentClick()
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
|
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
||||||
|
|
||||||
|
await page.keyboard.down(process.platform === 'linux' ? 'Control' : 'Meta')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await page.getByText(` |> line([-${commonPoints.num2}, 0], %)`).click()
|
||||||
|
|
||||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up(process.platform === 'linux' ? 'Control' : 'Meta')
|
||||||
|
|
||||||
// clear selection by clicking on nothing
|
// clear selection by clicking on nothing
|
||||||
await emptySpaceClick()
|
await emptySpaceClick()
|
||||||
@ -918,13 +934,13 @@ test('Can add multiple sketches', async ({ page }) => {
|
|||||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -1372,10 +1388,129 @@ test('Snap to close works (at any scale)', async ({ page }) => {
|
|||||||
) => `const part001 = startSketchOn('XZ')
|
) => `const part001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([${roundOff(scale * 87.68)}, ${roundOff(scale * 43.84)}], %)
|
|> startProfileAt([${roundOff(scale * 87.68)}, ${roundOff(scale * 43.84)}], %)
|
||||||
|> line([${roundOff(scale * 175.36)}, 0], %)
|
|> line([${roundOff(scale * 175.36)}, 0], %)
|
||||||
|> line([0, -${roundOff(scale * 175.37) + fudge}], %)
|
|> line([0, -${roundOff(scale * 175.36) + fudge}], %)
|
||||||
|> close(%)`
|
|> close(%)`
|
||||||
|
|
||||||
await doSnapAtDifferentScales([0, 100, 100], codeTemplate(0.01, 0.01))
|
await doSnapAtDifferentScales([0, 100, 100], codeTemplate(0.01, 0.01))
|
||||||
|
|
||||||
await doSnapAtDifferentScales([0, 10000, 10000], codeTemplate())
|
await doSnapAtDifferentScales([0, 10000, 10000], codeTemplate())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Sketch on face', async ({ page, context }) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
|
|> line([2.48, 2.44], %)
|
||||||
|
|> line([2.66, 1.17], %)
|
||||||
|
|> line([3.75, 0.46], %)
|
||||||
|
|> line([4.99, -0.46], %)
|
||||||
|
|> line([3.3, -2.12], %)
|
||||||
|
|> line([2.16, -3.33], %)
|
||||||
|
|> line([0.85, -3.08], %)
|
||||||
|
|> line([-0.18, -3.36], %)
|
||||||
|
|> line([-3.86, -2.73], %)
|
||||||
|
|> line([-17.67, 0.85], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
|
||||||
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(793, 133)
|
||||||
|
|
||||||
|
const firstClickPosition = [612, 238]
|
||||||
|
const secondClickPosition = [661, 242]
|
||||||
|
const thirdClickPosition = [609, 267]
|
||||||
|
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(secondClickPosition[0], secondClickPosition[1])
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(thirdClickPosition[0], thirdClickPosition[1])
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.mouse.click(firstClickPosition[0], firstClickPosition[1])
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|
|> line([4.18, -0.35], %)
|
||||||
|
|> line([-4.44, -2.13], %)
|
||||||
|
|> close(%)`)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await u.updateCamPosition([1049, 239, 686])
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||||
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
const pointToDragFirst = [691, 237]
|
||||||
|
await page.mouse.move(pointToDragFirst[0], pointToDragFirst[1])
|
||||||
|
await page.mouse.down()
|
||||||
|
await page.mouse.move(pointToDragFirst[0] - 20, pointToDragFirst[1], {
|
||||||
|
steps: 5,
|
||||||
|
})
|
||||||
|
await page.mouse.up()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|
|> line([2.81, -0.33], %)
|
||||||
|
|> line([-4.44, -2.13], %)
|
||||||
|
|> close(%)`)
|
||||||
|
|
||||||
|
// exit sketch
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
|
||||||
|
await page.getByText('startProfileAt([1.03, 1.03], %)').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||||
|
await page.getByRole('button', { name: 'Extrude' }).click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
|
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toContainText(`const part002 = startSketchOn(part001, 'seg01')
|
||||||
|
|> startProfileAt([1.03, 1.03], %)
|
||||||
|
|> line([2.81, -0.33], %)
|
||||||
|
|> line([-4.44, -2.13], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)`)
|
||||||
|
})
|
||||||
|
@ -22,7 +22,7 @@ test.beforeEach(async ({ context, page }) => {
|
|||||||
onboardingStatus: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
showDebugPanel: true,
|
showDebugPanel: true,
|
||||||
textWrapping: 'On',
|
textWrapping: 'On',
|
||||||
theme: 'system',
|
theme: 'dark',
|
||||||
unitSystem: 'imperial',
|
unitSystem: 'imperial',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -397,7 +397,7 @@ test('Draft segments should look right', async ({ page, context }) => {
|
|||||||
onboardingStatus: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
showDebugPanel: true,
|
showDebugPanel: true,
|
||||||
textWrapping: 'On',
|
textWrapping: 'On',
|
||||||
theme: 'system',
|
theme: 'dark',
|
||||||
unitSystem: 'imperial',
|
unitSystem: 'imperial',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -475,7 +475,7 @@ test('Client side scene scale should match engine scale inch', async ({
|
|||||||
onboardingStatus: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
showDebugPanel: true,
|
showDebugPanel: true,
|
||||||
textWrapping: 'On',
|
textWrapping: 'On',
|
||||||
theme: 'system',
|
theme: 'dark',
|
||||||
unitSystem: 'imperial',
|
unitSystem: 'imperial',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -575,7 +575,7 @@ test('Client side scene scale should match engine scale mm', async ({
|
|||||||
onboardingStatus: 'dismissed',
|
onboardingStatus: 'dismissed',
|
||||||
showDebugPanel: true,
|
showDebugPanel: true,
|
||||||
textWrapping: 'On',
|
textWrapping: 'On',
|
||||||
theme: 'system',
|
theme: 'dark',
|
||||||
unitSystem: 'metric',
|
unitSystem: 'metric',
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@ -612,7 +612,7 @@ test('Client side scene scale should match engine scale mm', async ({
|
|||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([230.03, -310.33], %)`)
|
|> startProfileAt([230.03, -310.32], %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -622,7 +622,7 @@ test('Client side scene scale should match engine scale mm', async ({
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([230.03, -310.33], %)
|
|> startProfileAt([230.03, -310.32], %)
|
||||||
|> line([232.2, 0], %)`)
|
|> line([232.2, 0], %)`)
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
@ -632,7 +632,7 @@ test('Client side scene scale should match engine scale mm', async ({
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([230.03, -310.33], %)
|
|> startProfileAt([230.03, -310.32], %)
|
||||||
|> line([232.2, 0], %)
|
|> line([232.2, 0], %)
|
||||||
|> tangentialArcTo([694.43, -78.12], %)`)
|
|> tangentialArcTo([694.43, -78.12], %)`)
|
||||||
|
|
||||||
@ -658,3 +658,48 @@ test('Client side scene scale should match engine scale mm', async ({
|
|||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Sketch on face with none z-up', async ({ page, context }) => {
|
||||||
|
const u = getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([1.4, 2.47], %)
|
||||||
|
|> line({ to: [9.31, 10.55], tag: 'seg01' }, %)
|
||||||
|
|> line([11.91, -10.42], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
||||||
|
const part002 = startSketchOn(part001, 'seg01')
|
||||||
|
|> startProfileAt([-2.89, 1.82], %)
|
||||||
|
|> line([4.68, 3.05], %)
|
||||||
|
|> line({ to: [0, -7.79], tag: 'seg02' }, %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(5 + 7, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
// click at 641, 135
|
||||||
|
await page.mouse.click(641, 135)
|
||||||
|
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||||
|
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
|
await page.waitForTimeout(300)
|
||||||
|
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
64
package.json
@ -1,30 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.16.0",
|
"version": "0.17.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.10.2",
|
"@codemirror/autocomplete": "^6.15.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
"@fortawesome/free-solid-svg-icons": "^6.4.2",
|
||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.17",
|
"@headlessui/react": "^1.7.18",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.55",
|
"@kittycad/lib": "^0.1.0",
|
||||||
"@lezer/javascript": "^1.4.9",
|
"@lezer/javascript": "^1.4.9",
|
||||||
"@open-rpc/client-js": "^1.8.1",
|
"@open-rpc/client-js": "^1.8.1",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
"@replit/codemirror-interact": "^6.3.0",
|
"@replit/codemirror-interact": "^6.3.0",
|
||||||
"@tauri-apps/api": "^1.5.1",
|
"@tauri-apps/api": "^1.5.3",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"@testing-library/user-event": "^14.5.1",
|
"@testing-library/user-event": "^14.5.2",
|
||||||
"@ts-stack/markdown": "^1.5.0",
|
"@ts-stack/markdown": "^1.5.0",
|
||||||
"@tweenjs/tween.js": "^23.1.1",
|
"@tweenjs/tween.js": "^23.1.1",
|
||||||
"@types/node": "^16.7.13",
|
"@types/node": "^18.19.26",
|
||||||
"@types/react": "^18.2.41",
|
"@types/react": "^18.2.67",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.2.22",
|
||||||
"@uiw/react-codemirror": "^4.21.20",
|
"@uiw/react-codemirror": "^4.21.24",
|
||||||
"@xstate/inspect": "^0.8.0",
|
"@xstate/inspect": "^0.8.0",
|
||||||
"@xstate/react": "^3.2.2",
|
"@xstate/react": "^3.2.2",
|
||||||
"crypto-js": "^4.2.0",
|
"crypto-js": "^4.2.0",
|
||||||
@ -39,27 +39,27 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-hot-toast": "^2.4.1",
|
"react-hot-toast": "^2.4.1",
|
||||||
"react-hotkeys-hook": "^4.4.1",
|
"react-hotkeys-hook": "^4.5.0",
|
||||||
"react-json-view": "^1.21.3",
|
"react-json-view": "^1.21.3",
|
||||||
"react-modal": "^3.16.1",
|
"react-modal": "^3.16.1",
|
||||||
"react-modal-promise": "^1.0.2",
|
"react-modal-promise": "^1.0.2",
|
||||||
"react-router-dom": "^6.14.2",
|
"react-router-dom": "^6.22.3",
|
||||||
"sketch-helpers": "^0.0.4",
|
"sketch-helpers": "^0.0.4",
|
||||||
"swr": "^2.2.2",
|
"swr": "^2.2.2",
|
||||||
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
||||||
"three": "^0.160.0",
|
"three": "^0.160.0",
|
||||||
"toml": "^3.0.0",
|
"toml": "^3.0.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.2",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.4.3",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vitest": "^1.3.1",
|
"vitest": "^1.4.0",
|
||||||
"vscode-jsonrpc": "^8.1.0",
|
"vscode-jsonrpc": "^8.1.0",
|
||||||
"vscode-languageserver-protocol": "^3.17.5",
|
"vscode-languageserver-protocol": "^3.17.5",
|
||||||
"wasm-pack": "^0.12.1",
|
"wasm-pack": "^0.12.1",
|
||||||
"web-vitals": "^3.5.0",
|
"web-vitals": "^3.5.2",
|
||||||
"ws": "^8.13.0",
|
"ws": "^8.13.0",
|
||||||
"xstate": "^4.38.2",
|
"xstate": "^4.38.2",
|
||||||
"zustand": "^4.4.5"
|
"zustand": "^4.5.2"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
@ -110,41 +110,41 @@
|
|||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@babel/preset-env": "^7.23.3",
|
"@babel/preset-env": "^7.23.3",
|
||||||
"@playwright/test": "^1.39.0",
|
"@playwright/test": "^1.39.0",
|
||||||
"@tauri-apps/cli": "^1.5.6",
|
"@tauri-apps/cli": "^1.5.11",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/debounce-promise": "^3.1.8",
|
"@types/debounce-promise": "^3.1.9",
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react-modal": "^3.16.3",
|
"@types/react-modal": "^3.16.3",
|
||||||
"@types/three": "^0.160.0",
|
"@types/three": "^0.160.0",
|
||||||
"@types/uuid": "^9.0.4",
|
"@types/uuid": "^9.0.8",
|
||||||
"@types/wait-on": "^5.3.4",
|
"@types/wait-on": "^5.3.4",
|
||||||
"@types/wicg-file-system-access": "^2020.9.6",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.5",
|
"@types/ws": "^8.5.5",
|
||||||
"@vitejs/plugin-react": "^4.2.1",
|
"@vitejs/plugin-react": "^4.2.1",
|
||||||
"@wdio/cli": "^8.24.3",
|
"@wdio/cli": "^8.24.3",
|
||||||
"@wdio/globals": "^8.24.3",
|
"@wdio/globals": "^8.24.3",
|
||||||
"@wdio/local-runner": "^8.24.3",
|
"@wdio/local-runner": "^8.35.1",
|
||||||
"@wdio/mocha-framework": "^8.24.3",
|
"@wdio/mocha-framework": "^8.35.0",
|
||||||
"@wdio/spec-reporter": "^8.24.2",
|
"@wdio/spec-reporter": "^8.32.4",
|
||||||
"@xstate/cli": "^0.5.17",
|
"@xstate/cli": "^0.5.17",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.19",
|
||||||
"eslint": "^8.53.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
"eslint-config-react-app": "^7.0.1",
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"happy-dom": "^10.8.0",
|
"happy-dom": "^14.3.1",
|
||||||
"husky": "^8.0.3",
|
"husky": "^9.0.11",
|
||||||
"pixelmatch": "^5.3.0",
|
"pixelmatch": "^5.3.0",
|
||||||
"pngjs": "^7.0.0",
|
"pngjs": "^7.0.0",
|
||||||
"postcss": "^8.4.31",
|
"postcss": "^8.4.31",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^2.8.0",
|
||||||
"setimmediate": "^1.0.5",
|
"setimmediate": "^1.0.5",
|
||||||
"tailwindcss": "^3.3.6",
|
"tailwindcss": "^3.4.1",
|
||||||
"vite": "^5.1.3",
|
"vite": "^5.2.2",
|
||||||
"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.1",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wait-on": "^7.2.0",
|
"wait-on": "^7.2.0",
|
||||||
"yarn": "^1.22.19"
|
"yarn": "^1.22.19"
|
||||||
|
6
src-tauri/Cargo.lock
generated
@ -1664,9 +1664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.60"
|
version = "0.2.61"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8aa5906d0730bd90f6b3331fe57c04951d00743169a29ee96408767b4060605"
|
checksum = "949555aa013e1cefa68f59671cfe96602a459c903376c53a410aebf3831d291f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -3876,7 +3876,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs-extra"
|
name = "tauri-plugin-fs-extra"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#6db4320e98a4278d605dd05d4d87e1433939a7cd"
|
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#8cd4a39864986ae3600be9f23d089afd3988c43a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -16,7 +16,7 @@ tauri-build = { version = "1.5.1", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
kittycad = "0.2.60"
|
kittycad = "0.2.61"
|
||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "zoo-modeling-app",
|
"productName": "zoo-modeling-app",
|
||||||
"version": "0.16.0"
|
"version": "0.17.0"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
@ -34,8 +34,11 @@
|
|||||||
"request": true,
|
"request": true,
|
||||||
"scope": [
|
"scope": [
|
||||||
"https://dev.kittycad.io/*",
|
"https://dev.kittycad.io/*",
|
||||||
|
"https://dev.zoo.dev/*",
|
||||||
"https://kittycad.io/*",
|
"https://kittycad.io/*",
|
||||||
"https://api.dev.kittycad.io/*"
|
"https://zoo.dev/*",
|
||||||
|
"https://api.dev.kittycad.io/*",
|
||||||
|
"https://api.dev.zoo.dev/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"os": {
|
"os": {
|
||||||
|
@ -28,7 +28,7 @@ import { CodeMenu } from 'components/CodeMenu'
|
|||||||
import { TextEditor } from 'components/TextEditor'
|
import { TextEditor } from 'components/TextEditor'
|
||||||
import { Themes, getSystemTheme } from 'lib/theme'
|
import { Themes, getSystemTheme } from 'lib/theme'
|
||||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||||
import { engineCommandManager } from './lang/std/engineConnection'
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
|
@ -28,7 +28,7 @@ import {
|
|||||||
import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider'
|
import { CommandBarProvider } from 'components/CommandBar/CommandBarProvider'
|
||||||
import SettingsAuthProvider from 'components/SettingsAuthProvider'
|
import SettingsAuthProvider from 'components/SettingsAuthProvider'
|
||||||
import LspProvider from 'components/LspProvider'
|
import LspProvider from 'components/LspProvider'
|
||||||
import { KclContextProvider } from 'lang/KclSingleton'
|
import { KclContextProvider } from 'lang/KclProvider'
|
||||||
|
|
||||||
export const BROWSER_FILE_NAME = 'new'
|
export const BROWSER_FILE_NAME = 'new'
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { WheelEvent, useRef, useMemo } from 'react'
|
import { WheelEvent, useRef, useMemo } from 'react'
|
||||||
import { isCursorInSketchCommandRange } from 'lang/util'
|
import { isCursorInSketchCommandRange } from 'lang/util'
|
||||||
import { engineCommandManager } from './lang/std/engineConnection'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import {
|
import {
|
||||||
NetworkHealthState,
|
NetworkHealthState,
|
||||||
useNetworkStatus,
|
useNetworkStatus,
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
EngineCommand,
|
EngineCommand,
|
||||||
Subscription,
|
Subscription,
|
||||||
engineCommandManager,
|
EngineCommandManager,
|
||||||
} from 'lang/std/engineConnection'
|
} from 'lang/std/engineConnection'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { deg2Rad } from 'lib/utils2d'
|
import { deg2Rad } from 'lib/utils2d'
|
||||||
@ -34,10 +34,6 @@ const tempQuaternion = new Quaternion() // just used for maths
|
|||||||
|
|
||||||
type interactionType = 'pan' | 'rotate' | 'zoom'
|
type interactionType = 'pan' | 'rotate' | 'zoom'
|
||||||
|
|
||||||
const throttledEngCmd = throttle((cmd: EngineCommand) => {
|
|
||||||
engineCommandManager.sendSceneCommand(cmd)
|
|
||||||
}, 1000 / 15)
|
|
||||||
|
|
||||||
interface ThreeCamValues {
|
interface ThreeCamValues {
|
||||||
position: Vector3
|
position: Vector3
|
||||||
quaternion: Quaternion
|
quaternion: Quaternion
|
||||||
@ -62,68 +58,8 @@ export type ReactCameraProperties =
|
|||||||
|
|
||||||
const lastCmdDelay = 50
|
const lastCmdDelay = 50
|
||||||
|
|
||||||
const throttledUpdateEngineCamera = throttle((threeValues: ThreeCamValues) => {
|
|
||||||
const cmd: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
...convertThreeCamValuesToEngineCam(threeValues),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand(cmd)
|
|
||||||
}, 1000 / 15)
|
|
||||||
|
|
||||||
let lastPerspectiveCmd: EngineCommand | null = null
|
|
||||||
let lastPerspectiveCmdTime: number = Date.now()
|
|
||||||
let lastPerspectiveCmdTimeoutId: number | null = null
|
|
||||||
|
|
||||||
const sendLastPerspectiveReliableChannel = () => {
|
|
||||||
if (
|
|
||||||
lastPerspectiveCmd &&
|
|
||||||
Date.now() - lastPerspectiveCmdTime >= lastCmdDelay
|
|
||||||
) {
|
|
||||||
engineCommandManager.sendSceneCommand(lastPerspectiveCmd, true)
|
|
||||||
lastPerspectiveCmdTime = Date.now()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const throttledUpdateEngineFov = throttle(
|
|
||||||
(vals: {
|
|
||||||
position: Vector3
|
|
||||||
quaternion: Quaternion
|
|
||||||
zoom: number
|
|
||||||
fov: number
|
|
||||||
target: Vector3
|
|
||||||
}) => {
|
|
||||||
const cmd: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_perspective_settings',
|
|
||||||
...convertThreeCamValuesToEngineCam({
|
|
||||||
...vals,
|
|
||||||
isPerspective: true,
|
|
||||||
}),
|
|
||||||
fov_y: vals.fov,
|
|
||||||
...calculateNearFarFromFOV(vals.fov),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand(cmd)
|
|
||||||
lastPerspectiveCmd = cmd
|
|
||||||
lastPerspectiveCmdTime = Date.now()
|
|
||||||
if (lastPerspectiveCmdTimeoutId !== null) {
|
|
||||||
clearTimeout(lastPerspectiveCmdTimeoutId)
|
|
||||||
}
|
|
||||||
lastPerspectiveCmdTimeoutId = setTimeout(
|
|
||||||
sendLastPerspectiveReliableChannel,
|
|
||||||
lastCmdDelay
|
|
||||||
) as any as number
|
|
||||||
},
|
|
||||||
1000 / 30
|
|
||||||
)
|
|
||||||
|
|
||||||
export class CameraControls {
|
export class CameraControls {
|
||||||
|
engineCommandManager: EngineCommandManager
|
||||||
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
|
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
|
||||||
camera: PerspectiveCamera | OrthographicCamera
|
camera: PerspectiveCamera | OrthographicCamera
|
||||||
target: Vector3
|
target: Vector3
|
||||||
@ -213,7 +149,77 @@ export class CameraControls {
|
|||||||
this.update(true)
|
this.update(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(isOrtho = false, domElement: HTMLCanvasElement) {
|
throttledEngCmd = throttle((cmd: EngineCommand) => {
|
||||||
|
this.engineCommandManager.sendSceneCommand(cmd)
|
||||||
|
}, 1000 / 30)
|
||||||
|
|
||||||
|
throttledUpdateEngineCamera = throttle((threeValues: ThreeCamValues) => {
|
||||||
|
const cmd: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
...convertThreeCamValuesToEngineCam(threeValues),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.engineCommandManager.sendSceneCommand(cmd)
|
||||||
|
}, 1000 / 15)
|
||||||
|
|
||||||
|
lastPerspectiveCmd: EngineCommand | null = null
|
||||||
|
lastPerspectiveCmdTime: number = Date.now()
|
||||||
|
lastPerspectiveCmdTimeoutId: number | null = null
|
||||||
|
|
||||||
|
sendLastPerspectiveReliableChannel = () => {
|
||||||
|
if (
|
||||||
|
this.lastPerspectiveCmd &&
|
||||||
|
Date.now() - this.lastPerspectiveCmdTime >= lastCmdDelay
|
||||||
|
) {
|
||||||
|
this.engineCommandManager.sendSceneCommand(this.lastPerspectiveCmd, true)
|
||||||
|
this.lastPerspectiveCmdTime = Date.now()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throttledUpdateEngineFov = throttle(
|
||||||
|
(vals: {
|
||||||
|
position: Vector3
|
||||||
|
quaternion: Quaternion
|
||||||
|
zoom: number
|
||||||
|
fov: number
|
||||||
|
target: Vector3
|
||||||
|
}) => {
|
||||||
|
const cmd: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_perspective_settings',
|
||||||
|
...convertThreeCamValuesToEngineCam({
|
||||||
|
...vals,
|
||||||
|
isPerspective: true,
|
||||||
|
}),
|
||||||
|
fov_y: vals.fov,
|
||||||
|
...calculateNearFarFromFOV(vals.fov),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
this.engineCommandManager.sendSceneCommand(cmd)
|
||||||
|
this.lastPerspectiveCmd = cmd
|
||||||
|
this.lastPerspectiveCmdTime = Date.now()
|
||||||
|
if (this.lastPerspectiveCmdTimeoutId !== null) {
|
||||||
|
clearTimeout(this.lastPerspectiveCmdTimeoutId)
|
||||||
|
}
|
||||||
|
this.lastPerspectiveCmdTimeoutId = setTimeout(
|
||||||
|
this.sendLastPerspectiveReliableChannel,
|
||||||
|
lastCmdDelay
|
||||||
|
) as any as number
|
||||||
|
},
|
||||||
|
1000 / 30
|
||||||
|
)
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
isOrtho = false,
|
||||||
|
domElement: HTMLCanvasElement,
|
||||||
|
engineCommandManager: EngineCommandManager
|
||||||
|
) {
|
||||||
|
this.engineCommandManager = engineCommandManager
|
||||||
this.camera = isOrtho ? new OrthographicCamera() : new PerspectiveCamera()
|
this.camera = isOrtho ? new OrthographicCamera() : new PerspectiveCamera()
|
||||||
this.camera.up.set(0, 0, 1)
|
this.camera.up.set(0, 0, 1)
|
||||||
this.camera.far = 20000
|
this.camera.far = 20000
|
||||||
@ -259,15 +265,15 @@ export class CameraControls {
|
|||||||
this.onCameraChange()
|
this.onCameraChange()
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'camera_drag_end',
|
event: 'camera_drag_end',
|
||||||
callback: cb,
|
callback: cb,
|
||||||
})
|
})
|
||||||
engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'default_camera_zoom',
|
event: 'default_camera_zoom',
|
||||||
callback: cb,
|
callback: cb,
|
||||||
})
|
})
|
||||||
engineCommandManager.subscribeTo({
|
this.engineCommandManager.subscribeTo({
|
||||||
event: 'default_camera_get_settings',
|
event: 'default_camera_get_settings',
|
||||||
callback: cb,
|
callback: cb,
|
||||||
})
|
})
|
||||||
@ -310,7 +316,7 @@ export class CameraControls {
|
|||||||
this.handleStart()
|
this.handleStart()
|
||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
void engineCommandManager.sendSceneCommand({
|
void this.engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'camera_drag_start',
|
type: 'camera_drag_start',
|
||||||
@ -334,7 +340,7 @@ export class CameraControls {
|
|||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
|
|
||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
throttledEngCmd({
|
this.throttledEngCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'camera_drag_move',
|
type: 'camera_drag_move',
|
||||||
@ -377,7 +383,7 @@ export class CameraControls {
|
|||||||
if (this.syncDirection === 'engineToClient') {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
const interaction = this.getInteractionType(event)
|
const interaction = this.getInteractionType(event)
|
||||||
if (interaction === 'none') return
|
if (interaction === 'none') return
|
||||||
void engineCommandManager.sendSceneCommand({
|
void this.engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'camera_drag_end',
|
type: 'camera_drag_end',
|
||||||
@ -401,7 +407,7 @@ export class CameraControls {
|
|||||||
this.handleEnd()
|
this.handleEnd()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
throttledEngCmd({
|
this.throttledEngCmd({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_zoom',
|
type: 'default_camera_zoom',
|
||||||
@ -425,6 +431,7 @@ export class CameraControls {
|
|||||||
if (this.camera instanceof OrthographicCamera) return
|
if (this.camera instanceof OrthographicCamera) return
|
||||||
const { x: px, y: py, z: pz } = this.camera.position
|
const { x: px, y: py, z: pz } = this.camera.position
|
||||||
const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion
|
const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion
|
||||||
|
const oldCamUp = this.camera.up.clone()
|
||||||
const aspect = window.innerWidth / window.innerHeight
|
const aspect = window.innerWidth / window.innerHeight
|
||||||
this.lastPerspectiveFov = this.camera.fov
|
this.lastPerspectiveFov = this.camera.fov
|
||||||
const { z_near, z_far } = calculateNearFarFromFOV(this.lastPerspectiveFov)
|
const { z_near, z_far } = calculateNearFarFromFOV(this.lastPerspectiveFov)
|
||||||
@ -436,7 +443,8 @@ export class CameraControls {
|
|||||||
z_near,
|
z_near,
|
||||||
z_far
|
z_far
|
||||||
)
|
)
|
||||||
this.camera.up.set(0, 0, 1)
|
|
||||||
|
this.camera.up.copy(oldCamUp)
|
||||||
this.camera.layers.enable(SKETCH_LAYER)
|
this.camera.layers.enable(SKETCH_LAYER)
|
||||||
if (DEBUG_SHOW_INTERSECTION_PLANE)
|
if (DEBUG_SHOW_INTERSECTION_PLANE)
|
||||||
this.camera.layers.enable(INTERSECTION_PLANE_LAYER)
|
this.camera.layers.enable(INTERSECTION_PLANE_LAYER)
|
||||||
@ -447,7 +455,7 @@ export class CameraControls {
|
|||||||
|
|
||||||
this.camera.quaternion.set(qx, qy, qz, qw)
|
this.camera.quaternion.set(qx, qy, qz, qw)
|
||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
engineCommandManager.sendSceneCommand({
|
this.engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
@ -458,13 +466,14 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
private createPerspectiveCamera = () => {
|
private createPerspectiveCamera = () => {
|
||||||
const { z_near, z_far } = calculateNearFarFromFOV(this.lastPerspectiveFov)
|
const { z_near, z_far } = calculateNearFarFromFOV(this.lastPerspectiveFov)
|
||||||
|
const previousCamUp = this.camera.up.clone()
|
||||||
this.camera = new PerspectiveCamera(
|
this.camera = new PerspectiveCamera(
|
||||||
this.lastPerspectiveFov,
|
this.lastPerspectiveFov,
|
||||||
window.innerWidth / window.innerHeight,
|
window.innerWidth / window.innerHeight,
|
||||||
z_near,
|
z_near,
|
||||||
z_far
|
z_far
|
||||||
)
|
)
|
||||||
this.camera.up.set(0, 0, 1)
|
this.camera.up.copy(previousCamUp)
|
||||||
this.camera.layers.enable(SKETCH_LAYER)
|
this.camera.layers.enable(SKETCH_LAYER)
|
||||||
if (DEBUG_SHOW_INTERSECTION_PLANE)
|
if (DEBUG_SHOW_INTERSECTION_PLANE)
|
||||||
this.camera.layers.enable(INTERSECTION_PLANE_LAYER)
|
this.camera.layers.enable(INTERSECTION_PLANE_LAYER)
|
||||||
@ -490,7 +499,7 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
usePerspectiveCamera = () => {
|
usePerspectiveCamera = () => {
|
||||||
this._usePerspectiveCamera()
|
this._usePerspectiveCamera()
|
||||||
engineCommandManager.sendSceneCommand({
|
this.engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
@ -558,7 +567,7 @@ export class CameraControls {
|
|||||||
this.camera.near = z_near
|
this.camera.near = z_near
|
||||||
this.camera.far = z_far
|
this.camera.far = z_far
|
||||||
|
|
||||||
throttledUpdateEngineFov({
|
this.throttledUpdateEngineFov({
|
||||||
fov: newFov,
|
fov: newFov,
|
||||||
position: newPosition,
|
position: newPosition,
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
@ -618,7 +627,7 @@ export class CameraControls {
|
|||||||
didChange = true
|
didChange = true
|
||||||
}
|
}
|
||||||
|
|
||||||
this.safeLookAtTarget()
|
this.safeLookAtTarget(this.camera.up)
|
||||||
|
|
||||||
// Update the camera's matrices
|
// Update the camera's matrices
|
||||||
this.camera.updateMatrixWorld()
|
this.camera.updateMatrixWorld()
|
||||||
@ -683,6 +692,7 @@ export class CameraControls {
|
|||||||
targetAngle = -Math.PI / 2,
|
targetAngle = -Math.PI / 2,
|
||||||
duration = 500
|
duration = 500
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
return new Promise((resolve) => {
|
||||||
// should tween the camera so that it has an xPosition of 0, and forcing it's yPosition to be negative
|
// should tween the camera so that it has an xPosition of 0, and forcing it's yPosition to be negative
|
||||||
// zPosition should stay the same
|
// zPosition should stay the same
|
||||||
const xyRadius = Math.sqrt(
|
const xyRadius = Math.sqrt(
|
||||||
@ -693,22 +703,7 @@ export class CameraControls {
|
|||||||
this.camera.position.y - this.target.y,
|
this.camera.position.y - this.target.y,
|
||||||
this.camera.position.x - this.target.x
|
this.camera.position.x - this.target.x
|
||||||
)
|
)
|
||||||
this._isCamMovingCallback(true, true)
|
const camAtTime = (obj: { angle: number }) => {
|
||||||
return new Promise((resolve) => {
|
|
||||||
new TWEEN.Tween({ angle: xyAngle })
|
|
||||||
.to({ angle: targetAngle }, duration)
|
|
||||||
.onUpdate((obj) => {
|
|
||||||
const x = xyRadius * Math.cos(obj.angle)
|
|
||||||
const y = xyRadius * Math.sin(obj.angle)
|
|
||||||
this.camera.position.set(
|
|
||||||
this.target.x + x,
|
|
||||||
this.target.y + y,
|
|
||||||
this.camera.position.z
|
|
||||||
)
|
|
||||||
this.update()
|
|
||||||
this.onCameraChange()
|
|
||||||
})
|
|
||||||
.onComplete((obj) => {
|
|
||||||
const x = xyRadius * Math.cos(obj.angle)
|
const x = xyRadius * Math.cos(obj.angle)
|
||||||
const y = xyRadius * Math.sin(obj.angle)
|
const y = xyRadius * Math.sin(obj.angle)
|
||||||
this.camera.position.set(
|
this.camera.position.set(
|
||||||
@ -718,13 +713,27 @@ export class CameraControls {
|
|||||||
)
|
)
|
||||||
this.update()
|
this.update()
|
||||||
this.onCameraChange()
|
this.onCameraChange()
|
||||||
|
}
|
||||||
|
const onComplete = (obj: { angle: number }) => {
|
||||||
|
camAtTime(obj)
|
||||||
this._isCamMovingCallback(false, true)
|
this._isCamMovingCallback(false, true)
|
||||||
|
|
||||||
// resolve after a couple of frames
|
// resolve after a couple of frames
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(() => resolve())
|
requestAnimationFrame(() => resolve())
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
|
this._isCamMovingCallback(true, true)
|
||||||
|
|
||||||
|
if (isReducedMotion()) {
|
||||||
|
onComplete({ angle: targetAngle })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
new TWEEN.Tween({ angle: xyAngle })
|
||||||
|
.to({ angle: targetAngle }, duration)
|
||||||
|
.onUpdate(camAtTime)
|
||||||
|
.onComplete(onComplete)
|
||||||
.start()
|
.start()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -778,6 +787,8 @@ export class CameraControls {
|
|||||||
targetQuaternion,
|
targetQuaternion,
|
||||||
animationProgress
|
animationProgress
|
||||||
)
|
)
|
||||||
|
const up = new Vector3(0, 0, 1).applyQuaternion(currentQ)
|
||||||
|
this.camera.up.copy(up)
|
||||||
const currentTarget = tempVec.lerpVectors(
|
const currentTarget = tempVec.lerpVectors(
|
||||||
initialTarget,
|
initialTarget,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
@ -802,7 +813,7 @@ export class CameraControls {
|
|||||||
|
|
||||||
const onComplete = async () => {
|
const onComplete = async () => {
|
||||||
if (isReducedMotion() && toOrthographic) {
|
if (isReducedMotion() && toOrthographic) {
|
||||||
cameraAtTime(0.99)
|
cameraAtTime(0.9999)
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
} else if (toOrthographic) {
|
} else if (toOrthographic) {
|
||||||
await this.animateToOrthographic()
|
await this.animateToOrthographic()
|
||||||
@ -863,37 +874,40 @@ export class CameraControls {
|
|||||||
|
|
||||||
animateFovChange() // Start the animation
|
animateFovChange() // Start the animation
|
||||||
})
|
})
|
||||||
animateToPerspective = () =>
|
animateToPerspective = (targetCamUp = new Vector3(0, 0, 1)) =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
if (this.syncDirection === 'engineToClient')
|
if (this.syncDirection === 'engineToClient') {
|
||||||
console.warn(
|
console.warn(
|
||||||
'animate To Perspective not design to work with engineToClient syncDirection.'
|
'animate To Perspective not design to work with engineToClient syncDirection.'
|
||||||
)
|
)
|
||||||
|
}
|
||||||
this.isFovAnimationInProgress = true
|
this.isFovAnimationInProgress = true
|
||||||
// Immediately set the camera to perspective with a very low FOV
|
|
||||||
const targetFov = this.fovBeforeOrtho // Target FOV for perspective
|
const targetFov = this.fovBeforeOrtho // Target FOV for perspective
|
||||||
this.lastPerspectiveFov = 4
|
this.lastPerspectiveFov = 4
|
||||||
let currentFov = 4
|
let currentFov = 4
|
||||||
this.camera.updateProjectionMatrix()
|
const initialCameraUp = this.camera.up.clone()
|
||||||
const fovAnimationStep = (targetFov - currentFov) / FRAMES_TO_ANIMATE_IN
|
|
||||||
this.usePerspectiveCamera()
|
this.usePerspectiveCamera()
|
||||||
|
const tempVec = new Vector3()
|
||||||
|
|
||||||
const animateFovChange = () => {
|
const cameraAtTime = (t: number) => {
|
||||||
if (this.camera instanceof OrthographicCamera) return
|
currentFov =
|
||||||
if (this.camera.fov < targetFov) {
|
this.lastPerspectiveFov + (targetFov - this.lastPerspectiveFov) * t
|
||||||
// Increase the FOV
|
const currentUp = tempVec.lerpVectors(initialCameraUp, targetCamUp, t)
|
||||||
currentFov = Math.min(currentFov + fovAnimationStep, targetFov)
|
this.camera.up.copy(currentUp)
|
||||||
// this.camera.fov = currentFov
|
|
||||||
this.camera.updateProjectionMatrix()
|
|
||||||
this.dollyZoom(currentFov)
|
this.dollyZoom(currentFov)
|
||||||
requestAnimationFrame(animateFovChange) // Continue the animation
|
}
|
||||||
} else {
|
|
||||||
// Set the flag to false as the FOV animation is complete
|
const onComplete = () => {
|
||||||
this.isFovAnimationInProgress = false
|
this.isFovAnimationInProgress = false
|
||||||
resolve(true)
|
resolve(true)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
animateFovChange() // Start the animation
|
new TWEEN.Tween({ t: 0 })
|
||||||
|
.to({ t: 1 }, isReducedMotion() ? 50 : FRAMES_TO_ANIMATE_IN * 16) // Assuming 60fps, hence 16ms per frame
|
||||||
|
.easing(TWEEN.Easing.Quadratic.InOut)
|
||||||
|
.onUpdate(({ t }) => cameraAtTime(t))
|
||||||
|
.onComplete(onComplete)
|
||||||
|
.start()
|
||||||
})
|
})
|
||||||
|
|
||||||
reactCameraPropertiesCallback: (a: ReactCameraProperties) => void = () => {}
|
reactCameraPropertiesCallback: (a: ReactCameraProperties) => void = () => {}
|
||||||
@ -916,7 +930,7 @@ export class CameraControls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
||||||
throttledUpdateEngineCamera({
|
this.throttledUpdateEngineCamera({
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
position: this.camera.position,
|
position: this.camera.position,
|
||||||
zoom: this.camera.zoom,
|
zoom: this.camera.zoom,
|
||||||
|
@ -4,9 +4,10 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
import { DEBUG_SHOW_BOTH_SCENES, sceneInfra } from './sceneInfra'
|
import { DEBUG_SHOW_BOTH_SCENES } from './sceneInfra'
|
||||||
import { ReactCameraProperties } from './CameraControls'
|
import { ReactCameraProperties } from './CameraControls'
|
||||||
import { throttle } from 'lib/utils'
|
import { throttle } from 'lib/utils'
|
||||||
|
import { sceneInfra } from 'lib/singletons'
|
||||||
|
|
||||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||||
|
@ -40,3 +40,12 @@ export function isQuaternionVertical(q: Quaternion) {
|
|||||||
// no x or y components means it's vertical
|
// no x or y components means it's vertical
|
||||||
return compareVec2Epsilon2([v.x, v.y], [0, 0])
|
return compareVec2Epsilon2([v.x, v.y], [0, 0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function quaternionFromUpNForward(up: Vector3, forward: Vector3) {
|
||||||
|
const dummyCam = new PerspectiveCamera()
|
||||||
|
dummyCam.up.copy(up)
|
||||||
|
dummyCam.position.copy(forward)
|
||||||
|
dummyCam.lookAt(0, 0, 0)
|
||||||
|
dummyCam.updateMatrix()
|
||||||
|
return dummyCam.quaternion.clone()
|
||||||
|
}
|
||||||
|
@ -5,7 +5,6 @@ import {
|
|||||||
Group,
|
Group,
|
||||||
Intersection,
|
Intersection,
|
||||||
LineCurve3,
|
LineCurve3,
|
||||||
Matrix4,
|
|
||||||
Mesh,
|
Mesh,
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
Object3D,
|
Object3D,
|
||||||
@ -29,7 +28,6 @@ import {
|
|||||||
INTERSECTION_PLANE_LAYER,
|
INTERSECTION_PLANE_LAYER,
|
||||||
OnMouseEnterLeaveArgs,
|
OnMouseEnterLeaveArgs,
|
||||||
RAYCASTABLE_PLANE,
|
RAYCASTABLE_PLANE,
|
||||||
sceneInfra,
|
|
||||||
SKETCH_GROUP_SEGMENTS,
|
SKETCH_GROUP_SEGMENTS,
|
||||||
SKETCH_LAYER,
|
SKETCH_LAYER,
|
||||||
X_AXIS,
|
X_AXIS,
|
||||||
@ -37,7 +35,7 @@ import {
|
|||||||
Y_AXIS,
|
Y_AXIS,
|
||||||
YZ_PLANE,
|
YZ_PLANE,
|
||||||
} from './sceneInfra'
|
} from './sceneInfra'
|
||||||
import { isQuaternionVertical } from './helpers'
|
import { isQuaternionVertical, quaternionFromUpNForward } from './helpers'
|
||||||
import {
|
import {
|
||||||
CallExpression,
|
CallExpression,
|
||||||
getTangentialArcToInfo,
|
getTangentialArcToInfo,
|
||||||
@ -53,10 +51,9 @@ import {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { engineCommandManager, kclManager, sceneInfra } from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { executeAst } from 'useStore'
|
import { executeAst, useStore } from 'useStore'
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
|
||||||
import {
|
import {
|
||||||
createArcGeometry,
|
createArcGeometry,
|
||||||
dashedStraight,
|
dashedStraight,
|
||||||
@ -70,16 +67,23 @@ import {
|
|||||||
changeSketchArguments,
|
changeSketchArguments,
|
||||||
updateStartProfileAtArgs,
|
updateStartProfileAtArgs,
|
||||||
} from 'lang/std/sketch'
|
} from 'lang/std/sketch'
|
||||||
import { isReducedMotion, throttle } from 'lib/utils'
|
import { throttle } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
createArrayExpression,
|
createArrayExpression,
|
||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
createPipeSubstitution,
|
createPipeSubstitution,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { getEventForSegmentSelection } from 'lib/selections'
|
import {
|
||||||
|
getEventForSegmentSelection,
|
||||||
|
sendSelectEventToEngine,
|
||||||
|
} from 'lib/selections'
|
||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import { createGridHelper, orthoScale, perspScale } from './helpers'
|
import { createGridHelper, orthoScale, perspScale } from './helpers'
|
||||||
|
import { Models } from '@kittycad/lib'
|
||||||
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
import { SketchDetails } from 'machines/modelingMachine'
|
||||||
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
|
|
||||||
type DraftSegment = 'line' | 'tangentialArcTo'
|
type DraftSegment = 'line' | 'tangentialArcTo'
|
||||||
|
|
||||||
@ -95,14 +99,16 @@ export const PROFILE_START = 'profile-start'
|
|||||||
// This singleton Class is responsible for all of the things the user sees and interacts with.
|
// This singleton Class is responsible for all of the things the user sees and interacts with.
|
||||||
// That mostly mean sketch elements.
|
// That mostly mean sketch elements.
|
||||||
// Cameras, controls, raycasters, etc are handled by sceneInfra
|
// Cameras, controls, raycasters, etc are handled by sceneInfra
|
||||||
class SceneEntities {
|
export class SceneEntities {
|
||||||
|
engineCommandManager: EngineCommandManager
|
||||||
scene: Scene
|
scene: Scene
|
||||||
sceneProgramMemory: ProgramMemory = { root: {}, return: null }
|
sceneProgramMemory: ProgramMemory = { root: {}, return: null }
|
||||||
activeSegments: { [key: string]: Group } = {}
|
activeSegments: { [key: string]: Group } = {}
|
||||||
intersectionPlane: Mesh | null = null
|
intersectionPlane: Mesh | null = null
|
||||||
axisGroup: Group | null = null
|
axisGroup: Group | null = null
|
||||||
currentSketchQuaternion: Quaternion | null = null
|
currentSketchQuaternion: Quaternion | null = null
|
||||||
constructor() {
|
constructor(engineCommandManager: EngineCommandManager) {
|
||||||
|
this.engineCommandManager = engineCommandManager
|
||||||
this.scene = sceneInfra?.scene
|
this.scene = sceneInfra?.scene
|
||||||
sceneInfra?.camControls.subscribeToCamChange(this.onCamChange)
|
sceneInfra?.camControls.subscribeToCamChange(this.onCamChange)
|
||||||
}
|
}
|
||||||
@ -164,7 +170,7 @@ class SceneEntities {
|
|||||||
console.warn('createIntersectionPlane called when it already exists')
|
console.warn('createIntersectionPlane called when it already exists')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const hundredM = 1000000
|
const hundredM = 100_0000
|
||||||
const planeGeometry = new PlaneGeometry(hundredM, hundredM)
|
const planeGeometry = new PlaneGeometry(hundredM, hundredM)
|
||||||
const planeMaterial = new MeshBasicMaterial({
|
const planeMaterial = new MeshBasicMaterial({
|
||||||
color: 0xff0000,
|
color: 0xff0000,
|
||||||
@ -178,7 +184,12 @@ class SceneEntities {
|
|||||||
this.intersectionPlane.layers.set(INTERSECTION_PLANE_LAYER)
|
this.intersectionPlane.layers.set(INTERSECTION_PLANE_LAYER)
|
||||||
this.scene.add(this.intersectionPlane)
|
this.scene.add(this.intersectionPlane)
|
||||||
}
|
}
|
||||||
createSketchAxis(sketchPathToNode: PathToNode) {
|
createSketchAxis(
|
||||||
|
sketchPathToNode: PathToNode,
|
||||||
|
forward: [number, number, number],
|
||||||
|
up: [number, number, number],
|
||||||
|
sketchPosition?: [number, number, number]
|
||||||
|
) {
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
const baseXColor = 0x000055
|
const baseXColor = 0x000055
|
||||||
const baseYColor = 0x550000
|
const baseYColor = 0x550000
|
||||||
@ -238,14 +249,12 @@ class SceneEntities {
|
|||||||
child.layers.set(SKETCH_LAYER)
|
child.layers.set(SKETCH_LAYER)
|
||||||
})
|
})
|
||||||
|
|
||||||
const quat = quaternionFromSketchGroup(
|
const quat = quaternionFromUpNForward(
|
||||||
sketchGroupFromPathToNode({
|
new Vector3(...up),
|
||||||
pathToNode: sketchPathToNode,
|
new Vector3(...forward)
|
||||||
ast: kclManager.ast,
|
|
||||||
programMemory: kclManager.programMemory,
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
this.axisGroup.setRotationFromQuaternion(quat)
|
this.axisGroup.setRotationFromQuaternion(quat)
|
||||||
|
sketchPosition && this.axisGroup.position.set(...sketchPosition)
|
||||||
this.scene.add(this.axisGroup)
|
this.scene.add(this.axisGroup)
|
||||||
}
|
}
|
||||||
removeIntersectionPlane() {
|
removeIntersectionPlane() {
|
||||||
@ -255,27 +264,33 @@ class SceneEntities {
|
|||||||
|
|
||||||
async setupSketch({
|
async setupSketch({
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
ast,
|
forward,
|
||||||
// is draft line assumes the last segment is a draft line, and mods it as the user moves the mouse
|
up,
|
||||||
draftSegment,
|
position,
|
||||||
|
maybeModdedAst,
|
||||||
|
draftExpressionsIndices,
|
||||||
}: {
|
}: {
|
||||||
sketchPathToNode: PathToNode
|
sketchPathToNode: PathToNode
|
||||||
ast?: Program
|
maybeModdedAst: Program
|
||||||
draftSegment?: DraftSegment
|
draftExpressionsIndices?: { start: number; end: number }
|
||||||
}) {
|
forward: [number, number, number]
|
||||||
|
up: [number, number, number]
|
||||||
|
position?: [number, number, number]
|
||||||
|
}): Promise<{
|
||||||
|
truncatedAst: Program
|
||||||
|
programMemoryOverride: ProgramMemory
|
||||||
|
sketchGroup: SketchGroup
|
||||||
|
variableDeclarationName: string
|
||||||
|
}> {
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
this.createIntersectionPlane()
|
this.createIntersectionPlane()
|
||||||
|
|
||||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||||
this.prepareTruncatedMemoryAndAst(
|
this.prepareTruncatedMemoryAndAst(sketchPathToNode || [], maybeModdedAst)
|
||||||
sketchPathToNode || [],
|
|
||||||
kclManager.ast,
|
|
||||||
draftSegment
|
|
||||||
)
|
|
||||||
const { programMemory } = await executeAst({
|
const { programMemory } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
useFakeExecutor: true,
|
||||||
engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const sketchGroup = sketchGroupFromPathToNode({
|
const sketchGroup = sketchGroupFromPathToNode({
|
||||||
@ -283,9 +298,16 @@ class SceneEntities {
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
programMemory,
|
programMemory,
|
||||||
})
|
})
|
||||||
if (!Array.isArray(sketchGroup?.value)) return
|
if (!Array.isArray(sketchGroup?.value))
|
||||||
|
return {
|
||||||
|
truncatedAst,
|
||||||
|
programMemoryOverride,
|
||||||
|
sketchGroup,
|
||||||
|
variableDeclarationName,
|
||||||
|
}
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
const group = new Group()
|
const group = new Group()
|
||||||
|
position && group.position.set(...position)
|
||||||
group.userData = {
|
group.userData = {
|
||||||
type: SKETCH_GROUP_SEGMENTS,
|
type: SKETCH_GROUP_SEGMENTS,
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
@ -325,7 +347,10 @@ class SceneEntities {
|
|||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
segment.__geoMeta.sourceRange
|
segment.__geoMeta.sourceRange
|
||||||
)
|
)
|
||||||
if (draftSegment && (sketchGroup.value[index - 1] || sketchGroup.start)) {
|
if (
|
||||||
|
draftExpressionsIndices &&
|
||||||
|
(sketchGroup.value[index - 1] || sketchGroup.start)
|
||||||
|
) {
|
||||||
const previousSegment =
|
const previousSegment =
|
||||||
sketchGroup.value[index - 1] || sketchGroup.start
|
sketchGroup.value[index - 1] || sketchGroup.start
|
||||||
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
const previousSegmentPathToNode = getNodePathFromSourceRange(
|
||||||
@ -340,7 +365,9 @@ class SceneEntities {
|
|||||||
segPathToNode[1][0] = bodyIndex
|
segPathToNode[1][0] = bodyIndex
|
||||||
}
|
}
|
||||||
const isDraftSegment =
|
const isDraftSegment =
|
||||||
draftSegment && index === sketchGroup.value.length - 1
|
draftExpressionsIndices &&
|
||||||
|
index <= draftExpressionsIndices.end &&
|
||||||
|
index >= draftExpressionsIndices.start
|
||||||
let seg
|
let seg
|
||||||
const callExpName = getNodeFromPath<CallExpression>(
|
const callExpName = getNodeFromPath<CallExpression>(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -377,45 +404,91 @@ class SceneEntities {
|
|||||||
this.activeSegments[JSON.stringify(segPathToNode)] = seg
|
this.activeSegments[JSON.stringify(segPathToNode)] = seg
|
||||||
})
|
})
|
||||||
|
|
||||||
this.currentSketchQuaternion = quaternionFromSketchGroup(sketchGroup)
|
this.currentSketchQuaternion = quaternionFromUpNForward(
|
||||||
|
new Vector3(...up),
|
||||||
|
new Vector3(...forward)
|
||||||
|
)
|
||||||
group.setRotationFromQuaternion(this.currentSketchQuaternion)
|
group.setRotationFromQuaternion(this.currentSketchQuaternion)
|
||||||
this.intersectionPlane &&
|
this.intersectionPlane &&
|
||||||
this.intersectionPlane.setRotationFromQuaternion(
|
this.intersectionPlane.setRotationFromQuaternion(
|
||||||
this.currentSketchQuaternion
|
this.currentSketchQuaternion
|
||||||
)
|
)
|
||||||
|
this.intersectionPlane &&
|
||||||
|
position &&
|
||||||
|
this.intersectionPlane.position.set(...position)
|
||||||
this.scene.add(group)
|
this.scene.add(group)
|
||||||
if (!draftSegment) {
|
sceneInfra.camControls.enableRotate = false
|
||||||
sceneInfra.setCallbacks({
|
|
||||||
onDrag: ({ selected, intersectionPoint, mouseEvent, intersects }) => {
|
return {
|
||||||
if (mouseEvent.which !== 1) return
|
truncatedAst,
|
||||||
this.onDragSegment({
|
programMemoryOverride,
|
||||||
object: selected,
|
sketchGroup,
|
||||||
intersection2d: intersectionPoint.twoD,
|
variableDeclarationName,
|
||||||
intersects,
|
|
||||||
sketchPathToNode,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onMove: () => {},
|
|
||||||
onClick: (args) => {
|
|
||||||
if (args?.mouseEvent.which !== 1) return
|
|
||||||
if (!args || !args.selected) {
|
|
||||||
sceneInfra.modelingSend({
|
|
||||||
type: 'Set selection',
|
|
||||||
data: {
|
|
||||||
selectionType: 'singleCodeCursor',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
const { selected } = args
|
}
|
||||||
const event = getEventForSegmentSelection(selected)
|
updateAstAndRejigSketch = async (
|
||||||
if (!event) return
|
sketchPathToNode: PathToNode,
|
||||||
sceneInfra.modelingSend(event)
|
modifiedAst: Program,
|
||||||
},
|
forward: [number, number, number],
|
||||||
...mouseEnterLeaveCallbacks(),
|
up: [number, number, number],
|
||||||
|
origin: [number, number, number]
|
||||||
|
) => {
|
||||||
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
|
await this.tearDownSketch({ removeAxis: false })
|
||||||
|
await this.setupSketch({
|
||||||
|
sketchPathToNode,
|
||||||
|
forward,
|
||||||
|
up,
|
||||||
|
position: origin,
|
||||||
|
maybeModdedAst: kclManager.ast,
|
||||||
|
})
|
||||||
|
this.setupSketchIdleCallbacks(sketchPathToNode)
|
||||||
|
}
|
||||||
|
setUpDraftSegment = async (
|
||||||
|
sketchPathToNode: PathToNode,
|
||||||
|
forward: [number, number, number],
|
||||||
|
up: [number, number, number],
|
||||||
|
origin: [number, number, number],
|
||||||
|
segmentName: 'line' | 'tangentialArcTo' = 'line',
|
||||||
|
shouldTearDown = true
|
||||||
|
) => {
|
||||||
|
const _ast = JSON.parse(JSON.stringify(kclManager.ast))
|
||||||
|
|
||||||
|
const variableDeclarationName =
|
||||||
|
getNodeFromPath<VariableDeclaration>(
|
||||||
|
_ast,
|
||||||
|
sketchPathToNode || [],
|
||||||
|
'VariableDeclaration'
|
||||||
|
)?.node?.declarations?.[0]?.id?.name || ''
|
||||||
|
const sg = kclManager.programMemory.root[
|
||||||
|
variableDeclarationName
|
||||||
|
] as SketchGroup
|
||||||
|
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`
|
||||||
|
|
||||||
|
let modifiedAst = addNewSketchLn({
|
||||||
|
node: kclManager.ast,
|
||||||
|
programMemory: kclManager.programMemory,
|
||||||
|
to: [lastSeg.to[0], lastSeg.to[1]],
|
||||||
|
from: [lastSeg.to[0], lastSeg.to[1]],
|
||||||
|
fnName: segmentName,
|
||||||
|
pathToNode: sketchPathToNode,
|
||||||
|
}).modifiedAst
|
||||||
|
modifiedAst = parse(recast(modifiedAst))
|
||||||
|
|
||||||
|
const draftExpressionsIndices = { start: index, end: index }
|
||||||
|
|
||||||
|
if (shouldTearDown) await this.tearDownSketch({ removeAxis: false })
|
||||||
|
const { truncatedAst, programMemoryOverride, sketchGroup } =
|
||||||
|
await this.setupSketch({
|
||||||
|
sketchPathToNode,
|
||||||
|
forward,
|
||||||
|
up,
|
||||||
|
position: origin,
|
||||||
|
maybeModdedAst: modifiedAst,
|
||||||
|
draftExpressionsIndices,
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: async (args) => {
|
onClick: async (args) => {
|
||||||
if (!args) return
|
if (!args) return
|
||||||
@ -451,9 +524,14 @@ class SceneEntities {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
kclManager.executeAstMock(modifiedAst, { updates: 'code' })
|
await kclManager.executeAstMock(modifiedAst, { updates: 'code' })
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
this.setUpDraftSegment(
|
||||||
this.setupSketch({ sketchPathToNode, draftSegment })
|
sketchPathToNode,
|
||||||
|
forward,
|
||||||
|
up,
|
||||||
|
origin,
|
||||||
|
segmentName
|
||||||
|
)
|
||||||
},
|
},
|
||||||
onMove: (args) => {
|
onMove: (args) => {
|
||||||
this.onDragSegment({
|
this.onDragSegment({
|
||||||
@ -462,7 +540,6 @@ class SceneEntities {
|
|||||||
intersects: args.intersects,
|
intersects: args.intersects,
|
||||||
sketchPathToNode,
|
sketchPathToNode,
|
||||||
draftInfo: {
|
draftInfo: {
|
||||||
draftSegment,
|
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
variableDeclarationName,
|
variableDeclarationName,
|
||||||
@ -472,27 +549,37 @@ class SceneEntities {
|
|||||||
...mouseEnterLeaveCallbacks(),
|
...mouseEnterLeaveCallbacks(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
sceneInfra.camControls.enableRotate = false
|
setupSketchIdleCallbacks = (pathToNode: PathToNode) => {
|
||||||
|
sceneInfra.setCallbacks({
|
||||||
|
onDrag: ({ selected, intersectionPoint, mouseEvent, intersects }) => {
|
||||||
|
if (mouseEvent.which !== 1) return
|
||||||
|
this.onDragSegment({
|
||||||
|
object: selected,
|
||||||
|
intersection2d: intersectionPoint.twoD,
|
||||||
|
intersects,
|
||||||
|
sketchPathToNode: pathToNode,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onMove: () => {},
|
||||||
|
onClick: (args) => {
|
||||||
|
if (args?.mouseEvent.which !== 1) return
|
||||||
|
if (!args || !args.selected) {
|
||||||
|
sceneInfra.modelingSend({
|
||||||
|
type: 'Set selection',
|
||||||
|
data: {
|
||||||
|
selectionType: 'singleCodeCursor',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
updateAstAndRejigSketch = async (
|
const { selected } = args
|
||||||
sketchPathToNode: PathToNode,
|
const event = getEventForSegmentSelection(selected)
|
||||||
modifiedAst: Program
|
if (!event) return
|
||||||
) => {
|
sceneInfra.modelingSend(event)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
},
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
...mouseEnterLeaveCallbacks(),
|
||||||
this.setupSketch({ sketchPathToNode })
|
})
|
||||||
}
|
}
|
||||||
setUpDraftArc = async (sketchPathToNode: PathToNode) => {
|
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
||||||
this.setupSketch({ sketchPathToNode, draftSegment: 'tangentialArcTo' })
|
|
||||||
}
|
|
||||||
setUpDraftLine = async (sketchPathToNode: PathToNode) => {
|
|
||||||
await this.tearDownSketch({ removeAxis: false })
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
|
||||||
this.setupSketch({ sketchPathToNode, draftSegment: 'line' })
|
|
||||||
}
|
|
||||||
onDraftLineMouseMove = () => {}
|
|
||||||
prepareTruncatedMemoryAndAst = (
|
prepareTruncatedMemoryAndAst = (
|
||||||
sketchPathToNode: PathToNode,
|
sketchPathToNode: PathToNode,
|
||||||
ast?: Program,
|
ast?: Program,
|
||||||
@ -516,7 +603,6 @@ class SceneEntities {
|
|||||||
sketchPathToNode: PathToNode
|
sketchPathToNode: PathToNode
|
||||||
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
||||||
draftInfo?: {
|
draftInfo?: {
|
||||||
draftSegment: DraftSegment
|
|
||||||
truncatedAst: Program
|
truncatedAst: Program
|
||||||
programMemoryOverride: ProgramMemory
|
programMemoryOverride: ProgramMemory
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
@ -595,7 +681,7 @@ class SceneEntities {
|
|||||||
const { programMemory } = await executeAst({
|
const { programMemory } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
useFakeExecutor: true,
|
useFakeExecutor: true,
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
@ -785,10 +871,10 @@ class SceneEntities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async animateAfterSketch() {
|
async animateAfterSketch() {
|
||||||
if (isReducedMotion()) {
|
// if (isReducedMotion()) {
|
||||||
sceneInfra.camControls.usePerspectiveCamera()
|
// sceneInfra.camControls.usePerspectiveCamera()
|
||||||
return
|
// return
|
||||||
}
|
// }
|
||||||
await sceneInfra.camControls.animateToPerspective()
|
await sceneInfra.camControls.animateToPerspective()
|
||||||
}
|
}
|
||||||
removeSketchGrid() {
|
removeSketchGrid() {
|
||||||
@ -853,26 +939,81 @@ class SceneEntities {
|
|||||||
const type: DefaultPlane = selected.userData.type
|
const type: DefaultPlane = selected.userData.type
|
||||||
selected.material.color = defaultPlaneColor(type)
|
selected.material.color = defaultPlaneColor(type)
|
||||||
},
|
},
|
||||||
onClick: (args) => {
|
onClick: async (args) => {
|
||||||
|
const checkExtrudeFaceClick = async (): Promise<boolean> => {
|
||||||
|
const { streamDimensions } = useStore.getState()
|
||||||
|
const { entity_id } = await sendSelectEventToEngine(
|
||||||
|
args?.mouseEvent,
|
||||||
|
document.getElementById('video-stream') as HTMLVideoElement,
|
||||||
|
streamDimensions
|
||||||
|
)
|
||||||
|
if (!entity_id) return false
|
||||||
|
const artifact = this.engineCommandManager.artifactMap[entity_id]
|
||||||
|
if (artifact?.commandType !== 'solid3d_get_extrusion_face_info')
|
||||||
|
return false
|
||||||
|
const faceInfo: Models['FaceIsPlanar_type'] = (
|
||||||
|
await this.engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'face_is_planar',
|
||||||
|
object_id: entity_id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)?.data?.data
|
||||||
|
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||||
|
return false
|
||||||
|
const { z_axis, origin, y_axis } = faceInfo
|
||||||
|
const pathToNode = getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
artifact.range
|
||||||
|
)
|
||||||
|
|
||||||
|
sceneInfra.modelingSend({
|
||||||
|
type: 'Select default plane',
|
||||||
|
data: {
|
||||||
|
type: 'extrudeFace',
|
||||||
|
zAxis: [z_axis.x, z_axis.y, z_axis.z],
|
||||||
|
yAxis: [y_axis.x, y_axis.y, y_axis.z],
|
||||||
|
position: [origin.x, origin.y, origin.z].map(
|
||||||
|
(num) => num / sceneInfra._baseUnitMultiplier
|
||||||
|
) as [number, number, number],
|
||||||
|
extrudeSegmentPathToNode: pathToNode,
|
||||||
|
cap:
|
||||||
|
artifact?.additionalData?.type === 'cap'
|
||||||
|
? artifact.additionalData.info
|
||||||
|
: 'none',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await checkExtrudeFaceClick()) return
|
||||||
|
|
||||||
if (!args || !args.intersects?.[0]) return
|
if (!args || !args.intersects?.[0]) return
|
||||||
if (args.mouseEvent.which !== 1) return
|
if (args.mouseEvent.which !== 1) return
|
||||||
const { intersects } = args
|
const { intersects } = args
|
||||||
const type = intersects?.[0].object.name || ''
|
const type = intersects?.[0].object.name || ''
|
||||||
const posNorm = Number(intersects?.[0]?.normal?.z) > 0
|
const posNorm = Number(intersects?.[0]?.normal?.z) > 0
|
||||||
let planeString: DefaultPlaneStr = posNorm ? 'XY' : '-XY'
|
let planeString: DefaultPlaneStr = posNorm ? 'XY' : '-XY'
|
||||||
let normal: [number, number, number] = posNorm ? [0, 0, 1] : [0, 0, -1]
|
let zAxis: [number, number, number] = posNorm ? [0, 0, 1] : [0, 0, -1]
|
||||||
|
let yAxis: [number, number, number] = [0, 1, 0]
|
||||||
if (type === YZ_PLANE) {
|
if (type === YZ_PLANE) {
|
||||||
planeString = posNorm ? 'YZ' : '-YZ'
|
planeString = posNorm ? 'YZ' : '-YZ'
|
||||||
normal = posNorm ? [1, 0, 0] : [-1, 0, 0]
|
zAxis = posNorm ? [1, 0, 0] : [-1, 0, 0]
|
||||||
|
yAxis = [0, 0, 1]
|
||||||
} else if (type === XZ_PLANE) {
|
} else if (type === XZ_PLANE) {
|
||||||
planeString = posNorm ? 'XZ' : '-XZ'
|
planeString = posNorm ? 'XZ' : '-XZ'
|
||||||
normal = posNorm ? [0, 1, 0] : [0, -1, 0]
|
zAxis = posNorm ? [0, 1, 0] : [0, -1, 0]
|
||||||
|
yAxis = [0, 0, 1]
|
||||||
}
|
}
|
||||||
sceneInfra.modelingSend({
|
sceneInfra.modelingSend({
|
||||||
type: 'Select default plane',
|
type: 'Select default plane',
|
||||||
data: {
|
data: {
|
||||||
|
type: 'defaultPlane',
|
||||||
plane: planeString,
|
plane: planeString,
|
||||||
normal,
|
zAxis,
|
||||||
|
yAxis,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@ -882,8 +1023,6 @@ class SceneEntities {
|
|||||||
|
|
||||||
export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
export type DefaultPlaneStr = 'XY' | 'XZ' | 'YZ' | '-XY' | '-XZ' | '-YZ'
|
||||||
|
|
||||||
export const sceneEntitiesManager = new SceneEntities()
|
|
||||||
|
|
||||||
// calculations/pure-functions/easy to test so no excuse not to
|
// calculations/pure-functions/easy to test so no excuse not to
|
||||||
|
|
||||||
function prepareTruncatedMemoryAndAst(
|
function prepareTruncatedMemoryAndAst(
|
||||||
@ -1005,30 +1144,6 @@ export function sketchGroupFromPathToNode({
|
|||||||
return programMemory.root[varDec?.id?.name || ''] as SketchGroup
|
return programMemory.root[varDec?.id?.name || ''] as SketchGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
export function quaternionFromSketchGroup(
|
|
||||||
sketchGroup: SketchGroup
|
|
||||||
): Quaternion {
|
|
||||||
// TODO figure what is happening in the executor that it's some times returning
|
|
||||||
// [x,y,z] and sometimes {x,y,z}
|
|
||||||
if (!sketchGroup?.zAxis) {
|
|
||||||
// sometimes sketchGroup is undefined,
|
|
||||||
// I don't quiet understand the circumstances yet
|
|
||||||
// and it's very intermittent so leaving this here for now
|
|
||||||
console.log('no zAxis', sketchGroup)
|
|
||||||
console.trace('no zAxis')
|
|
||||||
}
|
|
||||||
const zAxisVec = massageFormats(sketchGroup?.zAxis)
|
|
||||||
const yAxisVec = massageFormats(sketchGroup?.yAxis)
|
|
||||||
const xAxisVec = new Vector3().crossVectors(yAxisVec, zAxisVec).normalize()
|
|
||||||
|
|
||||||
let yAxisVecNormalized = yAxisVec.clone().normalize()
|
|
||||||
let zAxisVecNormalized = zAxisVec.clone().normalize()
|
|
||||||
|
|
||||||
let rotationMatrix = new Matrix4()
|
|
||||||
rotationMatrix.makeBasis(xAxisVec, yAxisVecNormalized, zAxisVecNormalized)
|
|
||||||
return new Quaternion().setFromRotationMatrix(rotationMatrix)
|
|
||||||
}
|
|
||||||
|
|
||||||
function colorSegment(object: any, color: number) {
|
function colorSegment(object: any, color: number) {
|
||||||
const segmentHead = getParentGroup(object, [ARROWHEAD, PROFILE_START])
|
const segmentHead = getParentGroup(object, [ARROWHEAD, PROFILE_START])
|
||||||
if (segmentHead) {
|
if (segmentHead) {
|
||||||
@ -1063,10 +1178,68 @@ export function getSketchQuaternion(
|
|||||||
programMemory: kclManager.programMemory,
|
programMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
const zAxis = sketchGroup?.zAxis || sketchNormalBackUp
|
const zAxis = sketchGroup?.zAxis || sketchNormalBackUp
|
||||||
|
return getQuaternionFromZAxis(massageFormats(zAxis))
|
||||||
|
}
|
||||||
|
export async function getSketchOrientationDetails(
|
||||||
|
sketchPathToNode: PathToNode
|
||||||
|
): Promise<{
|
||||||
|
quat: Quaternion
|
||||||
|
sketchDetails: SketchDetails
|
||||||
|
}> {
|
||||||
|
const sketchGroup = sketchGroupFromPathToNode({
|
||||||
|
pathToNode: sketchPathToNode,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
programMemory: kclManager.programMemory,
|
||||||
|
})
|
||||||
|
if (sketchGroup.on.type === 'plane') {
|
||||||
|
const zAxis = sketchGroup?.zAxis
|
||||||
|
return {
|
||||||
|
quat: getQuaternionFromZAxis(massageFormats(zAxis)),
|
||||||
|
sketchDetails: {
|
||||||
|
sketchPathToNode,
|
||||||
|
zAxis: [zAxis.x, zAxis.y, zAxis.z],
|
||||||
|
yAxis: [sketchGroup.yAxis.x, sketchGroup.yAxis.y, sketchGroup.yAxis.z],
|
||||||
|
origin: [0, 0, 0],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sketchGroup.on.type === 'face') {
|
||||||
|
const faceInfo: Models['FaceIsPlanar_type'] = (
|
||||||
|
await engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'face_is_planar',
|
||||||
|
object_id: sketchGroup.on.faceId,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)?.data?.data
|
||||||
|
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||||
|
throw new Error('faceInfo')
|
||||||
|
const { z_axis, y_axis, origin } = faceInfo
|
||||||
|
const quaternion = quaternionFromUpNForward(
|
||||||
|
new Vector3(y_axis.x, y_axis.y, y_axis.z),
|
||||||
|
new Vector3(z_axis.x, z_axis.y, z_axis.z)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
quat: quaternion,
|
||||||
|
sketchDetails: {
|
||||||
|
sketchPathToNode,
|
||||||
|
zAxis: [z_axis.x, z_axis.y, z_axis.z],
|
||||||
|
yAxis: [y_axis.x, y_axis.y, y_axis.z],
|
||||||
|
origin: [origin.x, origin.y, origin.z],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
'sketchGroup.on.type not recognized, has a new type been added?'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion {
|
||||||
const dummyCam = new PerspectiveCamera()
|
const dummyCam = new PerspectiveCamera()
|
||||||
dummyCam.up.set(0, 0, 1)
|
dummyCam.up.set(0, 0, 1)
|
||||||
const _zAxis = massageFormats(zAxis)
|
dummyCam.position.copy(zAxis)
|
||||||
dummyCam.position.copy(_zAxis)
|
|
||||||
dummyCam.lookAt(0, 0, 0)
|
dummyCam.lookAt(0, 0, 0)
|
||||||
dummyCam.updateMatrix()
|
dummyCam.updateMatrix()
|
||||||
const quaternion = dummyCam.quaternion.clone()
|
const quaternion = dummyCam.quaternion.clone()
|
||||||
@ -1075,7 +1248,7 @@ export function getSketchQuaternion(
|
|||||||
|
|
||||||
// because vertical quaternions are a gimbal lock, for the orbit controls
|
// because vertical quaternions are a gimbal lock, for the orbit controls
|
||||||
// it's best to set them explicitly to the vertical position with a known good camera up
|
// it's best to set them explicitly to the vertical position with a known good camera up
|
||||||
if (isVert && _zAxis.z < 0) {
|
if (isVert && zAxis.z < 0) {
|
||||||
quaternion.set(0, 1, 0, 0)
|
quaternion.set(0, 1, 0, 0)
|
||||||
} else if (isVert) {
|
} else if (isVert) {
|
||||||
quaternion.set(0, 0, 0, 1)
|
quaternion.set(0, 0, 0, 1)
|
||||||
|
@ -27,6 +27,7 @@ import { Axis } from 'lib/selections'
|
|||||||
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
import { type BaseUnit } from 'lib/settings/settingsTypes'
|
||||||
import { SETTINGS_PERSIST_KEY } from 'lib/constants'
|
import { SETTINGS_PERSIST_KEY } from 'lib/constants'
|
||||||
import { CameraControls } from './CameraControls'
|
import { CameraControls } from './CameraControls'
|
||||||
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
|
|
||||||
type SendType = ReturnType<typeof useModelingContext>['send']
|
type SendType = ReturnType<typeof useModelingContext>['send']
|
||||||
|
|
||||||
@ -37,8 +38,10 @@ export const ZOOM_MAGIC_NUMBER = 63.5
|
|||||||
|
|
||||||
export const INTERSECTION_PLANE_LAYER = 1
|
export const INTERSECTION_PLANE_LAYER = 1
|
||||||
export const SKETCH_LAYER = 2
|
export const SKETCH_LAYER = 2
|
||||||
export const DEBUG_SHOW_INTERSECTION_PLANE = false
|
|
||||||
export const DEBUG_SHOW_BOTH_SCENES = false
|
// redundant types so that it can be changed temporarily but CI will catch the wrong type
|
||||||
|
export const DEBUG_SHOW_INTERSECTION_PLANE: false = false
|
||||||
|
export const DEBUG_SHOW_BOTH_SCENES: false = false
|
||||||
|
|
||||||
export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
export const RAYCASTABLE_PLANE = 'raycastable-plane'
|
||||||
export const DEFAULT_PLANES = 'default-planes'
|
export const DEFAULT_PLANES = 'default-planes'
|
||||||
@ -84,7 +87,7 @@ 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
|
||||||
class SceneInfra {
|
export class SceneInfra {
|
||||||
static instance: SceneInfra
|
static instance: SceneInfra
|
||||||
scene: Scene
|
scene: Scene
|
||||||
renderer: WebGLRenderer
|
renderer: WebGLRenderer
|
||||||
@ -97,13 +100,13 @@ class SceneInfra {
|
|||||||
_baseUnitMultiplier = 1
|
_baseUnitMultiplier = 1
|
||||||
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
||||||
onMoveCallback: (arg: OnMoveCallbackArgs) => void = () => {}
|
onMoveCallback: (arg: OnMoveCallbackArgs) => void = () => {}
|
||||||
onClickCallback: (arg?: OnClickCallbackArgs) => void = () => {}
|
onClickCallback: (arg: OnClickCallbackArgs) => void = () => {}
|
||||||
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
onMouseEnter: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
||||||
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
onMouseLeave: (arg: OnMouseEnterLeaveArgs) => void = () => {}
|
||||||
setCallbacks = (callbacks: {
|
setCallbacks = (callbacks: {
|
||||||
onDrag?: (arg: OnDragCallbackArgs) => void
|
onDrag?: (arg: OnDragCallbackArgs) => void
|
||||||
onMove?: (arg: OnMoveCallbackArgs) => void
|
onMove?: (arg: OnMoveCallbackArgs) => void
|
||||||
onClick?: (arg?: OnClickCallbackArgs) => void
|
onClick?: (arg: OnClickCallbackArgs) => void
|
||||||
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => void
|
onMouseEnter?: (arg: OnMouseEnterLeaveArgs) => void
|
||||||
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => void
|
onMouseLeave?: (arg: OnMouseEnterLeaveArgs) => void
|
||||||
}) => {
|
}) => {
|
||||||
@ -124,7 +127,7 @@ class SceneInfra {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
resetMouseListeners = () => {
|
resetMouseListeners = () => {
|
||||||
sceneInfra.setCallbacks({
|
this.setCallbacks({
|
||||||
onDrag: () => {},
|
onDrag: () => {},
|
||||||
onMove: () => {},
|
onMove: () => {},
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
@ -153,7 +156,7 @@ class SceneInfra {
|
|||||||
} | null = null
|
} | null = null
|
||||||
mouseDownVector: null | Vector2 = null
|
mouseDownVector: null | Vector2 = null
|
||||||
|
|
||||||
constructor() {
|
constructor(engineCommandManager: EngineCommandManager) {
|
||||||
// SCENE
|
// SCENE
|
||||||
this.scene = new Scene()
|
this.scene = new Scene()
|
||||||
this.scene.background = new Color(0x000000)
|
this.scene.background = new Color(0x000000)
|
||||||
@ -176,7 +179,11 @@ class SceneInfra {
|
|||||||
const x = Math.cos(ang) * length
|
const x = Math.cos(ang) * length
|
||||||
const y = Math.sin(ang) * length
|
const y = Math.sin(ang) * length
|
||||||
|
|
||||||
this.camControls = new CameraControls(false, this.renderer.domElement)
|
this.camControls = new CameraControls(
|
||||||
|
false,
|
||||||
|
this.renderer.domElement,
|
||||||
|
engineCommandManager
|
||||||
|
)
|
||||||
this.camControls.subscribeToCamChange(() => this.onCameraChange())
|
this.camControls.subscribeToCamChange(() => this.onCameraChange())
|
||||||
this.camControls.camera.layers.enable(SKETCH_LAYER)
|
this.camControls.camera.layers.enable(SKETCH_LAYER)
|
||||||
this.camControls.camera.position.set(0, -x, y)
|
this.camControls.camera.position.set(0, -x, y)
|
||||||
@ -219,9 +226,9 @@ class SceneInfra {
|
|||||||
?.getObjectByName('gridHelper')
|
?.getObjectByName('gridHelper')
|
||||||
planesGroup &&
|
planesGroup &&
|
||||||
planesGroup.scale.set(
|
planesGroup.scale.set(
|
||||||
scale / sceneInfra._baseUnitMultiplier,
|
scale / this._baseUnitMultiplier,
|
||||||
scale / sceneInfra._baseUnitMultiplier,
|
scale / this._baseUnitMultiplier,
|
||||||
scale / sceneInfra._baseUnitMultiplier
|
scale / this._baseUnitMultiplier
|
||||||
)
|
)
|
||||||
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
||||||
}
|
}
|
||||||
@ -253,7 +260,7 @@ class SceneInfra {
|
|||||||
} | null => {
|
} | null => {
|
||||||
this.planeRaycaster.setFromCamera(
|
this.planeRaycaster.setFromCamera(
|
||||||
this.currentMouseVector,
|
this.currentMouseVector,
|
||||||
sceneInfra.camControls.camera
|
this.camControls.camera
|
||||||
)
|
)
|
||||||
const planeIntersects = this.planeRaycaster.intersectObjects(
|
const planeIntersects = this.planeRaycaster.intersectObjects(
|
||||||
this.scene.children,
|
this.scene.children,
|
||||||
@ -272,16 +279,19 @@ class SceneInfra {
|
|||||||
let transformedPoint = intersectPoint.clone()
|
let transformedPoint = intersectPoint.clone()
|
||||||
if (transformedPoint) {
|
if (transformedPoint) {
|
||||||
transformedPoint.applyQuaternion(inversePlaneQuaternion)
|
transformedPoint.applyQuaternion(inversePlaneQuaternion)
|
||||||
transformedPoint?.sub(
|
|
||||||
new Vector3(...planePosition).applyQuaternion(inversePlaneQuaternion)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
const twoD = new Vector2(
|
||||||
return {
|
// I think the intersection plane doesn't get scale when nearly everything else does, maybe that should change
|
||||||
twoD: new Vector2(
|
|
||||||
transformedPoint.x / this._baseUnitMultiplier,
|
transformedPoint.x / this._baseUnitMultiplier,
|
||||||
transformedPoint.y / this._baseUnitMultiplier
|
transformedPoint.y / this._baseUnitMultiplier
|
||||||
), // z should be 0
|
) // z should be 0
|
||||||
|
const planePositionCorrected = new Vector3(
|
||||||
|
...planePosition
|
||||||
|
).applyQuaternion(inversePlaneQuaternion)
|
||||||
|
twoD.sub(new Vector2(...planePositionCorrected))
|
||||||
|
|
||||||
|
return {
|
||||||
|
twoD,
|
||||||
threeD: intersectPoint.divideScalar(this._baseUnitMultiplier),
|
threeD: intersectPoint.divideScalar(this._baseUnitMultiplier),
|
||||||
intersection: planeIntersects[0],
|
intersection: planeIntersects[0],
|
||||||
}
|
}
|
||||||
@ -464,7 +474,7 @@ class SceneInfra {
|
|||||||
intersects,
|
intersects,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.onClickCallback()
|
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
|
||||||
@ -478,7 +488,7 @@ class SceneInfra {
|
|||||||
intersects,
|
intersects,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
this.onClickCallback()
|
this.onClickCallback({ mouseEvent, intersects })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showDefaultPlanes() {
|
showDefaultPlanes() {
|
||||||
@ -522,9 +532,9 @@ class SceneInfra {
|
|||||||
this.camControls.target
|
this.camControls.target
|
||||||
)
|
)
|
||||||
planesGroup.scale.set(
|
planesGroup.scale.set(
|
||||||
sceneScale / sceneInfra._baseUnitMultiplier,
|
sceneScale / this._baseUnitMultiplier,
|
||||||
sceneScale / sceneInfra._baseUnitMultiplier,
|
sceneScale / this._baseUnitMultiplier,
|
||||||
sceneScale / sceneInfra._baseUnitMultiplier
|
sceneScale / this._baseUnitMultiplier
|
||||||
)
|
)
|
||||||
this.scene.add(planesGroup)
|
this.scene.add(planesGroup)
|
||||||
}
|
}
|
||||||
@ -535,7 +545,7 @@ class SceneInfra {
|
|||||||
if (planesGroup) this.scene.remove(planesGroup)
|
if (planesGroup) this.scene.remove(planesGroup)
|
||||||
}
|
}
|
||||||
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
updateOtherSelectionColors = (otherSelections: Axis[]) => {
|
||||||
const axisGroup = sceneInfra.scene.children.find(
|
const axisGroup = this.scene.children.find(
|
||||||
({ userData }) => userData?.type === AXIS_GROUP
|
({ userData }) => userData?.type === AXIS_GROUP
|
||||||
)
|
)
|
||||||
const axisMap: { [key: string]: Axis } = {
|
const axisMap: { [key: string]: Axis } = {
|
||||||
@ -557,8 +567,6 @@ class SceneInfra {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const sceneInfra = new SceneInfra()
|
|
||||||
|
|
||||||
export function getSceneScale(
|
export function getSceneScale(
|
||||||
camera: PerspectiveCamera | OrthographicCamera,
|
camera: PerspectiveCamera | OrthographicCamera,
|
||||||
target: Vector3
|
target: Vector3
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { useEffect, useRef, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
|
@ -7,8 +7,8 @@ import {
|
|||||||
findUniqueName,
|
findUniqueName,
|
||||||
} from '../lang/modifyAst'
|
} from '../lang/modifyAst'
|
||||||
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { executeAst } from 'useStore'
|
import { executeAst } from 'useStore'
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { sceneInfra } from '../clientSideScene/sceneInfra'
|
import { engineCommandManager, sceneInfra } from 'lib/singletons'
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
|
||||||
import { throttle, isReducedMotion } from 'lib/utils'
|
import { throttle, isReducedMotion } from 'lib/utils'
|
||||||
|
|
||||||
const updateDollyZoom = throttle(
|
const updateDollyZoom = throttle(
|
||||||
|
@ -6,7 +6,7 @@ import styles from './CodeMenu.module.css'
|
|||||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||||
import { editorShortcutMeta } from './TextEditor'
|
import { editorShortcutMeta } from './TextEditor'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export const CodeMenu = ({ children }: PropsWithChildren) => {
|
export const CodeMenu = ({ children }: PropsWithChildren) => {
|
||||||
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
||||||
@ -77,7 +77,7 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
|
|||||||
<Menu.Item>
|
<Menu.Item>
|
||||||
<a
|
<a
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
href="https://github.com/KittyCAD/kcl-samples"
|
href="https://zoo.dev/docs/kcl-samples"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
>
|
>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { CommandArgument } from 'lib/commandTypes'
|
import { CommandArgument } from 'lib/commandTypes'
|
||||||
import {
|
import {
|
||||||
ResolvedSelectionType,
|
ResolvedSelectionType,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { CommandLog, engineCommandManager } from 'lang/std/engineConnection'
|
import { CommandLog } from 'lang/std/engineConnection'
|
||||||
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { useState, useEffect } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
|
|
||||||
function useEngineCommands(): [CommandLog[], () => void] {
|
function useEngineCommands(): [CommandLog[], () => void] {
|
||||||
|
@ -13,7 +13,7 @@ import { useHotkeys } from 'react-hotkeys-hook'
|
|||||||
import styles from './FileTree.module.css'
|
import styles from './FileTree.module.css'
|
||||||
import { FILE_EXT, sortProject } from 'lib/tauriFS'
|
import { FILE_EXT, sortProject } from 'lib/tauriFS'
|
||||||
import { CustomIcon } from './CustomIcon'
|
import { CustomIcon } from './CustomIcon'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus'
|
import { useDocumentHasFocus } from 'hooks/useDocumentHasFocus'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||||
import { Themes } from '../lib/theme'
|
import { Themes } from '../lib/theme'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
|
|
||||||
const ReactJsonTypeHack = ReactJson as any
|
const ReactJsonTypeHack = ReactJson as any
|
||||||
|
|
||||||
|
@ -38,7 +38,26 @@ describe('processMemory', () => {
|
|||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: undefined,
|
myFn: undefined,
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
theExtrude: [],
|
theExtrude: [
|
||||||
|
{
|
||||||
|
type: 'extrudePlane',
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0, 0, 0, 1],
|
||||||
|
faceId: expect.any(String),
|
||||||
|
name: '',
|
||||||
|
id: expect.any(String),
|
||||||
|
sourceRange: [170, 194],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'extrudePlane',
|
||||||
|
position: [0, 0, 0],
|
||||||
|
rotation: [0, 0, 0, 1],
|
||||||
|
faceId: expect.any(String),
|
||||||
|
name: '',
|
||||||
|
id: expect.any(String),
|
||||||
|
sourceRange: [202, 230],
|
||||||
|
},
|
||||||
|
],
|
||||||
theSketch: [
|
theSketch: [
|
||||||
{ type: 'ToPoint', to: [-3.35, 0.17], from: [0, 0], name: '' },
|
{ type: 'ToPoint', to: [-3.35, 0.17], from: [0, 0], name: '' },
|
||||||
{ type: 'ToPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' },
|
{ type: 'ToPoint', to: [0.98, 5.16], from: [-3.35, 0.17], name: '' },
|
||||||
|
@ -3,7 +3,7 @@ import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
|||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
||||||
import { Themes } from '../lib/theme'
|
import { Themes } from '../lib/theme'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
|
|
||||||
interface MemoryPanelProps extends CollapsiblePanelProps {
|
interface MemoryPanelProps extends CollapsiblePanelProps {
|
||||||
theme?: Exclude<Themes, Themes.System>
|
theme?: Exclude<Themes, Themes.System>
|
||||||
|
@ -12,8 +12,8 @@ import { SetSelections, modelingMachine } from 'machines/modelingMachine'
|
|||||||
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { isCursorInSketchCommandRange } from 'lang/util'
|
import { isCursorInSketchCommandRange } from 'lang/util'
|
||||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
import { kclManager, sceneInfra, engineCommandManager } from 'lib/singletons'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||||
import {
|
import {
|
||||||
angleBetweenInfo,
|
angleBetweenInfo,
|
||||||
@ -23,9 +23,9 @@ import { applyConstraintAngleLength } from './Toolbar/setAngleLength'
|
|||||||
import { pathMapToSelections } from 'lang/util'
|
import { pathMapToSelections } from 'lang/util'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
import {
|
import {
|
||||||
|
Selections,
|
||||||
canExtrudeSelection,
|
canExtrudeSelection,
|
||||||
handleSelectionBatch,
|
handleSelectionBatch,
|
||||||
handleSelectionWithShift,
|
|
||||||
isSelectionLastLine,
|
isSelectionLastLine,
|
||||||
isSketchPipe,
|
isSketchPipe,
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
@ -33,15 +33,20 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
|
|||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
import {
|
||||||
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
|
getSketchOrientationDetails,
|
||||||
import { startSketchOnDefault } from 'lang/modifyAst'
|
getSketchQuaternion,
|
||||||
import { Program } from 'lang/wasm'
|
} from 'clientSideScene/sceneEntities'
|
||||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
import { sketchOnExtrudedFace, startSketchOnDefault } from 'lang/modifyAst'
|
||||||
|
import { Program, parse } from 'lang/wasm'
|
||||||
|
import { getNodePathFromSourceRange, isSingleCursorInPipe } from 'lang/queryAst'
|
||||||
import { TEST } from 'env'
|
import { TEST } from 'env'
|
||||||
import { exportFromEngine } from 'lib/exportFromEngine'
|
import { exportFromEngine } from 'lib/exportFromEngine'
|
||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
|
import { EditorSelection } from '@uiw/react-codemirror'
|
||||||
|
import { Vector3 } from 'three'
|
||||||
|
import { quaternionFromUpNForward } from 'clientSideScene/helpers'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -61,17 +66,23 @@ export const ModelingMachineProvider = ({
|
|||||||
const {
|
const {
|
||||||
auth,
|
auth,
|
||||||
settings: {
|
settings: {
|
||||||
context: { baseUnit },
|
context: { baseUnit, theme },
|
||||||
},
|
},
|
||||||
} = useSettingsAuthContext()
|
} = useSettingsAuthContext()
|
||||||
const { code } = useKclContext()
|
const { code } = useKclContext()
|
||||||
const token = auth?.context?.token
|
const token = auth?.context?.token
|
||||||
const streamRef = useRef<HTMLDivElement>(null)
|
const streamRef = useRef<HTMLDivElement>(null)
|
||||||
useSetupEngineManager(streamRef, token)
|
useSetupEngineManager(streamRef, token, theme)
|
||||||
|
|
||||||
const { isShiftDown, editorView } = useStore((s) => ({
|
const {
|
||||||
|
isShiftDown,
|
||||||
|
editorView,
|
||||||
|
setLastCodeMirrorSelectionUpdatedFromScene,
|
||||||
|
} = useStore((s) => ({
|
||||||
isShiftDown: s.isShiftDown,
|
isShiftDown: s.isShiftDown,
|
||||||
editorView: s.editorView,
|
editorView: s.editorView,
|
||||||
|
setLastCodeMirrorSelectionUpdatedFromScene:
|
||||||
|
s.setLastCodeMirrorSelectionUpdatedFromScene,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Settings machine setup
|
// Settings machine setup
|
||||||
@ -92,92 +103,98 @@ export const ModelingMachineProvider = ({
|
|||||||
{
|
{
|
||||||
actions: {
|
actions: {
|
||||||
'sketch exit execute': () => {
|
'sketch exit execute': () => {
|
||||||
|
try {
|
||||||
|
kclManager.executeAst(parse(kclManager.code))
|
||||||
|
} catch (e) {
|
||||||
kclManager.executeAst()
|
kclManager.executeAst()
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'Set selection': assign(({ selectionRanges }, event) => {
|
'Set selection': assign(({ selectionRanges }, event) => {
|
||||||
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
if (event.type !== 'Set selection') return {} // this was needed for ts after adding 'Set selection' action to on done modal events
|
||||||
const setSelections = event.data
|
const setSelections = event.data
|
||||||
if (!editorView) return {}
|
if (!editorView) return {}
|
||||||
if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
|
const dispatchSelection = (selection?: EditorSelection) => {
|
||||||
return { selectionRanges: setSelections.selection }
|
if (!selection) return // TODO less of hack for the below please
|
||||||
else if (setSelections.selectionType === 'otherSelection') {
|
setLastCodeMirrorSelectionUpdatedFromScene(Date.now())
|
||||||
const {
|
setTimeout(() => editorView.dispatch({ selection }))
|
||||||
codeMirrorSelection,
|
}
|
||||||
selectionRangeTypeMap,
|
let selections: Selections = {
|
||||||
otherSelections,
|
codeBasedSelections: [],
|
||||||
} = handleSelectionWithShift({
|
otherSelections: [],
|
||||||
otherSelection: setSelections.selection,
|
}
|
||||||
currentSelections: selectionRanges,
|
if (setSelections.selectionType === 'singleCodeCursor') {
|
||||||
isShiftDown,
|
if (!setSelections.selection && isShiftDown) {
|
||||||
})
|
} else if (!setSelections.selection && !isShiftDown) {
|
||||||
setTimeout(() => {
|
selections = {
|
||||||
editorView.dispatch({
|
codeBasedSelections: [],
|
||||||
selection: codeMirrorSelection,
|
otherSelections: [],
|
||||||
})
|
}
|
||||||
})
|
} else if (setSelections.selection && !isShiftDown) {
|
||||||
return {
|
selections = {
|
||||||
selectionRangeTypeMap,
|
codeBasedSelections: [setSelections.selection],
|
||||||
selectionRanges: {
|
otherSelections: [],
|
||||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
}
|
||||||
otherSelections,
|
} else if (setSelections.selection && isShiftDown) {
|
||||||
},
|
selections = {
|
||||||
|
codeBasedSelections: [
|
||||||
|
...selectionRanges.codeBasedSelections,
|
||||||
|
setSelections.selection,
|
||||||
|
],
|
||||||
|
otherSelections: selectionRanges.otherSelections,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (setSelections.selectionType === 'singleCodeCursor') {
|
|
||||||
// This DOES NOT set the `selectionRanges` in xstate context
|
|
||||||
// instead it updates/dispatches to the editor, which in turn updates the xstate context
|
|
||||||
// I've found this the best way to deal with the editor without causing an infinite loop
|
|
||||||
// and really we want the editor to be in charge of cursor positions and for `selectionRanges` mirror it
|
|
||||||
// because we want to respect the user manually placing the cursor too.
|
|
||||||
|
|
||||||
// for more details on how selections see `src/lib/selections.ts`.
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
engineEvents,
|
||||||
codeMirrorSelection,
|
codeMirrorSelection,
|
||||||
selectionRangeTypeMap,
|
updateSceneObjectColors,
|
||||||
otherSelections,
|
} = handleSelectionBatch({
|
||||||
} = handleSelectionWithShift({
|
selections,
|
||||||
codeSelection: setSelections.selection,
|
|
||||||
currentSelections: selectionRanges,
|
|
||||||
isShiftDown,
|
|
||||||
})
|
})
|
||||||
if (codeMirrorSelection) {
|
codeMirrorSelection && dispatchSelection(codeMirrorSelection)
|
||||||
setTimeout(() => {
|
engineEvents &&
|
||||||
editorView.dispatch({
|
engineEvents.forEach((event) =>
|
||||||
selection: codeMirrorSelection,
|
engineCommandManager.sendSceneCommand(event)
|
||||||
})
|
)
|
||||||
})
|
updateSceneObjectColors()
|
||||||
}
|
|
||||||
if (!setSelections.selection) {
|
|
||||||
return {
|
return {
|
||||||
selectionRangeTypeMap,
|
selectionRanges: selections,
|
||||||
selectionRanges: {
|
|
||||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
|
||||||
otherSelections,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (setSelections.selectionType === 'mirrorCodeMirrorSelections') {
|
||||||
return {
|
return {
|
||||||
selectionRangeTypeMap,
|
selectionRanges: setSelections.selection,
|
||||||
selectionRanges: {
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setSelections.selectionType === 'otherSelection') {
|
||||||
|
if (isShiftDown) {
|
||||||
|
selections = {
|
||||||
codeBasedSelections: selectionRanges.codeBasedSelections,
|
codeBasedSelections: selectionRanges.codeBasedSelections,
|
||||||
otherSelections,
|
otherSelections: [setSelections.selection],
|
||||||
},
|
}
|
||||||
|
} else {
|
||||||
|
selections = {
|
||||||
|
codeBasedSelections: [],
|
||||||
|
otherSelections: [setSelections.selection],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This DOES NOT set the `selectionRanges` in xstate context
|
const { engineEvents, updateSceneObjectColors } =
|
||||||
// same as comment above
|
|
||||||
const { codeMirrorSelection, selectionRangeTypeMap } =
|
|
||||||
handleSelectionBatch({
|
handleSelectionBatch({
|
||||||
selections: setSelections.selection,
|
selections,
|
||||||
})
|
|
||||||
if (codeMirrorSelection) {
|
|
||||||
setTimeout(() => {
|
|
||||||
editorView.dispatch({
|
|
||||||
selection: codeMirrorSelection,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
engineEvents &&
|
||||||
|
engineEvents.forEach((event) =>
|
||||||
|
engineCommandManager.sendSceneCommand(event)
|
||||||
|
)
|
||||||
|
updateSceneObjectColors()
|
||||||
|
return {
|
||||||
|
selectionRanges: selections,
|
||||||
}
|
}
|
||||||
return { selectionRangeTypeMap }
|
}
|
||||||
|
|
||||||
|
return {}
|
||||||
}),
|
}),
|
||||||
'Engine export': (_, event) => {
|
'Engine export': (_, event) => {
|
||||||
if (event.type !== 'Export' || TEST) return
|
if (event.type !== 'Export' || TEST) return
|
||||||
@ -255,10 +272,10 @@ export const ModelingMachineProvider = ({
|
|||||||
kclManager.kclErrors.length === 0 && kclManager.ast.body.length > 0,
|
kclManager.kclErrors.length === 0 && kclManager.ast.body.length > 0,
|
||||||
},
|
},
|
||||||
services: {
|
services: {
|
||||||
'AST-undo-startSketchOn': async ({ sketchPathToNode }) => {
|
'AST-undo-startSketchOn': async ({ sketchDetails }) => {
|
||||||
if (!sketchPathToNode) return
|
if (!sketchDetails) return
|
||||||
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
||||||
const varDecIndex = sketchPathToNode[1][0]
|
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
||||||
// remove body item at varDecIndex
|
// remove body item at varDecIndex
|
||||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||||
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
||||||
@ -267,28 +284,69 @@ export const ModelingMachineProvider = ({
|
|||||||
onDrag: () => {},
|
onDrag: () => {},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
'animate-to-face': async (_, { data: { plane, normal } }) => {
|
'animate-to-face': async (_, { data }) => {
|
||||||
|
if (data.type === 'extrudeFace') {
|
||||||
|
const { modifiedAst, pathToNode: pathToNewSketchNode } =
|
||||||
|
sketchOnExtrudedFace(
|
||||||
|
kclManager.ast,
|
||||||
|
data.extrudeSegmentPathToNode,
|
||||||
|
kclManager.programMemory,
|
||||||
|
data.cap
|
||||||
|
)
|
||||||
|
await kclManager.executeAstMock(modifiedAst, { updates: 'code' })
|
||||||
|
|
||||||
|
const forward = new Vector3(...data.zAxis)
|
||||||
|
const up = new Vector3(...data.yAxis)
|
||||||
|
|
||||||
|
let target = new Vector3(...data.position).multiplyScalar(
|
||||||
|
sceneInfra._baseUnitMultiplier
|
||||||
|
)
|
||||||
|
const quaternion = quaternionFromUpNForward(up, forward)
|
||||||
|
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||||
|
quaternion,
|
||||||
|
target
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
sketchPathToNode: pathToNewSketchNode,
|
||||||
|
zAxis: data.zAxis,
|
||||||
|
yAxis: data.yAxis,
|
||||||
|
origin: data.position,
|
||||||
|
}
|
||||||
|
}
|
||||||
const { modifiedAst, pathToNode } = startSketchOnDefault(
|
const { modifiedAst, pathToNode } = startSketchOnDefault(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
plane
|
data.plane
|
||||||
)
|
)
|
||||||
await kclManager.updateAst(modifiedAst, false)
|
await kclManager.updateAst(modifiedAst, false)
|
||||||
const quaternion = getSketchQuaternion(pathToNode, normal)
|
const quat = await getSketchQuaternion(pathToNode, data.zAxis)
|
||||||
await sceneInfra.camControls.tweenCameraToQuaternion(quaternion)
|
await sceneInfra.camControls.tweenCameraToQuaternion(quat)
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: pathToNode,
|
sketchPathToNode: pathToNode,
|
||||||
sketchNormalBackUp: normal,
|
zAxis: data.zAxis,
|
||||||
|
yAxis: data.yAxis,
|
||||||
|
origin: [0, 0, 0],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'animate-to-sketch': async ({
|
'animate-to-sketch': async ({ selectionRanges }) => {
|
||||||
sketchPathToNode,
|
const sourceRange = selectionRanges.codeBasedSelections[0].range
|
||||||
sketchNormalBackUp,
|
const sketchPathToNode = getNodePathFromSourceRange(
|
||||||
}) => {
|
kclManager.ast,
|
||||||
const quaternion = getSketchQuaternion(
|
sourceRange
|
||||||
sketchPathToNode || [],
|
|
||||||
sketchNormalBackUp
|
|
||||||
)
|
)
|
||||||
await sceneInfra.camControls.tweenCameraToQuaternion(quaternion)
|
const info = await getSketchOrientationDetails(sketchPathToNode || [])
|
||||||
|
await sceneInfra.camControls.tweenCameraToQuaternion(
|
||||||
|
info.quat,
|
||||||
|
new Vector3(...info.sketchDetails.origin)
|
||||||
|
)
|
||||||
|
return {
|
||||||
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
|
zAxis: info.sketchDetails.zAxis || null,
|
||||||
|
yAxis: info.sketchDetails.yAxis || null,
|
||||||
|
origin: info.sketchDetails.origin.map(
|
||||||
|
(a) => a / sceneInfra._baseUnitMultiplier
|
||||||
|
) as [number, number, number],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
'Get horizontal info': async ({
|
'Get horizontal info': async ({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -5,12 +5,12 @@ import {
|
|||||||
ConnectingType,
|
ConnectingType,
|
||||||
ConnectingTypeGroup,
|
ConnectingTypeGroup,
|
||||||
DisconnectingType,
|
DisconnectingType,
|
||||||
engineCommandManager,
|
|
||||||
EngineConnectionState,
|
EngineConnectionState,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
ErrorType,
|
ErrorType,
|
||||||
initialConnectingTypeGroupState,
|
initialConnectingTypeGroupState,
|
||||||
} from '../lang/std/engineConnection'
|
} from '../lang/std/engineConnection'
|
||||||
|
import { engineCommandManager } from '../lib/singletons'
|
||||||
import Tooltip from './Tooltip'
|
import Tooltip from './Tooltip'
|
||||||
|
|
||||||
export enum NetworkHealthState {
|
export enum NetworkHealthState {
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
validateSettings,
|
validateSettings,
|
||||||
} from 'lib/settings/settingsUtils'
|
} from 'lib/settings/settingsUtils'
|
||||||
import { toast } from 'react-hot-toast'
|
import { toast } from 'react-hot-toast'
|
||||||
import { setThemeClass, Themes } from 'lib/theme'
|
import { getThemeColorForEngine, setThemeClass, Themes } from 'lib/theme'
|
||||||
import {
|
import {
|
||||||
AnyStateMachine,
|
AnyStateMachine,
|
||||||
ContextFrom,
|
ContextFrom,
|
||||||
@ -22,8 +22,8 @@ import {
|
|||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
|
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
|
||||||
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
|
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
|
||||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
import { kclManager, sceneInfra, engineCommandManager } from 'lib/singletons'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -96,6 +96,16 @@ export const SettingsAuthProviderBase = ({
|
|||||||
: context.baseUnit
|
: context.baseUnit
|
||||||
sceneInfra.baseUnit = newBaseUnit
|
sceneInfra.baseUnit = newBaseUnit
|
||||||
},
|
},
|
||||||
|
setEngineTheme: (context) => {
|
||||||
|
engineCommandManager.sendSceneCommand({
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'set_background_color',
|
||||||
|
color: getThemeColorForEngine(context.theme),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
toastSuccess: (context, event) => {
|
toastSuccess: (context, event) => {
|
||||||
const truncatedNewValue =
|
const truncatedNewValue =
|
||||||
'data' in event && event.data instanceof Object
|
'data' in event && event.data instanceof Object
|
||||||
@ -147,7 +157,6 @@ export const SettingsAuthProviderBase = ({
|
|||||||
actions: {
|
actions: {
|
||||||
goToSignInPage: () => {
|
goToSignInPage: () => {
|
||||||
navigate(paths.SIGN_IN)
|
navigate(paths.SIGN_IN)
|
||||||
|
|
||||||
logout()
|
logout()
|
||||||
},
|
},
|
||||||
goToIndexPage: () => {
|
goToIndexPage: () => {
|
||||||
|
@ -1,15 +1,13 @@
|
|||||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { getNormalisedCoordinates } from '../lib/utils'
|
import { getNormalisedCoordinates } from '../lib/utils'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { Models } from '@kittycad/lib'
|
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
|
||||||
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||||
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
|
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
|
||||||
|
import { butName } from 'lib/cameraControls'
|
||||||
|
import { sendSelectEventToEngine } from 'lib/selections'
|
||||||
|
|
||||||
export const Stream = ({ className = '' }: { className?: string }) => {
|
export const Stream = ({ className = '' }: { className?: string }) => {
|
||||||
const [isLoading, setIsLoading] = useState(true)
|
const [isLoading, setIsLoading] = useState(true)
|
||||||
@ -30,7 +28,6 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
}))
|
}))
|
||||||
const { settings } = useSettingsAuthContext()
|
const { settings } = useSettingsAuthContext()
|
||||||
const { state } = useModelingContext()
|
const { state } = useModelingContext()
|
||||||
const { isExecuting } = useKclContext()
|
|
||||||
const { overallState } = useNetworkStatus()
|
const { overallState } = useNetworkStatus()
|
||||||
const isNetworkOkay = overallState === NetworkHealthState.Ok
|
const isNetworkOkay = overallState === NetworkHealthState.Ok
|
||||||
|
|
||||||
@ -60,50 +57,14 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
setClickCoords({ x, y })
|
setClickCoords({ x, y })
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
|
const handleMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||||
clientX,
|
|
||||||
clientY,
|
|
||||||
ctrlKey,
|
|
||||||
}) => {
|
|
||||||
if (!videoRef.current) return
|
if (!videoRef.current) return
|
||||||
setButtonDownInStream(undefined)
|
setButtonDownInStream(undefined)
|
||||||
if (state.matches('Sketch')) return
|
if (state.matches('Sketch')) return
|
||||||
if (state.matches('Sketch no face')) return
|
if (state.matches('Sketch no face')) return
|
||||||
const { x, y } = getNormalisedCoordinates({
|
|
||||||
clientX,
|
|
||||||
clientY,
|
|
||||||
el: videoRef.current,
|
|
||||||
...streamDimensions,
|
|
||||||
})
|
|
||||||
|
|
||||||
const newCmdId = uuidv4()
|
if (!didDragInStream && butName(e).left) {
|
||||||
const interaction = ctrlKey ? 'pan' : 'rotate'
|
sendSelectEventToEngine(e, videoRef.current, streamDimensions)
|
||||||
|
|
||||||
const command: Models['WebSocketRequest_type'] = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'camera_drag_end',
|
|
||||||
interaction,
|
|
||||||
window: { x, y },
|
|
||||||
},
|
|
||||||
cmd_id: newCmdId,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!didDragInStream) {
|
|
||||||
command.cmd = {
|
|
||||||
type: 'select_with_point',
|
|
||||||
selected_at_window: { x, y },
|
|
||||||
selection_type: 'add',
|
|
||||||
}
|
|
||||||
engineCommandManager.sendSceneCommand(command)
|
|
||||||
} else if (didDragInStream) {
|
|
||||||
command.cmd = {
|
|
||||||
type: 'handle_mouse_drag_end',
|
|
||||||
window: { x, y },
|
|
||||||
}
|
|
||||||
void engineCommandManager.sendSceneCommand(command)
|
|
||||||
} else {
|
|
||||||
engineCommandManager.sendSceneCommand(command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setDidDragInStream(false)
|
setDidDragInStream(false)
|
||||||
@ -140,9 +101,10 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
controls={false}
|
controls={false}
|
||||||
onPlay={() => setIsLoading(false)}
|
onPlay={() => setIsLoading(false)}
|
||||||
onMouseMoveCapture={handleMouseMove}
|
onMouseMoveCapture={handleMouseMove}
|
||||||
className={`w-full cursor-pointer h-full ${isExecuting && 'blur-md'}`}
|
className="w-full cursor-pointer h-full"
|
||||||
disablePictureInPicture
|
disablePictureInPicture
|
||||||
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
||||||
|
id="video-stream"
|
||||||
/>
|
/>
|
||||||
<ClientSideScene cameraControls={settings.context?.cameraControls} />
|
<ClientSideScene cameraControls={settings.context?.cameraControls} />
|
||||||
{!isNetworkOkay && !isLoading && (
|
{!isNetworkOkay && !isLoading && (
|
||||||
|
@ -3,6 +3,7 @@ import ReactCodeMirror, {
|
|||||||
Extension,
|
Extension,
|
||||||
ViewUpdate,
|
ViewUpdate,
|
||||||
keymap,
|
keymap,
|
||||||
|
SelectionRange,
|
||||||
} from '@uiw/react-codemirror'
|
} from '@uiw/react-codemirror'
|
||||||
import { TEST } from 'env'
|
import { TEST } from 'env'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
@ -19,10 +20,9 @@ import { kclErrToDiagnostic } from 'lang/errors'
|
|||||||
import { CSSRuleObject } from 'tailwindcss/types/config'
|
import { CSSRuleObject } from 'tailwindcss/types/config'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import interact from '@replit/codemirror-interact'
|
import interact from '@replit/codemirror-interact'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager, sceneInfra, kclManager } from 'lib/singletons'
|
||||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
|
||||||
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
|
import { NetworkHealthState, useNetworkStatus } from './NetworkHealthIndicator'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
import { useLspContext } from './LspProvider'
|
import { useLspContext } from './LspProvider'
|
||||||
@ -75,7 +75,7 @@ export const TextEditor = ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const {
|
||||||
context: { selectionRanges, selectionRangeTypeMap },
|
context: { selectionRanges },
|
||||||
send,
|
send,
|
||||||
state,
|
state,
|
||||||
} = useModelingContext()
|
} = useModelingContext()
|
||||||
@ -91,10 +91,27 @@ export const TextEditor = ({
|
|||||||
if (isNetworkOkay) kclManager.setCodeAndExecute(newCode)
|
if (isNetworkOkay) kclManager.setCodeAndExecute(newCode)
|
||||||
else kclManager.setCode(newCode)
|
else kclManager.setCode(newCode)
|
||||||
} //, []);
|
} //, []);
|
||||||
|
const lastSelection = useRef('')
|
||||||
const onUpdate = (viewUpdate: ViewUpdate) => {
|
const onUpdate = (viewUpdate: ViewUpdate) => {
|
||||||
if (!editorView) {
|
if (!editorView) {
|
||||||
setEditorView(viewUpdate.view)
|
setEditorView(viewUpdate.view)
|
||||||
}
|
}
|
||||||
|
const selString = stringifyRanges(
|
||||||
|
viewUpdate?.state?.selection?.ranges || []
|
||||||
|
)
|
||||||
|
if (selString === lastSelection.current) {
|
||||||
|
// onUpdate is noisy and is fired a lot by extensions
|
||||||
|
// since we're only interested in selections changes we can ignore most of these.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
lastSelection.current = selString
|
||||||
|
|
||||||
|
if (
|
||||||
|
// TODO find a less lazy way of getting the last
|
||||||
|
Date.now() - useStore.getState().lastCodeMirrorSelectionUpdatedFromScene <
|
||||||
|
150
|
||||||
|
)
|
||||||
|
return // update triggered by scene selection
|
||||||
if (sceneInfra.selected) return // mid drag
|
if (sceneInfra.selected) return // mid drag
|
||||||
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||||
'Equip Line tool',
|
'Equip Line tool',
|
||||||
@ -104,7 +121,6 @@ export const TextEditor = ({
|
|||||||
const eventInfo = processCodeMirrorRanges({
|
const eventInfo = processCodeMirrorRanges({
|
||||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
selectionRangeTypeMap,
|
|
||||||
isShiftDown,
|
isShiftDown,
|
||||||
})
|
})
|
||||||
if (!eventInfo) return
|
if (!eventInfo) return
|
||||||
@ -226,3 +242,7 @@ export const TextEditor = ({
|
|||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringifyRanges(ranges: readonly SelectionRange[]): string {
|
||||||
|
return ranges.map(({ to, from }) => `${to}->${from}`).join('&')
|
||||||
|
}
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function equalAngleInfo({
|
export function equalAngleInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -11,7 +11,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function setEqualLengthInfo({
|
export function setEqualLengthInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
getTransformInfos,
|
getTransformInfos,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function horzVertInfo(
|
export function horzVertInfo(
|
||||||
selectionRanges: Selections,
|
selectionRanges: Selections,
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import {
|
|||||||
getRemoveConstraintsTransforms,
|
getRemoveConstraintsTransforms,
|
||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
} from '../../lang/std/sketchcombos'
|
} from '../../lang/std/sketchcombos'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function removeConstrainingValuesInfo({
|
export function removeConstrainingValuesInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
createVariableDeclaration,
|
createVariableDeclaration,
|
||||||
} from '../../lang/modifyAst'
|
} from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||||
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
import { createLiteral, createVariableDeclaration } from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
|
|
||||||
const getModalInfo = createInfoModal(GetInfoModal)
|
const getModalInfo = createInfoModal(GetInfoModal)
|
||||||
|
@ -21,7 +21,7 @@ import {
|
|||||||
} from '../../lang/modifyAst'
|
} from '../../lang/modifyAst'
|
||||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||||
import { normaliseAngle } from '../../lib/utils'
|
import { normaliseAngle } from '../../lib/utils'
|
||||||
import { kclManager } from 'lang/KclSingleton'
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { Dialog } from '@headlessui/react'
|
import { Dialog } from '@headlessui/react'
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { useKclContext } from 'lang/KclSingleton'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
|
|
||||||
export function WasmErrBanner() {
|
export function WasmErrBanner() {
|
||||||
const [isBannerDismissed, setBannerDismissed] = useState(false)
|
const [isBannerDismissed, setBannerDismissed] = useState(false)
|
||||||
|
@ -33,10 +33,12 @@ export default class Server {
|
|||||||
this.#fromServer,
|
this.#fromServer,
|
||||||
fileSystemManager
|
fileSystemManager
|
||||||
)
|
)
|
||||||
if (type_ === 'copilot') {
|
|
||||||
if (!token || token === '') {
|
if (!token || token === '') {
|
||||||
throw new Error('auth token is required for copilot')
|
throw new Error(
|
||||||
|
type_ + ': auth token is required for lsp server to start'
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
if (type_ === 'copilot') {
|
||||||
await copilotLspRun(config, token)
|
await copilotLspRun(config, token)
|
||||||
} else if (type_ === 'kcl') {
|
} else if (type_ === 'kcl') {
|
||||||
await kclLspRun(config, token || '')
|
await kclLspRun(config, token || '')
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect } from 'react'
|
||||||
import { useStore } from 'useStore'
|
import { useStore } from 'useStore'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
import { useModelingContext } from './useModelingContext'
|
import { useModelingContext } from './useModelingContext'
|
||||||
import { getEventForSelectWithPoint } from 'lib/selections'
|
import { getEventForSelectWithPoint } from 'lib/selections'
|
||||||
|
|
||||||
|