Compare commits
117 Commits
achalmers/
...
v0.31.0
Author | SHA1 | Date | |
---|---|---|---|
96652a0c48 | |||
04e586d07b | |||
fe5f574a77 | |||
e787495ad0 | |||
8bb9be7a5e | |||
00892464e8 | |||
05ed2a3367 | |||
10cc5bce59 | |||
a32f150fc1 | |||
ac60082e67 | |||
d44dc1b21a | |||
813962ea4c | |||
738443a6ab | |||
4b6bbbe2c5 | |||
6ff8addc8b | |||
da05c38b9e | |||
191b9b71fd | |||
05163fdded | |||
7ed26e21c6 | |||
c668d40efc | |||
f38c6b90b7 | |||
7bc8bae0ec | |||
3804aca27e | |||
b127680f2f | |||
b7de8e60cf | |||
058fccb5e1 | |||
00e97257ae | |||
aeb656d176 | |||
ac49ebd6e0 | |||
b40f03ad25 | |||
a8ad86e645 | |||
87f50cd5e9 | |||
0400e6228e | |||
26f150fd6c | |||
3049f405f5 | |||
53d40301dc | |||
671c01e36f | |||
e80151979b | |||
668e2afb99 | |||
548c664db0 | |||
d3a3f4410c | |||
22eb343171 | |||
f2cfa4d5cf | |||
3f1f40eeba | |||
ff2d161606 | |||
210c78029d | |||
e27840219b | |||
c943a3f192 | |||
6aa588f09f | |||
59a6333aad | |||
403f1507ae | |||
eac7b83504 | |||
667500d1b9 | |||
b15aac9f48 | |||
54153aa646 | |||
943cf21d34 | |||
5a6728c45a | |||
ff2103d493 | |||
2dfa8f2176 | |||
29ed330326 | |||
ca2cc825a6 | |||
83fe1b7ce0 | |||
157b76cc78 | |||
cf957d880e | |||
dfc3d19677 | |||
dd370a9365 | |||
2274d6459c | |||
32ce857119 | |||
88b51da417 | |||
30d365aeb3 | |||
7af62399ac | |||
441d957228 | |||
9e57034873 | |||
eb96d6539c | |||
513c76ecc8 | |||
51d9449280 | |||
6366bc4766 | |||
7a21918223 | |||
8072f1db63 | |||
18e1855fa9 | |||
7be53c7d4a | |||
2bf20988ef | |||
1495cc6d18 | |||
f876e6ca3c | |||
60a0c811ab | |||
cab0c1e6a1 | |||
417d720b22 | |||
77293952c0 | |||
ea3d604b73 | |||
023a659491 | |||
dd3a2b14f9 | |||
424b409cc1 | |||
82a58e69c2 | |||
776b420031 | |||
1087d4223b | |||
089d6df889 | |||
efb067af58 | |||
2aa27eab01 | |||
9c47ac5b57 | |||
5ae1aecd74 | |||
68ae7e98f9 | |||
56771d561a | |||
f09411817c | |||
bed7ae3b8b | |||
c43510732c | |||
51f0b669a4 | |||
3cbedcd3e7 | |||
5d2fa43150 | |||
ec49b0752e | |||
3b171fb881 | |||
0548409da0 | |||
dd052b35fd | |||
46be4e7eef | |||
412d1b7a99 | |||
cfdd22af74 | |||
68a11e7aa5 | |||
3139e18dc7 |
35
.github/workflows/build-apps.yml
vendored
@ -165,7 +165,6 @@ jobs:
|
|||||||
- name: Build the app (release)
|
- name: Build the app (release)
|
||||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||||
env:
|
env:
|
||||||
PUBLISH_FOR_PULL_REQUEST: true
|
|
||||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||||
@ -173,7 +172,6 @@ jobs:
|
|||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
CSC_FOR_PULL_REQUEST: true
|
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
@ -229,7 +227,6 @@ jobs:
|
|||||||
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }}
|
||||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||||
CSC_FOR_PULL_REQUEST: true
|
|
||||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||||
run: yarn electron-builder --config --publish always
|
run: yarn electron-builder --config --publish always
|
||||||
|
|
||||||
@ -362,6 +359,17 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
|
- name: Set more complete nightly release notes
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
run: |
|
||||||
|
# Note: preferred going this way instead of a full clone in the checkout step,
|
||||||
|
# see https://github.com/actions/checkout/issues/1471
|
||||||
|
git fetch --prune --unshallow --tags
|
||||||
|
export TAG="nightly-${VERSION}"
|
||||||
|
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
||||||
|
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
||||||
|
yarn files:set-notes
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.7'
|
uses: 'google-github-actions/auth@v2.1.7'
|
||||||
@ -383,12 +391,17 @@ jobs:
|
|||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
- name: Create draft release
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
uses: softprops/action-gh-release@v2
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
if: ${{ env.IS_RELEASE == 'true' }}
|
run: yarn files:invalidate-bucket:nightly
|
||||||
|
|
||||||
|
- name: Tag nightly commit
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
with:
|
with:
|
||||||
name: ${{ env.VERSION }}
|
script: |
|
||||||
tag_name: ${{ env.VERSION }}
|
const { VERSION } = process.env
|
||||||
draft: true
|
const { owner, repo } = context.repo
|
||||||
generate_release_notes: true
|
const { sha } = context
|
||||||
files: 'out/Zoo*'
|
const ref = `refs/tags/nightly-${VERSION}`
|
||||||
|
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||||
|
22
.github/workflows/cargo-test.yml
vendored
@ -2,28 +2,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
paths:
|
|
||||||
- 'src/wasm-lib/**.rs'
|
|
||||||
- 'src/wasm-lib/**.hbs'
|
|
||||||
- 'src/wasm-lib/**.gen'
|
|
||||||
- 'src/wasm-lib/**.snap'
|
|
||||||
- '**/Cargo.toml'
|
|
||||||
- '**/Cargo.lock'
|
|
||||||
- '**/rust-toolchain.toml'
|
|
||||||
- 'src/wasm-lib/**.kcl'
|
|
||||||
- .github/workflows/cargo-test.yml
|
|
||||||
|
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- 'src/wasm-lib/**.rs'
|
|
||||||
- 'src/wasm-lib/**.hbs'
|
|
||||||
- 'src/wasm-lib/**.gen'
|
|
||||||
- 'src/wasm-lib/**.snap'
|
|
||||||
- '**/Cargo.toml'
|
|
||||||
- '**/Cargo.lock'
|
|
||||||
- '**/rust-toolchain.toml'
|
|
||||||
- 'src/wasm-lib/**.kcl'
|
|
||||||
- .github/workflows/cargo-test.yml
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
permissions: read-all
|
permissions: read-all
|
||||||
concurrency:
|
concurrency:
|
||||||
@ -71,7 +51,7 @@ jobs:
|
|||||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||||
RUST_MIN_STACK: 10485760000
|
RUST_MIN_STACK: 10485760000
|
||||||
- name: Upload to codecov.io
|
- name: Upload to codecov.io
|
||||||
uses: codecov/codecov-action@v4
|
uses: codecov/codecov-action@v5
|
||||||
with:
|
with:
|
||||||
token: ${{secrets.CODECOV_TOKEN}}
|
token: ${{secrets.CODECOV_TOKEN}}
|
||||||
fail_ci_if_error: true
|
fail_ci_if_error: true
|
||||||
|
4
.github/workflows/e2e-tests.yml
vendored
@ -68,7 +68,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v7
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
@ -255,7 +255,7 @@ jobs:
|
|||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
if: needs.check-rust-changes.outputs.rust-changed == 'false'
|
||||||
uses: dawidd6/action-download-artifact@v6
|
uses: dawidd6/action-download-artifact@v7
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
github_token: ${{secrets.GITHUB_TOKEN}}
|
github_token: ${{secrets.GITHUB_TOKEN}}
|
||||||
|
12
.github/workflows/publish-apps-release.yml
vendored
@ -126,11 +126,13 @@ jobs:
|
|||||||
destination: 'dl.kittycad.io/releases/modeling-app'
|
destination: 'dl.kittycad.io/releases/modeling-app'
|
||||||
|
|
||||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||||
run: |
|
run: yarn files:invalidate-bucket
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/last_download.json" --async
|
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-linux-arm64.yml" --async
|
- name: Upload release files to Github
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
|
if: ${{ github.event_name == 'release' }}
|
||||||
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
|
uses: softprops/action-gh-release@v2
|
||||||
|
with:
|
||||||
|
files: 'out/Zoo*'
|
||||||
|
|
||||||
|
|
||||||
announce_release:
|
announce_release:
|
||||||
|
1
.gitignore
vendored
@ -61,6 +61,7 @@ Mac_App_Distribution.provisionprofile
|
|||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
src/wasm-lib/pkg
|
src/wasm-lib/pkg
|
||||||
|
|
||||||
|
.eslintcache
|
||||||
venv
|
venv
|
||||||
.vite/
|
.vite/
|
||||||
|
|
||||||
|
43
INSTALL.md
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# Setting Up Zoo Modeling App
|
||||||
|
|
||||||
|
Compared to other CAD software, getting Zoo Modeling App up and running is quick and straightforward across platforms. It's about 100MB to download and is quick to install.
|
||||||
|
|
||||||
|
## Windows
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Windows and for your processor type.
|
||||||
|
|
||||||
|
2. Once downloaded, run the installer `Zoo Modeling App-{version}-{arch}-win.exe` which should take a few seconds.
|
||||||
|
|
||||||
|
3. The installation happens at `C:\Program Files\Zoo Modeling App`. A shortcut in the start menu is also created so you can run the app easily by clicking on it.
|
||||||
|
|
||||||
|
## macOS
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for macOS and for your processor type.
|
||||||
|
|
||||||
|
2. Once downloaded, open the disk image `Zoo Modeling App-{version}-{arch}-mac.dmg` and drag the applications to your `Applications` directory.
|
||||||
|
|
||||||
|
3. You can then open your `Applications` directory and double-click on `Zoo Modeling App` to open.
|
||||||
|
|
||||||
|
|
||||||
|
## Linux
|
||||||
|
|
||||||
|
1. Download the [Zoo Modeling App installer](https://zoo.dev/modeling-app/download) for Linux and for your processor type.
|
||||||
|
|
||||||
|
2. Install the dependencies needed to run the [AppImage format](https://appimage.org/).
|
||||||
|
- On Ubuntu, install the FUSE library with these commands in a terminal.
|
||||||
|
```bash
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install libfuse2
|
||||||
|
```
|
||||||
|
- Optionally, follow [these steps](https://github.com/probonopd/go-appimage/blob/master/src/appimaged/README.md#initial-setup) to install `appimaged`. It is a daemon that makes interacting with AppImage files more seamless.
|
||||||
|
- Once installed, copy the downloaded `Zoo Modeling App-{version}-{arch}-linux.AppImage` to the directory of your choice, for instance `~/Applications`.
|
||||||
|
|
||||||
|
- `appimaged` should automatically find it and make it executable. If not, run:
|
||||||
|
```bash
|
||||||
|
chmod a+x ~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
||||||
|
```
|
||||||
|
|
||||||
|
3. You can double-click on the AppImage to run it, or in a terminal with this command:
|
||||||
|
```bash
|
||||||
|
~/Applications/Zoo\ Modeling\ App-{version}-{arch}-linux.AppImage
|
||||||
|
```
|
22
README.md
@ -99,7 +99,7 @@ yarn tron:start
|
|||||||
|
|
||||||
This will start the application and hot-reload on changes.
|
This will start the application and hot-reload on changes.
|
||||||
|
|
||||||
Devtools can be opened with the usual Cmd/Ctrl-Shift-I.
|
Devtools can be opened with the usual Cmd-Opt-I (Mac) or Ctrl-Shift-I (Linux and Windows).
|
||||||
|
|
||||||
To build, run `yarn tron:package`.
|
To build, run `yarn tron:package`.
|
||||||
|
|
||||||
@ -136,7 +136,7 @@ https://github.com/KittyCAD/modeling-app/issues/new
|
|||||||
|
|
||||||
#### 2. Push a new tag
|
#### 2. Push a new tag
|
||||||
|
|
||||||
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
|
Create a new tag and push it to the repo. The `semantic-release.sh` script will automatically bump the minor part, which we use the most. For instance going from `v0.27.0` to `v0.28.0`.
|
||||||
|
|
||||||
```
|
```
|
||||||
VERSION=$(./scripts/semantic-release.sh)
|
VERSION=$(./scripts/semantic-release.sh)
|
||||||
@ -146,16 +146,14 @@ git push origin --tags
|
|||||||
|
|
||||||
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
|
||||||
|
|
||||||
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
|
The workflow should be listed right away [in this list](https://github.com/KittyCAD/modeling-app/actions/workflows/build-apps.yml?query=event%3Apush)).
|
||||||
|
|
||||||
#### 3. Manually test artifacts from the Cut Release PR
|
#### 3. Manually test artifacts
|
||||||
|
|
||||||
##### Release builds
|
##### Release builds
|
||||||
|
|
||||||
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
The release builds can be found under the `out-{arch}-{platform}` zip files, at the very bottom of the `build-apps` summary page for the workflow (triggered by the tag in 2.).
|
||||||
|
|
||||||
Alternatively, the draft release will also include these builds.
|
|
||||||
|
|
||||||
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the issue.
|
||||||
|
|
||||||
##### Updater-test builds
|
##### Updater-test builds
|
||||||
@ -178,9 +176,11 @@ If the prompt doesn't show up, start the app in command line to grab the electro
|
|||||||
|
|
||||||
#### 4. Publish the release
|
#### 4. Publish the release
|
||||||
|
|
||||||
Head over to https://github.com/KittyCAD/modeling-app/releases, paste in the changelog discussed in the issue, and publish the draft release created by the `build-apps` workflow from step 2.
|
Head over to https://github.com/KittyCAD/modeling-app/releases/new, pick the newly created tag and type it in the _Release title_ field as well.
|
||||||
|
|
||||||
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter. On success, the files will be uploaded to the public bucket and the announcement on Discord will be sent.
|
Hit _Generate release notes_ as a starting point to discuss the changelog in the issue. Once done, make sure _Set as the latest release_ is checked, and hit _Publish release_.
|
||||||
|
|
||||||
|
A new `publish-apps-release` will kick in and you should be able to find it [here](https://github.com/KittyCAD/modeling-app/actions?query=event%3Arelease). On success, the files will be uploaded to the public bucket as well as to the GitHub release, and the announcement on Discord will be sent.
|
||||||
|
|
||||||
#### 5. Close the issue
|
#### 5. Close the issue
|
||||||
|
|
||||||
@ -450,3 +450,9 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
|
|||||||
## KCL
|
## KCL
|
||||||
|
|
||||||
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
For how to contribute to KCL, [see our KCL README](https://github.com/KittyCAD/modeling-app/tree/main/src/wasm-lib/kcl).
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
|
||||||
|
To display logging (to the terminal or console) set `ZOO_LOG=1`. This will log some warnings and simple performance metrics. To view these in test runs, use `-- --nocapture`.
|
||||||
|
|
||||||
|
To enable memory metrics, build with `--features dhat-heap`.
|
||||||
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 259 KiB |
BIN
assets/icon.ico
Before Width: | Height: | Size: 183 KiB After Width: | Height: | Size: 114 KiB |
BIN
assets/icon.png
Before Width: | Height: | Size: 113 KiB After Width: | Height: | Size: 113 KiB |
@ -22,3 +22,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
|
|
||||||
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
|
- **Chamfers**: Chamfers cannot intersect, you will get an error. Only simple
|
||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
|
- **Appearance**: Changing the appearance on a loft does not work.
|
||||||
|
239
docs/kcl/appearance.md
Normal file
49
docs/kcl/atan2.md
Normal file
@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Sketch on the face of a chamfer.
|
// Sketch on the face of a chamfer.
|
||||||
fn cube = (pos, scale) => {
|
fn cube(pos, scale) {
|
||||||
sg = startSketchOn('XY')
|
sg = startSketchOn('XY')
|
||||||
|> startProfileAt(pos, %)
|
|> startProfileAt(pos, %)
|
||||||
|> line([0, scale], %)
|
|> line([0, scale], %)
|
||||||
|
@ -19,6 +19,7 @@ layout: manual
|
|||||||
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
* [`angledLineThatIntersects`](kcl/angledLineThatIntersects)
|
||||||
* [`angledLineToX`](kcl/angledLineToX)
|
* [`angledLineToX`](kcl/angledLineToX)
|
||||||
* [`angledLineToY`](kcl/angledLineToY)
|
* [`angledLineToY`](kcl/angledLineToY)
|
||||||
|
* [`appearance`](kcl/appearance)
|
||||||
* [`arc`](kcl/arc)
|
* [`arc`](kcl/arc)
|
||||||
* [`arcTo`](kcl/arcTo)
|
* [`arcTo`](kcl/arcTo)
|
||||||
* [`asin`](kcl/asin)
|
* [`asin`](kcl/asin)
|
||||||
@ -29,6 +30,7 @@ layout: manual
|
|||||||
* [`assertLessThan`](kcl/assertLessThan)
|
* [`assertLessThan`](kcl/assertLessThan)
|
||||||
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
* [`assertLessThanOrEq`](kcl/assertLessThanOrEq)
|
||||||
* [`atan`](kcl/atan)
|
* [`atan`](kcl/atan)
|
||||||
|
* [`atan2`](kcl/atan2)
|
||||||
* [`bezierCurve`](kcl/bezierCurve)
|
* [`bezierCurve`](kcl/bezierCurve)
|
||||||
* [`ceil`](kcl/ceil)
|
* [`ceil`](kcl/ceil)
|
||||||
* [`chamfer`](kcl/chamfer)
|
* [`chamfer`](kcl/chamfer)
|
||||||
@ -101,6 +103,7 @@ layout: manual
|
|||||||
* [`startProfileAt`](kcl/startProfileAt)
|
* [`startProfileAt`](kcl/startProfileAt)
|
||||||
* [`startSketchAt`](kcl/startSketchAt)
|
* [`startSketchAt`](kcl/startSketchAt)
|
||||||
* [`startSketchOn`](kcl/startSketchOn)
|
* [`startSketchOn`](kcl/startSketchOn)
|
||||||
|
* [`sweep`](kcl/sweep)
|
||||||
* [`tan`](kcl/tan)
|
* [`tan`](kcl/tan)
|
||||||
* [`tangentToEnd`](kcl/tangentToEnd)
|
* [`tangentToEnd`](kcl/tangentToEnd)
|
||||||
* [`tangentialArc`](kcl/tangentialArc)
|
* [`tangentialArc`](kcl/tangentialArc)
|
||||||
|
@ -37,7 +37,7 @@ assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
|
|||||||
startSketchOn('XZ')
|
startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 2 }, %)
|
|> circle({ center = [0, 0], radius = 2 }, %)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|> patternTransform(n, (id) => {
|
|> patternTransform(n, fn(id) {
|
||||||
return { translate = [4 * id, 0, 0] }
|
return { translate = [4 * id, 0, 0] }
|
||||||
}, %)
|
}, %)
|
||||||
```
|
```
|
||||||
|
@ -29,7 +29,7 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
fn drawCircle = (id) => {
|
fn drawCircle(id) {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ circles = map([1..3], drawCircle)
|
|||||||
```js
|
```js
|
||||||
r = 10 // radius
|
r = 10 // radius
|
||||||
// Call `map`, using an anonymous function instead of a named one.
|
// Call `map`, using an anonymous function instead of a named one.
|
||||||
circles = map([1..3], (id) => {
|
circles = map([1..3], fn(id) {
|
||||||
return startSketchOn("XY")
|
return startSketchOn("XY")
|
||||||
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
|
||||||
})
|
})
|
||||||
|
@ -12,7 +12,7 @@ to other modules.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decrement = (x) => {
|
export fn decrement(x) {
|
||||||
return x - 1
|
return x - 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -30,7 +30,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Each instance will be shifted along the X axis.
|
// Each instance will be shifted along the X axis.
|
||||||
fn transform = (id) => {
|
fn transform(id) {
|
||||||
return { translate = [4 * id, 0] }
|
return { translate = [4 * id, 0] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,20 +30,20 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// This function adds two numbers.
|
// This function adds two numbers.
|
||||||
fn add = (a, b) => {
|
fn add(a, b) {
|
||||||
return a + b
|
return a + b
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function adds an array of numbers.
|
// This function adds an array of numbers.
|
||||||
// It uses the `reduce` function, to call the `add` function on every
|
// It uses the `reduce` function, to call the `add` function on every
|
||||||
// element of the `arr` parameter. The starting value is 0.
|
// element of the `arr` parameter. The starting value is 0.
|
||||||
fn sum = (arr) => {
|
fn sum(arr) {
|
||||||
return reduce(arr, 0, add)
|
return reduce(arr, 0, add)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The above is basically like this pseudo-code:
|
/* The above is basically like this pseudo-code:
|
||||||
fn sum(arr):
|
fn sum(arr):
|
||||||
let sumSoFar = 0
|
sumSoFar = 0
|
||||||
for i in arr:
|
for i in arr:
|
||||||
sumSoFar = add(sumSoFar, i)
|
sumSoFar = add(sumSoFar, i)
|
||||||
return sumSoFar */
|
return sumSoFar */
|
||||||
@ -61,7 +61,7 @@ assertEqual(sum([1, 2, 3]), 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
// an anonymous `add` function as its parameter, instead of declaring a
|
// an anonymous `add` function as its parameter, instead of declaring a
|
||||||
// named function outside.
|
// named function outside.
|
||||||
arr = [1, 2, 3]
|
arr = [1, 2, 3]
|
||||||
sum = reduce(arr, 0, (i, result_so_far) => {
|
sum = reduce(arr, 0, fn(i, result_so_far) {
|
||||||
return i + result_so_far
|
return i + result_so_far
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ assertEqual(sum, 6, 0.00001, "1 + 2 + 3 summed is 6")
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Declare a function that sketches a decagon.
|
// Declare a function that sketches a decagon.
|
||||||
fn decagon = (radius) => {
|
fn decagon(radius) {
|
||||||
// Each side of the decagon is turned this many degrees from the previous angle.
|
// Each side of the decagon is turned this many degrees from the previous angle.
|
||||||
stepAngle = 1 / 10 * tau()
|
stepAngle = 1 / 10 * tau()
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ fn decagon = (radius) => {
|
|||||||
// Use a `reduce` to draw the remaining decagon sides.
|
// Use a `reduce` to draw the remaining decagon sides.
|
||||||
// For each number in the array 1..10, run the given function,
|
// For each number in the array 1..10, run the given function,
|
||||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||||
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
|
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
|
||||||
// Draw one edge of the decagon.
|
// Draw one edge of the decagon.
|
||||||
x = cos(stepAngle * i) * radius
|
x = cos(stepAngle * i) * radius
|
||||||
y = sin(stepAngle * i) * radius
|
y = sin(stepAngle * i) * radius
|
||||||
@ -96,14 +96,14 @@ fn decagon = (radius) => {
|
|||||||
|
|
||||||
/* The `decagon` above is basically like this pseudo-code:
|
/* The `decagon` above is basically like this pseudo-code:
|
||||||
fn decagon(radius):
|
fn decagon(radius):
|
||||||
let stepAngle = (1/10) * tau()
|
stepAngle = (1/10) * tau()
|
||||||
let startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||||
|
|
||||||
// Here's the reduce part.
|
// Here's the reduce part.
|
||||||
let partialDecagon = startOfDecagonSketch
|
partialDecagon = startOfDecagonSketch
|
||||||
for i in [1..10]:
|
for i in [1..10]:
|
||||||
let x = cos(stepAngle * i) * radius
|
x = cos(stepAngle * i) * radius
|
||||||
let y = sin(stepAngle * i) * radius
|
y = sin(stepAngle * i) * radius
|
||||||
partialDecagon = lineTo([x, y], partialDecagon)
|
partialDecagon = lineTo([x, y], partialDecagon)
|
||||||
fullDecagon = partialDecagon // it's now full
|
fullDecagon = partialDecagon // it's now full
|
||||||
return fullDecagon */
|
return fullDecagon */
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder = (radius, tag) => {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
@ -36,7 +36,7 @@ cube = startSketchAt([0, 0])
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(5, %)
|
|> extrude(5, %)
|
||||||
|
|
||||||
fn cylinder = (radius, tag) => {
|
fn cylinder(radius, tag) {
|
||||||
return startSketchAt([0, 0])
|
return startSketchAt([0, 0])
|
||||||
|> circle({
|
|> circle({
|
||||||
radius = radius,
|
radius = radius,
|
||||||
|
85002
docs/kcl/std.json
55
docs/kcl/sweep.md
Normal file
@ -41,7 +41,7 @@ If you want to get a value from an array you can use the index like so:
|
|||||||
An object is defined with `{}` braces. Here is an example object:
|
An object is defined with `{}` braces. Here is an example object:
|
||||||
|
|
||||||
```
|
```
|
||||||
myObj = {a: 0, b: "thing"}
|
myObj = { a = 0, b = "thing" }
|
||||||
```
|
```
|
||||||
|
|
||||||
We support two different ways of getting properties from objects, you can call
|
We support two different ways of getting properties from objects, you can call
|
||||||
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
|||||||
type of argument. Below is an example of the syntax:
|
type of argument. Below is an example of the syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn myFn = (x) => {
|
fn myFn(x) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -90,12 +90,12 @@ startSketchOn('XZ')
|
|||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %) - 90,
|
segAng(rectangleSegmentA001) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %),
|
segAng(rectangleSegmentA001),
|
||||||
-segLen(rectangleSegmentA001, %)
|
-segLen(rectangleSegmentA001)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -118,17 +118,17 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
|||||||
However if the code was written like this:
|
However if the code was written like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %) - 90,
|
segAng(rectangleSegmentA001) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %),
|
segAng(rectangleSegmentA001),
|
||||||
-segLen(rectangleSegmentA001, %)
|
-segLen(rectangleSegmentA001)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -146,17 +146,17 @@ Tags are accessible through the sketch group they are declared in.
|
|||||||
For example the following code works.
|
For example the following code works.
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %) - 90,
|
segAng(rectangleSegmentA001) - 90,
|
||||||
196.99
|
196.99
|
||||||
], %, $rectangleSegmentB001)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA001, %),
|
segAng(rectangleSegmentA001),
|
||||||
-segLen(rectangleSegmentA001, %)
|
-segLen(rectangleSegmentA001)
|
||||||
], %, $rectangleSegmentC001)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
@ -167,7 +167,10 @@ myRect = rect([20, 0])
|
|||||||
|
|
||||||
myRect
|
myRect
|
||||||
|> extrude(10, %)
|
|> extrude(10, %)
|
||||||
|> fillet({radius: 0.5, tags: [myRect.tags.rectangleSegmentA001]}, %)
|
|> fillet({
|
||||||
|
radius = 0.5,
|
||||||
|
tags = [myRect.tags.rectangleSegmentA001]
|
||||||
|
}, %)
|
||||||
```
|
```
|
||||||
|
|
||||||
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
See how we use the tag `rectangleSegmentA001` in the `fillet` function outside
|
||||||
|
23
docs/kcl/types/AppearanceData.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "AppearanceData"
|
||||||
|
excerpt: "Data for appearance."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for appearance.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `color` |`string`| Color of the new material, a hex string like "#ff0000". | No |
|
||||||
|
| `metalness` |`number` (**maximum:** 100.0)| Metalness of the new material, a percentage like 95.7. | No |
|
||||||
|
| `roughness` |`number` (**maximum:** 100.0)| Roughness of the new material, a percentage like 95.7. | No |
|
||||||
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BinaryOperator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Add two numbers.
|
|
||||||
|
|
||||||
**enum:** `+`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Subtract two numbers.
|
|
||||||
|
|
||||||
**enum:** `-`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Multiply two numbers.
|
|
||||||
|
|
||||||
**enum:** `*`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Divide two numbers.
|
|
||||||
|
|
||||||
**enum:** `/`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Modulo two numbers.
|
|
||||||
|
|
||||||
**enum:** `%`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Raise a number to a power.
|
|
||||||
|
|
||||||
**enum:** `^`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Are two numbers equal?
|
|
||||||
|
|
||||||
**enum:** `==`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Are two numbers not equal?
|
|
||||||
|
|
||||||
**enum:** `!=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left greater than right
|
|
||||||
|
|
||||||
**enum:** `>`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left greater than or equal to right
|
|
||||||
|
|
||||||
**enum:** `>=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left less than right
|
|
||||||
|
|
||||||
**enum:** `<`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Is left less than or equal to right
|
|
||||||
|
|
||||||
**enum:** `<=`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,161 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BinaryPart"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| | No |
|
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `optional` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| | No |
|
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,97 +0,0 @@
|
|||||||
---
|
|
||||||
title: "BodyItem"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ImportStatement`| | No |
|
|
||||||
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
|
||||||
| `path` |`string`| | No |
|
|
||||||
| `raw_path` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ExpressionStatement`| | No |
|
|
||||||
| `expression` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `VariableDeclaration`| | No |
|
|
||||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
|
||||||
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
|
||||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ReturnStatement`| | No |
|
|
||||||
| `argument` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "CommentStyle"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Like // foo
|
|
||||||
|
|
||||||
**enum:** `line`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Like /* foo */
|
|
||||||
|
|
||||||
**enum:** `block`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ElseIf"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "EnvironmentRef"
|
|
||||||
excerpt: "An index pointing to an environment."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
An index pointing to an environment.
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,318 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Expr"
|
|
||||||
excerpt: "An expression can be evaluated to yield a single KCL value."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
An expression can be evaluated to yield a single KCL value.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`TagDeclarator`](/docs/kcl/types#tag-declaration)| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `BinaryExpression`| | No |
|
|
||||||
| `operator` |[`BinaryOperator`](/docs/kcl/types/BinaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `left` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `right` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`FunctionExpression`](/docs/kcl/types/FunctionExpression)| | No |
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `CallExpression`| | No |
|
|
||||||
| `callee` |[`Identifier`](/docs/kcl/types/Identifier)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `arguments` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `optional` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `PipeExpression`| | No |
|
|
||||||
| `body` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `PipeSubstitution`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ArrayExpression`| | No |
|
|
||||||
| `elements` |`[` [`Expr`](/docs/kcl/types/Expr) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ArrayRangeExpression`| | No |
|
|
||||||
| `startElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `endElement` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `endInclusive` |`boolean`| Is the `end_element` included in the range? | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `ObjectExpression`| | No |
|
|
||||||
| `properties` |`[` [`ObjectProperty`](/docs/kcl/types/ObjectProperty) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `UnaryExpression`| | No |
|
|
||||||
| `operator` |[`UnaryOperator`](/docs/kcl/types/UnaryOperator)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `argument` |[`BinaryPart`](/docs/kcl/types/BinaryPart)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `IfExpression`| | No |
|
|
||||||
| `cond` |[`Expr`](/docs/kcl/types/Expr)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `then_val` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `else_ifs` |`[` [`ElseIf`](/docs/kcl/types/ElseIf) `]`| | No |
|
|
||||||
| `final_else` |[`Program`](/docs/kcl/types/Program)| An expression can be evaluated to yield a single KCL value. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `None`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "FunctionExpression"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `params` |`[` [`Parameter`](/docs/kcl/types/Parameter) `]`| | No |
|
|
||||||
| `body` |[`Program`](/docs/kcl/types/Program)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Identifier"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ImportItem"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
|
||||||
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ItemVisibility"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**enum:** `default`, `export`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,5 +12,10 @@ KCL value for an optional parameter which was not given an argument. (remember,
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -317,7 +317,6 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Function`| | No |
|
| `type` |enum: `Function`| | No |
|
||||||
| `expression` |[`FunctionExpression`](/docs/kcl/types/FunctionExpression)| Any KCL value. | No |
|
|
||||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
---
|
|
||||||
title: "LiteralIdentifier"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Literal`| | No |
|
|
||||||
| `value` |[`LiteralValue`](/docs/kcl/types/LiteralValue)| | No |
|
|
||||||
| `raw` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "LiteralValue"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `number` (`double`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `string`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `boolean`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
title: "MemberObject"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `MemberExpression`| | No |
|
|
||||||
| `object` |[`MemberObject`](/docs/kcl/types/MemberObject)| | No |
|
|
||||||
| `property` |[`LiteralIdentifier`](/docs/kcl/types/LiteralIdentifier)| | No |
|
|
||||||
| `computed` |`boolean`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: [`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `name` |`string`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeMeta"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `nonCodeNodes` |`object`| | No |
|
|
||||||
| `startNodes` |`[` [`NonCodeNode`](/docs/kcl/types/NonCodeNode) `]`| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeNode"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `value` |[`NonCodeValue`](/docs/kcl/types/NonCodeValue)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NonCodeValue"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `inlineComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A block comment. An example of this is the following: ```python,no_run /* This is a block comment */ 1 + 1 ``` Now this is important. The block comment is attached to the next line. This is always the case. Also the block comment doesn't have a new line above it. If it did it would be a `NewLineBlockComment`.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `blockComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A block comment that has a new line above it. The user explicitly added a new line above the block comment.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `newLineBlockComment`| | No |
|
|
||||||
| `value` |`string`| | No |
|
|
||||||
| `style` |[`CommentStyle`](/docs/kcl/types/CommentStyle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `newLine`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "ObjectProperty"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `key` |[`Identifier`](/docs/kcl/types/Identifier)| | No |
|
|
||||||
| `value` |[`Expr`](/docs/kcl/types/Expr)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Parameter"
|
|
||||||
excerpt: "Parameter of a KCL function."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Parameter of a KCL function.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `identifier` |[`Identifier`](/docs/kcl/types/Identifier)| The parameter's label or name. | No |
|
|
||||||
| `optional` |`boolean`| Is the parameter optional? | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Program"
|
|
||||||
excerpt: "A KCL program top level, or function body."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A KCL program top level, or function body.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `body` |`[` [`BodyItem`](/docs/kcl/types/BodyItem) `]`| | No |
|
|
||||||
| `nonCodeMeta` |[`NonCodeMeta`](/docs/kcl/types/NonCodeMeta)| A KCL program top level, or function body. | No |
|
|
||||||
| `shebang` |[`Shebang`](/docs/kcl/types/Shebang)| | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Shebang"
|
|
||||||
excerpt: "A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```"
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A shebang. This is a special type of comment that is at the top of the file. It looks like this: ```python,no_run #!/usr/bin/env python ```
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `content` |`string`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
23
docs/kcl/types/SweepData.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
---
|
||||||
|
title: "SweepData"
|
||||||
|
excerpt: "Data for a sweep."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for a sweep.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| The path to sweep along. | No |
|
||||||
|
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
|
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
||||||
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Uint"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint32`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "UnaryOperator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Negate a number.
|
|
||||||
|
|
||||||
**enum:** `-`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Negate a boolean.
|
|
||||||
|
|
||||||
**enum:** `!`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
---
|
|
||||||
title: "VariableDeclarator"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `id` |[`Identifier`](/docs/kcl/types/Identifier)| The identifier of the variable. | No |
|
|
||||||
| `init` |[`Expr`](/docs/kcl/types/Expr)| The value of the variable. | No |
|
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
|
||||||
| `start` |`integer`| | No |
|
|
||||||
| `end` |`integer`| | No |
|
|
||||||
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
|||||||
---
|
|
||||||
title: "VariableKind"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
Declare a named constant.
|
|
||||||
|
|
||||||
**enum:** `const`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Declare a function.
|
|
||||||
|
|
||||||
**enum:** `fn`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -57,23 +57,26 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator).toContainText(
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||||
} else {
|
} else {
|
||||||
@ -82,8 +85,10 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||||
@ -140,8 +145,10 @@ async function doBasicSketch(page: Page, openPanes: string[]) {
|
|||||||
|
|
||||||
// Open the code pane.
|
// Open the code pane.
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %, $seg01)
|
|> xLine(${commonPoints.num1}, %, $seg01)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(-segLen(seg01), %)`)
|
|> xLine(-segLen(seg01), %)`)
|
||||||
|
@ -44,8 +44,7 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn('${plane}')
|
const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.9, -1.22], sketch001)`
|
||||||
|> startProfileAt([0.9, -1.22], %)`
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
@ -94,6 +94,51 @@ test.describe('Editor tests', () => {
|
|||||||
|> close(%)`)
|
|> close(%)`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('ensure we use the cache, and do not re-execute', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1000, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.type(`sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-10, -10], %)
|
||||||
|
|> line([20, 0], %)
|
||||||
|
|> line([0, 20], %)
|
||||||
|
|> line([-20, 0], %)
|
||||||
|
|> close(%)`)
|
||||||
|
|
||||||
|
// Ensure we execute the first time.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||||
|
).toHaveCount(2)
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-message-type="execution-done"]')
|
||||||
|
).toHaveCount(2)
|
||||||
|
|
||||||
|
// Add whitespace to the end of the code.
|
||||||
|
await u.codeLocator.click()
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await page.keyboard.press('Home')
|
||||||
|
await page.keyboard.type(' ')
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
await page.keyboard.type(' ')
|
||||||
|
|
||||||
|
// Ensure we don't execute the second time.
|
||||||
|
await u.openDebugPanel()
|
||||||
|
// Make sure we didn't clear the scene.
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-message-type="execution-done"]')
|
||||||
|
).toHaveCount(3)
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-receive-command-type="scene_clear_all"]')
|
||||||
|
).toHaveCount(2)
|
||||||
|
})
|
||||||
|
|
||||||
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
test('if you click the format button it formats your code and executes so lints are still there', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
@ -458,8 +503,8 @@ test.describe('Editor tests', () => {
|
|||||||
|
|
||||||
/* add the following code to the editor ($ error is not a valid line)
|
/* add the following code to the editor ($ error is not a valid line)
|
||||||
$ error
|
$ error
|
||||||
const topAng = 30
|
topAng = 30
|
||||||
const bottomAng = 25
|
bottomAng = 25
|
||||||
*/
|
*/
|
||||||
await u.codeLocator.click()
|
await u.codeLocator.click()
|
||||||
await page.keyboard.type('$ error')
|
await page.keyboard.type('$ error')
|
||||||
@ -474,12 +519,14 @@ test.describe('Editor tests', () => {
|
|||||||
await page.keyboard.type('bottomAng = 25')
|
await page.keyboard.type('bottomAng = 25')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// error in guter
|
// error in gutter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
// error text on hover
|
// error text on hover
|
||||||
await page.hover('.cm-lint-marker-error')
|
await page.hover('.cm-lint-marker-error')
|
||||||
await expect(page.getByText('Unexpected token: $').first()).toBeVisible()
|
await expect(
|
||||||
|
page.getByText('Tag names must not be empty').first()
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
// select the line that's causing the error and delete it
|
// select the line that's causing the error and delete it
|
||||||
await page.getByText('$ error').click()
|
await page.getByText('$ error').click()
|
||||||
@ -518,7 +565,10 @@ test.describe('Editor tests', () => {
|
|||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
test('error with 2 source ranges gets 2 diagnostics', async ({ page }) => {
|
// TODO currently multiple source ranges are not supported
|
||||||
|
test.skip('error with 2 source ranges gets 2 diagnostics', async ({
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -45,7 +45,6 @@ test.describe('integrations tests', () => {
|
|||||||
{
|
{
|
||||||
title: 'test-sample',
|
title: 'test-sample',
|
||||||
fileCount: 1,
|
fileCount: 1,
|
||||||
folderCount: 1,
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
@ -233,7 +232,6 @@ test.describe('when using the file tree to', () => {
|
|||||||
{
|
{
|
||||||
title: projectName,
|
title: projectName,
|
||||||
fileCount: 2,
|
fileCount: 2,
|
||||||
folderCount: 2, // TODO: This is a pre-existing bug, there are no folders within the project
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
sortBy: 'last-modified-desc',
|
sortBy: 'last-modified-desc',
|
||||||
|
@ -4,7 +4,6 @@ import { expect } from '@playwright/test'
|
|||||||
interface ProjectCardState {
|
interface ProjectCardState {
|
||||||
title: string
|
title: string
|
||||||
fileCount: number
|
fileCount: number
|
||||||
folderCount: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface HomePageState {
|
interface HomePageState {
|
||||||
@ -61,15 +60,13 @@ export class HomePageFixture {
|
|||||||
const projectCards = await this.projectCard.all()
|
const projectCards = await this.projectCard.all()
|
||||||
const projectCardStates: Array<ProjectCardState> = []
|
const projectCardStates: Array<ProjectCardState> = []
|
||||||
for (const projectCard of projectCards) {
|
for (const projectCard of projectCards) {
|
||||||
const [title, fileCount, folderCount] = await Promise.all([
|
const [title, fileCount] = await Promise.all([
|
||||||
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
(await projectCard.locator(this.projectCardTitle).textContent()) || '',
|
||||||
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
Number(await projectCard.locator(this.projectCardFile).textContent()),
|
||||||
Number(await projectCard.locator(this.projectCardFolder).textContent()),
|
|
||||||
])
|
])
|
||||||
projectCardStates.push({
|
projectCardStates.push({
|
||||||
title: title,
|
title: title,
|
||||||
fileCount,
|
fileCount,
|
||||||
folderCount,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return projectCardStates
|
return projectCardStates
|
||||||
|
@ -11,6 +11,7 @@ import {
|
|||||||
|
|
||||||
type mouseParams = {
|
type mouseParams = {
|
||||||
pixelDiff?: number
|
pixelDiff?: number
|
||||||
|
shouldDbClick?: boolean
|
||||||
}
|
}
|
||||||
type mouseDragToParams = mouseParams & {
|
type mouseDragToParams = mouseParams & {
|
||||||
fromPoint: { x: number; y: number }
|
fromPoint: { x: number; y: number }
|
||||||
@ -28,6 +29,7 @@ type SceneSerialised = {
|
|||||||
|
|
||||||
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||||
|
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||||
type DragFromHandler = (
|
type DragFromHandler = (
|
||||||
dragParams: mouseDragFromParams
|
dragParams: mouseDragFromParams
|
||||||
@ -68,17 +70,22 @@ export class SceneFixture {
|
|||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [ClickHandler, MoveHandler] =>
|
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
() => this.page.mouse.click(x, y),
|
() =>
|
||||||
|
clickParams?.shouldDbClick
|
||||||
|
? this.page.mouse.dblclick(x, y)
|
||||||
|
: this.page.mouse.click(x, y),
|
||||||
clickParams.pixelDiff
|
clickParams.pixelDiff
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return this.page.mouse.click(x, y)
|
return clickParams?.shouldDbClick
|
||||||
|
? this.page.mouse.dblclick(x, y)
|
||||||
|
: this.page.mouse.click(x, y)
|
||||||
},
|
},
|
||||||
(moveParams?: mouseParams) => {
|
(moveParams?: mouseParams) => {
|
||||||
if (moveParams?.pixelDiff) {
|
if (moveParams?.pixelDiff) {
|
||||||
@ -90,6 +97,16 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
|
(clickParams?: mouseParams) => {
|
||||||
|
if (clickParams?.pixelDiff) {
|
||||||
|
return doAndWaitForImageDiff(
|
||||||
|
this.page,
|
||||||
|
() => this.page.mouse.dblclick(x, y),
|
||||||
|
clickParams.pixelDiff
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return this.page.mouse.dblclick(x, y)
|
||||||
|
},
|
||||||
] as const
|
] as const
|
||||||
makeDragHelpers = (
|
makeDragHelpers = (
|
||||||
x: number,
|
x: number,
|
||||||
@ -203,23 +220,7 @@ export class SceneFixture {
|
|||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) => {
|
) => {
|
||||||
let finalValue = colour
|
await expectPixelColor(this.page, colour, coords, diff)
|
||||||
await expect
|
|
||||||
.poll(async () => {
|
|
||||||
const pixel = (await getPixelRGBs(this.page)(coords, 1))[0]
|
|
||||||
if (!pixel) return null
|
|
||||||
finalValue = pixel
|
|
||||||
return pixel.every(
|
|
||||||
(channel, index) => Math.abs(channel - colour[index]) < diff
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.toBeTruthy()
|
|
||||||
.catch((cause) => {
|
|
||||||
throw new Error(
|
|
||||||
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
|
||||||
{ cause }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get gizmo() {
|
get gizmo() {
|
||||||
@ -235,3 +236,28 @@ export class SceneFixture {
|
|||||||
await buttonToTest.click()
|
await buttonToTest.click()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function expectPixelColor(
|
||||||
|
page: Page,
|
||||||
|
colour: [number, number, number],
|
||||||
|
coords: { x: number; y: number },
|
||||||
|
diff: number
|
||||||
|
) {
|
||||||
|
let finalValue = colour
|
||||||
|
await expect
|
||||||
|
.poll(async () => {
|
||||||
|
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
||||||
|
if (!pixel) return null
|
||||||
|
finalValue = pixel
|
||||||
|
return pixel.every(
|
||||||
|
(channel, index) => Math.abs(channel - colour[index]) < diff
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.toBeTruthy()
|
||||||
|
.catch((cause) => {
|
||||||
|
throw new Error(
|
||||||
|
`ExpectPixelColor: expecting ${colour} got ${finalValue}`,
|
||||||
|
{ cause }
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -6,10 +6,15 @@ export class ToolbarFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
|
loftButton!: Locator
|
||||||
|
shellButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
|
tangentialArcBtn!: Locator
|
||||||
|
circleBtn!: Locator
|
||||||
rectangleBtn!: Locator
|
rectangleBtn!: Locator
|
||||||
|
lengthConstraintBtn!: Locator
|
||||||
exitSketchBtn!: Locator
|
exitSketchBtn!: Locator
|
||||||
editSketchBtn!: Locator
|
editSketchBtn!: Locator
|
||||||
fileTreeBtn!: Locator
|
fileTreeBtn!: Locator
|
||||||
@ -26,10 +31,15 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
|
this.loftButton = page.getByTestId('loft')
|
||||||
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
this.tangentialArcBtn = page.getByTestId('tangential-arc')
|
||||||
|
this.circleBtn = page.getByTestId('circle-center')
|
||||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||||
|
this.lengthConstraintBtn = page.getByTestId('constraint-length')
|
||||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||||
@ -87,4 +97,13 @@ export class ToolbarFixture {
|
|||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectCenterRectangle = async () => {
|
||||||
|
await this.page
|
||||||
|
.getByRole('button', { name: 'caret down Corner rectangle:' })
|
||||||
|
.click()
|
||||||
|
await expect(
|
||||||
|
this.page.getByTestId('dropdown-center-rectangle')
|
||||||
|
).toBeVisible()
|
||||||
|
await this.page.getByTestId('dropdown-center-rectangle').click()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
TEST_SETTINGS_ONBOARDING_USER_MENU,
|
||||||
} from './storageStates'
|
} from './storageStates'
|
||||||
import * as TOML from '@iarna/toml'
|
import * as TOML from '@iarna/toml'
|
||||||
|
import { expectPixelColor } from './fixtures/sceneFixture'
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||||
if (testInfo.tags.includes('@electron')) {
|
if (testInfo.tags.includes('@electron')) {
|
||||||
@ -45,7 +46,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
{ settingsKey: TEST_SETTINGS_KEY }
|
{ settingsKey: TEST_SETTINGS_KEY }
|
||||||
)
|
)
|
||||||
|
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||||
|
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
@ -54,6 +55,12 @@ test.describe('Onboarding tests', () => {
|
|||||||
|
|
||||||
// *and* that the code is shown in the editor
|
// *and* that the code is shown in the editor
|
||||||
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
await expect(page.locator('.cm-content')).toContainText('// Shelf Bracket')
|
||||||
|
|
||||||
|
// Make sure the model loaded
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const modelColor: [number, number, number] = [45, 45, 45]
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
expect(await u.getGreatestPixDiff(XYPlanePoint, modelColor)).toBeLessThan(8)
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test(
|
||||||
@ -72,7 +79,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 1000 }
|
||||||
await page.setViewportSize(viewportSize)
|
await page.setViewportSize(viewportSize)
|
||||||
|
|
||||||
await test.step(`Create a project and open to the onboarding`, async () => {
|
await test.step(`Create a project and open to the onboarding`, async () => {
|
||||||
@ -92,6 +99,14 @@ test.describe('Onboarding tests', () => {
|
|||||||
await expect(page.locator('.cm-content')).toContainText(
|
await expect(page.locator('.cm-content')).toContainText(
|
||||||
'// Shelf Bracket'
|
'// Shelf Bracket'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: jess make less shit
|
||||||
|
// Make sure the model loaded
|
||||||
|
//const XYPlanePoint = { x: 986, y: 522 } as const
|
||||||
|
//const modelColor: [number, number, number] = [76, 76, 76]
|
||||||
|
//await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
|
||||||
|
//await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||||
})
|
})
|
||||||
|
|
||||||
await electronApp.close()
|
await electronApp.close()
|
||||||
@ -108,7 +123,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
|
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||||
await u.waitForAuthSkipAppStart()
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
// Replay the onboarding
|
// Replay the onboarding
|
||||||
@ -140,6 +155,12 @@ test.describe('Onboarding tests', () => {
|
|||||||
return localStorage.getItem('persistCode')
|
return localStorage.getItem('persistCode')
|
||||||
})
|
})
|
||||||
).toContain('// Shelf Bracket')
|
).toContain('// Shelf Bracket')
|
||||||
|
|
||||||
|
// Make sure the model loaded
|
||||||
|
const XYPlanePoint = { x: 986, y: 522 } as const
|
||||||
|
const modelColor: [number, number, number] = [76, 76, 76]
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Click through each onboarding step', async ({ page }) => {
|
test('Click through each onboarding step', async ({ page }) => {
|
||||||
@ -179,6 +200,17 @@ test.describe('Onboarding tests', () => {
|
|||||||
// Test that the onboarding pane is gone
|
// Test that the onboarding pane is gone
|
||||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||||
await expect(page.url()).not.toContain('onboarding')
|
await expect(page.url()).not.toContain('onboarding')
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// TODO: jess to fix
|
||||||
|
// Make sure the model loaded
|
||||||
|
//const XYPlanePoint = { x: 774, y: 516 } as const
|
||||||
|
// const modelColor: [number, number, number] = [129, 129, 129]
|
||||||
|
// await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
// await expectPixelColor(page, modelColor, XYPlanePoint, 20)
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Onboarding redirects and code updating', async ({ page }) => {
|
test('Onboarding redirects and code updating', async ({ page }) => {
|
||||||
@ -439,7 +471,7 @@ test(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Navigate into project', async () => {
|
await test.step('Navigate into project', async () => {
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||||
|
|
||||||
page.on('console', console.log)
|
page.on('console', console.log)
|
||||||
|
|
||||||
@ -462,7 +494,15 @@ test(
|
|||||||
await test.step('Confirm that the onboarding has restarted', async () => {
|
await test.step('Confirm that the onboarding has restarted', async () => {
|
||||||
await expect(tutorialProjectIndicator).toBeVisible()
|
await expect(tutorialProjectIndicator).toBeVisible()
|
||||||
await expect(tutorialModalText).toBeVisible()
|
await expect(tutorialModalText).toBeVisible()
|
||||||
|
// Make sure the model loaded
|
||||||
|
const XYPlanePoint = { x: 988, y: 523 } as const
|
||||||
|
const modelColor: [number, number, number] = [76, 76, 76]
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||||
await tutorialDismissButton.click()
|
await tutorialDismissButton.click()
|
||||||
|
// Make sure model still there.
|
||||||
|
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Clear code and restart onboarding from settings', async () => {
|
await test.step('Clear code and restart onboarding from settings', async () => {
|
||||||
|
@ -135,7 +135,9 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
pixelDiff: 50,
|
pixelDiff: 50,
|
||||||
})
|
})
|
||||||
await rectangle2ndClick()
|
await rectangle2ndClick()
|
||||||
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet)
|
await editor.expectEditor.toContain(afterRectangle2ndClickSnippet, {
|
||||||
|
shouldNormalise: true,
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
await test.step('Clean up so that `_sketchOnAChamfer` util can be called again', async () => {
|
||||||
@ -177,16 +179,11 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
|
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet:
|
||||||
|
'startProfileAt([205.96, 254.59], sketch002)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
|> angledLine([
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
105.26
|
|
||||||
], %, $rectangleSegmentB001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA002),
|
|
||||||
-segLen(rectangleSegmentA002)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
||||||
|>close(%)`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
@ -209,19 +206,15 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
|
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
afterRectangle1stClickSnippet:
|
||||||
|
'startProfileAt([-209.64, 255.28], sketch003)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
||||||
|> angledLine([
|
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)
|
||||||
106.84
|
|
||||||
], %, $rectangleSegmentB002)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA003),
|
|
||||||
-segLen(rectangleSegmentA003)
|
|
||||||
], %, $rectangleSegmentC002)
|
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
||||||
|>close(%)`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
clickCoords: { x: 677, y: 87 },
|
clickCoords: { x: 677, y: 87 },
|
||||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||||
@ -234,19 +227,14 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
]
|
]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
'sketch004 = startSketchOn(extrude001, seg05)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
'startProfileAt([82.57, 322.96], sketch004)',
|
||||||
|> angledLine([
|
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004)
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)
|
||||||
106.84
|
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)
|
||||||
], %, $rectangleSegmentB002)
|
|>lineTo([profileStartX(%),profileStartY(%)],%)|
|
||||||
|> angledLine([
|
>close(%)`,
|
||||||
segAng(rectangleSegmentA003),
|
|
||||||
-segLen(rectangleSegmentA003)
|
|
||||||
], %, $rectangleSegmentC002)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)`,
|
|
||||||
})
|
})
|
||||||
/// last one
|
/// last one
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -259,17 +247,11 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch005 = startSketchOn(extrude001, seg06)',
|
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
afterRectangle1stClickSnippet:
|
||||||
|
'startProfileAt([-23.43, 19.69], sketch005)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
||||||
|
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)
|
||||||
|> angledLine([
|
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)
|
||||||
segAng(rectangleSegmentA005) - 90,
|
|
||||||
84.07
|
|
||||||
], %, $rectangleSegmentB004)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA005),
|
|
||||||
-segLen(rectangleSegmentA005)
|
|
||||||
], %, $rectangleSegmentC004)
|
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
||||||
|>close(%)`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
@ -277,7 +259,6 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|
||||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
@ -305,55 +286,55 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
tags = [getNextAdjacentEdge(yo)]
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
}, %, $seg06)
|
}, %, $seg06)
|
||||||
sketch005 = startSketchOn(extrude001, seg06)
|
sketch005 = startSketchOn(extrude001, seg06)
|
||||||
|> startProfileAt([-23.43, 19.69], %)
|
profile004 = startProfileAt([-23.43, 19.69], sketch005)
|
||||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA005) - 90,
|
segAng(rectangleSegmentA005) - 90,
|
||||||
84.07
|
84.07
|
||||||
], %, $rectangleSegmentB004)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA005),
|
segAng(rectangleSegmentA005),
|
||||||
-segLen(rectangleSegmentA005)
|
-segLen(rectangleSegmentA005)
|
||||||
], %, $rectangleSegmentC004)
|
], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch004 = startSketchOn(extrude001, seg05)
|
sketch004 = startSketchOn(extrude001, seg05)
|
||||||
|> startProfileAt([82.57, 322.96], %)
|
profile003 = startProfileAt([82.57, 322.96], sketch004)
|
||||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA004) - 90,
|
segAng(rectangleSegmentA004) - 90,
|
||||||
103.07
|
103.07
|
||||||
], %, $rectangleSegmentB003)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA004),
|
segAng(rectangleSegmentA004),
|
||||||
-segLen(rectangleSegmentA004)
|
-segLen(rectangleSegmentA004)
|
||||||
], %, $rectangleSegmentC003)
|
], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch003 = startSketchOn(extrude001, seg04)
|
sketch003 = startSketchOn(extrude001, seg04)
|
||||||
|> startProfileAt([-209.64, 255.28], %)
|
profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
||||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA003) - 90,
|
segAng(rectangleSegmentA003) - 90,
|
||||||
106.84
|
106.84
|
||||||
], %, $rectangleSegmentB002)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA003),
|
segAng(rectangleSegmentA003),
|
||||||
-segLen(rectangleSegmentA003)
|
-segLen(rectangleSegmentA003)
|
||||||
], %, $rectangleSegmentC002)
|
], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|> startProfileAt([205.96, 254.59], %)
|
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002) - 90,
|
segAng(rectangleSegmentA002) - 90,
|
||||||
105.26
|
105.26
|
||||||
], %, $rectangleSegmentB001)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %, $rectangleSegmentC001)
|
], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`,
|
`,
|
||||||
@ -392,16 +373,11 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
beforeChamferSnippetEnd: '}, extrude001)',
|
beforeChamferSnippetEnd: '}, extrude001)',
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet:
|
||||||
|
'startProfileAt([205.96, 254.59], sketch002)',
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
|> angledLine([
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
105.26
|
|
||||||
], %, $rectangleSegmentB001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA002),
|
|
||||||
-segLen(rectangleSegmentA002)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
||||||
|>close(%)`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
@ -433,16 +409,16 @@ chamf = chamfer({
|
|||||||
]
|
]
|
||||||
}, %)
|
}, %)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|> startProfileAt([205.96, 254.59], %)
|
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002) - 90,
|
segAng(rectangleSegmentA002) - 90,
|
||||||
105.26
|
105.26
|
||||||
], %, $rectangleSegmentB001)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %, $rectangleSegmentC001)
|
], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`,
|
`,
|
||||||
@ -504,10 +480,10 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
|
|
||||||
const expectedCodeSnippets = {
|
const expectedCodeSnippets = {
|
||||||
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
||||||
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
|
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], sketch001)`,
|
||||||
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
||||||
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
|
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], sketch001)`,
|
||||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], sketch001)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
await app.initialise()
|
await app.initialise()
|
||||||
@ -552,6 +528,82 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Verify user can double-click to edit a sketch`, async ({
|
||||||
|
app,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `closedSketch = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [8, 5], radius = 2 }, %)
|
||||||
|
openSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-5, 0], %)
|
||||||
|
|> lineTo([0, 5], %)
|
||||||
|
|> xLine(5, %)
|
||||||
|
|> tangentialArcTo([10, 0], %)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
const pointInsideCircle = {
|
||||||
|
x: app.viewPortSize.width * 0.63,
|
||||||
|
y: app.viewPortSize.height * 0.5,
|
||||||
|
}
|
||||||
|
const pointOnPathAfterSketching = {
|
||||||
|
x: app.viewPortSize.width * 0.58,
|
||||||
|
y: app.viewPortSize.height * 0.5,
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [_clickOpenPath, moveToOpenPath, dblClickOpenPath] =
|
||||||
|
scene.makeMouseHelpers(
|
||||||
|
pointOnPathAfterSketching.x,
|
||||||
|
pointOnPathAfterSketching.y
|
||||||
|
)
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const [_clickCircle, moveToCircle, dblClickCircle] = scene.makeMouseHelpers(
|
||||||
|
pointInsideCircle.x,
|
||||||
|
pointInsideCircle.y
|
||||||
|
)
|
||||||
|
|
||||||
|
const exitSketch = async () => {
|
||||||
|
await test.step(`Exit sketch mode`, async () => {
|
||||||
|
await toolbar.exitSketchBtn.click()
|
||||||
|
await expect(toolbar.exitSketchBtn).not.toBeVisible()
|
||||||
|
await expect(toolbar.startSketchBtn).toBeEnabled()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Double-click on the closed sketch`, async () => {
|
||||||
|
await moveToCircle()
|
||||||
|
await dblClickCircle()
|
||||||
|
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||||
|
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [`|>circle({center=[8,5],radius=2},%)`],
|
||||||
|
highlightedCode: 'circle({center=[8,5],radius=2},%)',
|
||||||
|
diagnostics: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
await exitSketch()
|
||||||
|
|
||||||
|
await test.step(`Double-click on the open sketch`, async () => {
|
||||||
|
await moveToOpenPath()
|
||||||
|
await scene.expectPixelColor([250, 250, 250], pointOnPathAfterSketching, 15)
|
||||||
|
// There is a full execution after exiting sketch that clears the scene.
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await dblClickOpenPath()
|
||||||
|
await expect(toolbar.startSketchBtn).not.toBeVisible()
|
||||||
|
await expect(toolbar.exitSketchBtn).toBeVisible()
|
||||||
|
// Wait for enter sketch mode to complete
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await editor.expectState({
|
||||||
|
activeLines: [`|>xLine(5,%)`],
|
||||||
|
highlightedCode: 'xLine(5,%)',
|
||||||
|
diagnostics: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test(`Offset plane point-and-click`, async ({
|
test(`Offset plane point-and-click`, async ({
|
||||||
app,
|
app,
|
||||||
scene,
|
scene,
|
||||||
@ -601,3 +653,259 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loftPointAndClickCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(
|
||||||
|
testPoint.x,
|
||||||
|
testPoint.y + 80
|
||||||
|
)
|
||||||
|
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
||||||
|
|
||||||
|
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function selectSketches() {
|
||||||
|
await clickOnSketch1()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnSketch2()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Selection: '' },
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await selectSketches()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the two sketches`, async () => {
|
||||||
|
await selectSketches()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(loftDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [loftDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const shellPointAndClickCapCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
shellPointAndClickCapCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Shell point-and-click cap (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
extrude001 = extrude(30, sketch001)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const shellDeclaration =
|
||||||
|
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected faces`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the cap`, async () => {
|
||||||
|
await clickOnCap()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with a preselected face (cap)`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [shellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Shell point-and-click wall', async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([-20, 20], %)
|
||||||
|
|> xLine(40, %)
|
||||||
|
|> yLine(-60, %)
|
||||||
|
|> xLine(-40, %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(40, sketch001)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 580, y: 180 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
||||||
|
const mutatedCode = 'xLine(-40, %, $seg01)'
|
||||||
|
const shellDeclaration =
|
||||||
|
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
||||||
|
const formattedOutLastLine = '}, extrude001)'
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow, selecting a wall and keeping default thickness`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnWall()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap, 1 face',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(mutatedCode)
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [formattedOutLastLine],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -136,6 +136,335 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const errorDir = join(dir, 'broken-code')
|
||||||
|
await fsp.mkdir(errorDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(errorDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening broken code project should clear the scene and show the error', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('broken-code')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('broken-code').click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
const emptyDir = join(dir, 'empty')
|
||||||
|
await fsp.mkdir(emptyDir, { recursive: true })
|
||||||
|
await fsp.writeFile(join(emptyDir, 'main.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Clicking the logo takes us back to the projects page / home', async () => {
|
||||||
|
await page.getByTestId('app-logo').click()
|
||||||
|
|
||||||
|
await expect(page.getByRole('link', { name: 'bracket' })).toBeVisible()
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
await expect(page.getByText('New Project')).toBeVisible()
|
||||||
|
})
|
||||||
|
await test.step('opening empty code project should clear the scene', async () => {
|
||||||
|
// Go back home.
|
||||||
|
await expect(page.getByText('empty')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('empty').click()
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(join(bracketDir, 'empty.kcl'), '')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('creating a empty file should clear the scene', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'empty.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// planes colors means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [92, 53, 53]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
|
||||||
|
// Ensure the code is empty.
|
||||||
|
await expect(u.codeLocator).toContainText('')
|
||||||
|
expect(u.codeLocator.innerHTML.length).toBeLessThan(2)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'open a file in a project works and renders, open another file in the same project with errors, it should clear the scene',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ browserName }, testInfo) => {
|
||||||
|
const { electronApp, page } = await setupElectron({
|
||||||
|
testInfo,
|
||||||
|
folderSetupFn: async (dir) => {
|
||||||
|
const bracketDir = join(dir, 'bracket')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('broken-code-test.kcl'),
|
||||||
|
join(bracketDir, 'broken-code-test.kcl')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the bracket project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('bracket')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('bracket').click()
|
||||||
|
|
||||||
|
await expect(page.getByTestId('loading')).toBeAttached()
|
||||||
|
await expect(page.getByTestId('loading')).not.toBeAttached({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
await test.step('opening broken code file should clear the scene and show the error', async () => {
|
||||||
|
// open the file pane.
|
||||||
|
await page.getByTestId('files-pane-button').click()
|
||||||
|
|
||||||
|
// OPen the other file.
|
||||||
|
const file = page.getByRole('button', { name: 'broken-code-test.kcl' })
|
||||||
|
await expect(file).toBeVisible()
|
||||||
|
|
||||||
|
await file.click()
|
||||||
|
|
||||||
|
// error in guter
|
||||||
|
await expect(page.locator('.cm-lint-marker-error')).toBeVisible()
|
||||||
|
|
||||||
|
// error text on hover
|
||||||
|
await page.hover('.cm-lint-marker-error')
|
||||||
|
const crypticErrorText = `Expected a tag declarator`
|
||||||
|
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||||
|
|
||||||
|
// black pixel means the scene has been cleared.
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [30, 30, 30]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await electronApp.close()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'when code with error first loads you get errors in console',
|
'when code with error first loads you get errors in console',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
|
@ -550,7 +550,7 @@ sketch001 = startSketchAt([-0, -0])
|
|||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
// Constants and locators
|
// Constants and locators
|
||||||
const planeColor: [number, number, number] = [170, 220, 170]
|
const planeColor: [number, number, number] = [161, 220, 155]
|
||||||
const bgColor: [number, number, number] = [27, 27, 27]
|
const bgColor: [number, number, number] = [27, 27, 27]
|
||||||
const middlePixelIsColor = async (color: [number, number, number]) => {
|
const middlePixelIsColor = async (color: [number, number, number]) => {
|
||||||
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
return u.getGreatestPixDiff({ x: 600, y: 250 }, color)
|
||||||
|
@ -7,6 +7,8 @@ try {
|
|||||||
.split('\n')
|
.split('\n')
|
||||||
.filter((line) => line && line.length > 1)
|
.filter((line) => line && line.length > 1)
|
||||||
.forEach((line) => {
|
.forEach((line) => {
|
||||||
|
// Allow line comments.
|
||||||
|
if (line.trimStart().startsWith('#')) return
|
||||||
const [key, value] = line.split('=')
|
const [key, value] = line.split('=')
|
||||||
// prefer env vars over secrets file
|
// prefer env vars over secrets file
|
||||||
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
secrets[key] = process.env[key] || (value as any).replaceAll('"', '')
|
||||||
|
@ -114,9 +114,9 @@ test.describe('Sketch tests', () => {
|
|||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([4.61, -14.01], %)
|
|> startProfileAt([2.61, -4.01], %)
|
||||||
|> xLine(12.73, %)
|
|> xLine(8.73, %)
|
||||||
|> tangentialArcTo([24.95, -5.38], %)`
|
|> tangentialArcTo([8.33, -1.31], %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -126,7 +126,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
await page.getByText('tangentialArcTo([8.33, -1.31], %)').click()
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Edit Sketch' })
|
page.getByRole('button', { name: 'Edit Sketch' })
|
||||||
).toBeEnabled({ timeout: 1000 })
|
).toBeEnabled({ timeout: 1000 })
|
||||||
@ -135,7 +135,7 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(600) // wait for animation
|
await page.waitForTimeout(600) // wait for animation
|
||||||
|
|
||||||
await page.getByText('tangentialArcTo([24.95, -5.38], %)').click()
|
await page.getByText('tangentialArcTo([8.33, -1.31], %)').click()
|
||||||
await page.keyboard.press('End')
|
await page.keyboard.press('End')
|
||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.keyboard.press('ArrowUp')
|
await page.keyboard.press('ArrowUp')
|
||||||
@ -149,17 +149,21 @@ test.describe('Sketch tests', () => {
|
|||||||
|
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
// click start profileAt handle to continue profile
|
||||||
|
await page.mouse.click(702, 407)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await expect(async () => {
|
await expect(async () => {
|
||||||
|
// click to add segment
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
await expect.poll(u.normalisedEditorCode, { timeout: 1000 })
|
||||||
.toBe(`sketch001 = startSketchOn('XZ')
|
.toBe(`sketch002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([12.34, -12.34], %)
|
sketch001 = startProfileAt([12.34, -12.34], sketch002)
|
||||||
|> yLine(12.34, %)
|
|> yLine(12.34, %)
|
||||||
|
|
||||||
`)
|
`)
|
||||||
}).toPass({ timeout: 40_000, intervals: [1_000] })
|
}).toPass({ timeout: 5_000, intervals: [1_000] })
|
||||||
})
|
})
|
||||||
test('Can exit selection of face', async ({ page }) => {
|
test('Can exit selection of face', async ({ page }) => {
|
||||||
// Load the app with the code panes
|
// Load the app with the code panes
|
||||||
@ -669,7 +673,7 @@ test.describe('Sketch tests', () => {
|
|||||||
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
await page.waitForTimeout(500) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
await click00r(0, 0)
|
await click00r(0, 0)
|
||||||
codeStr += ` |> startProfileAt(${toSU([0, 0])}, %)`
|
codeStr += `profile001 = startProfileAt(${toSU([0, 0])}, sketch001)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
await click00r(50, 0)
|
await click00r(50, 0)
|
||||||
@ -705,7 +709,7 @@ test.describe('Sketch tests', () => {
|
|||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await click00r(30, 0)
|
await click00r(30, 0)
|
||||||
codeStr += ` |> startProfileAt([2.03, 0], %)`
|
codeStr += `profile002 = startProfileAt([2.03, 0], sketch002)`
|
||||||
await expect(u.codeLocator).toHaveText(codeStr)
|
await expect(u.codeLocator).toHaveText(codeStr)
|
||||||
|
|
||||||
// TODO: I couldn't use `toSU` here because of some rounding error causing
|
// TODO: I couldn't use `toSU` here because of some rounding error causing
|
||||||
@ -742,7 +746,9 @@ test.describe('Sketch tests', () => {
|
|||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn('-XZ')
|
const code = `sketch001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(scale * 34.8)}], %)
|
profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
||||||
|
scale * 34.8
|
||||||
|
)}], sketch001)
|
||||||
|> xLine(${roundOff(scale * 139.19)}, %)
|
|> xLine(${roundOff(scale * 139.19)}, %)
|
||||||
|> yLine(-${roundOff(scale * 139.2)}, %)
|
|> yLine(-${roundOff(scale * 139.2)}, %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
@ -808,11 +814,17 @@ test.describe('Sketch tests', () => {
|
|||||||
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
await expect(page.locator('.cm-content')).not.toHaveText(prevContent)
|
||||||
prevContent = await page.locator('.cm-content').innerText()
|
prevContent = await page.locator('.cm-content').innerText()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
await expect
|
||||||
// Assert the tool was unequipped
|
.poll(async () => {
|
||||||
|
const text = await page.locator('.cm-content').innerText()
|
||||||
|
return text.replace(/\s/g, '')
|
||||||
|
})
|
||||||
|
.toBe(code.replace(/\s/g, ''))
|
||||||
|
|
||||||
|
// Assert the tool stays equipped after a profile is closed (ready for the next one)
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'line Line', exact: true })
|
page.getByRole('button', { name: 'line Line', exact: true })
|
||||||
).not.toHaveAttribute('aria-pressed', 'true')
|
).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
// exit sketch
|
// exit sketch
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
@ -943,6 +955,110 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* TODO: once we fix bug turn on.
|
||||||
|
test('empty-scene default-planes act as expected when spaces in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('empty-scene default-planes act as expected when only code comments in file', async ({
|
||||||
|
page,
|
||||||
|
browserName,
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
const XYPlanePoint = { x: 774, y: 116 } as const
|
||||||
|
const unHoveredColor: [number, number, number] = [47, 47, 93]
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
|
// Fill with spaces
|
||||||
|
await u.codeLocator.fill(`// this is a code comments ya nerds
|
||||||
|
`)
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
|
||||||
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// color should not change for having been hovered
|
||||||
|
expect(
|
||||||
|
await u.getGreatestPixDiff(XYPlanePoint, unHoveredColor)
|
||||||
|
).toBeLessThan(8)
|
||||||
|
})*/
|
||||||
|
|
||||||
test('empty-scene default-planes act as expected', async ({
|
test('empty-scene default-planes act as expected', async ({
|
||||||
page,
|
page,
|
||||||
browserName,
|
browserName,
|
||||||
@ -1026,11 +1142,17 @@ sketch002 = startSketchOn(extrude001, 'END')
|
|||||||
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
await page.mouse.click(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
|
await page.mouse.click(XYPlanePoint.x + 50, XYPlanePoint.y + 50)
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect
|
||||||
|> startProfileAt([11.8, 9.09], %)
|
.poll(async () => {
|
||||||
|
const text = await u.codeLocator.innerText()
|
||||||
|
return text.replace(/\s/g, '')
|
||||||
|
})
|
||||||
|
.toBe(
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = startProfileAt([11.8, 9.09], sketch001)
|
||||||
|> line([3.39, -3.39], %)
|
|> line([3.39, -3.39], %)
|
||||||
`)
|
`.replace(/\s/g, '')
|
||||||
|
)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
@ -1315,3 +1437,560 @@ test2.describe(`Sketching with offset planes`, () => {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test2.describe('multi-profile sketching', () => {
|
||||||
|
test2(
|
||||||
|
'Can add multiple profiles to a sketch (all tool types)',
|
||||||
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
|
await app.initialise(``)
|
||||||
|
|
||||||
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
||||||
|
|
||||||
|
const [startProfile1] = scene.makeMouseHelpers(568, 70)
|
||||||
|
const [endLineStartTanArc] = scene.makeMouseHelpers(701, 78)
|
||||||
|
const [endArcStartLine] = scene.makeMouseHelpers(745, 189)
|
||||||
|
|
||||||
|
const [startProfile2] = scene.makeMouseHelpers(782, 80)
|
||||||
|
const [profile2Point2] = scene.makeMouseHelpers(921, 90)
|
||||||
|
const [profile2Point3] = scene.makeMouseHelpers(953, 178)
|
||||||
|
|
||||||
|
const [circle1Center] = scene.makeMouseHelpers(842, 147)
|
||||||
|
const [circle1Radius] = scene.makeMouseHelpers(870, 171)
|
||||||
|
|
||||||
|
const [circle2Center] = scene.makeMouseHelpers(850, 222)
|
||||||
|
const [circle2Radius] = scene.makeMouseHelpers(843, 230)
|
||||||
|
|
||||||
|
const [crnRect1point1] = scene.makeMouseHelpers(583, 205)
|
||||||
|
const [crnRect1point2] = scene.makeMouseHelpers(618, 320)
|
||||||
|
|
||||||
|
const [crnRect2point1] = scene.makeMouseHelpers(663, 215)
|
||||||
|
const [crnRect2point2] = scene.makeMouseHelpers(744, 276)
|
||||||
|
|
||||||
|
const [cntrRect1point1] = scene.makeMouseHelpers(624, 387)
|
||||||
|
const [cntrRect1point2] = scene.makeMouseHelpers(676, 355)
|
||||||
|
|
||||||
|
const [cntrRect2point1] = scene.makeMouseHelpers(785, 332)
|
||||||
|
const [cntrRect2point2] = scene.makeMouseHelpers(808, 286)
|
||||||
|
|
||||||
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
await selectXZPlane()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn('XZ')`)
|
||||||
|
await test.step('Create a close profile stopping mid profile to equip the tangential arc, and than back to the line tool', async () => {
|
||||||
|
await startProfile1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile001 = startProfileAt([4.61, 12.21], sketch001)`
|
||||||
|
)
|
||||||
|
|
||||||
|
await endLineStartTanArc()
|
||||||
|
await editor.expectEditor.toContain(`|> line([9.02, -0.55], %)`)
|
||||||
|
await toolbar.tangentialArcBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await endLineStartTanArc()
|
||||||
|
|
||||||
|
await endArcStartLine()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`|> tangentialArcTo([16.61, 4.14], %)`
|
||||||
|
)
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await endArcStartLine()
|
||||||
|
|
||||||
|
await app.page.mouse.click(572, 110)
|
||||||
|
await editor.expectEditor.toContain(`|> line([-11.73, 5.35], %)`)
|
||||||
|
await startProfile1()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Without unequipping from the last step, make another profile, and one that is not closed', async () => {
|
||||||
|
await startProfile2()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile002 = startProfileAt([19.12, 11.53], sketch001)`
|
||||||
|
)
|
||||||
|
await profile2Point2()
|
||||||
|
await editor.expectEditor.toContain(`|> line([9.43, -0.68], %)`)
|
||||||
|
await profile2Point3()
|
||||||
|
await editor.expectEditor.toContain(`|> line([2.17, -5.97], %)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('create two circles in a row without unequip', async () => {
|
||||||
|
await toolbar.circleBtn.click()
|
||||||
|
|
||||||
|
await circle1Center()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await circle1Radius()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile003 = circle({ center = [23.19, 6.98], radius = 2.5 }, sketch001)`
|
||||||
|
)
|
||||||
|
|
||||||
|
await test.step('hover in empty space to wait for overlays to get out of the way', async () => {
|
||||||
|
await app.page.mouse.move(951, 223)
|
||||||
|
await app.page.waitForTimeout(1000)
|
||||||
|
})
|
||||||
|
|
||||||
|
await circle2Center()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await circle2Radius()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile004 = circle({ center = [23.74, 1.9], radius = 0.72 }, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
await test.step('create two corner rectangles in a row without unequip', async () => {
|
||||||
|
await toolbar.rectangleBtn.click()
|
||||||
|
|
||||||
|
await crnRect1point1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile005 = startProfileAt([5.63, 3.05], sketch001)`
|
||||||
|
)
|
||||||
|
await crnRect1point2()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> angledLine([0, 2.37], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([segAng(rectangleSegmentA001) - 90, 7.8], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await crnRect2point1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile006 = startProfileAt([11.05, 2.37], sketch001)`
|
||||||
|
)
|
||||||
|
await crnRect2point2()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> angledLine([0, 5.49], %, $rectangleSegmentA002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|
4.14
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('create two center rectangles in a row without unequip', async () => {
|
||||||
|
await toolbar.selectCenterRectangle()
|
||||||
|
|
||||||
|
await cntrRect1point1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile007 = startProfileAt([8.41, -9.29], sketch001)`
|
||||||
|
)
|
||||||
|
await cntrRect1point2()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> angledLine([0, 7.06], %, $rectangleSegmentA003)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003) + 90,
|
||||||
|
4.34
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003),
|
||||||
|
-segLen(rectangleSegmentA003)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await cntrRect2point1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile008 = startProfileAt([19.33, -5.56], sketch001)`
|
||||||
|
)
|
||||||
|
await cntrRect2point2()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> angledLine([0, 3.12], %, $rectangleSegmentA004)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA004) + 90,
|
||||||
|
6.24
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA004),
|
||||||
|
-segLen(rectangleSegmentA004)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test2(
|
||||||
|
'Can edit a sketch with multiple profiles, dragging segments to edit them, and adding one new profile',
|
||||||
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
|
await app.initialise(`sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||||
|
|> line([-0.41, 6.99], %)
|
||||||
|
|> line([8.61, 0.74], %)
|
||||||
|
|> line([10.99, -5.22], %)
|
||||||
|
profile002 = startProfileAt([11.19, 5.02], sketch001)
|
||||||
|
|> angledLine([0, 10.78], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
4.14
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
profile003 = circle({ center = [6.92, -4.2], radius = 3.16 }, sketch001)
|
||||||
|
`)
|
||||||
|
|
||||||
|
const [pointOnSegment] = scene.makeMouseHelpers(590, 141)
|
||||||
|
const [profileEnd] = scene.makeMouseHelpers(970, 105)
|
||||||
|
const profileEndMv = scene.makeMouseHelpers(951, 101)[1]
|
||||||
|
const [newProfileEnd] = scene.makeMouseHelpers(764, 104)
|
||||||
|
const dragSegmentTo = scene.makeMouseHelpers(850, 104)[1]
|
||||||
|
|
||||||
|
const rectHandle = scene.makeMouseHelpers(901, 150)[1]
|
||||||
|
const rectDragTo = scene.makeMouseHelpers(901, 180)[1]
|
||||||
|
|
||||||
|
const circleEdge = scene.makeMouseHelpers(691, 331)[1]
|
||||||
|
const dragCircleTo = scene.makeMouseHelpers(720, 331)[1]
|
||||||
|
|
||||||
|
const [rectStart] = scene.makeMouseHelpers(794, 322)
|
||||||
|
const [rectEnd] = scene.makeMouseHelpers(757, 395)
|
||||||
|
|
||||||
|
await test2.step('enter sketch and setup', async () => {
|
||||||
|
await pointOnSegment({ shouldDbClick: true })
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('extend existing profile', async () => {
|
||||||
|
await profileEnd()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await newProfileEnd()
|
||||||
|
await editor.expectEditor.toContain(`|> line([-11.4, 0.71], %)`)
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('edit existing profile', async () => {
|
||||||
|
await profileEndMv()
|
||||||
|
await app.page.mouse.down()
|
||||||
|
await dragSegmentTo()
|
||||||
|
await app.page.mouse.up()
|
||||||
|
await editor.expectEditor.toContain(`line([4.16, -4.51], %)`)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('edit existing rect', async () => {
|
||||||
|
await rectHandle()
|
||||||
|
await app.page.mouse.down()
|
||||||
|
await rectDragTo()
|
||||||
|
await app.page.mouse.up()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`angledLine([-7, 10.2], %, $rectangleSegmentA001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('edit existing circl', async () => {
|
||||||
|
await circleEdge()
|
||||||
|
await app.page.mouse.down()
|
||||||
|
await dragCircleTo()
|
||||||
|
await app.page.mouse.up()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile003 = circle({ center = [6.92, -4.2], radius = 4.77 }, sketch001)`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('add new profile', async () => {
|
||||||
|
await toolbar.rectangleBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await rectStart()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile004 = startProfileAt([15.62, -3.83], sketch001)`
|
||||||
|
)
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await rectEnd()
|
||||||
|
await editor.expectEditor
|
||||||
|
.toContain(`|> angledLine([180, 1.97], %, $rectangleSegmentA002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002) + 90,
|
||||||
|
3.88
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test2(
|
||||||
|
'Can delete a profile in the editor while is sketch mode, and sketch mode does not break, can ctrl+z to undo after constraint with variable was added',
|
||||||
|
async ({ app, scene, toolbar, editor, cmdBar }) => {
|
||||||
|
await app.initialise(`sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = startProfileAt([6.24, 4.54], sketch001)
|
||||||
|
|> line([-0.41, 6.99], %)
|
||||||
|
|> line([8.61, 0.74], %)
|
||||||
|
|> line([10.99, -5.22], %)
|
||||||
|
profile002 = startProfileAt([11.19, 5.02], sketch001)
|
||||||
|
|> angledLine([0, 10.78], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
4.14
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
profile003 = circle({ center = [6.92, -4.2], radius = 3.16 }, sketch001)
|
||||||
|
`)
|
||||||
|
|
||||||
|
const [pointOnSegment] = scene.makeMouseHelpers(590, 141)
|
||||||
|
const [segment1Click] = scene.makeMouseHelpers(616, 131)
|
||||||
|
const sketchIsDrawnProperly = async () => {
|
||||||
|
await test2.step(
|
||||||
|
'check the sketch is still drawn properly',
|
||||||
|
async () => {
|
||||||
|
await app.page.waitForTimeout(200)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
[255, 255, 255],
|
||||||
|
{ x: 617, y: 163 },
|
||||||
|
15
|
||||||
|
)
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
[255, 255, 255],
|
||||||
|
{ x: 629, y: 331 },
|
||||||
|
15
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await test2.step('enter sketch and setup', async () => {
|
||||||
|
await pointOnSegment({ shouldDbClick: true })
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step('select and delete code for a profile', async () => {})
|
||||||
|
await app.page.getByText('close(%)').click()
|
||||||
|
await app.page.keyboard.down('Shift')
|
||||||
|
for (let i = 0; i < 11; i++) {
|
||||||
|
await app.page.keyboard.press('ArrowUp')
|
||||||
|
}
|
||||||
|
await app.page.keyboard.press('Home')
|
||||||
|
await app.page.keyboard.up('Shift')
|
||||||
|
await app.page.keyboard.press('Backspace')
|
||||||
|
|
||||||
|
await sketchIsDrawnProperly()
|
||||||
|
|
||||||
|
await test2.step('add random new var between profiles', async () => {
|
||||||
|
await app.page.keyboard.type('myVar = 5')
|
||||||
|
await app.page.keyboard.press('Enter')
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
})
|
||||||
|
|
||||||
|
await sketchIsDrawnProperly()
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
'Adding a constraint with a variable, and than ctrl-z-ing which will remove the variable again does not break sketch mode',
|
||||||
|
async () => {
|
||||||
|
await expect(async () => {
|
||||||
|
await segment1Click()
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: ['|>line([-0.41,6.99],%)'],
|
||||||
|
highlightedCode: 'line([-0.41,6.99],%)',
|
||||||
|
})
|
||||||
|
}).toPass({ timeout: 5_000, intervals: [500] })
|
||||||
|
|
||||||
|
await toolbar.lengthConstraintBtn.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await editor.expectEditor.toContain('length001 = 7')
|
||||||
|
|
||||||
|
// wait for execute defer
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
await sketchIsDrawnProperly()
|
||||||
|
|
||||||
|
await app.page.keyboard.down('Meta')
|
||||||
|
await app.page.keyboard.press('KeyZ')
|
||||||
|
await app.page.keyboard.up('Meta')
|
||||||
|
|
||||||
|
await editor.expectEditor.not.toContain('length001 = 7')
|
||||||
|
await sketchIsDrawnProperly()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
test2(
|
||||||
|
'can enter sketch when there is an extrude',
|
||||||
|
async ({ app, scene, toolbar }) => {
|
||||||
|
await app.initialise(`sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = startProfileAt([-63.43, 193.08], sketch001)
|
||||||
|
|> line([168.52, 149.87], %)
|
||||||
|
|> line([190.29, -39.18], %)
|
||||||
|
|> tangentialArcTo([319.63, 129.65], %)
|
||||||
|
|> line([-217.65, -21.76], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
profile003 = startProfileAt([16.79, 38.24], sketch001)
|
||||||
|
|> angledLine([0, 182.82], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
105.71
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
profile004 = circle({
|
||||||
|
center = [280.45, 47.57],
|
||||||
|
radius = 55.26
|
||||||
|
}, sketch001)
|
||||||
|
extrude002 = extrude(50, profile001)
|
||||||
|
extrude001 = extrude(5, profile003)
|
||||||
|
`)
|
||||||
|
const [pointOnSegment] = scene.makeMouseHelpers(574, 207)
|
||||||
|
|
||||||
|
await pointOnSegment()
|
||||||
|
await toolbar.editSketch()
|
||||||
|
// wait for engine animation
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
|
||||||
|
await test2.step('check the sketch is still drawn properly', async () => {
|
||||||
|
await scene.expectPixelColor([255, 255, 255], { x: 591, y: 167 }, 15)
|
||||||
|
await scene.expectPixelColor([255, 255, 255], { x: 638, y: 222 }, 15)
|
||||||
|
await scene.expectPixelColor([255, 255, 255], { x: 756, y: 214 }, 15)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test2(
|
||||||
|
'exit new sketch without drawing anything should not be a problem',
|
||||||
|
async ({ app, scene, toolbar, editor, cmdBar }) => {
|
||||||
|
await app.initialise(`myVar = 5`)
|
||||||
|
const [selectXZPlane] = scene.makeMouseHelpers(650, 150)
|
||||||
|
|
||||||
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
await selectXZPlane()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain(`sketch001 = startSketchOn('XZ')`)
|
||||||
|
await toolbar.exitSketchBtn.click()
|
||||||
|
|
||||||
|
await editor.expectEditor.not.toContain(`sketch001 = startSketchOn('XZ')`)
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
"still renders code, hasn't got into a weird state",
|
||||||
|
async () => {
|
||||||
|
await editor.replaceCode(
|
||||||
|
'myVar = 5',
|
||||||
|
`myVar = 5
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = circle({
|
||||||
|
center = [12.41, 3.87],
|
||||||
|
radius = myVar
|
||||||
|
}, sketch001)`
|
||||||
|
)
|
||||||
|
|
||||||
|
await scene.expectPixelColor([255, 255, 255], { x: 633, y: 211 }, 15)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test2(
|
||||||
|
'A sketch with only "startProfileAt" and no segments should still be able to be continued ',
|
||||||
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
|
await app.initialise(`sketch001 = startSketchOn('XZ')
|
||||||
|
profile001 = startProfileAt([85.19, 338.59], sketch001)
|
||||||
|
|> line([213.3, -94.52], %)
|
||||||
|
|> line([-230.09, -55.34], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
sketch002 = startSketchOn('XY')
|
||||||
|
profile002 = startProfileAt([85.81, 52.55], sketch002)
|
||||||
|
|
||||||
|
`)
|
||||||
|
const [startProfileAt] = scene.makeMouseHelpers(606, 184)
|
||||||
|
const [nextPoint] = scene.makeMouseHelpers(763, 130)
|
||||||
|
await app.page
|
||||||
|
.getByText('startProfileAt([85.81, 52.55], sketch002)')
|
||||||
|
.click()
|
||||||
|
await toolbar.editSketch()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
|
||||||
|
// equip line tool
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await startProfileAt()
|
||||||
|
await app.page.waitForTimeout(100)
|
||||||
|
await nextPoint()
|
||||||
|
await editor.expectEditor.toContain(`|> line([126.05, 44.12], %)`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
test2(
|
||||||
|
'old style sketch all in one pipe (with extrude) will break up to allow users to add a new profile to the same sketch',
|
||||||
|
async ({ app, scene, toolbar, editor }) => {
|
||||||
|
await app.initialise(`thePart = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.53, 10.51], %)
|
||||||
|
|> line([12.54, 1.83], %)
|
||||||
|
|> line([6.65, -6.91], %)
|
||||||
|
|> line([-6.31, -8.69], %)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(75, thePart)
|
||||||
|
`)
|
||||||
|
const [objClick] = scene.makeMouseHelpers(565, 343)
|
||||||
|
const [profilePoint1] = scene.makeMouseHelpers(609, 289)
|
||||||
|
const [profilePoint2] = scene.makeMouseHelpers(714, 389)
|
||||||
|
|
||||||
|
await test2.step('enter sketch and setup', async () => {
|
||||||
|
await objClick()
|
||||||
|
await toolbar.editSketch()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await app.page.waitForTimeout(600)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
'expect code to match initial conditions still',
|
||||||
|
async () => {
|
||||||
|
await editor.expectEditor.toContain(`thePart = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([7.53, 10.51], %)`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
'equiping the line tool should break up the pipe expression',
|
||||||
|
async () => {
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`sketch001 = startSketchOn('XZ')thePart = startProfileAt([7.53, 10.51], sketch001)`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await test2.step(
|
||||||
|
'can continue on to add a new profile to this sketch',
|
||||||
|
async () => {
|
||||||
|
await profilePoint1()
|
||||||
|
await editor.expectEditor.toContain(
|
||||||
|
`profile001 = startProfileAt([19.77, -7.08], sketch001)`
|
||||||
|
)
|
||||||
|
await profilePoint2()
|
||||||
|
await editor.expectEditor.toContain(`|> line([19.05, -18.14], %)`)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
@ -446,8 +446,7 @@ test(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||||
|> startProfileAt([7.19, -9.7], %)`
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -469,6 +468,10 @@ test(
|
|||||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.move(813, 392, { steps: 10 })
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
@ -591,8 +594,7 @@ test(
|
|||||||
mask: [page.getByTestId('model-state-indicator')],
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')profile001 = circle({ center = [14.44, -2.44], radius = 1 }, sketch001)`
|
||||||
|> circle({ center = [14.44, -2.44], radius = 1 }, %)`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -636,8 +638,7 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||||
|> startProfileAt([7.19, -9.7], %)`
|
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -655,6 +656,10 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.click(813, 392)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -741,8 +746,7 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([182.59, -246.32], sketch001)`
|
||||||
|> startProfileAt([182.59, -246.32], %)`
|
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -760,6 +764,10 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.click(813, 392)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -950,7 +958,75 @@ test(
|
|||||||
|
|
||||||
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
test.describe('Grid visibility', { tag: '@snapshot' }, () => {
|
||||||
// FIXME: Skip on macos its being weird.
|
// FIXME: Skip on macos its being weird.
|
||||||
test.skip(process.platform === 'darwin', 'Skip on macos')
|
// test.skip(process.platform === 'darwin', 'Skip on macos')
|
||||||
|
|
||||||
|
test('Grid turned off to on via command bar', async ({ page }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
const stream = page.getByTestId('stream')
|
||||||
|
const mask = [
|
||||||
|
page.locator('#app-header'),
|
||||||
|
page.locator('#sidebar-top-ribbon'),
|
||||||
|
page.locator('#sidebar-bottom-ribbon'),
|
||||||
|
]
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
// wait for execution done
|
||||||
|
await expect(
|
||||||
|
page.locator('[data-message-type="execution-done"]')
|
||||||
|
).toHaveCount(1)
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
await u.closeKclCodePanel()
|
||||||
|
// TODO: Find a way to truly know that the objects have finished
|
||||||
|
// rendering, because an execution-done message is not sufficient.
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
|
// Open the command bar.
|
||||||
|
await page
|
||||||
|
.getByRole('button', { name: 'Commands', exact: false })
|
||||||
|
.or(page.getByRole('button', { name: '⌘K' }))
|
||||||
|
.click()
|
||||||
|
const commandName = 'show scale grid'
|
||||||
|
const commandOption = page.getByRole('option', {
|
||||||
|
name: commandName,
|
||||||
|
exact: false,
|
||||||
|
})
|
||||||
|
const cmdSearchBar = page.getByPlaceholder('Search commands')
|
||||||
|
// This selector changes after we set the setting
|
||||||
|
await cmdSearchBar.fill(commandName)
|
||||||
|
await expect(commandOption).toBeVisible()
|
||||||
|
await commandOption.click()
|
||||||
|
|
||||||
|
const toggleInput = page.getByPlaceholder('Off')
|
||||||
|
await expect(toggleInput).toBeVisible()
|
||||||
|
await expect(toggleInput).toBeFocused()
|
||||||
|
|
||||||
|
// Select On
|
||||||
|
await page.keyboard.press('ArrowDown')
|
||||||
|
await expect(page.getByRole('option', { name: 'Off' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active selected'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('ArrowUp')
|
||||||
|
await expect(page.getByRole('option', { name: 'On' })).toHaveAttribute(
|
||||||
|
'data-headlessui-state',
|
||||||
|
'active'
|
||||||
|
)
|
||||||
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
// Check the toast appeared
|
||||||
|
await expect(
|
||||||
|
page.getByText(`Set show scale grid to "true" as a user default`)
|
||||||
|
).toBeVisible()
|
||||||
|
|
||||||
|
await expect(stream).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
mask,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
test('Grid turned off', async ({ page }) => {
|
test('Grid turned off', async ({ page }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -1096,3 +1172,109 @@ test.fixme('theme persists', async ({ page, context }) => {
|
|||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('code color goober', { tag: '@snapshot' }, () => {
|
||||||
|
test('code color goober', async ({ page, context }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`// Create a pipe using a sweep.
|
||||||
|
|
||||||
|
// Create a path for the sweep.
|
||||||
|
sweepPath = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.05, 0.05], %)
|
||||||
|
|> line([0, 7], %)
|
||||||
|
|> tangentialArc({ offset = 90, radius = 5 }, %)
|
||||||
|
|> line([-3, 0], %)
|
||||||
|
|> tangentialArc({ offset = -90, radius = 5 }, %)
|
||||||
|
|> line([0, 7], %)
|
||||||
|
|
||||||
|
sweepSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([2, 0], %)
|
||||||
|
|> arc({
|
||||||
|
angleEnd = 360,
|
||||||
|
angleStart = 0,
|
||||||
|
radius = 2
|
||||||
|
}, %)
|
||||||
|
|> sweep({
|
||||||
|
path = sweepPath,
|
||||||
|
}, %)
|
||||||
|
|> appearance({
|
||||||
|
color = "#bb00ff",
|
||||||
|
metalness = 90,
|
||||||
|
roughness = 90
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
await expect(page, 'expect small color widget').toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('code color goober opening window', async ({ page, context }) => {
|
||||||
|
const u = await getUtils(page)
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'persistCode',
|
||||||
|
`// Create a pipe using a sweep.
|
||||||
|
|
||||||
|
// Create a path for the sweep.
|
||||||
|
sweepPath = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0.05, 0.05], %)
|
||||||
|
|> line([0, 7], %)
|
||||||
|
|> tangentialArc({ offset = 90, radius = 5 }, %)
|
||||||
|
|> line([-3, 0], %)
|
||||||
|
|> tangentialArc({ offset = -90, radius = 5 }, %)
|
||||||
|
|> line([0, 7], %)
|
||||||
|
|
||||||
|
sweepSketch = startSketchOn('XY')
|
||||||
|
|> startProfileAt([2, 0], %)
|
||||||
|
|> arc({
|
||||||
|
angleEnd = 360,
|
||||||
|
angleStart = 0,
|
||||||
|
radius = 2
|
||||||
|
}, %)
|
||||||
|
|> sweep({
|
||||||
|
path = sweepPath,
|
||||||
|
}, %)
|
||||||
|
|> appearance({
|
||||||
|
color = "#bb00ff",
|
||||||
|
metalness = 90,
|
||||||
|
roughness = 90
|
||||||
|
}, %)
|
||||||
|
`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setViewportSize({ width: 1200, height: 1000 })
|
||||||
|
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-css-color-picker-wrapper')).toBeVisible()
|
||||||
|
|
||||||
|
// Click the color widget
|
||||||
|
await page.locator('.cm-css-color-picker-wrapper input').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page,
|
||||||
|
'expect small color widget to have window open'
|
||||||
|
).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 144 KiB |
After Width: | Height: | Size: 130 KiB |