Compare commits
135 Commits
Author | SHA1 | Date | |
---|---|---|---|
f4c0347104 | |||
ad36b5f5fa | |||
b798cf19d3 | |||
7cfa897561 | |||
0d8804005a | |||
cbd26d29fa | |||
e501a542ac | |||
7cb4f4d101 | |||
1162f5f4c4 | |||
3975e6d8f5 | |||
d68d7a7e00 | |||
b135b97de6 | |||
de5885ce0b | |||
ad7c544754 | |||
4d77875bdc | |||
3377923dcb | |||
c6005660c8 | |||
66e62c6037 | |||
0a4a517bb4 | |||
70f3ded7e2 | |||
095108252b | |||
20b1c93f12 | |||
3747a1b993 | |||
198feb7d44 | |||
c7a8b8313e | |||
1576dc3256 | |||
341a3b7609 | |||
ecb42b89a6 | |||
f00ee3a44a | |||
900e3b96ad | |||
15fae05659 | |||
2730b6d152 | |||
602e7afef6 | |||
d9bcadb062 | |||
19f669b94c | |||
d9ef471385 | |||
39f8b306a2 | |||
19925d22c1 | |||
e1af4b4219 | |||
c699611f5b | |||
00ede7ec1a | |||
f30601bd2c | |||
cfbc77b62f | |||
808830d29e | |||
e714103655 | |||
fbcb96add5 | |||
7386ccf1bf | |||
6e73578933 | |||
b88d5c8799 | |||
5430c1fa66 | |||
c0d4bb6c9f | |||
25260a88c3 | |||
b6d6f0f4c1 | |||
b1276b2ed8 | |||
5f0f3f40d0 | |||
f1ea9b6ece | |||
b94c5be1af | |||
8378eb1e94 | |||
05f98a8c39 | |||
386571fa60 | |||
b0abdf4f70 | |||
81e70e139f | |||
d6bfc38d62 | |||
ada66de92d | |||
8f133f9662 | |||
b360dbb961 | |||
eca3dc2967 | |||
ae36ab6982 | |||
8cb6cf1b8a | |||
3c235c890a | |||
b6dfd30840 | |||
65d128eecd | |||
77b7c602f2 | |||
fa0e61a2be | |||
1cf35a611e | |||
952d0e4c7c | |||
0f85de9df8 | |||
0e8eed3f82 | |||
5b43a5075f | |||
f5ed4e37b2 | |||
19c8da1a86 | |||
a25f89aaba | |||
aeebe5416f | |||
661788b8b0 | |||
ac24563159 | |||
d17342dfb8 | |||
2e93b58ae6 | |||
6593656b08 | |||
47be749ec7 | |||
a03e7f5c41 | |||
b78e9fa131 | |||
c629233eaa | |||
f640f7a5e0 | |||
64398381a9 | |||
0bc5534056 | |||
9fc1df7c1d | |||
a5879ceeda | |||
379c30824e | |||
a4d3263b88 | |||
c1f661ab52 | |||
7d887a1497 | |||
4ca341e132 | |||
c6249f36d2 | |||
dcbe5d7f75 | |||
390cb2d51d | |||
98f7a564ea | |||
05f9e3c290 | |||
09760fc2e9 | |||
18ffc43e89 | |||
de63e4f19f | |||
b70b271e6b | |||
08b7cdc5f6 | |||
6efe6b54c0 | |||
69f72d62e0 | |||
e04b09fcd8 | |||
4903f6b9fc | |||
ef8149f03a | |||
1b75321bf1 | |||
3ed263da6b | |||
d59c4a2258 | |||
9c8351ea40 | |||
db98bcf2a0 | |||
15d96a072d | |||
088968c664 | |||
4bbf98bc34 | |||
ca08f5b337 | |||
a3649d09c0 | |||
635cb58036 | |||
7f050b114f | |||
c999819450 | |||
82905caad6 | |||
519e6d74ac | |||
edb7d68c05 | |||
345dd45caa | |||
b6a5f133f3 |
@ -1,3 +1,3 @@
|
||||
[codespell]
|
||||
ignore-words-list: crate,everytime
|
||||
skip: **/target,node_modules,build
|
||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo
|
||||
skip: **/target,node_modules,build,**/Cargo.lock
|
||||
|
@ -1,6 +1,6 @@
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.kittycad.io/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.dev.kittycad.io
|
||||
VITE_KC_SITE_BASE_URL=https://dev.kittycad.io
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||
VITE_KC_SENTRY_DSN=
|
||||
|
@ -1,5 +1,5 @@
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.kittycad.io/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.kittycad.io
|
||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||
VITE_KC_SKIP_AUTH=false
|
||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||
|
@ -1 +1,2 @@
|
||||
src/wasm-lib/*
|
||||
*.typegen.ts
|
||||
|
@ -17,12 +17,12 @@
|
||||
"never"
|
||||
],
|
||||
"react-hooks/exhaustive-deps": "off",
|
||||
"@typescript-eslint/no-floating-promises": "warn"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["e2e/**/*.ts"], // Update the pattern based on your file structure
|
||||
"rules": {
|
||||
"@typescript-eslint/no-floating-promises": "warn",
|
||||
"testing-library/prefer-screen-queries": "off"
|
||||
}
|
||||
}
|
||||
|
29
.github/workflows/announce_release.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: Announce Release
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
announce_release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Announce Release
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
RELEASE_VERSION: ${{ github.event.release.tag_name }}
|
||||
RELEASE_BODY: ${{ github.event.release.body}}
|
||||
run: python public/announce_release.py
|
11
.github/workflows/cargo-clippy.yml
vendored
@ -43,17 +43,6 @@ jobs:
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2.6.1
|
||||
|
||||
- name: Install ffmpeg
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
ffmpeg \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libclang-dev \
|
||||
libswscale-dev \
|
||||
--no-install-recommends
|
||||
|
||||
- name: Run clippy
|
||||
run: |
|
||||
cd "${{ matrix.dir }}"
|
||||
|
10
.github/workflows/cargo-test.yml
vendored
@ -44,16 +44,6 @@ jobs:
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2.6.1
|
||||
- name: Install ffmpeg
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install \
|
||||
ffmpeg \
|
||||
libavformat-dev \
|
||||
libavutil-dev \
|
||||
libclang-dev \
|
||||
libswscale-dev \
|
||||
--no-install-recommends
|
||||
- name: cargo test
|
||||
shell: bash
|
||||
run: |-
|
||||
|
46
.github/workflows/ci.yml
vendored
@ -46,6 +46,7 @@ jobs:
|
||||
workspaces: './src/wasm-lib'
|
||||
|
||||
- run: yarn build:wasm
|
||||
- run: yarn xstate:typegen
|
||||
- run: yarn tsc
|
||||
|
||||
|
||||
@ -85,8 +86,6 @@ jobs:
|
||||
|
||||
- run: yarn test:nowatch
|
||||
|
||||
- run: yarn test:cov
|
||||
|
||||
|
||||
prepare-json-files:
|
||||
runs-on: ubuntu-latest # seperate job on Ubuntu for easy string manipulations (compared to Windows)
|
||||
@ -123,8 +122,9 @@ jobs:
|
||||
needs: [prepare-json-files]
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-latest, windows-latest]
|
||||
os: [macos-14, ubuntu-latest, windows-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
@ -187,10 +187,10 @@ jobs:
|
||||
- name: Fix format
|
||||
run: yarn fmt
|
||||
|
||||
- name: Install Universal target (MacOS only)
|
||||
if: matrix.os == 'macos-latest'
|
||||
- name: Install x86 target for Universal builds (MacOS only)
|
||||
if: matrix.os == 'macos-14'
|
||||
run: |
|
||||
rustup target add aarch64-apple-darwin
|
||||
rustup target add x86_64-apple-darwin
|
||||
|
||||
- name: Prepare certificate and variables (Windows only)
|
||||
if: ${{ matrix.os == 'windows-latest' && env.BUILD_RELEASE == 'true' }}
|
||||
@ -224,7 +224,7 @@ jobs:
|
||||
with:
|
||||
includeRelease: false
|
||||
includeDebug: true
|
||||
args: ${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }}
|
||||
args: ${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }}
|
||||
|
||||
- name: Build the app (release) and sign
|
||||
uses: tauri-apps/tauri-action@v0
|
||||
@ -240,11 +240,12 @@ jobs:
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
TAURI_CONF_ARGS: "--config ${{ matrix.os == 'windows-latest' && 'src-tauri\\tauri.release.conf.json' || 'src-tauri/tauri.release.conf.json' }}"
|
||||
with:
|
||||
args: "${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
|
||||
args: "${{ matrix.os == 'macos-14' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
|
||||
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: matrix.os != 'ubuntu-latest'
|
||||
env:
|
||||
PREFIX: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
|
||||
PREFIX: ${{ matrix.os == 'macos-14' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
|
||||
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
|
||||
with:
|
||||
path: "${{ env.PREFIX }}/${{ env.MODE }}/bundle/*/*"
|
||||
@ -252,7 +253,7 @@ jobs:
|
||||
- name: Run e2e tests (linux only)
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
cargo install tauri-driver
|
||||
cargo install tauri-driver@0.1.3
|
||||
source .env.${{ env.BUILD_RELEASE == 'true' && 'production' || 'development' }}
|
||||
export VITE_KC_API_BASE_URL
|
||||
xvfb-run yarn test:e2e:tauri
|
||||
@ -279,7 +280,6 @@ jobs:
|
||||
run: |
|
||||
ls -l artifact/*/*oo*
|
||||
DARWIN_SIG=`cat artifact/macos/*.app.tar.gz.sig`
|
||||
LINUX_SIG=`cat artifact/appimage/*.AppImage.tar.gz.sig`
|
||||
WINDOWS_SIG=`cat artifact/msi/*.msi.zip.sig`
|
||||
RELEASE_DIR=https://${WEBSITE_DIR}/${VERSION}
|
||||
jq --null-input \
|
||||
@ -288,8 +288,6 @@ jobs:
|
||||
--arg notes "${NOTES}" \
|
||||
--arg darwin_sig "$DARWIN_SIG" \
|
||||
--arg darwin_url "$RELEASE_DIR/macos/Zoo%20Modeling%20App.app.tar.gz" \
|
||||
--arg linux_sig "$LINUX_SIG" \
|
||||
--arg linux_url "$RELEASE_DIR/appimage/zoo-modeling-app_${VERSION_NO_V}_amd64.AppImage.tar.gz" \
|
||||
--arg windows_sig "$WINDOWS_SIG" \
|
||||
--arg windows_url "$RELEASE_DIR/msi/Zoo%20Modeling%20App_${VERSION_NO_V}_x64_en-US.msi.zip" \
|
||||
'{
|
||||
@ -305,10 +303,6 @@ jobs:
|
||||
"signature": $darwin_sig,
|
||||
"url": $darwin_url
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"signature": $linux_sig,
|
||||
"url": $linux_url
|
||||
},
|
||||
"windows-x86_64": {
|
||||
"signature": $windows_sig,
|
||||
"url": $windows_url
|
||||
@ -325,7 +319,6 @@ jobs:
|
||||
--arg pub_date "${PUB_DATE}" \
|
||||
--arg notes "${NOTES}" \
|
||||
--arg darwin_url "$RELEASE_DIR/dmg/Zoo%20Modeling%20App_${VERSION_NO_V}_universal.dmg" \
|
||||
--arg linux_url "$RELEASE_DIR/appimage/zoo-modeling-app_${VERSION_NO_V}_amd64.AppImage" \
|
||||
--arg windows_url "$RELEASE_DIR/msi/Zoo%20Modeling%20App_${VERSION_NO_V}_x64_en-US.msi" \
|
||||
'{
|
||||
"version": $version,
|
||||
@ -335,9 +328,6 @@ jobs:
|
||||
"dmg-universal": {
|
||||
"url": $darwin_url
|
||||
},
|
||||
"appimage-x86_64": {
|
||||
"url": $linux_url
|
||||
},
|
||||
"msi-x86_64": {
|
||||
"url": $windows_url
|
||||
}
|
||||
@ -346,31 +336,31 @@ jobs:
|
||||
cat last_download.json
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
uses: 'google-github-actions/auth@v2.0.0'
|
||||
uses: 'google-github-actions/auth@v2.1.1'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
- name: Set up Google Cloud SDK
|
||||
uses: google-github-actions/setup-gcloud@v2.0.0
|
||||
uses: google-github-actions/setup-gcloud@v2.1.0
|
||||
with:
|
||||
project_id: kittycadapi
|
||||
|
||||
- name: Upload release files to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||
with:
|
||||
path: artifact
|
||||
glob: '*/*oo*'
|
||||
glob: '*/Zoo*'
|
||||
parent: false
|
||||
destination: ${{ env.BUCKET_DIR }}/${{ env.VERSION }}
|
||||
|
||||
- name: Upload update endpoint to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||
with:
|
||||
path: last_update.json
|
||||
destination: ${{ env.BUCKET_DIR }}
|
||||
|
||||
- name: Upload download endpoint to public bucket
|
||||
uses: google-github-actions/upload-cloud-storage@v2.0.0
|
||||
uses: google-github-actions/upload-cloud-storage@v2.1.0
|
||||
with:
|
||||
path: last_download.json
|
||||
destination: ${{ env.BUCKET_DIR }}
|
||||
@ -379,4 +369,4 @@ jobs:
|
||||
if: ${{ github.event_name == 'release' }}
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: artifact/*/*oo*
|
||||
files: 'artifact/*/Zoo*'
|
||||
|
2
.github/workflows/playwright.yml
vendored
@ -79,7 +79,7 @@ jobs:
|
||||
|
||||
playwright-macos:
|
||||
timeout-minutes: 60
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
needs: playwright-ubuntu
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
4
.gitignore
vendored
@ -50,3 +50,7 @@ e2e/playwright/export-snapshots/*embedded.gltf
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
|
||||
|
||||
## generated files
|
||||
src/**/*.typegen.ts
|
||||
|
@ -10,4 +10,4 @@ src/wasm-lib/kcl/bindings
|
||||
e2e/playwright/export-snapshots
|
||||
|
||||
# XState generated files
|
||||
src/machines/modelingMachine.typegen.ts
|
||||
src/machines/**.typegen.ts
|
||||
|
@ -94,7 +94,6 @@ For running the rust (not tauri rust though) only, you can
|
||||
cd src/wasm-lib
|
||||
cargo test
|
||||
```
|
||||
but you will need to have install ffmpeg prior to.
|
||||
|
||||
## Tauri
|
||||
|
||||
@ -137,6 +136,11 @@ Before you submit a contribution PR to this repo, please ensure that:
|
||||
VERSION=x.y.z yarn run bump-jsons
|
||||
```
|
||||
|
||||
Alternatively you can try the experimental `make-release.sh` bash script that will create the branch with the updated json files for you.
|
||||
run `./make-release.sh` for a patch update
|
||||
run `./make-release.sh "minor"` for minor
|
||||
run `./make-release.sh "major"` for major
|
||||
|
||||
The PR may serve as a place to discuss the human-readable changelog and extra QA. A quick way of getting PR's merged since the last bump is to [use this PR filter](https://github.com/KittyCAD/modeling-app/pulls?q=is%3Apr+sort%3Aupdated-desc+is%3Amerged+), open up the browser console and past in the following
|
||||
|
||||
```typescript
|
||||
@ -183,7 +187,7 @@ For more information on fuzzing you can check out
|
||||
First time running plawright locally, you'll need to add the secrets file
|
||||
```bash
|
||||
touch ./e2e/playwright/playwright-secrets.env
|
||||
echo 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets2.env
|
||||
printf 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets.env
|
||||
```
|
||||
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
|
||||
|
||||
|
16
docs/kcl/KNOWN-ISSUES.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Known Issues
|
||||
|
||||
The following are bugs that are not in modeling-app or kcl itself. These bugs
|
||||
once fixed in engine will just start working here with no language changes.
|
||||
|
||||
- **Sketch on Face**: If your sketch is outside the edges of the face (on which you
|
||||
are sketching) you will get multiple models returned instead of one single
|
||||
model for that sketch and its underlying 3D object.
|
||||
|
||||
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
|
||||
item in the pattern is being subtracted. This is an engine bug that is being
|
||||
worked on.
|
||||
|
||||
- **Import**: Right now you can import a file, even if that file has brep data
|
||||
you cannot edit it. You also cannot move or transform the imported objects at
|
||||
all. In the future, after v1, the engine will account for this.
|
31354
docs/kcl/std.json
5791
docs/kcl/std.md
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 259 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 220 KiB |
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 193 KiB |
@ -1,7 +1,7 @@
|
||||
ISO-10303-21;
|
||||
HEADER;
|
||||
FILE_DESCRIPTION((('kittycad.io export')), '2;1');
|
||||
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'kittycad.io beta', 'kittycad.io', 'Authorization unknown');
|
||||
FILE_DESCRIPTION((('zoo.dev export')), '2;1');
|
||||
FILE_NAME('dump.step', '1970-01-01T00:00:00.0+00:00', ('Author unknown'), ('Organization unknown'), 'zoo.dev beta', 'zoo.dev', 'Authorization unknown');
|
||||
FILE_SCHEMA(('AP203_CONFIGURATION_CONTROLLED_3D_DESIGN_OF_MECHANICAL_PARTS_AND_ASSEMBLIES_MIM_LF'));
|
||||
ENDSEC;
|
||||
DATA;
|
||||
@ -19,59 +19,59 @@ DATA;
|
||||
);
|
||||
#4 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||
#5 = VERTEX_POINT('NONE', #4);
|
||||
#6 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
||||
#6 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||
#7 = VERTEX_POINT('NONE', #6);
|
||||
#8 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
|
||||
#8 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
|
||||
#9 = VERTEX_POINT('NONE', #8);
|
||||
#10 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
||||
#10 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||
#11 = VERTEX_POINT('NONE', #10);
|
||||
#12 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
||||
#12 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||
#13 = VERTEX_POINT('NONE', #12);
|
||||
#14 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
|
||||
#14 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
|
||||
#15 = VERTEX_POINT('NONE', #14);
|
||||
#16 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
||||
#16 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||
#17 = VERTEX_POINT('NONE', #16);
|
||||
#18 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
|
||||
#18 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
|
||||
#19 = VERTEX_POINT('NONE', #18);
|
||||
#20 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
||||
#20 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||
#21 = VERTEX_POINT('NONE', #20);
|
||||
#22 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
|
||||
#22 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
|
||||
#23 = VERTEX_POINT('NONE', #22);
|
||||
#24 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
||||
#24 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||
#25 = VERTEX_POINT('NONE', #24);
|
||||
#26 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
|
||||
#26 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
|
||||
#27 = VERTEX_POINT('NONE', #26);
|
||||
#28 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
||||
#28 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||
#29 = VERTEX_POINT('NONE', #28);
|
||||
#30 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
|
||||
#30 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
|
||||
#31 = VERTEX_POINT('NONE', #30);
|
||||
#32 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
||||
#32 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||
#33 = VERTEX_POINT('NONE', #32);
|
||||
#34 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
|
||||
#34 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
|
||||
#35 = VERTEX_POINT('NONE', #34);
|
||||
#36 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
||||
#36 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||
#37 = VERTEX_POINT('NONE', #36);
|
||||
#38 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
|
||||
#38 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
|
||||
#39 = VERTEX_POINT('NONE', #38);
|
||||
#40 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
||||
#40 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||
#41 = VERTEX_POINT('NONE', #40);
|
||||
#42 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
|
||||
#42 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
|
||||
#43 = VERTEX_POINT('NONE', #42);
|
||||
#44 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
||||
#44 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||
#45 = VERTEX_POINT('NONE', #44);
|
||||
#46 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
|
||||
#46 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
|
||||
#47 = VERTEX_POINT('NONE', #46);
|
||||
#48 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
||||
#48 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||
#49 = VERTEX_POINT('NONE', #48);
|
||||
#50 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
|
||||
#50 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
|
||||
#51 = VERTEX_POINT('NONE', #50);
|
||||
#52 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
||||
#52 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||
#53 = VERTEX_POINT('NONE', #52);
|
||||
#54 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
|
||||
#54 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
|
||||
#55 = VERTEX_POINT('NONE', #54);
|
||||
#56 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
||||
#56 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||
#57 = VERTEX_POINT('NONE', #56);
|
||||
#58 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
|
||||
#58 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
|
||||
#59 = VERTEX_POINT('NONE', #58);
|
||||
#60 = DIRECTION('NONE', (0, -1, 0));
|
||||
#61 = VECTOR('NONE', #60, 1);
|
||||
@ -79,11 +79,11 @@ DATA;
|
||||
#63 = LINE('NONE', #62, #61);
|
||||
#64 = DIRECTION('NONE', (0, 0, 1));
|
||||
#65 = VECTOR('NONE', #64, 1);
|
||||
#66 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
||||
#66 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||
#67 = LINE('NONE', #66, #65);
|
||||
#68 = DIRECTION('NONE', (0, -1, 0));
|
||||
#69 = VECTOR('NONE', #68, 1);
|
||||
#70 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
||||
#70 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||
#71 = LINE('NONE', #70, #69);
|
||||
#72 = DIRECTION('NONE', (0, 0, 1));
|
||||
#73 = VECTOR('NONE', #72, 1);
|
||||
@ -91,155 +91,155 @@ DATA;
|
||||
#75 = LINE('NONE', #74, #73);
|
||||
#76 = DIRECTION('NONE', (1, 0, 0));
|
||||
#77 = VECTOR('NONE', #76, 1);
|
||||
#78 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
|
||||
#78 = CARTESIAN_POINT('NONE', (0, -0.64516, -0));
|
||||
#79 = LINE('NONE', #78, #77);
|
||||
#80 = DIRECTION('NONE', (0, 0, 1));
|
||||
#81 = VECTOR('NONE', #80, 1);
|
||||
#82 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
||||
#82 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||
#83 = LINE('NONE', #82, #81);
|
||||
#84 = DIRECTION('NONE', (1, 0, 0));
|
||||
#85 = VECTOR('NONE', #84, 1);
|
||||
#86 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
|
||||
#86 = CARTESIAN_POINT('NONE', (0, -0.64516, 2.58064));
|
||||
#87 = LINE('NONE', #86, #85);
|
||||
#88 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
|
||||
#88 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
|
||||
#89 = VECTOR('NONE', #88, 1);
|
||||
#90 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
|
||||
#90 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, -0));
|
||||
#91 = LINE('NONE', #90, #89);
|
||||
#92 = DIRECTION('NONE', (0, 0, 1));
|
||||
#93 = VECTOR('NONE', #92, 1);
|
||||
#94 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
||||
#94 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||
#95 = LINE('NONE', #94, #93);
|
||||
#96 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
|
||||
#96 = DIRECTION('NONE', (0.819152044288992, -0.5735764363510459, 0));
|
||||
#97 = VECTOR('NONE', #96, 1);
|
||||
#98 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
|
||||
#98 = CARTESIAN_POINT('NONE', (1.996782122555674, -0.64516, 2.58064));
|
||||
#99 = LINE('NONE', #98, #97);
|
||||
#100 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
|
||||
#100 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
|
||||
#101 = VECTOR('NONE', #100, 1);
|
||||
#102 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
|
||||
#102 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, -0));
|
||||
#103 = LINE('NONE', #102, #101);
|
||||
#104 = DIRECTION('NONE', (0, 0, 1));
|
||||
#105 = VECTOR('NONE', #104, 1);
|
||||
#106 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
||||
#106 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||
#107 = LINE('NONE', #106, #105);
|
||||
#108 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
|
||||
#108 = DIRECTION('NONE', (1, -0.00000000000000038794063361359933, 0));
|
||||
#109 = VECTOR('NONE', #108, 1);
|
||||
#110 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
|
||||
#110 = CARTESIAN_POINT('NONE', (3.839550058615159, -1.9354799999999992, 2.58064));
|
||||
#111 = LINE('NONE', #110, #109);
|
||||
#112 = DIRECTION('NONE', (0, 1, 0));
|
||||
#113 = VECTOR('NONE', #112, 1);
|
||||
#114 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
|
||||
#114 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, -0));
|
||||
#115 = LINE('NONE', #114, #113);
|
||||
#116 = DIRECTION('NONE', (0, 0, 1));
|
||||
#117 = VECTOR('NONE', #116, 1);
|
||||
#118 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
||||
#118 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||
#119 = LINE('NONE', #118, #117);
|
||||
#120 = DIRECTION('NONE', (0, 1, 0));
|
||||
#121 = VECTOR('NONE', #120, 1);
|
||||
#122 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
|
||||
#122 = CARTESIAN_POINT('NONE', (6.12902, -1.93548, 2.58064));
|
||||
#123 = LINE('NONE', #122, #121);
|
||||
#124 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#125 = VECTOR('NONE', #124, 1);
|
||||
#126 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
|
||||
#126 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, -0));
|
||||
#127 = LINE('NONE', #126, #125);
|
||||
#128 = DIRECTION('NONE', (0, 0, 1));
|
||||
#129 = VECTOR('NONE', #128, 1);
|
||||
#130 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
||||
#130 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||
#131 = LINE('NONE', #130, #129);
|
||||
#132 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#133 = VECTOR('NONE', #132, 1);
|
||||
#134 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
|
||||
#134 = CARTESIAN_POINT('NONE', (6.12902, -1.6129, 2.58064));
|
||||
#135 = LINE('NONE', #134, #133);
|
||||
#136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
||||
#137 = VECTOR('NONE', #136, 1);
|
||||
#138 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
|
||||
#138 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, -0));
|
||||
#139 = LINE('NONE', #138, #137);
|
||||
#140 = DIRECTION('NONE', (0, 0, 1));
|
||||
#141 = VECTOR('NONE', #140, 1);
|
||||
#142 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
||||
#142 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||
#143 = LINE('NONE', #142, #141);
|
||||
#144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
|
||||
#145 = VECTOR('NONE', #144, 1);
|
||||
#146 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
|
||||
#146 = CARTESIAN_POINT('NONE', (3.9412591419317424, -1.6129, 2.58064));
|
||||
#147 = LINE('NONE', #146, #145);
|
||||
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
|
||||
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
|
||||
#149 = VECTOR('NONE', #148, 1);
|
||||
#150 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
|
||||
#150 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, -0));
|
||||
#151 = LINE('NONE', #150, #149);
|
||||
#152 = DIRECTION('NONE', (0, 0, 1));
|
||||
#153 = VECTOR('NONE', #152, 1);
|
||||
#154 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
||||
#154 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||
#155 = LINE('NONE', #154, #153);
|
||||
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
|
||||
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406992, 0));
|
||||
#157 = VECTOR('NONE', #156, 1);
|
||||
#158 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
|
||||
#158 = CARTESIAN_POINT('NONE', (1.6377992218573856, 0, 2.58064));
|
||||
#159 = LINE('NONE', #158, #157);
|
||||
#160 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
|
||||
#160 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
|
||||
#161 = VECTOR('NONE', #160, 1);
|
||||
#162 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
|
||||
#162 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, -0));
|
||||
#163 = LINE('NONE', #162, #161);
|
||||
#164 = DIRECTION('NONE', (0, 0, 1));
|
||||
#165 = VECTOR('NONE', #164, 1);
|
||||
#166 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
||||
#166 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||
#167 = LINE('NONE', #166, #165);
|
||||
#168 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
|
||||
#168 = DIRECTION('NONE', (1, -0.0000000000000001378647737807002, 0));
|
||||
#169 = VECTOR('NONE', #168, 1);
|
||||
#170 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
|
||||
#170 = CARTESIAN_POINT('NONE', (3.7131243491113075, 0.9677400000000002, 2.58064));
|
||||
#171 = LINE('NONE', #170, #169);
|
||||
#172 = DIRECTION('NONE', (0, 1, 0));
|
||||
#173 = VECTOR('NONE', #172, 1);
|
||||
#174 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
|
||||
#174 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, -0));
|
||||
#175 = LINE('NONE', #174, #173);
|
||||
#176 = DIRECTION('NONE', (0, 0, 1));
|
||||
#177 = VECTOR('NONE', #176, 1);
|
||||
#178 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
||||
#178 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||
#179 = LINE('NONE', #178, #177);
|
||||
#180 = DIRECTION('NONE', (0, 1, 0));
|
||||
#181 = VECTOR('NONE', #180, 1);
|
||||
#182 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
|
||||
#182 = CARTESIAN_POINT('NONE', (6.12902, 0.9677399999999998, 2.58064));
|
||||
#183 = LINE('NONE', #182, #181);
|
||||
#184 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#185 = VECTOR('NONE', #184, 1);
|
||||
#186 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
|
||||
#186 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, -0));
|
||||
#187 = LINE('NONE', #186, #185);
|
||||
#188 = DIRECTION('NONE', (0, 0, 1));
|
||||
#189 = VECTOR('NONE', #188, 1);
|
||||
#190 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
||||
#190 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||
#191 = LINE('NONE', #190, #189);
|
||||
#192 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#193 = VECTOR('NONE', #192, 1);
|
||||
#194 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
|
||||
#194 = CARTESIAN_POINT('NONE', (6.12902, 1.29032, 2.58064));
|
||||
#195 = LINE('NONE', #194, #193);
|
||||
#196 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
|
||||
#196 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
|
||||
#197 = VECTOR('NONE', #196, 1);
|
||||
#198 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
|
||||
#198 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, -0));
|
||||
#199 = LINE('NONE', #198, #197);
|
||||
#200 = DIRECTION('NONE', (0, 0, 1));
|
||||
#201 = VECTOR('NONE', #200, 1);
|
||||
#202 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
||||
#202 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||
#203 = LINE('NONE', #202, #201);
|
||||
#204 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
|
||||
#204 = DIRECTION('NONE', (-0.90630778703665, -0.4226182617406995, 0));
|
||||
#205 = VECTOR('NONE', #204, 1);
|
||||
#206 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
|
||||
#206 = CARTESIAN_POINT('NONE', (3.6416100848359463, 1.29032, 2.58064));
|
||||
#207 = LINE('NONE', #206, #205);
|
||||
#208 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#209 = VECTOR('NONE', #208, 1);
|
||||
#210 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
|
||||
#210 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, -0));
|
||||
#211 = LINE('NONE', #210, #209);
|
||||
#212 = DIRECTION('NONE', (0, 0, 1));
|
||||
#213 = VECTOR('NONE', #212, 1);
|
||||
#214 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
||||
#214 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||
#215 = LINE('NONE', #214, #213);
|
||||
#216 = DIRECTION('NONE', (-1, 0, 0));
|
||||
#217 = VECTOR('NONE', #216, 1);
|
||||
#218 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
|
||||
#218 = CARTESIAN_POINT('NONE', (2.2580599999999995, 0.64516, 2.58064));
|
||||
#219 = LINE('NONE', #218, #217);
|
||||
#220 = DIRECTION('NONE', (0, -1, 0));
|
||||
#221 = VECTOR('NONE', #220, 1);
|
||||
#222 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
|
||||
#222 = CARTESIAN_POINT('NONE', (0, 0.64516, -0));
|
||||
#223 = LINE('NONE', #222, #221);
|
||||
#224 = DIRECTION('NONE', (0, -1, 0));
|
||||
#225 = VECTOR('NONE', #224, 1);
|
||||
#226 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
|
||||
#226 = CARTESIAN_POINT('NONE', (0, 0.64516, 2.58064));
|
||||
#227 = LINE('NONE', #226, #225);
|
||||
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);
|
||||
#229 = EDGE_CURVE('NONE', #7, #9, #67, .T.);
|
||||
@ -383,67 +383,67 @@ DATA;
|
||||
#367 = ORIENTED_EDGE('NONE', *, *, #267, .T.);
|
||||
#368 = ORIENTED_EDGE('NONE', *, *, #269, .T.);
|
||||
#369 = EDGE_LOOP('NONE', (#355, #356, #357, #358, #359, #360, #361, #362, #363, #364, #365, #366, #367, #368));
|
||||
#370 = CARTESIAN_POINT('NONE', (0, -0.0127, 0.0508));
|
||||
#371 = DIRECTION('NONE', (-1, 0, -0));
|
||||
#370 = CARTESIAN_POINT('NONE', (0, -0.3225799999999985, 1.2903199999999995));
|
||||
#371 = DIRECTION('NONE', (-1, -0, 0));
|
||||
#372 = AXIS2_PLACEMENT_3D('NONE', #370, #371, $);
|
||||
#373 = PLANE('NONE', #372);
|
||||
#374 = CARTESIAN_POINT('NONE', (0.039306734695977924, -0.025399999999999995, 0.0508));
|
||||
#375 = DIRECTION('NONE', (0, -1, -0));
|
||||
#374 = CARTESIAN_POINT('NONE', (0.9983910612778368, -0.6451599999999998, 1.2903199999999997));
|
||||
#375 = DIRECTION('NONE', (0, -1, 0));
|
||||
#376 = AXIS2_PLACEMENT_3D('NONE', #374, #375, $);
|
||||
#377 = PLANE('NONE', #376);
|
||||
#378 = CARTESIAN_POINT('NONE', (0.11488842876320533, -0.05079999999999996, 0.05079999999999999));
|
||||
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.819152044288992, 0));
|
||||
#378 = CARTESIAN_POINT('NONE', (2.918166090585415, -1.2903199999999988, 1.2903199999999997));
|
||||
#379 = DIRECTION('NONE', (-0.5735764363510459, -0.8191520442889919, 0));
|
||||
#380 = AXIS2_PLACEMENT_3D('NONE', #378, #379, $);
|
||||
#381 = PLANE('NONE', #380);
|
||||
#382 = CARTESIAN_POINT('NONE', (0.19623169406722757, -0.07619999999999999, 0.0508));
|
||||
#383 = DIRECTION('NONE', (0, -1, -0));
|
||||
#382 = CARTESIAN_POINT('NONE', (4.984285029307579, -1.9354799999999992, 1.2903199999999997));
|
||||
#383 = DIRECTION('NONE', (0, -1, 0));
|
||||
#384 = AXIS2_PLACEMENT_3D('NONE', #382, #383, $);
|
||||
#385 = PLANE('NONE', #384);
|
||||
#386 = CARTESIAN_POINT('NONE', (0.2413, -0.06985, 0.0508));
|
||||
#387 = DIRECTION('NONE', (1, 0, -0));
|
||||
#386 = CARTESIAN_POINT('NONE', (6.129019999999999, -1.7741899999999997, 1.2903199999999997));
|
||||
#387 = DIRECTION('NONE', (1, -0, 0));
|
||||
#388 = AXIS2_PLACEMENT_3D('NONE', #386, #387, $);
|
||||
#389 = PLANE('NONE', #388);
|
||||
#390 = CARTESIAN_POINT('NONE', (0.19823384137660915, -0.0635, 0.0508));
|
||||
#390 = CARTESIAN_POINT('NONE', (5.035139570965871, -1.6128999999999998, 1.2903199999999997));
|
||||
#391 = DIRECTION('NONE', (0, 1, -0));
|
||||
#392 = AXIS2_PLACEMENT_3D('NONE', #390, #391, $);
|
||||
#393 = PLANE('NONE', #392);
|
||||
#394 = CARTESIAN_POINT('NONE', (0.10982398353915601, -0.03174999999999997, 0.0508));
|
||||
#395 = DIRECTION('NONE', (0.573576436351046, 0.8191520442889918, -0));
|
||||
#394 = CARTESIAN_POINT('NONE', (2.7895291818945633, -0.8064499999999998, 1.2903199999999995));
|
||||
#395 = DIRECTION('NONE', (0.5735764363510459, 0.8191520442889918, -0));
|
||||
#396 = AXIS2_PLACEMENT_3D('NONE', #394, #395, $);
|
||||
#397 = PLANE('NONE', #396);
|
||||
#398 = CARTESIAN_POINT('NONE', (0.105333141160801, 0.019049999999999987, 0.0508));
|
||||
#399 = DIRECTION('NONE', (0.4226182617406993, -0.90630778703665, -0));
|
||||
#398 = CARTESIAN_POINT('NONE', (2.6754617854843468, 0.4838700000000003, 1.2903199999999997));
|
||||
#399 = DIRECTION('NONE', (0.4226182617406992, -0.90630778703665, 0));
|
||||
#400 = AXIS2_PLACEMENT_3D('NONE', #398, #399, $);
|
||||
#401 = PLANE('NONE', #400);
|
||||
#402 = CARTESIAN_POINT('NONE', (0.19374299899825406, 0.0381, 0.0508));
|
||||
#403 = DIRECTION('NONE', (0, -1, -0));
|
||||
#402 = CARTESIAN_POINT('NONE', (4.921072174555653, 0.9677399999999998, 1.2903199999999995));
|
||||
#403 = DIRECTION('NONE', (0, -1, 0));
|
||||
#404 = AXIS2_PLACEMENT_3D('NONE', #402, #403, $);
|
||||
#405 = PLANE('NONE', #404);
|
||||
#406 = CARTESIAN_POINT('NONE', (0.2413, 0.044449999999999996, 0.0508));
|
||||
#407 = DIRECTION('NONE', (1, 0, -0));
|
||||
#406 = CARTESIAN_POINT('NONE', (6.129019999999998, 1.1290299999999989, 1.2903199999999995));
|
||||
#407 = DIRECTION('NONE', (1, -0, 0));
|
||||
#408 = AXIS2_PLACEMENT_3D('NONE', #406, #407, $);
|
||||
#409 = PLANE('NONE', #408);
|
||||
#410 = CARTESIAN_POINT('NONE', (0.19233523789047138, 0.0508, 0.0508));
|
||||
#410 = CARTESIAN_POINT('NONE', (4.8853150424179725, 1.2903199999999997, 1.2903199999999997));
|
||||
#411 = DIRECTION('NONE', (0, 1, -0));
|
||||
#412 = AXIS2_PLACEMENT_3D('NONE', #410, #411, $);
|
||||
#413 = PLANE('NONE', #412);
|
||||
#414 = CARTESIAN_POINT('NONE', (0.11613523789047137, 0.0381, 0.05079999999999999));
|
||||
#415 = DIRECTION('NONE', (-0.42261826174069966, 0.90630778703665, -0));
|
||||
#414 = CARTESIAN_POINT('NONE', (2.9498350424179733, 0.9677399999999998, 1.2903199999999997));
|
||||
#415 = DIRECTION('NONE', (-0.42261826174069933, 0.9063077870366499, -0));
|
||||
#416 = AXIS2_PLACEMENT_3D('NONE', #414, #415, $);
|
||||
#417 = PLANE('NONE', #416);
|
||||
#418 = CARTESIAN_POINT('NONE', (0.044449999999999996, 0.0254, 0.0508));
|
||||
#418 = CARTESIAN_POINT('NONE', (1.1290299999999998, 0.6451599999999998, 1.29032));
|
||||
#419 = DIRECTION('NONE', (0, 1, -0));
|
||||
#420 = AXIS2_PLACEMENT_3D('NONE', #418, #419, $);
|
||||
#421 = PLANE('NONE', #420);
|
||||
#422 = CARTESIAN_POINT('NONE', (0, 0.0127, 0.0508));
|
||||
#423 = DIRECTION('NONE', (-1, 0, -0));
|
||||
#422 = CARTESIAN_POINT('NONE', (0, 0.32257999999999987, 1.2903199999999995));
|
||||
#423 = DIRECTION('NONE', (-1, -0, 0));
|
||||
#424 = AXIS2_PLACEMENT_3D('NONE', #422, #423, $);
|
||||
#425 = PLANE('NONE', #424);
|
||||
#426 = CARTESIAN_POINT('NONE', (0, 0, -0));
|
||||
#427 = DIRECTION('NONE', (0, 0, 1));
|
||||
#428 = AXIS2_PLACEMENT_3D('NONE', #426, #427, $);
|
||||
#429 = PLANE('NONE', #428);
|
||||
#430 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
|
||||
#430 = CARTESIAN_POINT('NONE', (0, 0, 2.58064));
|
||||
#431 = DIRECTION('NONE', (0, 0, 1));
|
||||
#432 = AXIS2_PLACEMENT_3D('NONE', #430, #431, $);
|
||||
#433 = PLANE('NONE', #432);
|
||||
@ -475,7 +475,7 @@ DATA;
|
||||
#459 = ADVANCED_FACE('NONE', (#458), #421, .T.);
|
||||
#460 = FACE_OUTER_BOUND('NONE', #339, .T.);
|
||||
#461 = ADVANCED_FACE('NONE', (#460), #425, .T.);
|
||||
#462 = FACE_OUTER_BOUND('NONE', #354, .T.);
|
||||
#462 = FACE_OUTER_BOUND('NONE', #354, .F.);
|
||||
#463 = ADVANCED_FACE('NONE', (#462), #429, .F.);
|
||||
#464 = FACE_OUTER_BOUND('NONE', #369, .T.);
|
||||
#465 = ADVANCED_FACE('NONE', (#464), #433, .T.);
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 221 KiB |
@ -1,478 +1,478 @@
|
||||
solid unnamed
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 -4 0
|
||||
vertex 0 -101.600006 0
|
||||
vertex 0 -0 0
|
||||
vertex 0 -4 -1
|
||||
vertex 0 -101.600006 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 -4 -1
|
||||
vertex 0 -101.600006 -25.400002
|
||||
vertex 0 -0 0
|
||||
vertex 0 -0 -1
|
||||
vertex 0 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 0 -4 -1
|
||||
vertex 0 -0 -1
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 0 -101.600006 -25.400002
|
||||
vertex 0 -0 -25.400002
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 0 -0 -1
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
vertex 0 -0 -25.400002
|
||||
vertex 78.613464 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.57357645 0 -0.81915206
|
||||
facet normal -0.5735764 0 -0.8191522
|
||||
outer loop
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
vertex 78.613464 -0 -25.400002
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.57357645 0 -0.81915206
|
||||
facet normal -0.5735764 0 -0.8191522
|
||||
outer loop
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
vertex 78.613464 -0 -25.400002
|
||||
vertex 151.16339 -0 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 9.5 -4 -3
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
vertex 151.16339 -0 -76.2
|
||||
vertex 241.3 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 9.5 -4 -3
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 9.5 -0 -3
|
||||
vertex 241.3 -101.600006 -76.2
|
||||
vertex 151.16339 -0 -76.2
|
||||
vertex 241.3 -0 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9.5 -4 -3
|
||||
vertex 9.5 -0 -3
|
||||
vertex 9.5 -4 -2.5
|
||||
vertex 241.3 -101.600006 -76.2
|
||||
vertex 241.3 -0 -76.2
|
||||
vertex 241.3 -101.600006 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 -0 0
|
||||
outer loop
|
||||
vertex 9.5 -4 -2.5
|
||||
vertex 9.5 -0 -3
|
||||
vertex 9.5 -0 -2.5
|
||||
vertex 241.3 -101.600006 -63.5
|
||||
vertex 241.3 -0 -76.2
|
||||
vertex 241.3 -0 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0 1
|
||||
outer loop
|
||||
vertex 241.3 -101.600006 -63.5
|
||||
vertex 241.3 -0 -63.5
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
vertex 241.3 -0 -63.5
|
||||
vertex 155.16768 -0 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.5735765 0 0.81915194
|
||||
outer loop
|
||||
vertex 87.15214 -101.600006 -15.875
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
vertex 109.82398 -0 -31.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 0 0.819152
|
||||
outer loop
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
vertex 155.16768 -0 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 0 0.81915206
|
||||
outer loop
|
||||
vertex 87.15214 -0 -15.875
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 87.15214 -101.600006 -15.875
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.5735765 0 0.81915194
|
||||
outer loop
|
||||
vertex 109.82398 -0 -31.75
|
||||
vertex 87.15214 -0 -15.875
|
||||
vertex 87.15214 -101.600006 -15.875
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 -0 0.819152
|
||||
outer loop
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
vertex 155.16768 -0 -63.5
|
||||
vertex 109.82398 -0 -31.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 -0 0.81915206
|
||||
outer loop
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 87.15214 -0 -15.875
|
||||
vertex 64.480286 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.4226182 0 -0.9063078
|
||||
outer loop
|
||||
vertex 84.906715 -101.600006 9.525
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 64.480286 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261833 0 -0.90630776
|
||||
outer loop
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
vertex 84.906715 -101.600006 9.525
|
||||
vertex 84.906715 -0 9.525
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.4226182 0 -0.9063078
|
||||
outer loop
|
||||
vertex 84.906715 -0 9.525
|
||||
vertex 84.906715 -101.600006 9.525
|
||||
vertex 64.480286 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.4226183 0 -0.9063078
|
||||
outer loop
|
||||
vertex 105.33314 -0 19.05
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261833 0 -0.90630776
|
||||
outer loop
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
vertex 84.906715 -0 9.525
|
||||
vertex 105.33314 -0 19.05
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.4226183 0 -0.9063078
|
||||
outer loop
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 105.33314 -0 19.05
|
||||
vertex 146.18599 -0 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 146.18599 -0 38.1
|
||||
vertex 241.3 -101.600006 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 241.3 -101.600006 38.1
|
||||
vertex 146.18599 -0 38.1
|
||||
vertex 241.3 -0 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 241.3 -101.600006 38.1
|
||||
vertex 241.3 -0 38.1
|
||||
vertex 241.3 -101.600006 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 -0 0
|
||||
outer loop
|
||||
vertex 241.3 -101.600006 50.800003
|
||||
vertex 241.3 -0 38.1
|
||||
vertex 241.3 -0 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0 0.99999994
|
||||
outer loop
|
||||
vertex 9.5 -4 -2.5
|
||||
vertex 9.5 -0 -2.5
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 241.3 -101.600006 50.800003
|
||||
vertex 241.3 -0 50.800003
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 0.99999994
|
||||
outer loop
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 9.5 -0 -2.5
|
||||
vertex 6.108964 -0 -2.5
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
vertex 241.3 -0 50.800003
|
||||
vertex 143.37048 -0 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.5735763 0 0.8191522
|
||||
facet normal -0.42261827 0 0.9063078
|
||||
outer loop
|
||||
vertex 3.4311862 -4 -0.625
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 4.323779 -0 -1.25
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
vertex 143.37048 -0 50.800003
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 0 0.819152
|
||||
facet normal -0.42261827 0 0.9063078
|
||||
outer loop
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 6.108964 -0 -2.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 0 0.819152
|
||||
outer loop
|
||||
vertex 3.4311862 -0 -0.625
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 2.5385938 -4 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 -0 0.819152
|
||||
outer loop
|
||||
vertex 3.4311862 -4 -0.625
|
||||
vertex 3.4311862 -0 -0.625
|
||||
vertex 2.5385938 -4 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.57357645 -0 0.819152
|
||||
outer loop
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 6.108964 -0 -2.5
|
||||
vertex 4.323779 -0 -1.25
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.5735763 0 0.8191522
|
||||
outer loop
|
||||
vertex 3.4311862 -0 -0.625
|
||||
vertex 3.4311862 -4 -0.625
|
||||
vertex 4.323779 -0 -1.25
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261824 0 -0.9063078
|
||||
outer loop
|
||||
vertex 3.342784 -4 0.375
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 2.5385938 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261824 0 -0.9063078
|
||||
outer loop
|
||||
vertex 4.146974 -4 0.75
|
||||
vertex 3.342784 -4 0.375
|
||||
vertex 3.342784 -0 0.375
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261824 0 -0.9063078
|
||||
outer loop
|
||||
vertex 3.342784 -0 0.375
|
||||
vertex 4.146974 -0 0.75
|
||||
vertex 4.146974 -4 0.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261833 0 -0.90630776
|
||||
outer loop
|
||||
vertex 4.146974 -0 0.75
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 5.755354 -4 1.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261824 0 -0.9063078
|
||||
outer loop
|
||||
vertex 3.342784 -4 0.375
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 3.342784 -0 0.375
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0.42261833 0 -0.90630776
|
||||
outer loop
|
||||
vertex 5.755354 -4 1.5
|
||||
vertex 4.146974 -4 0.75
|
||||
vertex 4.146974 -0 0.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 5.755354 -4 1.5
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 9.5 -4 1.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 -1
|
||||
outer loop
|
||||
vertex 9.5 -4 1.5
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 9.5 -0 1.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 0 0
|
||||
outer loop
|
||||
vertex 9.5 -4 1.5
|
||||
vertex 9.5 -0 1.5
|
||||
vertex 9.5 -4 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 1 -0 0
|
||||
outer loop
|
||||
vertex 9.5 -4 2
|
||||
vertex 9.5 -0 1.5
|
||||
vertex 9.5 -0 2
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
vertex 143.37048 -0 50.800003
|
||||
vertex 88.9 -0 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0 1
|
||||
outer loop
|
||||
vertex 9.5 -4 2
|
||||
vertex 9.5 -0 2
|
||||
vertex 5.644507 -4 2
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
vertex 88.9 -0 25.400002
|
||||
vertex 0 -101.600006 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 5.644507 -4 2
|
||||
vertex 9.5 -0 2
|
||||
vertex 5.644507 -0 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.42261824 0 0.90630776
|
||||
outer loop
|
||||
vertex 5.644507 -4 2
|
||||
vertex 5.644507 -0 2
|
||||
vertex 3.5 -4 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0.42261824 0 0.90630776
|
||||
outer loop
|
||||
vertex 3.5 -4 1
|
||||
vertex 5.644507 -0 2
|
||||
vertex 3.5 -0 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0 1
|
||||
outer loop
|
||||
vertex 3.5 -4 1
|
||||
vertex 3.5 -0 1
|
||||
vertex 0 -4 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0 1
|
||||
outer loop
|
||||
vertex 0 -4 1
|
||||
vertex 3.5 -0 1
|
||||
vertex 0 -0 1
|
||||
vertex 0 -101.600006 25.400002
|
||||
vertex 88.9 -0 25.400002
|
||||
vertex 0 -0 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 -4 1
|
||||
vertex 0 -0 1
|
||||
vertex 0 -4 0
|
||||
vertex 0 -101.600006 25.400002
|
||||
vertex 0 -0 25.400002
|
||||
vertex 0 -101.600006 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -1 0 0
|
||||
outer loop
|
||||
vertex 0 -4 0
|
||||
vertex 0 -0 1
|
||||
vertex 0 -101.600006 0
|
||||
vertex 0 -0 25.400002
|
||||
vertex 0 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 3.342784 -0 0.375
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 3.5 -0 1
|
||||
vertex 84.906715 -0 9.525
|
||||
vertex 64.480286 -0 0
|
||||
vertex 88.9 -0 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 3.4311862 -0 -0.625
|
||||
vertex 4.323779 -0 -1.25
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 105.33314 -0 19.05
|
||||
vertex 84.906715 -0 9.525
|
||||
vertex 88.9 -0 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 3.342784 -0 0.375
|
||||
vertex 3.5 -0 1
|
||||
vertex 4.146974 -0 0.75
|
||||
vertex 87.15214 -0 -15.875
|
||||
vertex 109.82398 -0 -31.75
|
||||
vertex 78.613464 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 105.33314 -0 19.05
|
||||
vertex 143.37048 -0 50.800003
|
||||
vertex 146.18599 -0 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 1 0
|
||||
outer loop
|
||||
vertex 0 -0 25.400002
|
||||
vertex 88.9 -0 25.400002
|
||||
vertex 64.480286 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 -0 25.400002
|
||||
vertex 64.480286 -0 0
|
||||
vertex 0 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 1 0
|
||||
outer loop
|
||||
vertex 143.37048 -0 50.800003
|
||||
vertex 241.3 -0 50.800003
|
||||
vertex 146.18599 -0 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 241.3 -0 50.800003
|
||||
vertex 241.3 -0 38.1
|
||||
vertex 146.18599 -0 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 105.33314 -0 19.05
|
||||
vertex 88.9 -0 25.400002
|
||||
vertex 143.37048 -0 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0.99999994 0
|
||||
outer loop
|
||||
vertex 4.323779 -0 -1.25
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 64.480286 -0 0
|
||||
vertex 87.15214 -0 -15.875
|
||||
vertex 78.613464 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 -0 -1
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 109.82398 -0 -31.75
|
||||
vertex 151.16339 -0 -76.2
|
||||
vertex 78.613464 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 -0 -1
|
||||
vertex 155.16768 -0 -63.5
|
||||
vertex 151.16339 -0 -76.2
|
||||
vertex 109.82398 -0 -31.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 241.3 -0 -63.5
|
||||
vertex 241.3 -0 -76.2
|
||||
vertex 155.16768 -0 -63.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 155.16768 -0 -63.5
|
||||
vertex 241.3 -0 -76.2
|
||||
vertex 151.16339 -0 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 64.480286 -0 0
|
||||
vertex 78.613464 -0 -25.400002
|
||||
vertex 0 -0 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 0 -0 -25.400002
|
||||
vertex 0 -0 0
|
||||
vertex 2.5385938 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0.99999994 -0
|
||||
outer loop
|
||||
vertex 9.5 -0 -3
|
||||
vertex 6.108964 -0 -2.5
|
||||
vertex 9.5 -0 -2.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 9.5 -0 -3
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 6.108964 -0 -2.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 5.9513144 -0 -3
|
||||
vertex 4.323779 -0 -1.25
|
||||
vertex 6.108964 -0 -2.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 5.644507 -0 2
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 4.146974 -0 0.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 0.99999994 -0
|
||||
outer loop
|
||||
vertex 3.0950184 -0 -1
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 3.4311862 -0 -0.625
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 4.146974 -0 0.75
|
||||
vertex 3.5 -0 1
|
||||
vertex 5.644507 -0 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 9.5 -0 1.5
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 9.5 -0 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 -0
|
||||
outer loop
|
||||
vertex 5.755354 -0 1.5
|
||||
vertex 5.644507 -0 2
|
||||
vertex 9.5 -0 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 0 -0 0
|
||||
vertex 0 -0 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 1 0
|
||||
outer loop
|
||||
vertex 3.5 -0 1
|
||||
vertex 2.5385938 -0 0
|
||||
vertex 0 -0 1
|
||||
vertex 64.480286 -0 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 -1 0
|
||||
outer loop
|
||||
vertex 3.342784 -4 0.375
|
||||
vertex 3.5 -4 1
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 84.906715 -101.600006 9.525
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
vertex 64.480286 -101.600006 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 -1 0
|
||||
outer loop
|
||||
vertex 4.146974 -4 0.75
|
||||
vertex 3.5 -4 1
|
||||
vertex 3.342784 -4 0.375
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
vertex 84.906715 -101.600006 9.525
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 3.4311862 -4 -0.625
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 87.15214 -101.600006 -15.875
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 -101.600006 25.400002
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 -101.600006 25.400002
|
||||
vertex 0 -101.600006 0
|
||||
vertex 64.480286 -101.600006 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 241.3 -101.600006 50.800003
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 241.3 -101.600006 50.800003
|
||||
vertex 146.18599 -101.600006 38.1
|
||||
vertex 241.3 -101.600006 38.1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 105.33314 -101.600006 19.05
|
||||
vertex 143.37048 -101.600006 50.800003
|
||||
vertex 88.9 -101.600006 25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0.99999994 0
|
||||
outer loop
|
||||
vertex 4.146974 -4 0.75
|
||||
vertex 5.755354 -4 1.5
|
||||
vertex 5.644507 -4 2
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
vertex 87.15214 -101.600006 -15.875
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
facet normal -0 -1 -0
|
||||
outer loop
|
||||
vertex 0 -4 1
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 3.5 -4 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 -4 1
|
||||
vertex 0 -4 0
|
||||
vertex 2.5385938 -4 0
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 5.644507 -4 2
|
||||
vertex 5.755354 -4 1.5
|
||||
vertex 9.5 -4 2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 9.5 -4 2
|
||||
vertex 5.755354 -4 1.5
|
||||
vertex 9.5 -4 1.5
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 4.146974 -4 0.75
|
||||
vertex 5.644507 -4 2
|
||||
vertex 3.5 -4 1
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -0.99999994 0
|
||||
outer loop
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 3.4311862 -4 -0.625
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 -0.99999994 -0
|
||||
outer loop
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 -1 0
|
||||
outer loop
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 4.323779 -4 -1.25
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
vertex 109.82398 -101.600006 -31.75
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal -0 -0.99999994 -0
|
||||
facet normal -0 -1 -0
|
||||
outer loop
|
||||
vertex 9.5 -4 -2.5
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 9.5 -4 -3
|
||||
vertex 241.3 -101.600006 -63.5
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
vertex 241.3 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 6.108964 -4 -2.5
|
||||
vertex 5.9513144 -4 -3
|
||||
vertex 9.5 -4 -3
|
||||
vertex 155.16768 -101.600006 -63.5
|
||||
vertex 151.16339 -101.600006 -76.2
|
||||
vertex 241.3 -101.600006 -76.2
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 -0
|
||||
outer loop
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 0 -4 -1
|
||||
vertex 3.0950184 -4 -1
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 0 -101.600006 -25.400002
|
||||
vertex 78.613464 -101.600006 -25.400002
|
||||
endloop
|
||||
endfacet
|
||||
facet normal 0 -1 0
|
||||
outer loop
|
||||
vertex 0 -4 -1
|
||||
vertex 2.5385938 -4 0
|
||||
vertex 0 -4 0
|
||||
vertex 0 -101.600006 -25.400002
|
||||
vertex 64.480286 -101.600006 0
|
||||
vertex 0 -101.600006 0
|
||||
endloop
|
||||
endfacet
|
||||
endsolid unnamed
|
||||
|
Before Width: | Height: | Size: 57 KiB After Width: | Height: | Size: 221 KiB |
@ -1,7 +1,5 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { secrets } from './secrets'
|
||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getUtils } from './test-utils'
|
||||
import waitOn from 'wait-on'
|
||||
import { Themes } from '../../src/lib/theme'
|
||||
@ -53,40 +51,38 @@ test('Basic sketch', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await Promise.all([
|
||||
u.doAndWaitForImageDiff(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
200
|
||||
),
|
||||
u.waitForDefaultPlanesVisibilityChange(),
|
||||
])
|
||||
await u.doAndWaitForImageDiff(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
200
|
||||
)
|
||||
|
||||
// select a plane
|
||||
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
|
||||
await u.waitForCmdReceive('set_tool')
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||
'set_tool'
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`const part001 = startSketchOn('-XZ')`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||
|
||||
const startXPx = 600
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
|
||||
'mouse_click',
|
||||
false
|
||||
)
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
const startAt = '[23.74, -32.03]'
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const startAt = '[18.26, -24.63]'
|
||||
const num = '18.43'
|
||||
const num = 23.97
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
@ -104,20 +100,15 @@ test('Basic sketch', async ({ page }) => {
|
||||
|> startProfileAt(${startAt}, %)
|
||||
|> line([${num}, 0], %)
|
||||
|> line([0, ${num}], %)
|
||||
|> line([-36.69, 0], %)`)
|
||||
|> line([-47.71, 0], %)`)
|
||||
|
||||
// deselect line tool
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||
'set_tool'
|
||||
)
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// click between first two clicks to get center of the line
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10),
|
||||
'select_with_point'
|
||||
)
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 15, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// hold down shift
|
||||
await page.keyboard.down('Shift')
|
||||
@ -206,7 +197,7 @@ test('if you write invalid kcl you get inlined errors', async ({ page }) => {
|
||||
|
||||
test('executes on load', async ({ page, context }) => {
|
||||
const u = getUtils(page)
|
||||
await context.addInitScript(async (token) => {
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const part001 = startSketchOn('-XZ')
|
||||
@ -271,59 +262,41 @@ test('Can create sketches on all planes and their back sides', async ({
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
const camCmd: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 15, y: 0, z: 0 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
vantage: { x: 30, y: 30, z: 30 },
|
||||
},
|
||||
}
|
||||
const camPos: [number, number, number] = [100, 100, 100]
|
||||
|
||||
const TestSinglePlane = async ({
|
||||
viewCmd,
|
||||
expectedCode,
|
||||
clickCoords,
|
||||
}: {
|
||||
viewCmd: EngineCommand
|
||||
viewCmd: [number, number, number]
|
||||
expectedCode: string
|
||||
clickCoords: { x: number; y: number }
|
||||
}) => {
|
||||
await u.openDebugPanel()
|
||||
await u.sendCustomCmd(viewCmd)
|
||||
|
||||
await u.updateCamPosition(viewCmd)
|
||||
|
||||
await u.clearCommandLogs()
|
||||
// await page.waitForTimeout(200)
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||
await u.openDebugPanel()
|
||||
await page.waitForTimeout(300) // wait for animation
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Line' })).toBeVisible()
|
||||
|
||||
// draw a line
|
||||
const startXPx = 600
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
await u.waitForCmdReceive('set_tool')
|
||||
await u.clearCommandLogs()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
await u.openDebugPanel()
|
||||
await u.waitForCmdReceive('mouse_click')
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(expectedCode)
|
||||
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
await u.clearCommandLogs()
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
@ -333,51 +306,41 @@ test('Can create sketches on all planes and their back sides', async ({
|
||||
|
||||
const codeTemplate = (
|
||||
plane = 'XY',
|
||||
sign = ''
|
||||
rounded = false,
|
||||
otherThing = '1'
|
||||
) => `const part001 = startSketchOn('${plane}')
|
||||
|> startProfileAt([${sign}6.88, -9.29], %)
|
||||
|> line([${sign}6.95, 0], %)`
|
||||
|> startProfileAt([28.9${otherThing}, -39${rounded ? '' : '.01'}], %)`
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmd,
|
||||
viewCmd: camPos,
|
||||
expectedCode: codeTemplate('XY'),
|
||||
clickCoords: { x: 700, y: 350 }, // red plane
|
||||
clickCoords: { x: 600, y: 388 }, // red plane
|
||||
// clickCoords: { x: 600, y: 400 }, // red plane // clicks grid helper and that causes problems, should fix so that these coords work too.
|
||||
})
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmd,
|
||||
expectedCode: codeTemplate('YZ'),
|
||||
clickCoords: { x: 1000, y: 200 }, // green plane
|
||||
viewCmd: camPos,
|
||||
expectedCode: codeTemplate('YZ', true),
|
||||
clickCoords: { x: 700, y: 300 }, // green plane
|
||||
})
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmd,
|
||||
expectedCode: codeTemplate('XZ', '-'),
|
||||
clickCoords: { x: 630, y: 130 }, // blue plane
|
||||
viewCmd: camPos,
|
||||
expectedCode: codeTemplate('XZ'),
|
||||
clickCoords: { x: 700, y: 80 }, // blue plane
|
||||
})
|
||||
|
||||
// new camera angle to click the back side of all three planes
|
||||
const camCmdBackSide: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: -15, y: 0, z: 0 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
vantage: { x: -30, y: -30, z: -30 },
|
||||
},
|
||||
}
|
||||
const camCmdBackSide: [number, number, number] = [-100, -100, -100]
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmdBackSide,
|
||||
expectedCode: codeTemplate('-XY', '-'),
|
||||
clickCoords: { x: 705, y: 136 }, // back of red plane
|
||||
expectedCode: codeTemplate('-XY', false, '3'),
|
||||
clickCoords: { x: 601, y: 118 }, // back of red plane
|
||||
})
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmdBackSide,
|
||||
expectedCode: codeTemplate('-YZ', '-'),
|
||||
clickCoords: { x: 1000, y: 350 }, // back of green plane
|
||||
expectedCode: codeTemplate('-YZ'),
|
||||
clickCoords: { x: 730, y: 219 }, // back of green plane
|
||||
})
|
||||
await TestSinglePlane({
|
||||
viewCmd: camCmdBackSide,
|
||||
expectedCode: codeTemplate('-XZ'),
|
||||
clickCoords: { x: 600, y: 400 }, // back of blue plane
|
||||
expectedCode: codeTemplate('-XZ', true),
|
||||
clickCoords: { x: 680, y: 427 }, // back of blue plane
|
||||
})
|
||||
})
|
||||
|
||||
@ -387,7 +350,6 @@ test('Auto complete works', async ({ page }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
// this test might be brittle as we add and remove functions
|
||||
// but should also be easy to update.
|
||||
@ -405,6 +367,7 @@ test('Auto complete works', async ({ page }) => {
|
||||
await page.keyboard.type(' |> startProfi')
|
||||
// expect there be a single auto complete option that we can just hit enter on
|
||||
await expect(page.locator('.cm-completionLabel')).toBeVisible()
|
||||
await page.waitForTimeout(100)
|
||||
await page.keyboard.press('Enter') // accepting the auto complete, not a new line
|
||||
|
||||
await page.keyboard.type('([0,0], %)')
|
||||
@ -412,6 +375,7 @@ test('Auto complete works', async ({ page }) => {
|
||||
await page.keyboard.type(' |> lin')
|
||||
|
||||
await expect(page.locator('.cm-tooltip-autocomplete')).toBeVisible()
|
||||
await page.waitForTimeout(100)
|
||||
// press arrow down twice then enter to accept xLine
|
||||
await page.keyboard.press('ArrowDown')
|
||||
await page.keyboard.press('ArrowDown')
|
||||
@ -478,38 +442,36 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
const xAxisClick = () => page.mouse.click(700, 250)
|
||||
const emptySpaceClick = () => page.mouse.click(700, 300)
|
||||
const topHorzSegmentClick = () => page.mouse.click(700, 285)
|
||||
const bottomHorzSegmentClick = () => page.mouse.click(750, 393)
|
||||
const xAxisClick = () =>
|
||||
page.mouse.click(700, 250).then(() => page.waitForTimeout(100))
|
||||
const emptySpaceClick = () =>
|
||||
page.mouse.click(728, 343).then(() => page.waitForTimeout(100))
|
||||
const topHorzSegmentClick = () =>
|
||||
page.mouse.click(709, 289).then(() => page.waitForTimeout(100))
|
||||
const bottomHorzSegmentClick = () =>
|
||||
page.mouse.click(767, 396).then(() => page.waitForTimeout(100))
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
// select a plane
|
||||
await u.doAndWaitForCmd(() => page.mouse.click(700, 200), 'edit_mode_enter')
|
||||
await u.waitForCmdReceive('set_tool')
|
||||
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||
'set_tool'
|
||||
)
|
||||
await page.mouse.click(700, 200)
|
||||
await page.waitForTimeout(700) // wait for animation
|
||||
|
||||
const startXPx = 600
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10),
|
||||
'mouse_click',
|
||||
false
|
||||
)
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
const startAt = '[23.74, -32.03]'
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)`)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
|
||||
const startAt = '[18.26, -24.63]'
|
||||
const num = '18.43'
|
||||
const num2 = '36.69'
|
||||
const num = 23.97
|
||||
const num2 = '47.71'
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
@ -530,10 +492,7 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
|> line([-${num2}, 0], %)`)
|
||||
|
||||
// deselect line tool
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Line' }).click(),
|
||||
'set_tool'
|
||||
)
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
const selectionSequence = async () => {
|
||||
@ -555,79 +514,73 @@ test('Selections work on fresh and edited sketch', async ({ page }) => {
|
||||
// now check clicking works including axis
|
||||
|
||||
// click a segment hold shift and click an axis, see that a relevant constraint is enabled
|
||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
|
||||
await topHorzSegmentClick()
|
||||
await page.keyboard.down('Shift')
|
||||
const absYButton = page.getByRole('button', { name: 'ABS Y' })
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
||||
await xAxisClick()
|
||||
await page.keyboard.up('Shift')
|
||||
await absYButton.and(page.locator(':not([disabled])')).waitFor()
|
||||
await expect(absYButton).not.toBeDisabled()
|
||||
|
||||
// clear selection by clicking on nothing
|
||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
||||
await emptySpaceClick()
|
||||
|
||||
// same selection but click the axis first
|
||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
||||
await xAxisClick()
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await page.keyboard.down('Shift')
|
||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_with_point', false)
|
||||
await topHorzSegmentClick()
|
||||
|
||||
await page.keyboard.up('Shift')
|
||||
await expect(absYButton).not.toBeDisabled()
|
||||
|
||||
// clear selection by clicking on nothing
|
||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
||||
await emptySpaceClick()
|
||||
|
||||
// check the same selection again by putting cursor in code first then selecting axis
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByText(` |> line([-${num2}, 0], %)`).click(),
|
||||
'select_clear',
|
||||
false
|
||||
)
|
||||
await page.getByText(` |> line([-${num2}, 0], %)`).click()
|
||||
await page.keyboard.down('Shift')
|
||||
await expect(absYButton).toBeDisabled()
|
||||
await u.doAndWaitForCmd(xAxisClick, 'select_with_point', false)
|
||||
await xAxisClick()
|
||||
await page.keyboard.up('Shift')
|
||||
await expect(absYButton).not.toBeDisabled()
|
||||
|
||||
// clear selection by clicking on nothing
|
||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
||||
await emptySpaceClick()
|
||||
|
||||
// select segment in editor than another segment in scene and check there are two cursors
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByText(` |> line([-${num2}, 0], %)`).click(),
|
||||
'select_clear',
|
||||
false
|
||||
)
|
||||
await page.getByText(` |> line([-${num2}, 0], %)`).click()
|
||||
await page.waitForTimeout(300)
|
||||
await page.keyboard.down('Shift')
|
||||
await expect(page.locator('.cm-cursor')).toHaveCount(1)
|
||||
await u.doAndWaitForCmd(bottomHorzSegmentClick, 'select_with_point', false) // another segment, bottom one
|
||||
await bottomHorzSegmentClick()
|
||||
await page.keyboard.up('Shift')
|
||||
await expect(page.locator('.cm-cursor')).toHaveCount(2)
|
||||
|
||||
// clear selection by clicking on nothing
|
||||
await u.doAndWaitForCmd(emptySpaceClick, 'select_clear', false)
|
||||
await emptySpaceClick()
|
||||
}
|
||||
|
||||
await selectionSequence()
|
||||
|
||||
// hovering in fresh sketch worked, lets try exiting and re-entering
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Exit Sketch' }).click(),
|
||||
'edit_mode_exit'
|
||||
)
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await page.waitForTimeout(200)
|
||||
// wait for execution done
|
||||
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// select a line
|
||||
await u.doAndWaitForCmd(topHorzSegmentClick, 'select_clear', false)
|
||||
// await topHorzSegmentClick()
|
||||
await page.getByText(startAt).click() // TODO remove this and reinstate // await topHorzSegmentClick()
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// enter sketch again
|
||||
await u.doAndWaitForCmd(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
'edit_mode_enter',
|
||||
false
|
||||
)
|
||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
await page.waitForTimeout(300) // wait for animation
|
||||
|
||||
// hover again and check it works
|
||||
await selectionSequence()
|
||||
@ -697,6 +650,8 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
let cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||
await page.keyboard.press('Meta+K')
|
||||
@ -710,10 +665,7 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
||||
await expect(page.getByRole('button', { name: 'selection' })).toBeDisabled()
|
||||
|
||||
// Click to select face and set distance
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByText('|> startProfileAt([-6.95, 4.98], %)').click()
|
||||
await u.waitForCmdReceive('select_add')
|
||||
await u.closeDebugPanel()
|
||||
await page.getByRole('button', { name: 'Continue' }).click()
|
||||
await expect(page.getByRole('button', { name: 'distance' })).toBeDisabled()
|
||||
await page.keyboard.press('Enter')
|
||||
@ -735,3 +687,330 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
||||
|> extrude(5, %)`
|
||||
)
|
||||
})
|
||||
|
||||
test('Can add multiple sketches', async ({ page }) => {
|
||||
const u = getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await u.doAndWaitForImageDiff(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
200
|
||||
)
|
||||
|
||||
// select a plane
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`const part001 = startSketchOn('-XZ')`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||
|
||||
const startXPx = 600
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
const startAt = '[23.74, -32.03]'
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const num = 23.97
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
|> line([${num}, 0], %)`)
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
|> line([${num}, 0], %)
|
||||
|> line([0, ${num}], %)`)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
const finalCodeFirstSketch = `const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
|> line([${num}, 0], %)
|
||||
|> line([0, ${num}], %)
|
||||
|> line([-47.71, 0], %)`
|
||||
await expect(page.locator('.cm-content')).toHaveText(finalCodeFirstSketch)
|
||||
|
||||
// exit the sketch
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
await u.updateCamPosition([0, 100, 100])
|
||||
|
||||
// start a new sketch
|
||||
await u.clearCommandLogs()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(673, 384)
|
||||
|
||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||
await u.clearAndCloseDebugPanel()
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
const startAt2 = '[23.61, -31.85]'
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)`.replace(/\s/g, '')
|
||||
)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const num2 = 23.83
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)`.replace(/\s/g, '')
|
||||
)
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${num2}], %)`.replace(/\s/g, '')
|
||||
)
|
||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||
await expect(
|
||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||
).toBe(
|
||||
`${finalCodeFirstSketch}
|
||||
const part002 = startSketchOn('XY')
|
||||
|> startProfileAt(${startAt2}, %)
|
||||
|> line([${num2}, 0], %)
|
||||
|> line([0, ${num2}], %)
|
||||
|> line([-47.44, 0], %)`.replace(/\s/g, '')
|
||||
)
|
||||
})
|
||||
|
||||
test('ProgramMemory can be serialised', async ({ page, context }) => {
|
||||
const u = getUtils(page)
|
||||
await context.addInitScript(async () => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const part = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, 1], %)
|
||||
|> line([1, 0], %)
|
||||
|> line([0, -1], %)
|
||||
|> close(%)
|
||||
|> extrude(1, %)
|
||||
|> patternLinear({
|
||||
axis: [1, 0, 1],
|
||||
repetitions: 3,
|
||||
distance: 6
|
||||
}, %)`
|
||||
)
|
||||
})
|
||||
await page.setViewportSize({ width: 1000, height: 500 })
|
||||
await page.goto('/')
|
||||
const messages: string[] = []
|
||||
|
||||
// Listen for all console events and push the message text to an array
|
||||
page.on('console', (message) => messages.push(message.text()))
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
|
||||
const forbiddenMessages = ['cannot serialize tagged newtype variant']
|
||||
forbiddenMessages.forEach((forbiddenMessage) => {
|
||||
messages.forEach((message) => {
|
||||
expect(message).not.toContain(forbiddenMessage)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test("Various pipe expressions should and shouldn't allow edit and or extrude", async ({
|
||||
page,
|
||||
context,
|
||||
}) => {
|
||||
const u = getUtils(page)
|
||||
const selectionsSnippets = {
|
||||
extrudeAndEditBlocked: '|> startProfileAt([10.81, 32.99], %)',
|
||||
extrudeAndEditBlockedInFunction: '|> startProfileAt(pos, %)',
|
||||
extrudeAndEditAllowed: '|> startProfileAt([15.72, 4.7], %)',
|
||||
editOnly: '|> startProfileAt([15.79, -14.6], %)',
|
||||
}
|
||||
await context.addInitScript(
|
||||
async ({
|
||||
extrudeAndEditBlocked,
|
||||
extrudeAndEditBlockedInFunction,
|
||||
extrudeAndEditAllowed,
|
||||
editOnly,
|
||||
}: any) => {
|
||||
localStorage.setItem(
|
||||
'persistCode',
|
||||
`const part001 = startSketchOn('-XZ')
|
||||
${extrudeAndEditBlocked}
|
||||
|> line([25.96, 2.93], %)
|
||||
|> line([5.25, -5.72], %)
|
||||
|> line([-2.01, -10.35], %)
|
||||
|> line([-27.65, -2.78], %)
|
||||
|> close(%)
|
||||
|> extrude(5, %)
|
||||
const part002 = startSketchOn('-XZ')
|
||||
${extrudeAndEditAllowed}
|
||||
|> line([10.32, 6.47], %)
|
||||
|> line([9.71, -6.16], %)
|
||||
|> line([-3.08, -9.86], %)
|
||||
|> line([-12.02, -1.54], %)
|
||||
|> close(%)
|
||||
const part003 = startSketchOn('-XZ')
|
||||
${editOnly}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -8], %)
|
||||
|> line([-20.38, -10.12], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
|
||||
fn yohey = (pos) => {
|
||||
const part004 = startSketchOn('-XZ')
|
||||
${extrudeAndEditBlockedInFunction}
|
||||
|> line([27.55, -1.65], %)
|
||||
|> line([4.95, -10.53], %)
|
||||
|> line([-20.38, -8], %)
|
||||
|> line([-15.79, 17.08], %)
|
||||
return ''
|
||||
}
|
||||
|
||||
yohey([15.79, -34.6])
|
||||
`
|
||||
)
|
||||
},
|
||||
selectionsSnippets
|
||||
)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await page.getByText(selectionsSnippets.extrudeAndEditBlocked).click()
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).not.toBeVisible()
|
||||
|
||||
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).not.toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).not.toBeDisabled()
|
||||
|
||||
await page.getByText(selectionsSnippets.editOnly).click()
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).not.toBeDisabled()
|
||||
|
||||
await page
|
||||
.getByText(selectionsSnippets.extrudeAndEditBlockedInFunction)
|
||||
.click()
|
||||
await expect(page.getByRole('button', { name: 'Extrude' })).toBeDisabled()
|
||||
await expect(
|
||||
page.getByRole('button', { name: 'Edit Sketch' })
|
||||
).not.toBeVisible()
|
||||
|
||||
// selecting an editable sketch but clicking "start sktech" should start a new sketch and not edit the existing one
|
||||
await page.getByText(selectionsSnippets.extrudeAndEditAllowed).click()
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await page.mouse.click(700, 200)
|
||||
// expect main content to contain `part005` i.e. started a new sketch
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
/part005 = startSketchOn\('-XZ'\)/
|
||||
)
|
||||
})
|
||||
|
||||
test('Deselecting line tool should mean nothing happens on click', async ({
|
||||
page,
|
||||
}) => {
|
||||
const u = getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await u.doAndWaitForImageDiff(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
200
|
||||
)
|
||||
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`const part001 = startSketchOn('-XZ')`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(300)
|
||||
|
||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
// deselect the line tool by clicking it
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
|
||||
await page.mouse.click(700, 200)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(700, 250)
|
||||
await page.waitForTimeout(100)
|
||||
await page.mouse.click(750, 200)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
// expect no change
|
||||
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
|
||||
|
||||
// select line tool again
|
||||
await page.getByRole('button', { name: 'Line' }).click()
|
||||
|
||||
await u.closeDebugPanel()
|
||||
|
||||
// line tool should work as expected again
|
||||
await page.mouse.click(700, 200)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.mouse.click(700, 300)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
|
||||
await page.mouse.click(750, 300)
|
||||
await expect(page.locator('.cm-content')).not.toHaveText(previousCodeContent)
|
||||
previousCodeContent = await page.locator('.cm-content').innerText()
|
||||
})
|
||||
|
@ -1,7 +1,5 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
import { secrets } from './secrets'
|
||||
import { EngineCommand } from '../../src/lang/std/engineConnection'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { getUtils } from './test-utils'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import fsp from 'fs/promises'
|
||||
@ -40,19 +38,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openAndClearDebugPanel()
|
||||
|
||||
const camCmd: EngineCommand = {
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'default_camera_look_at',
|
||||
center: { x: 0, y: 0, z: 0 },
|
||||
up: { x: 0, y: 0, z: 1 },
|
||||
vantage: { x: 0, y: 85, z: 85 },
|
||||
},
|
||||
}
|
||||
|
||||
await u.sendCustomCmd(camCmd)
|
||||
await u.waitForCmdReceive('default_camera_look_at')
|
||||
const camPos: [number, number, number] = [0, 85, 85]
|
||||
await u.updateCamPosition(camPos)
|
||||
|
||||
// rotate
|
||||
await u.closeDebugPanel()
|
||||
@ -62,13 +49,11 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
await page.mouse.up({ button: 'right' })
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.waitForCmdReceive('camera_drag_end')
|
||||
await page.waitForTimeout(500)
|
||||
await u.clearCommandLogs()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await expect(page).toHaveScreenshot({
|
||||
@ -77,10 +62,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
await u.sendCustomCmd(camCmd)
|
||||
await u.waitForCmdReceive('default_camera_look_at')
|
||||
await u.updateCamPosition(camPos)
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await u.closeDebugPanel()
|
||||
@ -93,12 +76,10 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
await page.keyboard.up('Shift')
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.waitForCmdReceive('camera_drag_end')
|
||||
await page.waitForTimeout(300)
|
||||
await u.clearCommandLogs()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await expect(page).toHaveScreenshot({
|
||||
@ -107,10 +88,8 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
|
||||
await u.sendCustomCmd(camCmd)
|
||||
await u.waitForCmdReceive('default_camera_look_at')
|
||||
await u.updateCamPosition(camPos)
|
||||
|
||||
await u.clearCommandLogs()
|
||||
await u.closeDebugPanel()
|
||||
@ -119,17 +98,15 @@ test('change camera, show planes', async ({ page, context }) => {
|
||||
await page.keyboard.down('Control')
|
||||
await page.mouse.move(700, 400)
|
||||
await page.mouse.down({ button: 'right' })
|
||||
await page.mouse.move(700, 350)
|
||||
await page.mouse.move(700, 300)
|
||||
await page.mouse.up({ button: 'right' })
|
||||
await page.keyboard.up('Control')
|
||||
|
||||
await u.openDebugPanel()
|
||||
await u.waitForCmdReceive('camera_drag_end')
|
||||
await page.waitForTimeout(300)
|
||||
await u.clearCommandLogs()
|
||||
|
||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
await u.closeDebugPanel()
|
||||
|
||||
await expect(page).toHaveScreenshot({
|
||||
@ -164,11 +141,11 @@ const part001 = startSketchOn('-XZ')
|
||||
|> xLineTo({ to: totalLen, tag: 'seg03' }, %)
|
||||
|> yLine({ length: -armThick, tag: 'seg01' }, %)
|
||||
|> angledLineThatIntersects({
|
||||
angle: _180,
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: 'seg04'
|
||||
}, %)
|
||||
|> angledLineToY([segAng('seg04', %) + 180, _0], %)
|
||||
|> angledLineToY([segAng('seg04', %) + 180, ZERO], %)
|
||||
|> angledLineToY({
|
||||
angle: -bottomAng,
|
||||
to: -totalHeightHalf - armThick,
|
||||
@ -177,12 +154,12 @@ const part001 = startSketchOn('-XZ')
|
||||
|> xLineTo(segEndX('seg03', %) + 0, %)
|
||||
|> yLine(-segLen('seg01', %), %)
|
||||
|> angledLineThatIntersects({
|
||||
angle: _180,
|
||||
angle: HALF_TURN,
|
||||
offset: -armThick,
|
||||
intersectTag: 'seg02'
|
||||
}, %)
|
||||
|> angledLineToY([segAng('seg02', %) + 180, -baseHeight], %)
|
||||
|> xLineTo(_0, %)
|
||||
|> xLineTo(ZERO, %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)`
|
||||
)
|
||||
@ -191,7 +168,6 @@ const part001 = startSketchOn('-XZ')
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
await u.waitForDefaultPlanesVisibilityChange()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.waitForCmdReceive('extrude')
|
||||
await page.waitForTimeout(1000)
|
||||
@ -386,3 +362,118 @@ const part001 = startSketchOn('-XZ')
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('extrude on each default plane should be stable', async ({
|
||||
page,
|
||||
context,
|
||||
}) => {
|
||||
const u = getUtils(page)
|
||||
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|
||||
|> startProfileAt([0.70, 0.44], %)
|
||||
|> line([0.66, -0.02], %)
|
||||
|> line([0.28, 0.50], %)
|
||||
|> line([-0.56, 0.44], %)
|
||||
|> line([-0.54, -0.38], %)
|
||||
|> close(%)
|
||||
|> extrude(1.00, %)
|
||||
`
|
||||
await context.addInitScript(async (code) => {
|
||||
localStorage.setItem('persistCode', code)
|
||||
}, makeCode('XY'))
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.clearAndCloseDebugPanel()
|
||||
|
||||
await page.getByText('Code').click()
|
||||
await expect(page).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
await page.getByText('Code').click()
|
||||
|
||||
const runSnapshotsForOtherPlanes = async (plane = 'XY') => {
|
||||
// clear code
|
||||
await u.removeCurrentCode()
|
||||
// add makeCode('XZ')
|
||||
await page.locator('.cm-content').fill(makeCode(plane))
|
||||
// wait for execution done
|
||||
await u.openDebugPanel()
|
||||
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||
await u.clearAndCloseDebugPanel()
|
||||
|
||||
await page.getByText('Code').click()
|
||||
await expect(page).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
await page.getByText('Code').click()
|
||||
}
|
||||
await runSnapshotsForOtherPlanes('-XY')
|
||||
|
||||
await runSnapshotsForOtherPlanes('XZ')
|
||||
await runSnapshotsForOtherPlanes('-XZ')
|
||||
|
||||
await runSnapshotsForOtherPlanes('YZ')
|
||||
await runSnapshotsForOtherPlanes('-YZ')
|
||||
})
|
||||
|
||||
test('Draft segments should look right', async ({ page }) => {
|
||||
const u = getUtils(page)
|
||||
await page.setViewportSize({ width: 1200, height: 500 })
|
||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||
await page.goto('/')
|
||||
await u.waitForAuthSkipAppStart()
|
||||
await u.openDebugPanel()
|
||||
|
||||
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||
|
||||
// click on "Start Sketch" button
|
||||
await u.clearCommandLogs()
|
||||
await u.doAndWaitForImageDiff(
|
||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||
200
|
||||
)
|
||||
|
||||
// select a plane
|
||||
await page.mouse.click(700, 200)
|
||||
|
||||
await expect(page.locator('.cm-content')).toHaveText(
|
||||
`const part001 = startSketchOn('-XZ')`
|
||||
)
|
||||
|
||||
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||
|
||||
const startXPx = 600
|
||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||
const startAt = '[23.74, -32.03]'
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)`)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
await u.closeDebugPanel()
|
||||
await page.mouse.move(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await expect(page).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
|
||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||
await page.waitForTimeout(100)
|
||||
|
||||
const num = 23.97
|
||||
await expect(page.locator('.cm-content'))
|
||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||
|> startProfileAt(${startAt}, %)
|
||||
|> line([${num}, 0], %)`)
|
||||
|
||||
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||
|
||||
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
||||
|
||||
await expect(page).toHaveScreenshot({
|
||||
maxDiffPixels: 100,
|
||||
})
|
||||
})
|
||||
|
After Width: | Height: | Size: 41 KiB |
After Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 56 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 52 KiB |
@ -80,10 +80,21 @@ export function getUtils(page: Page) {
|
||||
waitForAuthSkipAppStart: () => waitForPageLoad(page),
|
||||
removeCurrentCode: () => removeCurrentCode(page),
|
||||
sendCustomCmd: (cmd: EngineCommand) => sendCustomCmd(page, cmd),
|
||||
updateCamPosition: async (xyz: [number, number, number]) => {
|
||||
const fillInput = async () => {
|
||||
await page.fill('[data-testid="cam-x-position"]', String(xyz[0]))
|
||||
await page.fill('[data-testid="cam-y-position"]', String(xyz[1]))
|
||||
await page.fill('[data-testid="cam-z-position"]', String(xyz[2]))
|
||||
}
|
||||
await fillInput()
|
||||
await page.waitForTimeout(100)
|
||||
await fillInput()
|
||||
await page.waitForTimeout(100)
|
||||
await fillInput()
|
||||
await page.waitForTimeout(100)
|
||||
},
|
||||
clearCommandLogs: () => clearCommandLogs(page),
|
||||
expectCmdLog: (locatorStr: string) => expectCmdLog(page, locatorStr),
|
||||
waitForDefaultPlanesVisibilityChange: () =>
|
||||
waitForDefaultPlanesToBeVisible(page),
|
||||
openDebugPanel: () => openDebugPanel(page),
|
||||
closeDebugPanel: () => closeDebugPanel(page),
|
||||
openAndClearDebugPanel: async () => {
|
||||
|
@ -1,19 +1,26 @@
|
||||
import { browser, $, expect } from '@wdio/globals'
|
||||
import fs from 'fs/promises'
|
||||
|
||||
describe('KCMA (Tauri, Linux)', () => {
|
||||
it('opens the auth page, signs in, and signs out', async () => {
|
||||
// Clean up previous tests
|
||||
const defaultDir = `${process.env.HOME}/Documents/zoo-modeling-app-projects`
|
||||
const userCodeDir = '/tmp/kittycad_user_code'
|
||||
|
||||
async function click(element: WebdriverIO.Element): Promise<void> {
|
||||
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
||||
await element.waitForClickable()
|
||||
await browser.execute('arguments[0].click();', element)
|
||||
}
|
||||
|
||||
describe('ZMA (Tauri, Linux)', () => {
|
||||
it('opens the auth page and signs in', async () => {
|
||||
// Clean up filesystem from previous tests
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
await fs.rm('/tmp/kittycad_user_code', { force: true })
|
||||
await browser.execute('window.localStorage.clear()')
|
||||
await fs.rm(defaultDir, { force: true, recursive: true })
|
||||
await fs.rm(userCodeDir, { force: true })
|
||||
|
||||
const signInButton = await $('[data-testid="sign-in-button"]')
|
||||
expect(await signInButton.getText()).toEqual('Sign in')
|
||||
|
||||
// Workaround for .click(), see https://github.com/tauri-apps/tauri/issues/6541
|
||||
await signInButton.waitForClickable()
|
||||
await browser.execute('arguments[0].click();', signInButton)
|
||||
await click(signInButton)
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000))
|
||||
|
||||
// Get from main.rs
|
||||
@ -49,14 +56,49 @@ describe('KCMA (Tauri, Linux)', () => {
|
||||
// Now should be signed in
|
||||
const newFileButton = await $('[data-testid="home-new-file"]')
|
||||
expect(await newFileButton.getText()).toEqual('New file')
|
||||
})
|
||||
|
||||
// So let's sign out!
|
||||
it('opens the settings page, checks filesystem settings, and closes the settings page', async () => {
|
||||
const menuButton = await $('[data-testid="user-sidebar-toggle"]')
|
||||
await menuButton.waitForClickable()
|
||||
await browser.execute('arguments[0].click();', menuButton)
|
||||
await click(menuButton)
|
||||
|
||||
const settingsButton = await $('[data-testid="settings-button"]')
|
||||
await click(settingsButton)
|
||||
|
||||
const defaultDirInput = await $('[data-testid="default-directory-input"]')
|
||||
expect(await defaultDirInput.getValue()).toEqual(defaultDir)
|
||||
|
||||
const nameInput = await $('[data-testid="name-input"]')
|
||||
expect(await nameInput.getValue()).toEqual('project-$nnn')
|
||||
|
||||
const closeButton = await $('[data-testid="close-button"]')
|
||||
await click(closeButton)
|
||||
})
|
||||
|
||||
it('checks that no file exists, creates a new file', async () => {
|
||||
const homeSection = await $('[data-testid="home-section"]')
|
||||
expect(await homeSection.getText()).toContain('No Projects found')
|
||||
|
||||
const newFileButton = await $('[data-testid="home-new-file"]')
|
||||
await click(newFileButton)
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000))
|
||||
|
||||
expect(await homeSection.getText()).toContain('project-000')
|
||||
})
|
||||
|
||||
it('opens the new file and expects a loading stream', async () => {
|
||||
const projectLink = await $('[data-testid="project-link"]')
|
||||
await click(projectLink)
|
||||
const loadingText = await $('[data-testid="loading-stream"]')
|
||||
expect(await loadingText.getText()).toContain('Loading stream...')
|
||||
await browser.execute('window.location.href = "tauri://localhost/home"')
|
||||
})
|
||||
|
||||
it('signs out', async () => {
|
||||
const menuButton = await $('[data-testid="user-sidebar-toggle"]')
|
||||
await click(menuButton)
|
||||
const signoutButton = await $('[data-testid="user-sidebar-sign-out"]')
|
||||
await signoutButton.waitForClickable()
|
||||
await browser.execute('arguments[0].click();', signoutButton)
|
||||
await click(signoutButton)
|
||||
const newSignInButton = await $('[data-testid="sign-in-button"]')
|
||||
expect(await newSignInButton.getText()).toEqual('Sign in')
|
||||
})
|
||||
|
@ -14,8 +14,8 @@
|
||||
<link rel="stylesheet" href="https://use.typekit.net/zzv8rvm.css" />
|
||||
<script
|
||||
defer
|
||||
data-domain="app.kittycad.io"
|
||||
src="https://plausible.corp.kittycad.io/js/script.js"
|
||||
data-domain="app.zoo.dev"
|
||||
src="https://plausible.corp.zoo.dev/js/script.js"
|
||||
></script>
|
||||
<title>Zoo Modeling App</title>
|
||||
</head>
|
||||
|
69
make-release.sh
Executable file
@ -0,0 +1,69 @@
|
||||
#!/bin/bash
|
||||
|
||||
if ! git diff-index --quiet HEAD --; then
|
||||
echo "Please stash uncommitted changes before running release script"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
git checkout main
|
||||
git pull
|
||||
git fetch --all
|
||||
|
||||
# Get the latest semver tag from git
|
||||
latest_tag=$(jq -r '.version' package.json)
|
||||
latest_tag="v$latest_tag"
|
||||
|
||||
# Print the latest semver tag
|
||||
echo "Latest semver tag: $latest_tag"
|
||||
|
||||
# Function to bump version numbers
|
||||
bump_version() {
|
||||
local version=$1
|
||||
local bump_type=$2
|
||||
local major=$(echo $version | cut -d '.' -f 1 | sed 's/v//')
|
||||
local minor=$(echo $version | cut -d '.' -f 2)
|
||||
local patch=$(echo $version | cut -d '.' -f 3)
|
||||
|
||||
case "$bump_type" in
|
||||
major)
|
||||
major=$((major + 1))
|
||||
minor=0
|
||||
patch=0
|
||||
;;
|
||||
minor)
|
||||
minor=$((minor + 1))
|
||||
patch=0
|
||||
;;
|
||||
*)
|
||||
patch=$((patch + 1))
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "v${major}.${minor}.${patch}"
|
||||
}
|
||||
|
||||
# Determine the type of bump based on the argument
|
||||
bump_type=${1:-patch}
|
||||
|
||||
# Bump the version
|
||||
new_version=$(bump_version $latest_tag $bump_type)
|
||||
|
||||
# Print the new semver tag
|
||||
echo "New semver tag: $new_version"
|
||||
new_version_number=${new_version:1}
|
||||
echo "New version number without 'v': $new_version_number"
|
||||
|
||||
|
||||
git checkout -b "cut-release-$new_version"
|
||||
|
||||
echo "$(jq --arg v "$new_version_number" '.version=$v' package.json --indent 2)" > package.json
|
||||
echo "$(jq --arg v "$new_version_number" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)" > src-tauri/tauri.conf.json
|
||||
|
||||
git add package.json src-tauri/tauri.conf.json
|
||||
git commit -m "Cut release $new_version"
|
||||
|
||||
echo ""
|
||||
echo "Versions has been bumped in relevant json files, a branch has been created and committed to."
|
||||
echo ""
|
||||
echo "What's left for you to do is, push the branch and make the release PR."
|
||||
echo ""
|
28
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.14.0",
|
||||
"version": "0.15.2",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.10.2",
|
||||
@ -10,7 +10,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.46",
|
||||
"@kittycad/lib": "^0.0.53",
|
||||
"@lezer/javascript": "^1.4.9",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
@ -21,6 +21,7 @@
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"@ts-stack/markdown": "^1.5.0",
|
||||
"@tweenjs/tween.js": "^23.1.1",
|
||||
"@types/node": "^16.7.13",
|
||||
"@types/react": "^18.2.41",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
@ -33,6 +34,7 @@
|
||||
"fuse.js": "^7.0.0",
|
||||
"http-server": "^14.1.1",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"re-resizable": "^6.9.11",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -45,11 +47,12 @@
|
||||
"sketch-helpers": "^0.0.4",
|
||||
"swr": "^2.2.2",
|
||||
"tauri-plugin-fs-extra-api": "https://github.com/tauri-apps/tauri-plugin-fs-extra#v1",
|
||||
"three": "^0.160.0",
|
||||
"toml": "^3.0.0",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2",
|
||||
"uuid": "^9.0.1",
|
||||
"vitest": "^0.34.6",
|
||||
"vitest": "^1.3.1",
|
||||
"vscode-jsonrpc": "^8.1.0",
|
||||
"vscode-languageserver-protocol": "^3.17.5",
|
||||
"wasm-pack": "^0.12.1",
|
||||
@ -70,7 +73,6 @@
|
||||
"test": "vitest --mode development",
|
||||
"test:nowatch": "vitest run --mode development",
|
||||
"test:rust": "(cd src/wasm-lib && cargo test --all && cargo clippy --all --tests --benches)",
|
||||
"test:cov": "vitest run --coverage --mode development",
|
||||
"test:e2e:tauri": "E2E_TAURI_ENABLED=true TS_NODE_COMPILER_OPTIONS='{\"module\": \"commonjs\"}' wdio run wdio.conf.ts",
|
||||
"simpleserver:ci": "yarn pretest && http-server ./public --cors -p 3000 &",
|
||||
"simpleserver": "yarn pretest && http-server ./public --cors -p 3000",
|
||||
@ -82,7 +84,9 @@
|
||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||
"wasm-prep": "rm -rf src/wasm-lib/pkg && mkdir src/wasm-lib/pkg && rm -rf src/wasm-lib/kcl/bindings",
|
||||
"lint": "eslint --fix src",
|
||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json"
|
||||
"bump-jsons": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json && echo \"$(jq --arg v \"$VERSION\" '.package.version=$v' src-tauri/tauri.conf.json --indent 2)\" > src-tauri/tauri.conf.json",
|
||||
"postinstall": "patch-package && yarn xstate:typegen",
|
||||
"xstate:typegen": "yarn xstate typegen \"src/**/*.ts?(x)\""
|
||||
},
|
||||
"prettier": {
|
||||
"trailingComma": "es5",
|
||||
@ -109,36 +113,40 @@
|
||||
"@tauri-apps/cli": "^1.5.6",
|
||||
"@types/crypto-js": "^4.1.1",
|
||||
"@types/debounce-promise": "^3.1.8",
|
||||
"@types/isomorphic-fetch": "^0.0.36",
|
||||
"@types/pixelmatch": "^5.2.6",
|
||||
"@types/pngjs": "^6.0.4",
|
||||
"@types/react-modal": "^3.16.3",
|
||||
"@types/three": "^0.160.0",
|
||||
"@types/uuid": "^9.0.4",
|
||||
"@types/wait-on": "^5.3.4",
|
||||
"@types/wicg-file-system-access": "^2020.9.6",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@vitejs/plugin-react": "^4.1.1",
|
||||
"@vitest/coverage-istanbul": "^0.34.6",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"@wdio/cli": "^8.24.3",
|
||||
"@wdio/globals": "^8.24.3",
|
||||
"@wdio/local-runner": "^8.24.3",
|
||||
"@wdio/mocha-framework": "^8.24.3",
|
||||
"@wdio/spec-reporter": "^8.24.2",
|
||||
"@xstate/cli": "^0.5.17",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-config-react-app": "^7.0.1",
|
||||
"eslint-plugin-css-modules": "^2.12.0",
|
||||
"happy-dom": "^10.8.0",
|
||||
"husky": "^8.0.3",
|
||||
"patch-package": "^8.0.0",
|
||||
"pixelmatch": "^5.3.0",
|
||||
"pngjs": "^7.0.0",
|
||||
"postcss": "^8.4.31",
|
||||
"postinstall-postinstall": "^2.1.0",
|
||||
"prettier": "^2.8.0",
|
||||
"setimmediate": "^1.0.5",
|
||||
"tailwindcss": "^3.3.6",
|
||||
"vite": "^4.5.0",
|
||||
"vite": "^5.1.3",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-tsconfig-paths": "^4.2.1",
|
||||
"vite-plugin-package-version": "^1.1.0",
|
||||
"vite-tsconfig-paths": "^4.3.1",
|
||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||
"wait-on": "^7.2.0",
|
||||
"yarn": "^1.22.19"
|
||||
}
|
||||
|
138
patches/three+0.160.0.patch
Normal file
@ -0,0 +1,138 @@
|
||||
diff --git a/node_modules/three/examples/jsm/controls/OrbitControls.js b/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||
index f29e7fe..0ef636b 100644
|
||||
--- a/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||
+++ b/node_modules/three/examples/jsm/controls/OrbitControls.js
|
||||
@@ -113,6 +113,25 @@ class OrbitControls extends EventDispatcher {
|
||||
// public methods
|
||||
//
|
||||
|
||||
+ this.interactionGuards = {
|
||||
+ pan: {
|
||||
+ description: 'Right click + Shift + drag or middle click + drag',
|
||||
+ callback: (e) => e.button === 2 && !e.ctrlKey,
|
||||
+ },
|
||||
+ zoom: {
|
||||
+ description: 'Scroll wheel or Right click + Ctrl + drag',
|
||||
+ dragCallback: (e) => e.button === 2 && e.ctrlKey,
|
||||
+ scrollCallback: () => true,
|
||||
+ },
|
||||
+ rotate: {
|
||||
+ description: 'Right click + drag',
|
||||
+ callback: (e) => e.button === 0,
|
||||
+ },
|
||||
+ }
|
||||
+ this.setMouseGuards = (interactionGuards) => {
|
||||
+ this.interactionGuards = interactionGuards
|
||||
+ }
|
||||
+
|
||||
this.getPolarAngle = function () {
|
||||
|
||||
return spherical.phi;
|
||||
@@ -1057,92 +1076,21 @@ class OrbitControls extends EventDispatcher {
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
- let mouseAction;
|
||||
-
|
||||
- switch ( event.button ) {
|
||||
-
|
||||
- case 0:
|
||||
-
|
||||
- mouseAction = scope.mouseButtons.LEFT;
|
||||
- break;
|
||||
-
|
||||
- case 1:
|
||||
-
|
||||
- mouseAction = scope.mouseButtons.MIDDLE;
|
||||
- break;
|
||||
-
|
||||
- case 2:
|
||||
-
|
||||
- mouseAction = scope.mouseButtons.RIGHT;
|
||||
- break;
|
||||
-
|
||||
- default:
|
||||
-
|
||||
- mouseAction = - 1;
|
||||
-
|
||||
- }
|
||||
-
|
||||
- switch ( mouseAction ) {
|
||||
-
|
||||
- case MOUSE.DOLLY:
|
||||
-
|
||||
- if ( scope.enableZoom === false ) return;
|
||||
-
|
||||
- handleMouseDownDolly( event );
|
||||
-
|
||||
- state = STATE.DOLLY;
|
||||
-
|
||||
- break;
|
||||
-
|
||||
- case MOUSE.ROTATE:
|
||||
-
|
||||
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
|
||||
-
|
||||
- if ( scope.enablePan === false ) return;
|
||||
-
|
||||
- handleMouseDownPan( event );
|
||||
-
|
||||
- state = STATE.PAN;
|
||||
-
|
||||
- } else {
|
||||
-
|
||||
- if ( scope.enableRotate === false ) return;
|
||||
-
|
||||
- handleMouseDownRotate( event );
|
||||
-
|
||||
- state = STATE.ROTATE;
|
||||
-
|
||||
- }
|
||||
-
|
||||
- break;
|
||||
-
|
||||
- case MOUSE.PAN:
|
||||
-
|
||||
- if ( event.ctrlKey || event.metaKey || event.shiftKey ) {
|
||||
-
|
||||
- if ( scope.enableRotate === false ) return;
|
||||
-
|
||||
- handleMouseDownRotate( event );
|
||||
-
|
||||
- state = STATE.ROTATE;
|
||||
-
|
||||
- } else {
|
||||
-
|
||||
- if ( scope.enablePan === false ) return;
|
||||
-
|
||||
- handleMouseDownPan( event );
|
||||
-
|
||||
- state = STATE.PAN;
|
||||
-
|
||||
- }
|
||||
-
|
||||
- break;
|
||||
-
|
||||
- default:
|
||||
-
|
||||
- state = STATE.NONE;
|
||||
-
|
||||
- }
|
||||
+ if (scope.interactionGuards.pan.callback(event)) {
|
||||
+ if (scope.enablePan === false) return
|
||||
+ handleMouseDownPan(event)
|
||||
+ state = STATE.PAN
|
||||
+ } else if (scope.interactionGuards.rotate.callback(event)) {
|
||||
+ if (scope.enableRotate === false) return
|
||||
+ handleMouseDownRotate(event)
|
||||
+ state = STATE.ROTATE
|
||||
+ } else if (scope.interactionGuards.zoom.dragCallback(event)) {
|
||||
+ if (scope.enableZoom === false) return
|
||||
+ handleMouseDownDolly(event)
|
||||
+ state = STATE.DOLLY
|
||||
+ } else {
|
||||
+ return
|
||||
+ }
|
||||
|
||||
if ( state !== STATE.NONE ) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { defineConfig, devices } from '@playwright/test';
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* Read environment variables from file.
|
||||
@ -78,5 +78,4 @@ export default defineConfig({
|
||||
// url: 'http://127.0.0.1:3000',
|
||||
reuseExistingServer: !process.env.CI,
|
||||
},
|
||||
});
|
||||
|
||||
})
|
26
public/announce_release.py
Normal file
@ -0,0 +1,26 @@
|
||||
import requests
|
||||
import os
|
||||
|
||||
webhook_url = os.getenv('DISCORD_WEBHOOK_URL')
|
||||
release_version = os.getenv('RELEASE_VERSION')
|
||||
release_body = os.getenv('RELEASE_BODY')
|
||||
|
||||
# message to send to Discord
|
||||
data = {
|
||||
"content":
|
||||
f'''
|
||||
**{release_version}** is now available! Check out the latest features and improvements here: https://zoo.dev/modeling-app/download
|
||||
{release_body}
|
||||
''',
|
||||
"username": "Modeling App Release Updates",
|
||||
"avatar_url": "https://raw.githubusercontent.com/KittyCAD/modeling-app/main/public/discord-avatar.png"
|
||||
}
|
||||
|
||||
# POST request to the Discord webhook
|
||||
response = requests.post(webhook_url, json=data)
|
||||
|
||||
# Check for success
|
||||
if response.status_code == 204:
|
||||
print("Successfully sent the message to Discord.")
|
||||
else:
|
||||
print("Failed to send the message to Discord.")
|
BIN
public/discord-avatar.png
Normal file
After Width: | Height: | Size: 11 KiB |
110
src-tauri/Cargo.lock
generated
@ -67,9 +67,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.75"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
||||
|
||||
[[package]]
|
||||
name = "app"
|
||||
@ -95,7 +95,7 @@ checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -542,7 +542,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -552,7 +552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -582,7 +582,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -593,7 +593,7 @@ checksum = "29a358ff9f12ec09c3e61fef9b5a9902623a695a46a917b07f269bff1445611a"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -899,7 +899,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1242,9 +1242,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.3.20"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049"
|
||||
checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
@ -1252,7 +1252,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-util",
|
||||
"http",
|
||||
"indexmap 1.9.3",
|
||||
"indexmap 2.0.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
@ -1664,9 +1664,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.42"
|
||||
version = "0.2.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6aa554d86b6dbbd976a659c912ae25ce817b4378eb12a5684907e263410f0a7b"
|
||||
checksum = "a086e1a1bbddb3b38959c0f0ce6de6b3a3b7566e38e0b7d5fb101e91911beed4"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2215,7 +2215,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2368,7 +2368,7 @@ dependencies = [
|
||||
"regex",
|
||||
"regex-syntax 0.7.5",
|
||||
"structmeta",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2487,7 +2487,7 @@ dependencies = [
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2555,7 +2555,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2657,9 +2657,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.67"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -2675,9 +2675,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.33"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -3235,9 +3235,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
@ -3253,13 +3253,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.193"
|
||||
version = "1.0.196"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3275,9 +3275,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.108"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa 1.0.6",
|
||||
"ryu",
|
||||
@ -3302,7 +3302,7 @@ checksum = "bcec881020c684085e55a25f7fd888954d56609ef363479dc5a1305eb0d40cab"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3352,7 +3352,7 @@ dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3556,7 +3556,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"structmeta-derive",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3567,7 +3567,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3605,9 +3605,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.33"
|
||||
version = "2.0.48"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9caece70c63bfba29ec2fed841a09851b14a235c60010fa4de58089b6c025668"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -3760,9 +3760,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.5.3"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32d563b672acde8d0cc4c1b1f5b855976923f67e8d6fe1eba51df0211e197be2"
|
||||
checksum = "fd27c04b9543776a972c86ccf70660b517ecabbeced9fb58d8b961a13ad129af"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -3812,9 +3812,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "defbfc551bd38ab997e5f8e458f87396d2559d05ce32095076ad6c30f7fc5f9c"
|
||||
checksum = "e9914a4715e0b75d9f387a285c7e26b5bbfeb1249ad9f842675a82481565c532"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@ -3831,9 +3831,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "1.4.1"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b3475e55acec0b4a50fb96435f19631fb58cbcd31923e1a213de5c382536bbb"
|
||||
checksum = "a1554c5857f65dbc377cefb6b97c8ac77b1cb2a90d30d3448114d5d6b48a77fc"
|
||||
dependencies = [
|
||||
"base64 0.21.2",
|
||||
"brotli",
|
||||
@ -3857,9 +3857,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "1.4.2"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acea6445eececebd72ed7720cfcca46eee3b5bad8eb408be8f7ef2e3f7411500"
|
||||
checksum = "277abf361a3a6993ec16bcbb179de0d6518009b851090a01adfea12ac89fa875"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
@ -3872,7 +3872,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs-extra"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#537053d3171a7374a1a86fed422523e7b45a4fb8"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@ -3883,9 +3883,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "0.14.1"
|
||||
version = "0.14.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07f8e9e53e00e9f41212c115749e87d5cd2a9eebccafca77a19722eeecd56d43"
|
||||
checksum = "cf2d0652aa2891ff3e9caa2401405257ea29ab8372cce01f186a5825f1bd0e76"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@ -3904,9 +3904,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "0.14.2"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "803a01101bc611ba03e13329951a1bde44287a54234189b9024b78619c1bc206"
|
||||
checksum = "6cae61fbc731f690a4899681c9052dde6d05b159b44563ace8186fc1bfb7d158"
|
||||
dependencies = [
|
||||
"cocoa",
|
||||
"gtk",
|
||||
@ -3924,9 +3924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a52165bb340e6f6a75f1f5eeeab1bb49f861c12abe3a176067d53642b5454986"
|
||||
checksum = "ece74810b1d3d44f29f732a7ae09a63183d63949bbdd59c61f8ed2a1b70150db"
|
||||
dependencies = [
|
||||
"brotli",
|
||||
"ctor",
|
||||
@ -4009,7 +4009,7 @@ checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4051,9 +4051,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.34.0"
|
||||
version = "1.36.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9"
|
||||
checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"bytes",
|
||||
@ -4193,7 +4193,7 @@ checksum = "0f57e3ca2a01450b1a921183a9c9cbfda207fd822cef4ccb00a65402cbba7a74"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4443,7 +4443,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
@ -4477,7 +4477,7 @@ checksum = "e128beba882dd1eb6200e1dc92ae6c5dbaa4311aa7bb211ca035779e5efc39f8"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.33",
|
||||
"syn 2.0.48",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -12,17 +12,17 @@ rust-version = "1.60"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "1.5.0", features = [] }
|
||||
tauri-build = { version = "1.5.1", features = [] }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
kittycad = "0.2.42"
|
||||
kittycad = "0.2.53"
|
||||
oauth2 = "4.4.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tauri = { version = "1.5.3", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
|
||||
tauri = { version = "1.5.4", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
|
||||
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tokio = { version = "1.34.0", features = ["time"] }
|
||||
tokio = { version = "1.36.0", features = ["time"] }
|
||||
toml = "0.8.2"
|
||||
|
||||
[features]
|
||||
|
@ -7,6 +7,7 @@ use std::io::Read;
|
||||
|
||||
use anyhow::Result;
|
||||
use oauth2::TokenResponse;
|
||||
use std::process::Command;
|
||||
use tauri::{InvokeError, Manager};
|
||||
const DEFAULT_HOST: &str = "https://api.kittycad.io";
|
||||
|
||||
|
@ -7,7 +7,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "zoo-modeling-app",
|
||||
"version": "0.14.0"
|
||||
"version": "0.15.2"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
@ -1,73 +0,0 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { App } from './App'
|
||||
import { describe, test, vi } from 'vitest'
|
||||
import {
|
||||
Route,
|
||||
RouterProvider,
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom'
|
||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
||||
import CommandBarProvider from 'components/CommandBar/CommandBar'
|
||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import { BROWSER_FILE_NAME } from 'Router'
|
||||
|
||||
let listener: ((rect: any) => void) | undefined = undefined
|
||||
;(global as any).ResizeObserver = class ResizeObserver {
|
||||
constructor(ls: ((rect: any) => void) | undefined) {
|
||||
listener = ls
|
||||
}
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
}
|
||||
|
||||
describe('App tests', () => {
|
||||
test('Renders the modeling app screen, including "Variables" pane.', () => {
|
||||
vi.mock('react-router-dom', async () => {
|
||||
const actual = (await vi.importActual('react-router-dom')) as Record<
|
||||
string,
|
||||
any
|
||||
>
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ id: BROWSER_FILE_NAME }),
|
||||
useLoaderData: () => ({ code: null }),
|
||||
}
|
||||
})
|
||||
render(
|
||||
<TestWrap>
|
||||
<App />
|
||||
</TestWrap>
|
||||
)
|
||||
const linkElement = screen.getByText(/Variables/i)
|
||||
expect(linkElement).toBeInTheDocument()
|
||||
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
})
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// We have to use a memory router in the testing environment,
|
||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
||||
const router = createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="/file/:id"
|
||||
element={
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>
|
||||
<ModelingMachineProvider>{children}</ModelingMachineProvider>
|
||||
</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
initialEntries: ['/file/new'],
|
||||
initialIndex: 0,
|
||||
}
|
||||
)
|
||||
return <RouterProvider router={router} />
|
||||
}
|
84
src/App.tsx
@ -19,21 +19,24 @@ import {
|
||||
} from '@fortawesome/free-solid-svg-icons'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { getNormalisedCoordinates } from './lib/utils'
|
||||
import { useLoaderData } from 'react-router-dom'
|
||||
import { IndexLoaderData } from './Router'
|
||||
import { useLoaderData, useNavigate } from 'react-router-dom'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { onboardingPaths } from 'routes/Onboarding'
|
||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
||||
import { onboardingPaths } from 'routes/Onboarding/paths'
|
||||
import { CodeMenu } from 'components/CodeMenu'
|
||||
import { TextEditor } from 'components/TextEditor'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { useEngineConnectionSubscriptions } from 'hooks/useEngineConnectionSubscriptions'
|
||||
import { engineCommandManager } from './lang/std/engineConnection'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
|
||||
export function App() {
|
||||
const { project, file } = useLoaderData() as IndexLoaderData
|
||||
const navigate = useNavigate()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
|
||||
useHotKeyListener()
|
||||
const {
|
||||
@ -51,8 +54,7 @@ export function App() {
|
||||
}))
|
||||
|
||||
const { settings } = useGlobalStateContext()
|
||||
const { showDebugPanel, onboardingStatus, cameraControls, theme } =
|
||||
settings?.context || {}
|
||||
const { showDebugPanel, onboardingStatus, theme } = settings?.context || {}
|
||||
const { state, send } = useModelingContext()
|
||||
|
||||
const editorTheme = theme === Themes.System ? getSystemTheme() : theme
|
||||
@ -71,6 +73,16 @@ export function App() {
|
||||
useHotkeys('shift + e', () => togglePane('kclErrors'))
|
||||
useHotkeys('shift + d', () => togglePane('debug'))
|
||||
useHotkeys('esc', () => send('Cancel'))
|
||||
useHotkeys('backspace', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
useHotkeys(
|
||||
isTauri() ? 'mod + ,' : 'shift + mod + ,',
|
||||
() => navigate(filePath + paths.SETTINGS),
|
||||
{
|
||||
splitKey: '|',
|
||||
}
|
||||
)
|
||||
|
||||
const paneOpacity = [onboardingPaths.CAMERA, onboardingPaths.STREAMING].some(
|
||||
(p) => p === onboardingStatus
|
||||
@ -84,9 +96,11 @@ export function App() {
|
||||
|
||||
const debounceSocketSend = throttle<EngineCommand>((message) => {
|
||||
engineCommandManager.sendSceneCommand(message)
|
||||
}, 16)
|
||||
}, 1000 / 15)
|
||||
const handleMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
e.nativeEvent.preventDefault()
|
||||
if (state.matches('Sketch')) {
|
||||
return
|
||||
}
|
||||
|
||||
const { x, y } = getNormalisedCoordinates({
|
||||
clientX: e.clientX,
|
||||
@ -97,58 +111,11 @@ export function App() {
|
||||
|
||||
const newCmdId = uuidv4()
|
||||
if (buttonDownInStream === undefined) {
|
||||
if (state.matches('Sketch.Line Tool')) {
|
||||
debounceSocketSend({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: newCmdId,
|
||||
cmd: {
|
||||
type: 'mouse_move',
|
||||
window: { x, y },
|
||||
},
|
||||
})
|
||||
} else {
|
||||
debounceSocketSend({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'highlight_set_entity',
|
||||
selected_at_window: { x, y },
|
||||
},
|
||||
cmd_id: newCmdId,
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (state.matches('Sketch.Move Tool')) {
|
||||
debounceSocketSend({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: newCmdId,
|
||||
cmd: {
|
||||
type: 'handle_mouse_drag_move',
|
||||
window: { x, y },
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
const interactionGuards = cameraMouseDragGuards[cameraControls]
|
||||
let interaction: CameraDragInteractionType_type
|
||||
|
||||
const eWithButton = { ...e, button: buttonDownInStream }
|
||||
|
||||
if (interactionGuards.pan.callback(eWithButton)) {
|
||||
interaction = 'pan'
|
||||
} else if (interactionGuards.rotate.callback(eWithButton)) {
|
||||
interaction = 'rotate'
|
||||
} else if (interactionGuards.zoom.dragCallback(eWithButton)) {
|
||||
interaction = 'zoom'
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
debounceSocketSend({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'camera_drag_move',
|
||||
interaction,
|
||||
window: { x, y },
|
||||
type: 'highlight_set_entity',
|
||||
selected_at_window: { x, y },
|
||||
},
|
||||
cmd_id: newCmdId,
|
||||
})
|
||||
@ -238,6 +205,7 @@ export function App() {
|
||||
open={openPanes.includes('debug')}
|
||||
/>
|
||||
)}
|
||||
{/* <CamToggle /> */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
@ -14,10 +14,7 @@ import {
|
||||
import { useEffect } from 'react'
|
||||
import { ErrorPage } from './components/ErrorPage'
|
||||
import { Settings } from './routes/Settings'
|
||||
import Onboarding, {
|
||||
onboardingRoutes,
|
||||
onboardingPaths,
|
||||
} from './routes/Onboarding'
|
||||
import Onboarding, { onboardingRoutes } from './routes/Onboarding'
|
||||
import SignIn from './routes/SignIn'
|
||||
import { Auth } from './Auth'
|
||||
import { isTauri } from './lib/isTauri'
|
||||
@ -29,7 +26,7 @@ import {
|
||||
isProjectDirectory,
|
||||
PROJECT_ENTRYPOINT,
|
||||
} from './lib/tauriFS'
|
||||
import { metadata, type Metadata } from 'tauri-plugin-fs-extra-api'
|
||||
import { metadata } from 'tauri-plugin-fs-extra-api'
|
||||
import DownloadAppBanner from './components/DownloadAppBanner'
|
||||
import { WasmErrBanner } from './components/WasmErrBanner'
|
||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
||||
@ -42,9 +39,12 @@ import CommandBarProvider from 'components/CommandBar/CommandBar'
|
||||
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
||||
import * as Sentry from '@sentry/react'
|
||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||
import { KclContextProvider, kclManager } from 'lang/KclSinglton'
|
||||
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
|
||||
import FileMachineProvider from 'components/FileMachineProvider'
|
||||
import { sep } from '@tauri-apps/api/path'
|
||||
import { paths } from 'lib/paths'
|
||||
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
|
||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||
|
||||
if (VITE_KC_SENTRY_DSN && !TEST) {
|
||||
Sentry.init({
|
||||
@ -78,42 +78,8 @@ if (VITE_KC_SENTRY_DSN && !TEST) {
|
||||
})
|
||||
}
|
||||
|
||||
const prependRoutes =
|
||||
(routesObject: Record<string, string>) => (prepend: string) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(routesObject).map(([constName, path]) => [
|
||||
constName,
|
||||
prepend + path,
|
||||
])
|
||||
)
|
||||
}
|
||||
|
||||
export const paths = {
|
||||
INDEX: '/',
|
||||
HOME: '/home',
|
||||
FILE: '/file',
|
||||
SETTINGS: '/settings',
|
||||
SIGN_IN: '/signin',
|
||||
ONBOARDING: prependRoutes(onboardingPaths)(
|
||||
'/onboarding'
|
||||
) as typeof onboardingPaths,
|
||||
}
|
||||
|
||||
export const BROWSER_FILE_NAME = 'new'
|
||||
|
||||
export type IndexLoaderData = {
|
||||
code: string | null
|
||||
project?: ProjectWithEntryPointMetadata
|
||||
file?: FileEntry
|
||||
}
|
||||
|
||||
export type ProjectWithEntryPointMetadata = FileEntry & {
|
||||
entrypointMetadata: Metadata
|
||||
}
|
||||
export type HomeLoaderData = {
|
||||
projects: ProjectWithEntryPointMetadata[]
|
||||
}
|
||||
|
||||
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
||||
|
||||
const addGlobalContextToElements = (
|
||||
@ -145,18 +111,18 @@ const router = createBrowserRouter(
|
||||
{
|
||||
path: paths.FILE + '/:id',
|
||||
element: (
|
||||
<Auth>
|
||||
<FileMachineProvider>
|
||||
<KclContextProvider>
|
||||
<KclContextProvider>
|
||||
<Auth>
|
||||
<FileMachineProvider>
|
||||
<ModelingMachineProvider>
|
||||
<Outlet />
|
||||
<App />
|
||||
</ModelingMachineProvider>
|
||||
<WasmErrBanner />
|
||||
</KclContextProvider>
|
||||
</FileMachineProvider>
|
||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||
</Auth>
|
||||
</FileMachineProvider>
|
||||
{!isTauri() && import.meta.env.PROD && <DownloadAppBanner />}
|
||||
</Auth>
|
||||
</KclContextProvider>
|
||||
),
|
||||
id: paths.FILE,
|
||||
loader: async ({
|
||||
@ -209,6 +175,10 @@ const router = createBrowserRouter(
|
||||
const children = await readDir(projectPath, { recursive: true })
|
||||
kclManager.setCodeAndExecute(code, false)
|
||||
|
||||
// Set the file system manager to the project path
|
||||
// So that WASM gets an updated path for operations
|
||||
fileSystemManager.dir = projectPath
|
||||
|
||||
return {
|
||||
code,
|
||||
project: {
|
||||
@ -248,7 +218,7 @@ const router = createBrowserRouter(
|
||||
<Home />
|
||||
</Auth>
|
||||
),
|
||||
loader: async () => {
|
||||
loader: async (): Promise<HomeLoaderData | Response> => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
||||
}
|
||||
@ -259,6 +229,7 @@ const router = createBrowserRouter(
|
||||
const projectDir = await initializeProjectDirectory(
|
||||
persistedSettings.defaultDirectory || ''
|
||||
)
|
||||
let newDefaultDirectory: string | undefined = undefined
|
||||
if (projectDir !== persistedSettings.defaultDirectory) {
|
||||
localStorage.setItem(
|
||||
SETTINGS_PERSIST_KEY,
|
||||
@ -267,6 +238,7 @@ const router = createBrowserRouter(
|
||||
defaultDirectory: projectDir,
|
||||
})
|
||||
)
|
||||
newDefaultDirectory = projectDir
|
||||
}
|
||||
const projectsNoMeta = (await readDir(projectDir)).filter(
|
||||
isProjectDirectory
|
||||
@ -282,6 +254,7 @@ const router = createBrowserRouter(
|
||||
|
||||
return {
|
||||
projects,
|
||||
newDefaultDirectory,
|
||||
}
|
||||
},
|
||||
children: [
|
||||
|
105
src/Toolbar.tsx
@ -5,6 +5,8 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { ActionButton } from 'components/ActionButton'
|
||||
import usePlatform from 'hooks/usePlatform'
|
||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export const Toolbar = () => {
|
||||
const platform = usePlatform()
|
||||
@ -13,14 +15,15 @@ export const Toolbar = () => {
|
||||
const toolbarButtonsRef = useRef<HTMLUListElement>(null)
|
||||
const bgClassName =
|
||||
'group-enabled:group-hover:bg-energy-10 group-pressed:bg-energy-10 dark:group-enabled:group-hover:bg-chalkboard-80 dark:group-pressed:bg-chalkboard-80'
|
||||
const pathId = useMemo(
|
||||
() =>
|
||||
isCursorInSketchCommandRange(
|
||||
engineCommandManager.artifactMap,
|
||||
context.selectionRanges
|
||||
),
|
||||
[engineCommandManager.artifactMap, context.selectionRanges]
|
||||
)
|
||||
const pathId = useMemo(() => {
|
||||
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast)) {
|
||||
return false
|
||||
}
|
||||
return isCursorInSketchCommandRange(
|
||||
engineCommandManager.artifactMap,
|
||||
context.selectionRanges
|
||||
)
|
||||
}, [engineCommandManager.artifactMap, context.selectionRanges])
|
||||
|
||||
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
||||
const span = toolbarButtonsRef.current
|
||||
@ -50,7 +53,9 @@ export const Toolbar = () => {
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => send({ type: 'Enter sketch' })}
|
||||
onClick={() =>
|
||||
send({ type: 'Enter sketch', data: { forceNewSketch: true } })
|
||||
}
|
||||
icon={{
|
||||
icon: 'sketch',
|
||||
bgClassName,
|
||||
@ -89,44 +94,48 @@ export const Toolbar = () => {
|
||||
</li>
|
||||
)}
|
||||
{state.matches('Sketch') && !state.matches('idle') && (
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
state.matches('Sketch.Line Tool')
|
||||
? send('CancelSketch')
|
||||
: send('Equip tool')
|
||||
}
|
||||
aria-pressed={state.matches('Sketch.Line Tool')}
|
||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||
icon={{
|
||||
icon: 'line',
|
||||
bgClassName,
|
||||
}}
|
||||
>
|
||||
Line
|
||||
</ActionButton>
|
||||
</li>
|
||||
)}
|
||||
{state.matches('Sketch') && (
|
||||
<li className="contents">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
state.matches('Sketch.Move Tool')
|
||||
? send('CancelSketch')
|
||||
: send('Equip move tool')
|
||||
}
|
||||
aria-pressed={state.matches('Sketch.Move Tool')}
|
||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||
icon={{
|
||||
icon: 'move',
|
||||
bgClassName,
|
||||
}}
|
||||
>
|
||||
Move
|
||||
</ActionButton>
|
||||
</li>
|
||||
<>
|
||||
<li className="contents" key="line-button">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
state?.matches('Sketch.Line tool')
|
||||
? send('CancelSketch')
|
||||
: send('Equip Line tool')
|
||||
}
|
||||
aria-pressed={state?.matches('Sketch.Line tool')}
|
||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||
icon={{
|
||||
icon: 'line',
|
||||
bgClassName,
|
||||
}}
|
||||
>
|
||||
Line
|
||||
</ActionButton>
|
||||
</li>
|
||||
<li className="contents" key="tangential-arc-button">
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() =>
|
||||
state.matches('Sketch.Tangential arc to')
|
||||
? send('CancelSketch')
|
||||
: send('Equip tangential arc to')
|
||||
}
|
||||
aria-pressed={state.matches('Sketch.Tangential arc to')}
|
||||
className="pressed:bg-energy-10/20 dark:pressed:bg-energy-80"
|
||||
icon={{
|
||||
icon: 'arc',
|
||||
bgClassName,
|
||||
}}
|
||||
disabled={
|
||||
!state.can('Equip tangential arc to') &&
|
||||
!state.matches('Sketch.Tangential arc to')
|
||||
}
|
||||
>
|
||||
Tangential Arc
|
||||
</ActionButton>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
{state.matches('Sketch.SketchIdle') &&
|
||||
state.nextEvents
|
||||
@ -151,7 +160,7 @@ export const Toolbar = () => {
|
||||
return 0
|
||||
})
|
||||
.map((eventName) => (
|
||||
<li className="contents">
|
||||
<li className="contents" key={eventName}>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
className="text-sm"
|
||||
|
252
src/clientSideScene/ClientSideSceneComp.tsx
Normal file
@ -0,0 +1,252 @@
|
||||
import { useRef, useEffect, useState } from 'react'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { useStore } from 'useStore'
|
||||
import {
|
||||
DEBUG_SHOW_BOTH_SCENES,
|
||||
ReactCameraProperties,
|
||||
sceneInfra,
|
||||
} from './sceneInfra'
|
||||
import { throttle } from 'lib/utils'
|
||||
|
||||
function useShouldHideScene(): { hideClient: boolean; hideServer: boolean } {
|
||||
const [isCamMoving, setIsCamMoving] = useState(false)
|
||||
const [isTween, setIsTween] = useState(false)
|
||||
|
||||
const { state } = useModelingContext()
|
||||
|
||||
useEffect(() => {
|
||||
sceneInfra.setIsCamMovingCallback((isMoving, isTween) => {
|
||||
setIsCamMoving(isMoving)
|
||||
setIsTween(isTween)
|
||||
})
|
||||
}, [])
|
||||
|
||||
if (DEBUG_SHOW_BOTH_SCENES || !isCamMoving)
|
||||
return { hideClient: false, hideServer: false }
|
||||
let hideServer = state.matches('Sketch')
|
||||
if (isTween) {
|
||||
hideServer = false
|
||||
}
|
||||
|
||||
return { hideClient: !hideServer, hideServer }
|
||||
}
|
||||
|
||||
export const ClientSideScene = ({
|
||||
cameraControls,
|
||||
}: {
|
||||
cameraControls: ReturnType<
|
||||
typeof useGlobalStateContext
|
||||
>['settings']['context']['cameraControls']
|
||||
}) => {
|
||||
const canvasRef = useRef<HTMLDivElement>(null)
|
||||
const { state, send } = useModelingContext()
|
||||
const { hideClient, hideServer } = useShouldHideScene()
|
||||
const { setHighlightRange } = useStore((s) => ({
|
||||
setHighlightRange: s.setHighlightRange,
|
||||
highlightRange: s.highlightRange,
|
||||
}))
|
||||
|
||||
// Listen for changes to the camera controls setting
|
||||
// and update the client-side scene's controls accordingly.
|
||||
useEffect(() => {
|
||||
sceneInfra.setInteractionGuards(cameraMouseDragGuards[cameraControls])
|
||||
}, [cameraControls])
|
||||
useEffect(() => {
|
||||
sceneInfra.updateOtherSelectionColors(
|
||||
state?.context?.selectionRanges?.otherSelections || []
|
||||
)
|
||||
}, [state?.context?.selectionRanges?.otherSelections])
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvasRef.current) return
|
||||
const canvas = canvasRef.current
|
||||
canvas.appendChild(sceneInfra.renderer.domElement)
|
||||
sceneInfra.animate()
|
||||
sceneInfra.setHighlightCallback(setHighlightRange)
|
||||
canvas.addEventListener('mousemove', sceneInfra.onMouseMove, false)
|
||||
canvas.addEventListener('mousedown', sceneInfra.onMouseDown, false)
|
||||
canvas.addEventListener('mouseup', sceneInfra.onMouseUp, false)
|
||||
sceneInfra.setSend(send)
|
||||
return () => {
|
||||
canvas?.removeEventListener('mousemove', sceneInfra.onMouseMove)
|
||||
canvas?.removeEventListener('mousedown', sceneInfra.onMouseDown)
|
||||
canvas?.removeEventListener('mouseup', sceneInfra.onMouseUp)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={canvasRef}
|
||||
className={`absolute inset-0 h-full w-full transition-all duration-300 ${
|
||||
hideClient ? 'opacity-0' : 'opacity-100'
|
||||
} ${hideServer ? 'bg-black' : ''} ${
|
||||
!hideClient && !hideServer && state.matches('Sketch')
|
||||
? 'bg-black/80'
|
||||
: ''
|
||||
}`}
|
||||
></div>
|
||||
)
|
||||
}
|
||||
|
||||
const throttled = throttle((a: ReactCameraProperties) => {
|
||||
if (a.type === 'perspective' && a.fov) {
|
||||
sceneInfra.dollyZoom(a.fov)
|
||||
}
|
||||
}, 1000 / 15)
|
||||
|
||||
export const CamDebugSettings = () => {
|
||||
const [camSettings, setCamSettings] = useState<ReactCameraProperties>({
|
||||
type: 'perspective',
|
||||
fov: 12,
|
||||
position: [0, 0, 0],
|
||||
quaternion: [0, 0, 0, 1],
|
||||
})
|
||||
const [fov, setFov] = useState(12)
|
||||
|
||||
useEffect(() => {
|
||||
sceneInfra.setReactCameraPropertiesCallback(setCamSettings)
|
||||
}, [sceneInfra])
|
||||
useEffect(() => {
|
||||
if (camSettings.type === 'perspective' && camSettings.fov) {
|
||||
setFov(camSettings.fov)
|
||||
}
|
||||
}, [(camSettings as any)?.fov])
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>cam settings</h3>
|
||||
perspective cam
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={camSettings.type === 'perspective'}
|
||||
onChange={(e) => {
|
||||
if (camSettings.type === 'perspective') {
|
||||
sceneInfra.useOrthographicCamera()
|
||||
} else {
|
||||
sceneInfra.usePerspectiveCamera()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{camSettings.type === 'perspective' && (
|
||||
<input
|
||||
type="range"
|
||||
min="4"
|
||||
max="90"
|
||||
step={0.5}
|
||||
value={fov}
|
||||
onChange={(e) => {
|
||||
setFov(parseFloat(e.target.value))
|
||||
|
||||
throttled({
|
||||
...camSettings,
|
||||
fov: parseFloat(e.target.value),
|
||||
})
|
||||
}}
|
||||
className="w-full cursor-pointer pointer-events-auto"
|
||||
/>
|
||||
)}
|
||||
{camSettings.type === 'perspective' && (
|
||||
<div>
|
||||
<span>fov</span>
|
||||
<input
|
||||
type="number"
|
||||
value={camSettings.fov}
|
||||
className="text-black w-16"
|
||||
onChange={(e) => {
|
||||
sceneInfra.setCam({
|
||||
...camSettings,
|
||||
fov: parseFloat(e.target.value),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{camSettings.type === 'orthographic' && (
|
||||
<>
|
||||
<div>
|
||||
<span>fov</span>
|
||||
<input
|
||||
type="number"
|
||||
value={camSettings.zoom}
|
||||
className="text-black w-16"
|
||||
onChange={(e) => {
|
||||
sceneInfra.setCam({
|
||||
...camSettings,
|
||||
zoom: parseFloat(e.target.value),
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<div>
|
||||
Position
|
||||
<ul className="flex">
|
||||
<li>
|
||||
<span className="pl-2 pr-1">x:</span>
|
||||
<input
|
||||
type="number"
|
||||
step={5}
|
||||
data-testid="cam-x-position"
|
||||
value={camSettings.position[0]}
|
||||
className="text-black w-16"
|
||||
onChange={(e) => {
|
||||
sceneInfra.setCam({
|
||||
...camSettings,
|
||||
position: [
|
||||
parseFloat(e.target.value),
|
||||
camSettings.position[1],
|
||||
camSettings.position[2],
|
||||
],
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span className="pl-2 pr-1">y:</span>
|
||||
<input
|
||||
type="number"
|
||||
step={5}
|
||||
data-testid="cam-y-position"
|
||||
value={camSettings.position[1]}
|
||||
className="text-black w-16"
|
||||
onChange={(e) => {
|
||||
sceneInfra.setCam({
|
||||
...camSettings,
|
||||
position: [
|
||||
camSettings.position[0],
|
||||
parseFloat(e.target.value),
|
||||
camSettings.position[2],
|
||||
],
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<span className="pl-2 pr-1">z:</span>
|
||||
<input
|
||||
type="number"
|
||||
step={5}
|
||||
data-testid="cam-z-position"
|
||||
value={camSettings.position[2]}
|
||||
className="text-black w-16"
|
||||
onChange={(e) => {
|
||||
sceneInfra.setCam({
|
||||
...camSettings,
|
||||
position: [
|
||||
camSettings.position[0],
|
||||
camSettings.position[1],
|
||||
parseFloat(e.target.value),
|
||||
],
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
33
src/clientSideScene/helpers.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import {
|
||||
GridHelper,
|
||||
LineBasicMaterial,
|
||||
OrthographicCamera,
|
||||
PerspectiveCamera,
|
||||
Group,
|
||||
Mesh,
|
||||
} from 'three'
|
||||
|
||||
export function createGridHelper({
|
||||
size,
|
||||
divisions,
|
||||
}: {
|
||||
size: number
|
||||
divisions: number
|
||||
}) {
|
||||
const gridHelperMaterial = new LineBasicMaterial({
|
||||
color: 0xaaaaaa,
|
||||
transparent: true,
|
||||
opacity: 0.5,
|
||||
depthTest: false,
|
||||
})
|
||||
const gridHelper = new GridHelper(size, divisions, 0x0000ff, 0xffffff)
|
||||
gridHelper.material = gridHelperMaterial
|
||||
gridHelper.rotation.x = Math.PI / 2
|
||||
return gridHelper
|
||||
}
|
||||
|
||||
export const orthoScale = (cam: OrthographicCamera | PerspectiveCamera) =>
|
||||
0.55 / cam.zoom
|
||||
|
||||
export const perspScale = (cam: PerspectiveCamera, group: Group | Mesh) =>
|
||||
(group.position.distanceTo(cam.position) * cam.fov) / 4000
|
1032
src/clientSideScene/sceneEntities.ts
Normal file
28
src/clientSideScene/sceneInfra.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { Quaternion } from 'three'
|
||||
import { isQuaternionVertical } from './sceneInfra'
|
||||
|
||||
describe('isQuaternionVertical', () => {
|
||||
it('should identify vertical quaternions', () => {
|
||||
const verticalQuaternions = [
|
||||
new Quaternion(1, 0, 0, 0).normalize(), // bottom
|
||||
new Quaternion(-0.7, 0.7, 0, 0).normalize(), // bottom 2
|
||||
new Quaternion(0, 1, 0, 0).normalize(), // bottom 3
|
||||
new Quaternion(0, 0, 0, 1).normalize(), // look from top
|
||||
]
|
||||
verticalQuaternions.forEach((quaternion) => {
|
||||
expect(isQuaternionVertical(quaternion)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
it('should identify non-vertical quaternions', () => {
|
||||
const nonVerticalQuaternions = [
|
||||
new Quaternion(0.7, 0, 0, 0.7).normalize(), // front
|
||||
new Quaternion(0, 0.7, 0.7, 0).normalize(), // back
|
||||
new Quaternion(-0.5, 0.5, 0.5, -0.5).normalize(), // left side
|
||||
new Quaternion(0.5, 0.5, 0.5, 0.5).normalize(), // right side
|
||||
]
|
||||
nonVerticalQuaternions.forEach((quaternion) => {
|
||||
expect(isQuaternionVertical(quaternion)).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
1163
src/clientSideScene/sceneInfra.ts
Normal file
358
src/clientSideScene/segments.ts
Normal file
@ -0,0 +1,358 @@
|
||||
import { Coords2d } from 'lang/std/sketch'
|
||||
import {
|
||||
BufferGeometry,
|
||||
CatmullRomCurve3,
|
||||
ConeGeometry,
|
||||
CurvePath,
|
||||
EllipseCurve,
|
||||
ExtrudeGeometry,
|
||||
Group,
|
||||
LineCurve3,
|
||||
Mesh,
|
||||
MeshBasicMaterial,
|
||||
NormalBufferAttributes,
|
||||
Shape,
|
||||
SphereGeometry,
|
||||
Vector2,
|
||||
Vector3,
|
||||
} from 'three'
|
||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
||||
import {
|
||||
STRAIGHT_SEGMENT,
|
||||
STRAIGHT_SEGMENT_BODY,
|
||||
STRAIGHT_SEGMENT_DASH,
|
||||
TANGENTIAL_ARC_TO_SEGMENT,
|
||||
TANGENTIAL_ARC_TO_SEGMENT_BODY,
|
||||
TANGENTIAL_ARC_TO__SEGMENT_DASH,
|
||||
} from './sceneEntities'
|
||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||
import { ARROWHEAD } from './sceneInfra'
|
||||
|
||||
export function straightSegment({
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
}: {
|
||||
from: Coords2d
|
||||
to: Coords2d
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
isDraftSegment?: boolean
|
||||
scale?: number
|
||||
}): Group {
|
||||
const group = new Group()
|
||||
|
||||
const shape = new Shape()
|
||||
shape.moveTo(0, -0.08 * scale)
|
||||
shape.lineTo(0, 0.08 * scale) // The width of the line
|
||||
|
||||
let geometry
|
||||
if (isDraftSegment) {
|
||||
geometry = dashedStraight(from, to, shape, scale)
|
||||
} else {
|
||||
const line = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
|
||||
geometry = new ExtrudeGeometry(shape, {
|
||||
steps: 2,
|
||||
bevelEnabled: false,
|
||||
extrudePath: line,
|
||||
})
|
||||
}
|
||||
|
||||
const body = new MeshBasicMaterial({ color: 0xffffff })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
mesh.userData.type = isDraftSegment
|
||||
? STRAIGHT_SEGMENT_DASH
|
||||
: STRAIGHT_SEGMENT_BODY
|
||||
mesh.name = STRAIGHT_SEGMENT_BODY
|
||||
|
||||
group.userData = {
|
||||
type: STRAIGHT_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
pathToNode,
|
||||
isSelected: false,
|
||||
}
|
||||
|
||||
const arrowGroup = createArrowhead(scale)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
const dir = new Vector3()
|
||||
.subVectors(new Vector3(to[0], to[1], 0), new Vector3(from[0], from[1], 0))
|
||||
.normalize()
|
||||
arrowGroup.quaternion.setFromUnitVectors(new Vector3(0, 1, 0), dir)
|
||||
|
||||
group.add(mesh, arrowGroup)
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
function createArrowhead(scale = 1): Group {
|
||||
const arrowMaterial = new MeshBasicMaterial({ color: 0xffffff })
|
||||
const arrowheadMesh = new Mesh(new ConeGeometry(0.31, 1.5, 12), arrowMaterial)
|
||||
arrowheadMesh.position.set(0, -0.6, 0)
|
||||
const sphereMesh = new Mesh(new SphereGeometry(0.27, 12, 12), arrowMaterial)
|
||||
|
||||
const arrowGroup = new Group()
|
||||
arrowGroup.userData.type = ARROWHEAD
|
||||
arrowGroup.name = ARROWHEAD
|
||||
arrowGroup.add(arrowheadMesh, sphereMesh)
|
||||
arrowGroup.lookAt(new Vector3(0, 1, 0))
|
||||
arrowGroup.scale.set(scale, scale, scale)
|
||||
return arrowGroup
|
||||
}
|
||||
|
||||
export function tangentialArcToSegment({
|
||||
prevSegment,
|
||||
from,
|
||||
to,
|
||||
id,
|
||||
pathToNode,
|
||||
isDraftSegment,
|
||||
scale = 1,
|
||||
}: {
|
||||
prevSegment: SketchGroup['value'][number]
|
||||
from: Coords2d
|
||||
to: Coords2d
|
||||
id: string
|
||||
pathToNode: PathToNode
|
||||
isDraftSegment?: boolean
|
||||
scale?: number
|
||||
}): Group {
|
||||
const group = new Group()
|
||||
|
||||
const previousPoint =
|
||||
prevSegment?.type === 'TangentialArcTo'
|
||||
? getTangentPointFromPreviousArc(
|
||||
prevSegment.center,
|
||||
prevSegment.ccw,
|
||||
prevSegment.to
|
||||
)
|
||||
: prevSegment.from
|
||||
|
||||
const { center, radius, startAngle, endAngle, ccw } = getTangentialArcToInfo({
|
||||
arcStartPoint: from,
|
||||
arcEndPoint: to,
|
||||
tanPreviousPoint: previousPoint,
|
||||
obtuse: true,
|
||||
})
|
||||
|
||||
const geometry = createArcGeometry({
|
||||
center,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
ccw,
|
||||
isDashed: isDraftSegment,
|
||||
scale,
|
||||
})
|
||||
|
||||
const body = new MeshBasicMaterial({ color: 0xffffff })
|
||||
const mesh = new Mesh(geometry, body)
|
||||
mesh.userData.type = isDraftSegment
|
||||
? TANGENTIAL_ARC_TO__SEGMENT_DASH
|
||||
: TANGENTIAL_ARC_TO_SEGMENT_BODY
|
||||
|
||||
group.userData = {
|
||||
type: TANGENTIAL_ARC_TO_SEGMENT,
|
||||
id,
|
||||
from,
|
||||
to,
|
||||
prevSegment,
|
||||
pathToNode,
|
||||
isSelected: false,
|
||||
}
|
||||
|
||||
const arrowGroup = createArrowhead(scale)
|
||||
arrowGroup.position.set(to[0], to[1], 0)
|
||||
const arrowheadAngle = endAngle + (Math.PI / 2) * (ccw ? 1 : -1)
|
||||
arrowGroup.quaternion.setFromUnitVectors(
|
||||
new Vector3(0, 1, 0),
|
||||
new Vector3(Math.cos(arrowheadAngle), Math.sin(arrowheadAngle), 0)
|
||||
)
|
||||
|
||||
group.add(mesh, arrowGroup)
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
export function createArcGeometry({
|
||||
center,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
ccw,
|
||||
isDashed = false,
|
||||
scale = 1,
|
||||
}: {
|
||||
center: Coords2d
|
||||
radius: number
|
||||
startAngle: number
|
||||
endAngle: number
|
||||
ccw: boolean
|
||||
isDashed?: boolean
|
||||
scale?: number
|
||||
}): BufferGeometry {
|
||||
const dashSize = 1.2 * scale
|
||||
const gapSize = 1.2 * scale
|
||||
const arcStart = new EllipseCurve(
|
||||
center[0],
|
||||
center[1],
|
||||
radius,
|
||||
radius,
|
||||
startAngle,
|
||||
endAngle,
|
||||
!ccw,
|
||||
0
|
||||
)
|
||||
const arcEnd = new EllipseCurve(
|
||||
center[0],
|
||||
center[1],
|
||||
radius,
|
||||
radius,
|
||||
endAngle,
|
||||
startAngle,
|
||||
ccw,
|
||||
0
|
||||
)
|
||||
const shape = new Shape()
|
||||
shape.moveTo(0, -0.08 * scale)
|
||||
shape.lineTo(0, 0.08 * scale) // The width of the line
|
||||
|
||||
if (!isDashed) {
|
||||
const points = arcStart.getPoints(50)
|
||||
const path = new CurvePath<Vector3>()
|
||||
path.add(new CatmullRomCurve3(points.map((p) => new Vector3(p.x, p.y, 0))))
|
||||
|
||||
return new ExtrudeGeometry(shape, {
|
||||
steps: 100,
|
||||
bevelEnabled: false,
|
||||
extrudePath: path,
|
||||
})
|
||||
}
|
||||
|
||||
const length = arcStart.getLength()
|
||||
const totalDashes = length / (dashSize + gapSize) // rounding makes the dashes jittery since the new dash is suddenly appears instead of growing into place
|
||||
const dashesAtEachEnd = Math.min(100, totalDashes / 2) // Assuming we want 50 dashes total, 25 at each end
|
||||
|
||||
const dashGeometries = []
|
||||
|
||||
// Function to create a dash at a specific t value (0 to 1 along the curve)
|
||||
const createDashAt = (t: number, curve: EllipseCurve) => {
|
||||
const startVec = curve.getPoint(t)
|
||||
const endVec = curve.getPoint(Math.min(0.5, t + dashSize / length))
|
||||
const midVec = curve.getPoint(Math.min(0.5, t + dashSize / length / 2))
|
||||
const dashCurve = new CurvePath<Vector3>()
|
||||
dashCurve.add(
|
||||
new CatmullRomCurve3([
|
||||
new Vector3(startVec.x, startVec.y, 0),
|
||||
new Vector3(midVec.x, midVec.y, 0),
|
||||
new Vector3(endVec.x, endVec.y, 0),
|
||||
])
|
||||
)
|
||||
return new ExtrudeGeometry(shape, {
|
||||
steps: 3,
|
||||
bevelEnabled: false,
|
||||
extrudePath: dashCurve,
|
||||
})
|
||||
}
|
||||
|
||||
// Create dashes at the start of the arc
|
||||
for (let i = 0; i < dashesAtEachEnd; i++) {
|
||||
const t = i / totalDashes
|
||||
dashGeometries.push(createDashAt(t, arcStart))
|
||||
dashGeometries.push(createDashAt(t, arcEnd))
|
||||
}
|
||||
|
||||
// fill in the remaining arc
|
||||
const remainingArcLength = length - dashesAtEachEnd * 2 * (dashSize + gapSize)
|
||||
if (remainingArcLength > 0) {
|
||||
const remainingArcStartT = dashesAtEachEnd / totalDashes
|
||||
const remainingArcEndT = 1 - remainingArcStartT
|
||||
const centerVec = new Vector2(center[0], center[1])
|
||||
const remainingArcStartVec = arcStart.getPoint(remainingArcStartT)
|
||||
const remainingArcEndVec = arcStart.getPoint(remainingArcEndT)
|
||||
const remainingArcCurve = new EllipseCurve(
|
||||
arcStart.aX,
|
||||
arcStart.aY,
|
||||
arcStart.xRadius,
|
||||
arcStart.yRadius,
|
||||
new Vector2().subVectors(centerVec, remainingArcStartVec).angle() +
|
||||
Math.PI,
|
||||
new Vector2().subVectors(centerVec, remainingArcEndVec).angle() + Math.PI,
|
||||
!ccw
|
||||
)
|
||||
const remainingArcPoints = remainingArcCurve.getPoints(50)
|
||||
const remainingArcPath = new CurvePath<Vector3>()
|
||||
remainingArcPath.add(
|
||||
new CatmullRomCurve3(
|
||||
remainingArcPoints.map((p) => new Vector3(p.x, p.y, 0))
|
||||
)
|
||||
)
|
||||
const remainingArcGeometry = new ExtrudeGeometry(shape, {
|
||||
steps: 50,
|
||||
bevelEnabled: false,
|
||||
extrudePath: remainingArcPath,
|
||||
})
|
||||
dashGeometries.push(remainingArcGeometry)
|
||||
}
|
||||
|
||||
const geo = dashGeometries.length
|
||||
? mergeGeometries(dashGeometries)
|
||||
: new BufferGeometry()
|
||||
geo.userData.type = 'dashed'
|
||||
return geo
|
||||
}
|
||||
|
||||
export function dashedStraight(
|
||||
from: Coords2d,
|
||||
to: Coords2d,
|
||||
shape: Shape,
|
||||
scale = 1
|
||||
): BufferGeometry<NormalBufferAttributes> {
|
||||
const dashSize = 1.2 * scale
|
||||
const gapSize = 1.2 * scale // todo: gabSize is not respected
|
||||
const dashLine = new LineCurve3(
|
||||
new Vector3(from[0], from[1], 0),
|
||||
new Vector3(to[0], to[1], 0)
|
||||
)
|
||||
const length = dashLine.getLength()
|
||||
const numberOfPoints = (length / (dashSize + gapSize)) * 2
|
||||
const startOfLine = new Vector3(from[0], from[1], 0)
|
||||
const endOfLine = new Vector3(to[0], to[1], 0)
|
||||
const dashGeometries = []
|
||||
const dashComponent = (xOrY: number, pointIndex: number) =>
|
||||
((to[xOrY] - from[xOrY]) / numberOfPoints) * pointIndex + from[xOrY]
|
||||
for (let i = 0; i < numberOfPoints; i += 2) {
|
||||
const dashStart = new Vector3(dashComponent(0, i), dashComponent(1, i), 0)
|
||||
let dashEnd = new Vector3(
|
||||
dashComponent(0, i + 1),
|
||||
dashComponent(1, i + 1),
|
||||
0
|
||||
)
|
||||
if (startOfLine.distanceTo(dashEnd) > startOfLine.distanceTo(endOfLine))
|
||||
dashEnd = endOfLine
|
||||
|
||||
if (dashEnd) {
|
||||
const dashCurve = new LineCurve3(dashStart, dashEnd)
|
||||
const dashGeometry = new ExtrudeGeometry(shape, {
|
||||
steps: 1,
|
||||
bevelEnabled: false,
|
||||
extrudePath: dashCurve,
|
||||
})
|
||||
dashGeometries.push(dashGeometry)
|
||||
}
|
||||
}
|
||||
const geo = dashGeometries.length
|
||||
? mergeGeometries(dashGeometries)
|
||||
: new BufferGeometry()
|
||||
geo.userData.type = 'dashed'
|
||||
return geo
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
||||
import React from 'react'
|
||||
import { paths } from '../Router'
|
||||
import { paths } from 'lib/paths'
|
||||
import { Link } from 'react-router-dom'
|
||||
import type { LinkProps } from 'react-router-dom'
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Toolbar } from '../Toolbar'
|
||||
import UserSidebarMenu from './UserSidebarMenu'
|
||||
import { IndexLoaderData } from '../Router'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import styles from './AppHeader.module.css'
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
import { getNodeFromPath, getNodePathFromSourceRange } from 'lang/queryAst'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useStore } from 'useStore'
|
||||
|
@ -8,7 +8,7 @@ import {
|
||||
} from '../lang/modifyAst'
|
||||
import { findAllPreviousVariables, PrevVariable } from '../lang/queryAst'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { executeAst } from 'useStore'
|
||||
|
||||
@ -148,7 +148,6 @@ export function useCalc({
|
||||
executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes: kclManager.defaultPlanes,
|
||||
useFakeExecutor: true,
|
||||
programMemoryOverride: JSON.parse(
|
||||
JSON.stringify(kclManager.programMemory)
|
||||
|
75
src/components/CamToggle.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { sceneInfra } from '../clientSideScene/sceneInfra'
|
||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||
import { throttle, isReducedMotion } from 'lib/utils'
|
||||
|
||||
const updateDollyZoom = throttle(
|
||||
(newFov: number) => sceneInfra.dollyZoom(newFov),
|
||||
1000 / 15
|
||||
)
|
||||
|
||||
export const CamToggle = () => {
|
||||
const [isPerspective, setIsPerspective] = useState(true)
|
||||
const [fov, setFov] = useState(40)
|
||||
const [enableRotate, setEnableRotate] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
engineCommandManager.waitForReady.then(async () => {
|
||||
sceneInfra.dollyZoom(fov)
|
||||
})
|
||||
}, [])
|
||||
|
||||
const toggleCamera = () => {
|
||||
if (isPerspective) {
|
||||
isReducedMotion()
|
||||
? sceneInfra.useOrthographicCamera()
|
||||
: sceneInfra.animateToOrthographic()
|
||||
} else {
|
||||
isReducedMotion()
|
||||
? sceneInfra.usePerspectiveCamera()
|
||||
: sceneInfra.animateToPerspective()
|
||||
}
|
||||
setIsPerspective(!isPerspective)
|
||||
}
|
||||
|
||||
const handleFovChange = (newFov: number) => {
|
||||
setFov(newFov)
|
||||
updateDollyZoom(newFov)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="absolute right-14 bottom-3">
|
||||
{isPerspective && (
|
||||
<div className="">
|
||||
<input
|
||||
type="range"
|
||||
min="4"
|
||||
max="90"
|
||||
step={0.5}
|
||||
value={fov}
|
||||
onChange={(e) => handleFovChange(Number(e.target.value))}
|
||||
className="w-full cursor-pointer pointer-events-auto"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<button onClick={toggleCamera} className="">
|
||||
{isPerspective
|
||||
? 'Switch to Orthographic Camera'
|
||||
: 'Switch to Perspective Camera'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
if (enableRotate) {
|
||||
sceneInfra.controls.enableRotate = false
|
||||
} else {
|
||||
sceneInfra.controls.enableRotate = true
|
||||
}
|
||||
setEnableRotate(!enableRotate)
|
||||
}}
|
||||
className=""
|
||||
>
|
||||
{enableRotate ? 'Disable Rotation' : 'Enable Rotation'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
@ -9,7 +9,7 @@ import styles from './CodeMenu.module.css'
|
||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||
import { editorShortcutMeta } from './TextEditor'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export const CodeMenu = ({ children }: PropsWithChildren) => {
|
||||
const { enable: convertToVarEnabled, handleClick: handleConvertToVarClick } =
|
||||
@ -77,6 +77,24 @@ export const CodeMenu = ({ children }: PropsWithChildren) => {
|
||||
</small>
|
||||
</a>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<a
|
||||
className={styles.button}
|
||||
href="https://github.com/KittyCAD/kcl-samples"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span>KCL samples</span>
|
||||
<small>
|
||||
On GitHub
|
||||
<FontAwesomeIcon
|
||||
icon={faArrowUpRightFromSquare}
|
||||
className="ml-1 align-text-top"
|
||||
width={12}
|
||||
/>
|
||||
</small>
|
||||
</a>
|
||||
</Menu.Item>
|
||||
</Menu.Items>
|
||||
</div>
|
||||
</Menu>
|
||||
|
@ -27,6 +27,7 @@ export const CommandBarProvider = ({
|
||||
}) => {
|
||||
const { pathname } = useLocation()
|
||||
const [commandBarState, commandBarSend] = useMachine(commandBarMachine, {
|
||||
devTools: true,
|
||||
guards: {
|
||||
'Arguments are ready': (context, _) => {
|
||||
return context.selectedCommand?.args
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { CustomIcon } from '../CustomIcon'
|
||||
import React, { useState } from 'react'
|
||||
import React, { ReactNode, useState } from 'react'
|
||||
import { ActionButton } from '../ActionButton'
|
||||
import { Selections, getSelectionTypeDisplayText } from 'lib/selections'
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
@ -99,15 +99,7 @@ function CommandBarHeader({ children }: React.PropsWithChildren<{}>) {
|
||||
) : typeof argumentsToSubmit[argName] === 'object' ? (
|
||||
JSON.stringify(argumentsToSubmit[argName])
|
||||
) : (
|
||||
argumentsToSubmit[argName]
|
||||
)
|
||||
) : arg.payload ? (
|
||||
arg.inputType === 'selection' ? (
|
||||
getSelectionTypeDisplayText(arg.payload as Selections)
|
||||
) : typeof arg.payload === 'object' ? (
|
||||
JSON.stringify(arg.payload)
|
||||
) : (
|
||||
arg.payload
|
||||
<em>{argumentsToSubmit[argName] as ReactNode}</em>
|
||||
)
|
||||
) : (
|
||||
<em>{argName}</em>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useSelector } from '@xstate/react'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { useKclContext } from 'lang/KclSinglton'
|
||||
import { useKclContext } from 'lang/KclSingleton'
|
||||
import { CommandArgument } from 'lib/commandTypes'
|
||||
import {
|
||||
ResolvedSelectionType,
|
||||
@ -27,7 +27,7 @@ function CommandBarSelectionInput({
|
||||
}) {
|
||||
const { code } = useKclContext()
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const { commandBarState, commandBarSend } = useCommandsContext()
|
||||
const [hasSubmitted, setHasSubmitted] = useState(false)
|
||||
const selection = useSelector(arg.actor, selectionSelector)
|
||||
const [selectionsByType, setSelectionsByType] = useState<
|
||||
@ -59,8 +59,16 @@ function CommandBarSelectionInput({
|
||||
)
|
||||
}, [selection])
|
||||
|
||||
// Fast-forward through this arg if it's marked as skippable
|
||||
// and we have a valid selection already
|
||||
useEffect(() => {
|
||||
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
|
||||
const argValue = commandBarState.context.argumentsToSubmit[arg.name]
|
||||
if (canSubmitSelection && arg.skip && argValue === undefined) {
|
||||
handleSubmit({
|
||||
preventDefault: () => {},
|
||||
} as React.FormEvent<HTMLFormElement>)
|
||||
}
|
||||
}, [selectionsByType, arg])
|
||||
|
||||
function handleChange() {
|
||||
|
@ -48,6 +48,7 @@ function CommandComboBox({
|
||||
(event.metaKey && event.key === 'k') ||
|
||||
(event.key === 'Backspace' && !event.currentTarget.value)
|
||||
) {
|
||||
event.preventDefault()
|
||||
commandBarSend({ type: 'Close' })
|
||||
}
|
||||
}}
|
||||
|
@ -1,9 +1,12 @@
|
||||
export type CustomIconName =
|
||||
| 'arc'
|
||||
| 'arrowDown'
|
||||
| 'arrowLeft'
|
||||
| 'arrowRight'
|
||||
| 'arrowUp'
|
||||
| 'checkmark'
|
||||
| 'clipboardPlus'
|
||||
| 'clipboardCheckmark'
|
||||
| 'close'
|
||||
| 'equal'
|
||||
| 'extrude'
|
||||
@ -13,10 +16,14 @@ export type CustomIconName =
|
||||
| 'folderPlus'
|
||||
| 'gear'
|
||||
| 'horizontal'
|
||||
| 'horizontalDash'
|
||||
| 'line'
|
||||
| 'move'
|
||||
| 'network'
|
||||
| 'networkCrossedOut'
|
||||
| 'parallel'
|
||||
| 'search'
|
||||
| 'settings'
|
||||
| 'sketch'
|
||||
| 'vertical'
|
||||
|
||||
@ -27,6 +34,22 @@ export const CustomIcon = ({
|
||||
name: CustomIconName
|
||||
} & React.SVGProps<SVGSVGElement>) => {
|
||||
switch (name) {
|
||||
case 'arc':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M10 1.5C8.60217 1.5 7.22591 1.84474 5.99313 2.50367C4.76035 3.1626 3.70911 4.1154 2.93251 5.27765C2.15592 6.43991 1.67794 7.77575 1.54093 9.16685C1.40392 10.558 1.6121 11.9614 2.14703 13.2528C2.68195 14.5442 3.52712 15.6838 4.60766 16.5706C5.6882 17.4574 6.97076 18.064 8.34173 18.3367C9.71271 18.6094 11.1298 18.5398 12.4674 18.134C13.8051 17.7282 15.022 16.9988 16.0104 16.0104C16.3068 15.714 16.5796 15.3974 16.8273 15.0634L16.0241 14.4677C15.8055 14.7624 15.5648 15.0418 15.3033 15.3033C14.4312 16.1754 13.3574 16.819 12.1771 17.1771C10.9969 17.5351 9.74651 17.5965 8.53682 17.3559C7.32714 17.1153 6.19547 16.58 5.24205 15.7976C4.28863 15.0151 3.5429 14.0096 3.07091 12.8701C2.59891 11.7306 2.41522 10.4923 2.53612 9.26487C2.65701 8.03743 3.07875 6.85874 3.76398 5.83322C4.44921 4.8077 5.37678 3.967 6.46453 3.38559C7.55227 2.80418 8.76662 2.5 10 2.5C10.3699 2.5 10.7376 2.52734 11.1005 2.58117L11.2472 1.59199C10.836 1.53099 10.4192 1.5 10 1.5ZM13.2067 3.22008C13.5383 3.37691 13.8593 3.5585 14.1668 3.76398C14.4743 3.96946 14.7649 4.19652 15.0367 4.44286L15.7083 3.70191C15.4002 3.42271 15.0709 3.16538 14.7223 2.93251C14.3738 2.69964 14.0101 2.49384 13.6342 2.31609L13.2067 3.22008ZM16.433 6.14423C16.6216 6.45886 16.7876 6.78818 16.9291 7.12987C17.0706 7.47157 17.1861 7.82181 17.2752 8.17765L18.2453 7.93467C18.1443 7.53138 18.0134 7.13444 17.853 6.74719C17.6926 6.35995 17.5044 5.98672 17.2907 5.63012L16.433 6.14423ZM17.491 10.368C17.473 10.7344 17.428 11.1004 17.3559 11.4632C17.2837 11.8259 17.1852 12.1813 17.0616 12.5267L18.0031 12.8636C18.1432 12.4721 18.2549 12.0694 18.3367 11.6583C18.4184 11.2472 18.4694 10.8323 18.4898 10.4171L17.491 10.368Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'arrowDown':
|
||||
return (
|
||||
<svg
|
||||
@ -107,6 +130,38 @@ export const CustomIcon = ({
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'clipboardCheckmark':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM15.6855 11.5L13.2101 14.8005L12.2071 13.7975L11.5 14.5046L12.9107 15.9153L13.6642 15.8617L16.4855 12.1L15.6855 11.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'clipboardPlus':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M6.5 3H7L13 3L13.5 3V3.5V4.00001L15.5 4.00002L16 4.00002V4.50002V10.0351C15.6905 9.85609 15.3548 9.71733 15 9.62602V5.00002L13.5 5.00001V6.50001V7.00001L13 7.00001L7 7.00001L6.5 7.00001V6.50001V5.00001L5 5.00001V16H10.8773C11.2024 16.4055 11.6047 16.7463 12.062 17H4.5H4V16.5V4.50001V4.00001L4.5 4.00001L6.5 4.00001V3.5V3ZM15.938 17C15.9588 16.9885 15.9794 16.9768 16 16.9649V17H15.938ZM7.5 4V4.50001V6.00001L12.5 6.00001V4.50001V4L7.5 4ZM13 9H7V8H13V9ZM13.5 11V13H11.5V14H13.5V16H14.5V14H16.5V13H14.5V11H13.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'close':
|
||||
return (
|
||||
<svg
|
||||
@ -249,6 +304,22 @@ export const CustomIcon = ({
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'horizontalDash':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M14 10.5H6V9.5H14V10.5Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'line':
|
||||
return (
|
||||
<svg
|
||||
@ -281,6 +352,38 @@ export const CustomIcon = ({
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'network':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M18 9.64741C17.1925 8.24871 16.0344 7.08457 14.6399 6.26971C13.2455 5.45486 11.6628 5.01742 10.0478 5.00051C8.4328 4.9836 6.84127 5.38779 5.43006 6.17326C4.01884 6.95873 2.83666 8.09837 2 9.47985L2.76881 9.94546C3.52456 8.69756 4.59243 7.66813 5.86718 6.95862C7.14193 6.2491 8.57955 5.88399 10.0384 5.89927C11.4972 5.91455 12.9269 6.30968 14.1865 7.04574C15.4461 7.7818 16.4922 8.83337 17.2216 10.0968L18 9.64741ZM15.2155 11.0953C14.6772 10.1628 13.9051 9.3867 12.9755 8.84347C12.0459 8.30023 10.9907 8.00861 9.91406 7.99733C8.8374 7.98606 7.77638 8.25552 6.83557 8.77917C5.89476 9.30281 5.10664 10.0626 4.54887 10.9836L5.34391 11.4651C5.81802 10.6822 6.48792 10.0364 7.28761 9.59132C8.0873 9.14622 8.98916 8.91718 9.90432 8.92676C10.8195 8.93635 11.7164 9.18423 12.5065 9.64598C13.2967 10.1077 13.953 10.7674 14.4106 11.56L15.2155 11.0953ZM10 14C10.8284 14 11.5 13.3284 11.5 12.5C11.5 11.6716 10.8284 11 10 11C9.17157 11 8.5 11.6716 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'networkCrossedOut':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M4.35352 5.39647L14.253 15.296L14.9601 14.5889L5.06062 4.68936L4.35352 5.39647ZM12.5065 9.64599C11.9609 9.32713 11.3643 9.11025 10.746 9.00341L9.74058 7.99796C9.79835 7.99694 9.85618 7.99674 9.91406 7.99735C10.9907 8.00862 12.0459 8.30025 12.9755 8.84348C13.9051 9.38672 14.6772 10.1628 15.2155 11.0953L14.4106 11.56C13.953 10.7674 13.2967 10.1077 12.5065 9.64599ZM6.48788 8.98789L7.16295 9.66297C6.41824 10.1045 5.79317 10.7233 5.34391 11.4651L4.54887 10.9836C5.03646 10.1785 5.70009 9.49656 6.48788 8.98789ZM10.0384 5.89928C9.3134 5.89169 8.59366 5.97804 7.89655 6.15392L7.16867 5.42605C8.09637 5.13507 9.06776 4.99026 10.0478 5.00052C11.6628 5.01744 13.2455 5.45488 14.6399 6.26973C16.0344 7.08458 17.1925 8.24872 18 9.64742L17.2216 10.0968C16.4922 8.83338 15.4461 7.78181 14.1865 7.04575C12.9269 6.3097 11.4972 5.91456 10.0384 5.89928ZM5.00782 7.50783L4.36522 6.86524C3.42033 7.57557 2.61639 8.46208 2 9.47986L2.76881 9.94547C3.34775 8.98952 4.10986 8.16177 5.00782 7.50783ZM10 14C10.4142 14 10.7892 13.8321 11.0607 13.5607L8.93934 11.4394C8.66789 11.7108 8.5 12.0858 8.5 12.5C8.5 13.3284 9.17157 14 10 14Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'parallel':
|
||||
return (
|
||||
<svg
|
||||
@ -313,6 +416,22 @@ export const CustomIcon = ({
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'settings':
|
||||
return (
|
||||
<svg
|
||||
{...props}
|
||||
viewBox="0 0 20 20"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
d="M8 5.5C8 5.77614 7.77614 6 7.5 6C7.22386 6 7 5.77614 7 5.5C7 5.22386 7.22386 5 7.5 5C7.77614 5 8 5.22386 8 5.5ZM6.08535 6C6.29127 6.5826 6.84689 7 7.5 7C8.32843 7 9 6.32843 9 5.5C9 4.67157 8.32843 4 7.5 4C6.84689 4 6.29127 4.4174 6.08535 5H5V6H6.08535ZM15 6H9.94999C9.98278 5.83844 10 5.67123 10 5.5C10 5.32877 9.98278 5.16155 9.94999 5H15V6ZM11 14.5C11 14.7761 10.7761 15 10.5 15C10.2239 15 10 14.7761 10 14.5C10 14.2239 10.2239 14 10.5 14C10.7761 14 11 14.2239 11 14.5ZM9.08535 15C9.29127 15.5826 9.84689 16 10.5 16C11.3284 16 12 15.3284 12 14.5C12 13.6716 11.3284 13 10.5 13C9.84689 13 9.29127 13.4174 9.08535 14H5V15H9.08535ZM15 15H12.95C12.9828 14.8384 13 14.6712 13 14.5C13 14.3288 12.9828 14.1616 12.95 14H15V15ZM11.5 10.5C11.7761 10.5 12 10.2761 12 10C12 9.72386 11.7761 9.5 11.5 9.5C11.2239 9.5 11 9.72386 11 10C11 10.2761 11.2239 10.5 11.5 10.5ZM11.5 8.5C12.1531 8.5 12.7087 8.9174 12.9146 9.5H15V10.5H12.9146C12.7087 11.0826 12.1531 11.5 11.5 11.5C10.6716 11.5 10 10.8284 10 10C10 9.17157 10.6716 8.5 11.5 8.5ZM9.05001 10.5C9.01722 10.3384 9 10.1712 9 10C9 9.82877 9.01722 9.66155 9.05001 9.5H5V10.5H9.05001Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
case 'sketch':
|
||||
return (
|
||||
<svg
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||
import { AstExplorer } from './AstExplorer'
|
||||
import { EngineCommands } from './EngineCommands'
|
||||
import { CamDebugSettings } from 'clientSideScene/ClientSideSceneComp'
|
||||
|
||||
export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||
return (
|
||||
@ -15,6 +16,7 @@ export const DebugPanel = ({ className, ...props }: CollapsiblePanelProps) => {
|
||||
>
|
||||
<section className="p-4 flex flex-col gap-4">
|
||||
<EngineCommands />
|
||||
<CamDebugSettings />
|
||||
<div style={{ height: '400px' }} className="overflow-y-auto">
|
||||
<AstExplorer />
|
||||
</div>
|
||||
|
43
src/components/DragWarningToast.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
import { MoveDesc } from 'machines/modelingMachine'
|
||||
|
||||
export const DragWarningToast = (moveDescs: MoveDesc[]) => {
|
||||
if (moveDescs.length === 1) {
|
||||
return (
|
||||
<div className="flex items-center">
|
||||
<div>🔒</div>
|
||||
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
|
||||
move disabled: line{' '}
|
||||
<span className="dark:text-energy-20 text-lime-600">
|
||||
{moveDescs[0].line}
|
||||
</span>
|
||||
:{' '}
|
||||
<pre>
|
||||
<code className="dark:text-energy-20 text-lime-600">
|
||||
{moveDescs[0].snippet}
|
||||
</code>
|
||||
</pre>{' '}
|
||||
is fully constrained
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
} else if (moveDescs.length > 1) {
|
||||
return (
|
||||
<div className="dark:bg-slate-950/50 bg-slate-400/50 p-1 px-3 rounded-xl text-sm">
|
||||
<div>Move disabled as The following lines are constrained</div>
|
||||
{moveDescs.map((desc, i) => {
|
||||
return (
|
||||
<div key={i}>
|
||||
line {desc.line}:{' '}
|
||||
<pre className="inline-block">
|
||||
<code className="dark:text-energy-20 text-lime-600">
|
||||
{moveDescs[0].snippet}
|
||||
</code>
|
||||
</pre>{' '}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useNavigate, useRouteLoaderData } from 'react-router-dom'
|
||||
import { IndexLoaderData, paths } from '../Router'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import React, { createContext } from 'react'
|
||||
import { toast } from 'react-hot-toast'
|
||||
import {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { IndexLoaderData, paths } from 'Router'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import Tooltip from './Tooltip'
|
||||
import { FileEntry } from '@tauri-apps/api/fs'
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useMachine } from '@xstate/react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { paths } from '../Router'
|
||||
import { paths } from 'lib/paths'
|
||||
import { authMachine, TOKEN_PERSIST_KEY } from '../machines/authMachine'
|
||||
import withBaseUrl from '../lib/withBaseURL'
|
||||
import React, { createContext, useEffect, useRef } from 'react'
|
||||
@ -30,6 +30,12 @@ type GlobalContext = {
|
||||
settings: MachineContext<typeof settingsMachine>
|
||||
}
|
||||
|
||||
// a little hacky for sure, open to changing it
|
||||
// this implies that we should only even have one instance of this provider mounted at any one time
|
||||
// but I think that's a safe assumption
|
||||
let settingsStateRef: (typeof settingsMachine)['context'] | undefined
|
||||
export const getSettingsState = () => settingsStateRef
|
||||
|
||||
export const GlobalStateContext = createContext({} as GlobalContext)
|
||||
|
||||
export const GlobalStateProvider = ({
|
||||
@ -71,6 +77,7 @@ export const GlobalStateProvider = ({
|
||||
},
|
||||
},
|
||||
})
|
||||
settingsStateRef = settingsState.context
|
||||
|
||||
useStateMachineCommands({
|
||||
machineId: 'settings',
|
||||
|
@ -2,7 +2,7 @@ import ReactJson from 'react-json-view'
|
||||
import { useEffect } from 'react'
|
||||
import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||
import { Themes } from '../lib/theme'
|
||||
import { useKclContext } from 'lang/KclSinglton'
|
||||
import { useKclContext } from 'lang/KclSingleton'
|
||||
|
||||
const ReactJsonTypeHack = ReactJson as any
|
||||
|
||||
|
@ -41,9 +41,9 @@ describe('processMemory', () => {
|
||||
otherVar: 3,
|
||||
theExtrude: [],
|
||||
theSketch: [
|
||||
{ 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: [2.15, 4.32], from: [0.98, 5.16], 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: [2.15, 4.32], from: [0.98, 5.16], name: '' },
|
||||
],
|
||||
})
|
||||
})
|
||||
|
@ -3,7 +3,7 @@ import { CollapsiblePanel, CollapsiblePanelProps } from './CollapsiblePanel'
|
||||
import { useMemo } from 'react'
|
||||
import { ProgramMemory, Path, ExtrudeSurface } from '../lang/wasm'
|
||||
import { Themes } from '../lib/theme'
|
||||
import { useKclContext } from 'lang/KclSinglton'
|
||||
import { useKclContext } from 'lang/KclSingleton'
|
||||
|
||||
interface MemoryPanelProps extends CollapsiblePanelProps {
|
||||
theme?: Exclude<Themes, Themes.System>
|
||||
|
@ -13,23 +13,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { isCursorInSketchCommandRange } from 'lang/util'
|
||||
import { engineCommandManager } from 'lang/std/engineConnection'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { addStartSketch } from 'lang/modifyAst'
|
||||
import { roundOff } from 'lib/utils'
|
||||
import {
|
||||
recast,
|
||||
parse,
|
||||
Program,
|
||||
PipeExpression,
|
||||
CallExpression,
|
||||
} from 'lang/wasm'
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import {
|
||||
addCloseToPipe,
|
||||
addNewSketchLn,
|
||||
compareVec2Epsilon,
|
||||
} from 'lang/std/sketch'
|
||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||
import { applyConstraintHorzVertDistance } from './Toolbar/SetHorzVertDistance'
|
||||
import {
|
||||
angleBetweenInfo,
|
||||
@ -49,6 +33,11 @@ import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||
import useStateMachineCommands from 'hooks/useStateMachineCommands'
|
||||
import { modelingMachineConfig } from 'lib/commandBarConfigs/modelingCommandConfig'
|
||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||
import { getSketchQuaternion } from 'clientSideScene/sceneEntities'
|
||||
import { startSketchOnDefault } from 'lang/modifyAst'
|
||||
import { Program } from 'lang/wasm'
|
||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||
|
||||
type MachineContext<T extends AnyStateMachine> = {
|
||||
state: StateFrom<T>
|
||||
@ -92,181 +81,10 @@ export const ModelingMachineProvider = ({
|
||||
const [modelingState, modelingSend, modelingActor] = useMachine(
|
||||
modelingMachine,
|
||||
{
|
||||
// context: persistedSettings,
|
||||
actions: {
|
||||
'Modify AST': () => {},
|
||||
'Update code selection cursors': () => {},
|
||||
'show default planes': () => {
|
||||
kclManager.showPlanes()
|
||||
},
|
||||
'create path': assign({
|
||||
sketchEnginePathId: () => {
|
||||
const sketchUuid = uuidv4()
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: sketchUuid,
|
||||
cmd: {
|
||||
type: 'start_path',
|
||||
},
|
||||
})
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'edit_mode_enter',
|
||||
target: sketchUuid,
|
||||
},
|
||||
})
|
||||
return sketchUuid
|
||||
},
|
||||
}),
|
||||
'AST start new sketch': assign(
|
||||
({ sketchEnginePathId }, { data: { coords, axis, segmentId } }) => {
|
||||
if (!axis) {
|
||||
// Something really weird must have happened for this to happen.
|
||||
console.error('axis is undefined for starting a new sketch')
|
||||
return {}
|
||||
}
|
||||
if (!segmentId) {
|
||||
// Something really weird must have happened for this to happen.
|
||||
console.error('segmentId is undefined for starting a new sketch')
|
||||
return {}
|
||||
}
|
||||
|
||||
const _addStartSketch = addStartSketch(
|
||||
kclManager.ast,
|
||||
axis,
|
||||
[roundOff(coords[0].x), roundOff(coords[0].y)],
|
||||
[
|
||||
roundOff(coords[1].x - coords[0].x),
|
||||
roundOff(coords[1].y - coords[0].y),
|
||||
]
|
||||
)
|
||||
const _modifiedAst = _addStartSketch.modifiedAst
|
||||
const _pathToNode = _addStartSketch.pathToNode
|
||||
const newCode = recast(_modifiedAst)
|
||||
const astWithUpdatedSource = parse(newCode)
|
||||
const updatedPipeNode = getNodeFromPath<PipeExpression>(
|
||||
astWithUpdatedSource,
|
||||
_pathToNode
|
||||
).node
|
||||
const startProfileAtCallExp = updatedPipeNode.body.find(
|
||||
(exp) =>
|
||||
exp.type === 'CallExpression' &&
|
||||
exp.callee.name === 'startProfileAt'
|
||||
)
|
||||
if (startProfileAtCallExp)
|
||||
engineCommandManager.artifactMap[sketchEnginePathId] = {
|
||||
type: 'result',
|
||||
range: [startProfileAtCallExp.start, startProfileAtCallExp.end],
|
||||
commandType: 'start_path',
|
||||
data: null,
|
||||
raw: {} as any,
|
||||
}
|
||||
const lineCallExp = updatedPipeNode.body.find(
|
||||
(exp) =>
|
||||
exp.type === 'CallExpression' && exp.callee.name === 'line'
|
||||
)
|
||||
if (lineCallExp)
|
||||
engineCommandManager.artifactMap[segmentId] = {
|
||||
type: 'result',
|
||||
range: [lineCallExp.start, lineCallExp.end],
|
||||
commandType: 'extend_path',
|
||||
parentId: sketchEnginePathId,
|
||||
data: null,
|
||||
raw: {} as any,
|
||||
}
|
||||
|
||||
kclManager.executeAstMock(astWithUpdatedSource, true)
|
||||
|
||||
return {
|
||||
sketchPathToNode: _pathToNode,
|
||||
}
|
||||
}
|
||||
),
|
||||
'AST add line segment': async (
|
||||
{ sketchPathToNode, sketchEnginePathId },
|
||||
{ data: { coords, segmentId } }
|
||||
) => {
|
||||
if (!sketchPathToNode) return
|
||||
const lastCoord = coords[coords.length - 1]
|
||||
|
||||
const pathInfo = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'path_get_info',
|
||||
path_id: sketchEnginePathId,
|
||||
},
|
||||
})
|
||||
const firstSegment = pathInfo?.data?.data?.segments.find(
|
||||
(seg: any) => seg.command === 'line_to'
|
||||
)
|
||||
const firstSegCoords = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'curve_get_control_points',
|
||||
curve_id: firstSegment.command_id,
|
||||
},
|
||||
})
|
||||
const startPathCoord = firstSegCoords?.data?.data?.control_points[0]
|
||||
|
||||
const isClose = compareVec2Epsilon(
|
||||
[startPathCoord.x, startPathCoord.y],
|
||||
[lastCoord.x, lastCoord.y]
|
||||
)
|
||||
|
||||
let _modifiedAst: Program
|
||||
if (!isClose) {
|
||||
const newSketchLn = addNewSketchLn({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
to: [lastCoord.x, lastCoord.y],
|
||||
from: [coords[0].x, coords[0].y],
|
||||
fnName: 'line',
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
const _modifiedAst = newSketchLn.modifiedAst
|
||||
kclManager.executeAstMock(_modifiedAst, true).then(() => {
|
||||
const lineCallExp = getNodeFromPath<CallExpression>(
|
||||
kclManager.ast,
|
||||
newSketchLn.pathToNode
|
||||
).node
|
||||
if (segmentId)
|
||||
engineCommandManager.artifactMap[segmentId] = {
|
||||
type: 'result',
|
||||
range: [lineCallExp.start, lineCallExp.end],
|
||||
commandType: 'extend_path',
|
||||
parentId: sketchEnginePathId,
|
||||
data: null,
|
||||
raw: {} as any,
|
||||
}
|
||||
})
|
||||
} else {
|
||||
_modifiedAst = addCloseToPipe({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'edit_mode_exit' },
|
||||
})
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: { type: 'default_camera_disable_sketch_mode' },
|
||||
})
|
||||
kclManager.executeAstMock(_modifiedAst, true)
|
||||
// updateAst(_modifiedAst, true)
|
||||
}
|
||||
},
|
||||
'sketch exit execute': () => {
|
||||
kclManager.executeAst()
|
||||
},
|
||||
'set tool': () => {}, // TODO
|
||||
'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
|
||||
const setSelections = event.data
|
||||
@ -274,36 +92,6 @@ export const ModelingMachineProvider = ({
|
||||
if (setSelections.selectionType === 'mirrorCodeMirrorSelections')
|
||||
return { selectionRanges: setSelections.selection }
|
||||
else if (setSelections.selectionType === 'otherSelection') {
|
||||
// TODO KittyCAD/engine/issues/1620: send axis highlight when it's working (if that's what we settle on)
|
||||
// const axisAddCmd: EngineCommand = {
|
||||
// type: 'modeling_cmd_req',
|
||||
// cmd: {
|
||||
// type: 'highlight_set_entities',
|
||||
// entities: [
|
||||
// setSelections.selection === 'x-axis'
|
||||
// ? X_AXIS_UUID
|
||||
// : Y_AXIS_UUID,
|
||||
// ],
|
||||
// },
|
||||
// cmd_id: uuidv4(),
|
||||
// }
|
||||
|
||||
// if (!isShiftDown) {
|
||||
// engineCommandManager
|
||||
// .sendSceneCommand({
|
||||
// type: 'modeling_cmd_req',
|
||||
// cmd: {
|
||||
// type: 'select_clear',
|
||||
// },
|
||||
// cmd_id: uuidv4(),
|
||||
// })
|
||||
// .then(() => {
|
||||
// engineCommandManager.sendSceneCommand(axisAddCmd)
|
||||
// })
|
||||
// } else {
|
||||
// engineCommandManager.sendSceneCommand(axisAddCmd)
|
||||
// }
|
||||
|
||||
const {
|
||||
codeMirrorSelection,
|
||||
selectionRangeTypeMap,
|
||||
@ -384,12 +172,6 @@ export const ModelingMachineProvider = ({
|
||||
}),
|
||||
},
|
||||
guards: {
|
||||
'Selection contains axis': () => true,
|
||||
'Selection contains edge': () => true,
|
||||
'Selection contains face': () => true,
|
||||
'Selection contains line': () => true,
|
||||
'Selection contains point': () => true,
|
||||
'Selection is not empty': () => true,
|
||||
'has valid extrude selection': ({ selectionRanges }) => {
|
||||
// A user can begin extruding if they either have 1+ faces selected or nothing selected
|
||||
// TODO: I believe this guard only allows for extruding a single face at a time
|
||||
@ -401,7 +183,10 @@ export const ModelingMachineProvider = ({
|
||||
|
||||
return canExtrudeSelection(selectionRanges)
|
||||
},
|
||||
'Selection is one face': ({ selectionRanges }) => {
|
||||
'Selection is on face': ({ selectionRanges }, { data }) => {
|
||||
if (data?.forceNewSketch) return false
|
||||
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||
return false
|
||||
return !!isCursorInSketchCommandRange(
|
||||
engineCommandManager.artifactMap,
|
||||
selectionRanges
|
||||
@ -409,6 +194,41 @@ export const ModelingMachineProvider = ({
|
||||
},
|
||||
},
|
||||
services: {
|
||||
'AST-undo-startSketchOn': async ({ sketchPathToNode }) => {
|
||||
if (!sketchPathToNode) return
|
||||
const newAst: Program = JSON.parse(JSON.stringify(kclManager.ast))
|
||||
const varDecIndex = sketchPathToNode[1][0]
|
||||
// remove body item at varDecIndex
|
||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||
await kclManager.executeAstMock(newAst, { updates: 'code' })
|
||||
sceneInfra.setCallbacks({
|
||||
onClick: () => {},
|
||||
onDrag: () => {},
|
||||
})
|
||||
},
|
||||
'animate-to-face': async (_, { data: { plane, normal } }) => {
|
||||
const { modifiedAst, pathToNode } = startSketchOnDefault(
|
||||
kclManager.ast,
|
||||
plane
|
||||
)
|
||||
await kclManager.updateAst(modifiedAst, false)
|
||||
const quaternion = getSketchQuaternion(pathToNode, normal)
|
||||
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||
return {
|
||||
sketchPathToNode: pathToNode,
|
||||
sketchNormalBackUp: normal,
|
||||
}
|
||||
},
|
||||
'animate-to-sketch': async ({
|
||||
sketchPathToNode,
|
||||
sketchNormalBackUp,
|
||||
}) => {
|
||||
const quaternion = getSketchQuaternion(
|
||||
sketchPathToNode || [],
|
||||
sketchNormalBackUp
|
||||
)
|
||||
await sceneInfra.tweenCameraToQuaternion(quaternion)
|
||||
},
|
||||
'Get horizontal info': async ({
|
||||
selectionRanges,
|
||||
}): Promise<SetSelections> => {
|
||||
@ -542,17 +362,6 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
engineCommandManager.onPlaneSelected((plane_id: string) => {
|
||||
if (modelingState.nextEvents.includes('Select default plane')) {
|
||||
modelingSend({
|
||||
type: 'Select default plane',
|
||||
data: { planeId: plane_id },
|
||||
})
|
||||
}
|
||||
})
|
||||
}, [modelingSend, modelingState.nextEvents])
|
||||
|
||||
useEffect(() => {
|
||||
kclManager.registerExecuteCallback(() => {
|
||||
modelingSend({ type: 'Re-execute' })
|
||||
@ -565,10 +374,7 @@ export const ModelingMachineProvider = ({
|
||||
send: modelingSend,
|
||||
actor: modelingActor,
|
||||
commandBarConfig: modelingMachineConfig,
|
||||
onCancel: () => {
|
||||
console.log('firing onCancel!!')
|
||||
modelingSend({ type: 'Cancel' })
|
||||
},
|
||||
onCancel: () => modelingSend({ type: 'Cancel' }),
|
||||
})
|
||||
|
||||
return (
|
||||
|
@ -3,8 +3,9 @@ import { BrowserRouter } from 'react-router-dom'
|
||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||
import CommandBarProvider from './CommandBar/CommandBar'
|
||||
import {
|
||||
NETWORK_CONTENT,
|
||||
NETWORK_HEALTH_TEXT,
|
||||
NetworkHealthIndicator,
|
||||
NetworkHealthState,
|
||||
} from './NetworkHealthIndicator'
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
@ -28,8 +29,8 @@ describe('NetworkHealthIndicator tests', () => {
|
||||
|
||||
fireEvent.click(screen.getByTestId('network-toggle'))
|
||||
|
||||
expect(screen.getByTestId('network-good')).toHaveTextContent(
|
||||
NETWORK_CONTENT.good
|
||||
expect(screen.getByTestId('network')).toHaveTextContent(
|
||||
NETWORK_HEALTH_TEXT[NetworkHealthState.Ok]
|
||||
)
|
||||
})
|
||||
|
||||
@ -43,8 +44,8 @@ describe('NetworkHealthIndicator tests', () => {
|
||||
fireEvent.offline(window)
|
||||
fireEvent.click(screen.getByTestId('network-toggle'))
|
||||
|
||||
expect(screen.getByTestId('network-bad')).toHaveTextContent(
|
||||
NETWORK_CONTENT.bad
|
||||
expect(screen.getByTestId('network')).toHaveTextContent(
|
||||
NETWORK_HEALTH_TEXT[NetworkHealthState.Disconnected]
|
||||
)
|
||||
})
|
||||
})
|
||||
|
@ -1,41 +1,186 @@
|
||||
import { faExclamation, faWifi } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { ActionIcon } from './ActionIcon'
|
||||
import { ActionIcon, ActionIconProps } from './ActionIcon'
|
||||
import {
|
||||
ConnectingType,
|
||||
ConnectingTypeGroup,
|
||||
DisconnectingType,
|
||||
engineCommandManager,
|
||||
EngineConnectionState,
|
||||
EngineConnectionStateType,
|
||||
ErrorType,
|
||||
initialConnectingTypeGroupState,
|
||||
} from '../lang/std/engineConnection'
|
||||
import Tooltip from './Tooltip'
|
||||
|
||||
export const NETWORK_CONTENT = {
|
||||
good: 'Network health is good',
|
||||
bad: 'Network issue',
|
||||
export enum NetworkHealthState {
|
||||
Ok,
|
||||
Issue,
|
||||
Disconnected,
|
||||
}
|
||||
|
||||
const NETWORK_MESSAGES = {
|
||||
offline: 'You are offline',
|
||||
export const NETWORK_HEALTH_TEXT: Record<NetworkHealthState, string> = {
|
||||
[NetworkHealthState.Ok]: 'Connected',
|
||||
[NetworkHealthState.Issue]: 'Problem',
|
||||
[NetworkHealthState.Disconnected]: 'Offline',
|
||||
}
|
||||
|
||||
type IconColorConfig = {
|
||||
icon: string
|
||||
bg: string
|
||||
}
|
||||
|
||||
const hasIssueToIcon: Record<
|
||||
string | number | symbol,
|
||||
ActionIconProps['icon']
|
||||
> = {
|
||||
true: 'close',
|
||||
undefined: 'horizontalDash',
|
||||
false: 'checkmark',
|
||||
}
|
||||
|
||||
const hasIssueToIconColors: Record<string | number | symbol, IconColorConfig> =
|
||||
{
|
||||
true: {
|
||||
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||
bg: 'bg-destroy-10 dark:bg-destroy-80',
|
||||
},
|
||||
undefined: {
|
||||
icon: 'text-chalkboard-70 dark:text-chalkboard-30',
|
||||
bg: 'bg-chalkboard-30 dark:bg-chalkboard-70',
|
||||
},
|
||||
false: {
|
||||
icon: 'text-chalkboard-110 dark:!text-chalkboard-10',
|
||||
bg: 'bg-transparent dark:bg-transparent',
|
||||
},
|
||||
}
|
||||
|
||||
const overallConnectionStateColor: Record<NetworkHealthState, IconColorConfig> =
|
||||
{
|
||||
[NetworkHealthState.Ok]: {
|
||||
icon: 'text-energy-80 dark:text-energy-10',
|
||||
bg: 'bg-energy-10/30 dark:bg-energy-80/50',
|
||||
},
|
||||
[NetworkHealthState.Issue]: {
|
||||
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||
bg: 'bg-destroy-10 dark:bg-destroy-80/80',
|
||||
},
|
||||
[NetworkHealthState.Disconnected]: {
|
||||
icon: 'text-destroy-80 dark:text-destroy-10',
|
||||
bg: 'bg-destroy-10 dark:bg-destroy-80',
|
||||
},
|
||||
}
|
||||
|
||||
const overallConnectionStateIcon: Record<
|
||||
NetworkHealthState,
|
||||
ActionIconProps['icon']
|
||||
> = {
|
||||
[NetworkHealthState.Ok]: 'network',
|
||||
[NetworkHealthState.Issue]: 'networkCrossedOut',
|
||||
[NetworkHealthState.Disconnected]: 'networkCrossedOut',
|
||||
}
|
||||
|
||||
export const NetworkHealthIndicator = () => {
|
||||
const [networkIssues, setNetworkIssues] = useState<string[]>([])
|
||||
const hasIssues = [...networkIssues.values()].length > 0
|
||||
const [steps, setSteps] = useState(initialConnectingTypeGroupState)
|
||||
const [internetConnected, setInternetConnected] = useState<boolean>(true)
|
||||
const [overallState, setOverallState] = useState<NetworkHealthState>(
|
||||
NetworkHealthState.Ok
|
||||
)
|
||||
const [hasCopied, setHasCopied] = useState<boolean>(false)
|
||||
|
||||
const [error, setError] = useState<ErrorType | undefined>(undefined)
|
||||
|
||||
const issues: Record<ConnectingTypeGroup, boolean> = {
|
||||
[ConnectingTypeGroup.WebSocket]: steps[ConnectingTypeGroup.WebSocket].some(
|
||||
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||
),
|
||||
[ConnectingTypeGroup.ICE]: steps[ConnectingTypeGroup.ICE].some(
|
||||
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||
),
|
||||
[ConnectingTypeGroup.WebRTC]: steps[ConnectingTypeGroup.WebRTC].some(
|
||||
(a: [ConnectingType, boolean | undefined]) => a[1] === false
|
||||
),
|
||||
}
|
||||
|
||||
const hasIssues: boolean =
|
||||
issues[ConnectingTypeGroup.WebSocket] ||
|
||||
issues[ConnectingTypeGroup.ICE] ||
|
||||
issues[ConnectingTypeGroup.WebRTC]
|
||||
|
||||
useEffect(() => {
|
||||
const offlineListener = () =>
|
||||
setNetworkIssues((issues) => {
|
||||
return [
|
||||
...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline),
|
||||
NETWORK_MESSAGES.offline,
|
||||
]
|
||||
})
|
||||
window.addEventListener('offline', offlineListener)
|
||||
setOverallState(
|
||||
!internetConnected
|
||||
? NetworkHealthState.Disconnected
|
||||
: hasIssues
|
||||
? NetworkHealthState.Issue
|
||||
: NetworkHealthState.Ok
|
||||
)
|
||||
}, [hasIssues, internetConnected])
|
||||
|
||||
const onlineListener = () =>
|
||||
setNetworkIssues((issues) => {
|
||||
return [...issues.filter((issue) => issue !== NETWORK_MESSAGES.offline)]
|
||||
})
|
||||
window.addEventListener('online', onlineListener)
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('offline', offlineListener)
|
||||
window.removeEventListener('online', onlineListener)
|
||||
useEffect(() => {
|
||||
const cb1 = () => {
|
||||
setSteps(initialConnectingTypeGroupState)
|
||||
setInternetConnected(true)
|
||||
}
|
||||
const cb2 = () => {
|
||||
setInternetConnected(false)
|
||||
}
|
||||
window.addEventListener('online', cb1)
|
||||
window.addEventListener('offline', cb2)
|
||||
return () => {
|
||||
window.removeEventListener('online', cb1)
|
||||
window.removeEventListener('offline', cb2)
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
engineCommandManager.onConnectionStateChange(
|
||||
(engineConnectionState: EngineConnectionState) => {
|
||||
let hasSetAStep = false
|
||||
|
||||
if (
|
||||
engineConnectionState.type === EngineConnectionStateType.Connecting
|
||||
) {
|
||||
const groups = Object.values(steps)
|
||||
for (let group of groups) {
|
||||
for (let step of group) {
|
||||
if (step[0] !== engineConnectionState.value.type) continue
|
||||
step[1] = true
|
||||
hasSetAStep = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
engineConnectionState.type === EngineConnectionStateType.Disconnecting
|
||||
) {
|
||||
const groups = Object.values(steps)
|
||||
for (let group of groups) {
|
||||
for (let step of group) {
|
||||
if (
|
||||
engineConnectionState.value.type === DisconnectingType.Error
|
||||
) {
|
||||
if (
|
||||
engineConnectionState.value.value.lastConnectingValue
|
||||
?.type === step[0]
|
||||
) {
|
||||
step[1] = false
|
||||
hasSetAStep = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (engineConnectionState.value.type === DisconnectingType.Error) {
|
||||
setError(engineConnectionState.value.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSetAStep) {
|
||||
setSteps(steps)
|
||||
}
|
||||
}
|
||||
)
|
||||
}, [])
|
||||
|
||||
return (
|
||||
@ -45,65 +190,94 @@ export const NetworkHealthIndicator = () => {
|
||||
'p-0 border-none bg-transparent dark:bg-transparent relative ' +
|
||||
(hasIssues
|
||||
? 'focus-visible:outline-destroy-80'
|
||||
: 'focus-visible:outline-succeed-80')
|
||||
: 'focus-visible:outline-energy-80')
|
||||
}
|
||||
data-testid="network-toggle"
|
||||
>
|
||||
<span className="sr-only">Network Health</span>
|
||||
<ActionIcon
|
||||
icon={faWifi}
|
||||
icon={overallConnectionStateIcon[overallState]}
|
||||
className="p-1"
|
||||
iconClassName={
|
||||
hasIssues
|
||||
? 'text-destroy-80 dark:text-destroy-30'
|
||||
: 'text-succeed-80 dark:text-succeed-30'
|
||||
}
|
||||
iconClassName={overallConnectionStateColor[overallState].icon}
|
||||
bgClassName={
|
||||
'bg-transparent dark:bg-transparent ' +
|
||||
(hasIssues
|
||||
? 'hover:bg-destroy-10/50 hover:dark:bg-destroy-80/50 rounded'
|
||||
: 'hover:bg-succeed-10/50 hover:dark:bg-succeed-80/50 rounded')
|
||||
'rounded-sm ' + overallConnectionStateColor[overallState].bg
|
||||
}
|
||||
/>
|
||||
<Tooltip position="blockEnd" delay={750} className="ui-open:hidden">
|
||||
Network Health ({NETWORK_HEALTH_TEXT[overallState]})
|
||||
</Tooltip>
|
||||
</Popover.Button>
|
||||
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-56 flex flex-col gap-1 divide-y divide-chalkboard-20 dark:divide-chalkboard-70 align-stretch py-2 bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
||||
{!hasIssues ? (
|
||||
<span
|
||||
className="flex items-center justify-center gap-1 px-4"
|
||||
data-testid="network-good"
|
||||
<Popover.Panel className="absolute right-0 left-auto top-full mt-1 w-64 flex flex-col gap-1 align-stretch bg-chalkboard-10 dark:bg-chalkboard-90 rounded shadow-lg border border-solid border-chalkboard-20/50 dark:border-chalkboard-80/50 text-sm">
|
||||
<div
|
||||
className={`flex items-center justify-between p-2 rounded-t-sm ${overallConnectionStateColor[overallState].bg} ${overallConnectionStateColor[overallState].icon}`}
|
||||
>
|
||||
<h2 className="text-sm font-sans font-normal">Network health</h2>
|
||||
<p
|
||||
data-testid="network"
|
||||
className="font-bold text-xs uppercase px-2 py-1 rounded-sm"
|
||||
>
|
||||
<ActionIcon
|
||||
icon="checkmark"
|
||||
bgClassName={'bg-succeed-10/50 dark:bg-succeed-80/50 rounded-sm'}
|
||||
iconClassName={'text-succeed-80 dark:text-succeed-30'}
|
||||
/>
|
||||
{NETWORK_CONTENT.good}
|
||||
</span>
|
||||
) : (
|
||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||
<span
|
||||
className="font-bold text-xs uppercase text-destroy-60 dark:text-destroy-50 px-4"
|
||||
data-testid="network-bad"
|
||||
{NETWORK_HEALTH_TEXT[overallState]}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="divide-y divide-chalkboard-20 dark:divide-chalkboard-80">
|
||||
{Object.keys(steps).map((name) => (
|
||||
<li
|
||||
key={name}
|
||||
className={'flex flex-col px-2 py-4 gap-1 last:mb-0 '}
|
||||
>
|
||||
{NETWORK_CONTENT.bad}
|
||||
{networkIssues.length > 1 ? 's' : ''}
|
||||
</span>
|
||||
{networkIssues.map((issue) => (
|
||||
<li
|
||||
key={issue}
|
||||
className="flex items-center gap-1 py-2 my-2 last:mb-0"
|
||||
>
|
||||
<ActionIcon
|
||||
icon={faExclamation}
|
||||
bgClassName={'bg-destroy-10/50 dark:bg-destroy-80/50 rounded'}
|
||||
iconClassName={'text-destroy-80 dark:text-destroy-30'}
|
||||
className="ml-4"
|
||||
/>
|
||||
<p className="flex-1 mr-4">{issue}</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
<div className="flex items-center text-left gap-1">
|
||||
<p className="flex-1">{name}</p>
|
||||
{internetConnected ? (
|
||||
<ActionIcon
|
||||
size="lg"
|
||||
icon={
|
||||
hasIssueToIcon[
|
||||
issues[name as ConnectingTypeGroup].toString()
|
||||
]
|
||||
}
|
||||
iconClassName={
|
||||
hasIssueToIconColors[
|
||||
issues[name as ConnectingTypeGroup].toString()
|
||||
].icon
|
||||
}
|
||||
bgClassName={
|
||||
'rounded-sm ' +
|
||||
hasIssueToIconColors[
|
||||
issues[name as ConnectingTypeGroup].toString()
|
||||
].bg
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<ActionIcon
|
||||
icon={hasIssueToIcon.true}
|
||||
bgClassName={hasIssueToIconColors.true.bg}
|
||||
iconClassName={hasIssueToIconColors.true.icon}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{issues[name as ConnectingTypeGroup] && (
|
||||
<button
|
||||
onClick={async () => {
|
||||
await navigator.clipboard.writeText(
|
||||
JSON.stringify(error, null, 2) || ''
|
||||
)
|
||||
setHasCopied(true)
|
||||
setTimeout(() => setHasCopied(false), 5000)
|
||||
}}
|
||||
className="flex w-fit gap-2 items-center bg-transparent text-sm p-1 py-0 my-0 -mx-1 text-destroy-80 dark:text-destroy-10 hover:bg-transparent border-transparent dark:border-transparent hover:border-destroy-80 dark:hover:border-destroy-80 dark:hover:bg-destroy-80"
|
||||
>
|
||||
{hasCopied ? 'Copied' : 'Copy Error'}
|
||||
<ActionIcon
|
||||
size="lg"
|
||||
icon={hasCopied ? 'clipboardCheckmark' : 'clipboardPlus'}
|
||||
iconClassName="text-inherit dark:text-inherit"
|
||||
bgClassName="!bg-transparent"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Popover.Panel>
|
||||
</Popover>
|
||||
)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { FormEvent, useEffect, useRef, useState } from 'react'
|
||||
import { type ProjectWithEntryPointMetadata, paths } from '../Router'
|
||||
import { type ProjectWithEntryPointMetadata } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import {
|
||||
@ -107,6 +108,7 @@ function ProjectCard({
|
||||
<Link
|
||||
className="flex-1 text-liquid-100 after:content-[''] after:absolute after:inset-0"
|
||||
to={`${paths.FILE}/${encodeURIComponent(project.path)}`}
|
||||
data-testid="project-link"
|
||||
>
|
||||
{project.name?.replace(FILE_EXT, '')}
|
||||
</Link>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import ProjectSidebarMenu from './ProjectSidebarMenu'
|
||||
import { ProjectWithEntryPointMetadata } from '../Router'
|
||||
import { type ProjectWithEntryPointMetadata } from 'lib/types'
|
||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||
import CommandBarProvider from './CommandBar/CommandBar'
|
||||
import { APP_NAME } from 'lib/constants'
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
import { ActionButton } from './ActionButton'
|
||||
import { faHome } from '@fortawesome/free-solid-svg-icons'
|
||||
import { IndexLoaderData, paths } from '../Router'
|
||||
import { type IndexLoaderData } from 'lib/types'
|
||||
import { paths } from 'lib/paths'
|
||||
import { isTauri } from '../lib/isTauri'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ExportButton } from './ExportButton'
|
||||
|
@ -7,20 +7,17 @@ import {
|
||||
} from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { useStore } from '../useStore'
|
||||
import { getNormalisedCoordinates } from '../lib/utils'
|
||||
import { getNormalisedCoordinates, throttle } from '../lib/utils'
|
||||
import Loading from './Loading'
|
||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { CameraDragInteractionType_type } from '@kittycad/lib/dist/types/src/models'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { getNodeFromPath } from 'lang/queryAst'
|
||||
import { VariableDeclarator, recast, CallExpression } from 'lang/wasm'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
||||
import { changeSketchArguments } from 'lang/std/sketch'
|
||||
import { useKclContext } from 'lang/KclSingleton'
|
||||
import { ClientSideScene } from 'clientSideScene/ClientSideSceneComp'
|
||||
|
||||
export const Stream = ({ className = '' }) => {
|
||||
export const Stream = ({ className = '' }: { className?: string }) => {
|
||||
const [isLoading, setIsLoading] = useState(true)
|
||||
const [clickCoords, setClickCoords] = useState<{ x: number; y: number }>()
|
||||
const videoRef = useRef<HTMLVideoElement>(null)
|
||||
@ -39,7 +36,7 @@ export const Stream = ({ className = '' }) => {
|
||||
}))
|
||||
const { settings } = useGlobalStateContext()
|
||||
const cameraControls = settings?.context?.cameraControls
|
||||
const { send, state, context } = useModelingContext()
|
||||
const { state } = useModelingContext()
|
||||
const { isExecuting } = useKclContext()
|
||||
|
||||
useEffect(() => {
|
||||
@ -53,8 +50,10 @@ export const Stream = ({ className = '' }) => {
|
||||
videoRef.current.srcObject = mediaStream
|
||||
}, [mediaStream])
|
||||
|
||||
const handleMouseDown: MouseEventHandler<HTMLVideoElement> = (e) => {
|
||||
const handleMouseDown: MouseEventHandler<HTMLDivElement> = (e) => {
|
||||
if (!videoRef.current) return
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches('Sketch no face')) return
|
||||
const { x, y } = getNormalisedCoordinates({
|
||||
clientX: e.clientX,
|
||||
clientY: e.clientY,
|
||||
@ -62,62 +61,13 @@ export const Stream = ({ className = '' }) => {
|
||||
...streamDimensions,
|
||||
})
|
||||
|
||||
const newId = uuidv4()
|
||||
|
||||
const interactionGuards = cameraMouseDragGuards[cameraControls]
|
||||
let interaction: CameraDragInteractionType_type = 'rotate'
|
||||
|
||||
if (
|
||||
interactionGuards.pan.callback(e) ||
|
||||
interactionGuards.pan.lenientDragStartButton === e.button
|
||||
) {
|
||||
interaction = 'pan'
|
||||
} else if (
|
||||
interactionGuards.rotate.callback(e) ||
|
||||
interactionGuards.rotate.lenientDragStartButton === e.button
|
||||
) {
|
||||
interaction = 'rotate'
|
||||
} else if (
|
||||
interactionGuards.zoom.dragCallback(e) ||
|
||||
interactionGuards.zoom.lenientDragStartButton === e.button
|
||||
) {
|
||||
interaction = 'zoom'
|
||||
}
|
||||
|
||||
if (state.matches('Sketch.Move Tool')) {
|
||||
if (
|
||||
state.matches('Sketch.Move Tool.No move') ||
|
||||
state.matches('Sketch.Move Tool.Move with execute')
|
||||
) {
|
||||
return
|
||||
}
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'handle_mouse_drag_start',
|
||||
window: { x, y },
|
||||
},
|
||||
cmd_id: newId,
|
||||
})
|
||||
} else if (!state.matches('Sketch.Line Tool')) {
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
type: 'camera_drag_start',
|
||||
interaction,
|
||||
window: { x, y },
|
||||
},
|
||||
cmd_id: newId,
|
||||
})
|
||||
}
|
||||
|
||||
setButtonDownInStream(e.button)
|
||||
setClickCoords({ x, y })
|
||||
}
|
||||
|
||||
const handleScroll: WheelEventHandler<HTMLVideoElement> = (e) => {
|
||||
const fps = 60
|
||||
const handleScroll: WheelEventHandler<HTMLVideoElement> = throttle((e) => {
|
||||
if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return
|
||||
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd: {
|
||||
@ -126,15 +76,17 @@ export const Stream = ({ className = '' }) => {
|
||||
},
|
||||
cmd_id: uuidv4(),
|
||||
})
|
||||
}
|
||||
}, Math.round(1000 / fps))
|
||||
|
||||
const handleMouseUp: MouseEventHandler<HTMLVideoElement> = ({
|
||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
|
||||
clientX,
|
||||
clientY,
|
||||
ctrlKey,
|
||||
}) => {
|
||||
if (!videoRef.current) return
|
||||
setButtonDownInStream(undefined)
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches('Sketch no face')) return
|
||||
const { x, y } = getNormalisedCoordinates({
|
||||
clientX,
|
||||
clientY,
|
||||
@ -155,206 +107,19 @@ export const Stream = ({ className = '' }) => {
|
||||
cmd_id: newCmdId,
|
||||
}
|
||||
|
||||
if (!didDragInStream && state.matches('Sketch no face')) {
|
||||
command.cmd = {
|
||||
type: 'select_with_point',
|
||||
selection_type: 'add',
|
||||
selected_at_window: { x, y },
|
||||
}
|
||||
engineCommandManager.sendSceneCommand(command)
|
||||
} else if (!didDragInStream && state.matches('Sketch.Line Tool')) {
|
||||
command.cmd = {
|
||||
type: 'mouse_click',
|
||||
window: { x, y },
|
||||
}
|
||||
engineCommandManager.sendSceneCommand(command).then(async (resp) => {
|
||||
const entities_modified = resp?.data?.data?.entities_modified
|
||||
if (!entities_modified) return
|
||||
if (state.matches('Sketch.Line Tool.No Points')) {
|
||||
send('Add point')
|
||||
} else if (state.matches('Sketch.Line Tool.Point Added')) {
|
||||
const curve = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'curve_get_control_points',
|
||||
curve_id: entities_modified[0],
|
||||
},
|
||||
})
|
||||
const coords: { x: number; y: number }[] =
|
||||
curve.data.data.control_points
|
||||
// We need the normal for the plane we are on.
|
||||
const plane = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'get_sketch_mode_plane',
|
||||
},
|
||||
})
|
||||
const z_axis = plane.data.data.z_axis
|
||||
|
||||
// Get the current axis.
|
||||
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
|
||||
null
|
||||
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
||||
if (z_axis.z === -1) {
|
||||
currentAxis = '-xy'
|
||||
} else {
|
||||
currentAxis = 'xy'
|
||||
}
|
||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
|
||||
if (z_axis.x === -1) {
|
||||
currentAxis = '-yz'
|
||||
} else {
|
||||
currentAxis = 'yz'
|
||||
}
|
||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
|
||||
if (z_axis.y === -1) {
|
||||
currentAxis = '-xz'
|
||||
} else {
|
||||
currentAxis = 'xz'
|
||||
}
|
||||
}
|
||||
|
||||
send({
|
||||
type: 'Add point',
|
||||
data: {
|
||||
coords,
|
||||
axis: currentAxis,
|
||||
segmentId: entities_modified[0],
|
||||
},
|
||||
})
|
||||
} else if (state.matches('Sketch.Line Tool.Segment Added')) {
|
||||
const curve = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'curve_get_control_points',
|
||||
curve_id: entities_modified[0],
|
||||
},
|
||||
})
|
||||
const coords: { x: number; y: number }[] =
|
||||
curve.data.data.control_points
|
||||
send({
|
||||
type: 'Add point',
|
||||
data: { coords, axis: null, segmentId: entities_modified[0] },
|
||||
})
|
||||
}
|
||||
})
|
||||
} else if (
|
||||
!didDragInStream &&
|
||||
(state.matches('Sketch.SketchIdle') || state.matches('idle'))
|
||||
) {
|
||||
command.cmd = {
|
||||
type: 'select_with_point',
|
||||
selected_at_window: { x, y },
|
||||
selection_type: 'add',
|
||||
}
|
||||
|
||||
engineCommandManager.sendSceneCommand(command)
|
||||
} else if (!didDragInStream && state.matches('Sketch.Move Tool')) {
|
||||
if (!didDragInStream) {
|
||||
command.cmd = {
|
||||
type: 'select_with_point',
|
||||
selected_at_window: { x, y },
|
||||
selection_type: 'add',
|
||||
}
|
||||
engineCommandManager.sendSceneCommand(command)
|
||||
} else if (didDragInStream && state.matches('Sketch.Move Tool')) {
|
||||
} else if (didDragInStream) {
|
||||
command.cmd = {
|
||||
type: 'handle_mouse_drag_end',
|
||||
window: { x, y },
|
||||
}
|
||||
engineCommandManager.sendSceneCommand(command).then(async () => {
|
||||
if (!context.sketchPathToNode) return
|
||||
getNodeFromPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
context.sketchPathToNode,
|
||||
'VariableDeclarator'
|
||||
)
|
||||
// Get the current plane string for plane we are on.
|
||||
let currentPlaneString = ''
|
||||
if (context.sketchPlaneId === kclManager.getPlaneId('xy')) {
|
||||
currentPlaneString = 'XY'
|
||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('yz')) {
|
||||
currentPlaneString = 'YZ'
|
||||
} else if (context.sketchPlaneId === kclManager.getPlaneId('xz')) {
|
||||
currentPlaneString = 'XZ'
|
||||
}
|
||||
|
||||
// Do not supporting editing/moving lines on a non-default plane.
|
||||
// Eventually we can support this but for now we will just throw an
|
||||
// error.
|
||||
if (currentPlaneString === '') return
|
||||
|
||||
const pathInfo = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'path_get_info',
|
||||
path_id: context.sketchEnginePathId,
|
||||
},
|
||||
})
|
||||
const segmentsWithMappings = (
|
||||
pathInfo?.data?.data?.segments as { command_id: string }[]
|
||||
)
|
||||
.filter(({ command_id }) => {
|
||||
return command_id && engineCommandManager.artifactMap[command_id]
|
||||
})
|
||||
.map(({ command_id }) => command_id)
|
||||
const segment2dInfo = await Promise.all(
|
||||
segmentsWithMappings.map(async (segmentId) => {
|
||||
const response = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'curve_get_control_points',
|
||||
curve_id: segmentId,
|
||||
},
|
||||
})
|
||||
const controlPoints: [
|
||||
{ x: number; y: number },
|
||||
{ x: number; y: number }
|
||||
] = response.data.data.control_points
|
||||
return {
|
||||
controlPoints,
|
||||
segmentId,
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
let modifiedAst = { ...kclManager.ast }
|
||||
let code = kclManager.code
|
||||
for (const controlPoint of segment2dInfo) {
|
||||
const range =
|
||||
engineCommandManager.artifactMap[controlPoint.segmentId].range
|
||||
if (!range) continue
|
||||
const from = controlPoint.controlPoints[0]
|
||||
const to = controlPoint.controlPoints[1]
|
||||
const modded = changeSketchArguments(
|
||||
modifiedAst,
|
||||
kclManager.programMemory,
|
||||
range,
|
||||
[to.x, to.y],
|
||||
[from.x, from.y]
|
||||
)
|
||||
modifiedAst = modded.modifiedAst
|
||||
|
||||
// update artifact map ranges now that we have updated the ast.
|
||||
code = recast(modded.modifiedAst)
|
||||
const astWithCurrentRanges = kclManager.safeParse(code)
|
||||
if (!astWithCurrentRanges) return
|
||||
const updateNode = getNodeFromPath<CallExpression>(
|
||||
astWithCurrentRanges,
|
||||
modded.pathToNode
|
||||
).node
|
||||
engineCommandManager.artifactMap[controlPoint.segmentId].range = [
|
||||
updateNode.start,
|
||||
updateNode.end,
|
||||
]
|
||||
}
|
||||
|
||||
kclManager.executeAstMock(modifiedAst, true)
|
||||
})
|
||||
void engineCommandManager.sendSceneCommand(command)
|
||||
} else {
|
||||
engineCommandManager.sendSceneCommand(command)
|
||||
}
|
||||
@ -364,6 +129,8 @@ export const Stream = ({ className = '' }) => {
|
||||
}
|
||||
|
||||
const handleMouseMove: MouseEventHandler<HTMLVideoElement> = (e) => {
|
||||
if (state.matches('Sketch')) return
|
||||
if (state.matches('Sketch no face')) return
|
||||
if (!clickCoords) return
|
||||
|
||||
const delta =
|
||||
@ -376,16 +143,19 @@ export const Stream = ({ className = '' }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="stream" className={className}>
|
||||
<div
|
||||
id="stream"
|
||||
className={className}
|
||||
onMouseUp={handleMouseUp}
|
||||
onMouseDown={handleMouseDown}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onContextMenuCapture={(e) => e.preventDefault()}
|
||||
>
|
||||
<video
|
||||
ref={videoRef}
|
||||
muted
|
||||
autoPlay
|
||||
controls={false}
|
||||
onMouseDown={handleMouseDown}
|
||||
onMouseUp={handleMouseUp}
|
||||
onContextMenu={(e) => e.preventDefault()}
|
||||
onContextMenuCapture={(e) => e.preventDefault()}
|
||||
onWheel={handleScroll}
|
||||
onPlay={() => setIsLoading(false)}
|
||||
onMouseMoveCapture={handleMouseMove}
|
||||
@ -393,6 +163,7 @@ export const Stream = ({ className = '' }) => {
|
||||
disablePictureInPicture
|
||||
style={{ transitionDuration: '200ms', transitionProperty: 'filter' }}
|
||||
/>
|
||||
<ClientSideScene cameraControls={settings.context.cameraControls} />
|
||||
{isLoading && (
|
||||
<div className="text-center absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
|
||||
<Loading>
|
||||
|
@ -3,20 +3,20 @@ import ReactCodeMirror, {
|
||||
ViewUpdate,
|
||||
keymap,
|
||||
} from '@uiw/react-codemirror'
|
||||
import { FromServer, IntoServer } from 'editor/lsp/codec'
|
||||
import Server from '../editor/lsp/server'
|
||||
import Client from '../editor/lsp/client'
|
||||
import { FromServer, IntoServer } from 'editor/plugins/lsp/codec'
|
||||
import Server from '../editor/plugins/lsp/server'
|
||||
import Client from '../editor/plugins/lsp/client'
|
||||
import { TEST } from 'env'
|
||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { useConvertToVariable } from 'hooks/useToolbarGuards'
|
||||
import { Themes } from 'lib/theme'
|
||||
import { useMemo } from 'react'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { linter, lintGutter } from '@codemirror/lint'
|
||||
import { useStore } from 'useStore'
|
||||
import { processCodeMirrorRanges } from 'lib/selections'
|
||||
import { LanguageServerClient } from 'editor/lsp'
|
||||
import kclLanguage from 'editor/lsp/language'
|
||||
import { LanguageServerClient } from 'editor/plugins/lsp'
|
||||
import kclLanguage from 'editor/plugins/lsp/kcl/language'
|
||||
import { EditorView, lineHighlightField } from 'editor/highlightextension'
|
||||
import { roundOff } from 'lib/utils'
|
||||
import { kclErrToDiagnostic } from 'lang/errors'
|
||||
@ -24,7 +24,12 @@ import { CSSRuleObject } from 'tailwindcss/types/config'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
import interact from '@replit/codemirror-interact'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { kclManager, useKclContext } from 'lang/KclSinglton'
|
||||
import { kclManager, useKclContext } from 'lang/KclSingleton'
|
||||
import { ModelingMachineEvent } from 'machines/modelingMachine'
|
||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||
import { copilotPlugin } from 'editor/plugins/lsp/copilot'
|
||||
import { isTauri } from 'lib/isTauri'
|
||||
import type * as LSP from 'vscode-languageserver-protocol'
|
||||
|
||||
export const editorShortcutMeta = {
|
||||
formatCode: {
|
||||
@ -37,6 +42,15 @@ export const editorShortcutMeta = {
|
||||
},
|
||||
}
|
||||
|
||||
function getWorkspaceFolders(): LSP.WorkspaceFolder[] {
|
||||
// We only use workspace folders in Tauri since that is where we use more than
|
||||
// one file.
|
||||
if (isTauri()) {
|
||||
return [{ uri: 'file://', name: 'ProjectRoot' }]
|
||||
}
|
||||
return []
|
||||
}
|
||||
|
||||
export const TextEditor = ({
|
||||
theme,
|
||||
}: {
|
||||
@ -44,25 +58,31 @@ export const TextEditor = ({
|
||||
}) => {
|
||||
const {
|
||||
editorView,
|
||||
isLSPServerReady,
|
||||
isKclLspServerReady,
|
||||
isCopilotLspServerReady,
|
||||
setEditorView,
|
||||
setIsLSPServerReady,
|
||||
setIsKclLspServerReady,
|
||||
setIsCopilotLspServerReady,
|
||||
isShiftDown,
|
||||
} = useStore((s) => ({
|
||||
editorView: s.editorView,
|
||||
isLSPServerReady: s.isLSPServerReady,
|
||||
isKclLspServerReady: s.isKclLspServerReady,
|
||||
isCopilotLspServerReady: s.isCopilotLspServerReady,
|
||||
setEditorView: s.setEditorView,
|
||||
setIsLSPServerReady: s.setIsLSPServerReady,
|
||||
setIsKclLspServerReady: s.setIsKclLspServerReady,
|
||||
setIsCopilotLspServerReady: s.setIsCopilotLspServerReady,
|
||||
isShiftDown: s.isShiftDown,
|
||||
}))
|
||||
const { code, errors } = useKclContext()
|
||||
const lastEvent = useRef({ event: '', time: Date.now() })
|
||||
|
||||
const {
|
||||
context: { selectionRanges, selectionRangeTypeMap },
|
||||
send,
|
||||
state,
|
||||
} = useModelingContext()
|
||||
|
||||
const { settings: { context: { textWrapping } = {} } = {} } =
|
||||
const { settings: { context: { textWrapping } = {} } = {}, auth } =
|
||||
useGlobalStateContext()
|
||||
const { commandBarSend } = useCommandsContext()
|
||||
const { enable: convertEnabled, handleClick: convertCallback } =
|
||||
@ -71,20 +91,20 @@ export const TextEditor = ({
|
||||
// So this is a bit weird, we need to initialize the lsp server and client.
|
||||
// But the server happens async so we break this into two parts.
|
||||
// Below is the client and server promise.
|
||||
const { lspClient } = useMemo(() => {
|
||||
const { lspClient: kclLspClient } = useMemo(() => {
|
||||
const intoServer: IntoServer = new IntoServer()
|
||||
const fromServer: FromServer = FromServer.create()
|
||||
const client = new Client(fromServer, intoServer)
|
||||
if (!TEST) {
|
||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||
lspServer.start()
|
||||
setIsLSPServerReady(true)
|
||||
lspServer.start('kcl')
|
||||
setIsKclLspServerReady(true)
|
||||
})
|
||||
}
|
||||
|
||||
const lspClient = new LanguageServerClient({ client })
|
||||
const lspClient = new LanguageServerClient({ client, name: 'kcl' })
|
||||
return { lspClient }
|
||||
}, [setIsLSPServerReady])
|
||||
}, [setIsKclLspServerReady])
|
||||
|
||||
// Here we initialize the plugin which will start the client.
|
||||
// When we have multi-file support the name of the file will be a dep of
|
||||
@ -93,19 +113,57 @@ export const TextEditor = ({
|
||||
// We do not want to restart the server, its just wasteful.
|
||||
const kclLSP = useMemo(() => {
|
||||
let plugin = null
|
||||
if (isLSPServerReady && !TEST) {
|
||||
if (isKclLspServerReady && !TEST) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = kclLanguage({
|
||||
// When we have more than one file, we'll need to change this.
|
||||
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
||||
workspaceFolders: null,
|
||||
client: lspClient,
|
||||
workspaceFolders: getWorkspaceFolders(),
|
||||
client: kclLspClient,
|
||||
})
|
||||
|
||||
plugin = lsp
|
||||
}
|
||||
return plugin
|
||||
}, [lspClient, isLSPServerReady])
|
||||
}, [kclLspClient, isKclLspServerReady])
|
||||
|
||||
const { lspClient: copilotLspClient } = useMemo(() => {
|
||||
const intoServer: IntoServer = new IntoServer()
|
||||
const fromServer: FromServer = FromServer.create()
|
||||
const client = new Client(fromServer, intoServer)
|
||||
if (!TEST) {
|
||||
Server.initialize(intoServer, fromServer).then((lspServer) => {
|
||||
const token = auth?.context?.token
|
||||
lspServer.start('copilot', token)
|
||||
setIsCopilotLspServerReady(true)
|
||||
})
|
||||
}
|
||||
|
||||
const lspClient = new LanguageServerClient({ client, name: 'copilot' })
|
||||
return { lspClient }
|
||||
}, [setIsCopilotLspServerReady])
|
||||
|
||||
// Here we initialize the plugin which will start the client.
|
||||
// When we have multi-file support the name of the file will be a dep of
|
||||
// this use memo, as well as the directory structure, which I think is
|
||||
// a good setup because it will restart the client but not the server :)
|
||||
// We do not want to restart the server, its just wasteful.
|
||||
const copilotLSP = useMemo(() => {
|
||||
let plugin = null
|
||||
if (isCopilotLspServerReady && !TEST) {
|
||||
// Set up the lsp plugin.
|
||||
const lsp = copilotPlugin({
|
||||
// When we have more than one file, we'll need to change this.
|
||||
documentUri: `file:///we-just-have-one-file-for-now.kcl`,
|
||||
workspaceFolders: getWorkspaceFolders(),
|
||||
client: copilotLspClient,
|
||||
allowHTMLContent: true,
|
||||
})
|
||||
|
||||
plugin = lsp
|
||||
}
|
||||
return plugin
|
||||
}, [copilotLspClient, isCopilotLspServerReady])
|
||||
|
||||
// const onChange = React.useCallback((value: string, viewUpdate: ViewUpdate) => {
|
||||
const onChange = (newCode: string) => {
|
||||
@ -115,6 +173,12 @@ export const TextEditor = ({
|
||||
if (!editorView) {
|
||||
setEditorView(viewUpdate.view)
|
||||
}
|
||||
if (sceneInfra.selected) return // mid drag
|
||||
const ignoreEvents: ModelingMachineEvent['type'][] = [
|
||||
'Equip Line tool',
|
||||
'Equip tangential arc to',
|
||||
]
|
||||
if (ignoreEvents.includes(state.event.type)) return
|
||||
const eventInfo = processCodeMirrorRanges({
|
||||
codeMirrorRanges: viewUpdate.state.selection.ranges,
|
||||
selectionRanges,
|
||||
@ -122,7 +186,20 @@ export const TextEditor = ({
|
||||
isShiftDown,
|
||||
})
|
||||
if (!eventInfo) return
|
||||
|
||||
const deterministicEventInfo = {
|
||||
...eventInfo,
|
||||
engineEvents: eventInfo.engineEvents.map((e) => ({
|
||||
...e,
|
||||
cmd_id: 'static',
|
||||
})),
|
||||
}
|
||||
const stringEvent = JSON.stringify(deterministicEventInfo)
|
||||
if (
|
||||
stringEvent === lastEvent.current.event &&
|
||||
Date.now() - lastEvent.current.time < 500
|
||||
)
|
||||
return // don't repeat events
|
||||
lastEvent.current = { event: stringEvent, time: Date.now() }
|
||||
send(eventInfo.modelingEvent)
|
||||
eventInfo.engineEvents.forEach((event) =>
|
||||
engineCommandManager.sendSceneCommand(event)
|
||||
@ -161,6 +238,7 @@ export const TextEditor = ({
|
||||
] as Extension[]
|
||||
|
||||
if (kclLSP) extensions.push(kclLSP)
|
||||
if (copilotLSP) extensions.push(copilotLSP)
|
||||
|
||||
// These extensions have proven to mess with vitest
|
||||
if (!TEST) {
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export function equalAngleInfo({
|
||||
selectionRanges,
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
getTransformInfos,
|
||||
PathToNodeMap,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export function setEqualLengthInfo({
|
||||
selectionRanges,
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
getTransformInfos,
|
||||
transformAstSketchLines,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export function horzVertInfo(
|
||||
selectionRanges: Selections,
|
||||
|
@ -15,7 +15,7 @@ import {
|
||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
getRemoveConstraintsTransforms,
|
||||
transformAstSketchLines,
|
||||
} from '../../lang/std/sketchcombos'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
export function removeConstrainingValuesInfo({
|
||||
selectionRanges,
|
||||
|
@ -19,7 +19,7 @@ import {
|
||||
createVariableDeclaration,
|
||||
} from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
const getModalInfo = createSetAngleLengthModal(SetAngleLengthModal)
|
||||
|
||||
@ -139,7 +139,7 @@ export function applyConstraintAxisAlign({
|
||||
constraint,
|
||||
}).transforms
|
||||
|
||||
let finalValue = createIdentifier('_0')
|
||||
let finalValue = createIdentifier('ZERO')
|
||||
|
||||
return transformAstSketchLines({
|
||||
ast: JSON.parse(JSON.stringify(kclManager.ast)),
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import { GetInfoModal, createInfoModal } from '../SetHorVertDistanceModal'
|
||||
import { createVariableDeclaration } from '../../lang/modifyAst'
|
||||
import { removeDoubleNegatives } from '../AvailableVarsHelpers'
|
||||
import { kclManager } from 'lang/KclSinglton'
|
||||
import { kclManager } from 'lang/KclSingleton'
|
||||
|
||||
const getModalInfo = createInfoModal(GetInfoModal)
|
||||
|
||||
|