Merge branch 'main' into move-tests-to-electon

This commit is contained in:
49lf
2024-11-29 11:47:16 -05:00
520 changed files with 59528 additions and 176750 deletions

View File

@ -1,21 +1,20 @@
name: build-publish-apps
name: build-apps
on:
pull_request:
push:
branches:
- main
release:
types: [published]
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
schedule:
- cron: '0 4 * * *'
# Daily at 04:00 AM UTC
# Will checkout the last commit from the default branch (main as of 2023-10-04)
env:
CUT_RELEASE_PR: ${{ github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
BUILD_RELEASE: ${{ github.event_name == 'release' || github.event_name == 'schedule' || github.event_name == 'pull_request' && (contains(github.event.pull_request.title, 'Cut release v')) }}
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Non-release build, commit {0}', github.sha) }}
IS_RELEASE: ${{ github.ref_type == 'tag' }}
IS_NIGHTLY: ${{ github.event_name == 'schedule' }}
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@ -26,6 +25,7 @@ jobs:
runs-on: ubuntu-22.04 # seperate job on Ubuntu for easy string manipulations (compared to Windows)
outputs:
version: ${{ steps.export_version.outputs.version }}
notes: ${{ steps.export_notes.outputs.notes }}
steps:
- uses: actions/checkout@v4
@ -47,48 +47,40 @@ jobs:
- name: Run build:wasm
run: "yarn build:wasm"
- name: Set nightly version
if: github.event_name == 'schedule'
- name: Set nightly version, product name, release notes, and icons
if: ${{ env.IS_NIGHTLY == 'true' }}
run: yarn files:flip-to-nightly
- name: Set release version
if: ${{ env.IS_RELEASE == 'true' }}
run: |
VERSION=$(date +'%-y.%-m.%-d') yarn bump-jsons
export VERSION=${GITHUB_REF_NAME#v}
yarn files:set-version
# TODO: see if we need to inject updater nightly URL here https://dl.zoo.dev/releases/modeling-app/nightly/last_update.json
- name: Generate release notes
run: |
echo "$NOTES" > release-notes.md
cat release-notes.md
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: prepared-files
path: |
package.json
electron-builder.yml
src/wasm-lib/pkg/wasm_lib*
release-notes.md
assets/icon.ico
assets/icon.png
- id: export_version
run: echo "version=`cat package.json | jq -r '.version'`" >> "$GITHUB_OUTPUT"
- name: Prepare electron-builder.yml file for nightly
if: ${{ github.event_name == 'schedule' }}
run: |
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
- uses: actions/upload-artifact@v3
if: ${{ github.event_name == 'schedule' }}
with:
name: prepared-files-nightly
path: |
electron-builder.yml
- id: export_notes
run: echo "notes=`cat release-notes.md`" >> "$GITHUB_OUTPUT"
- name: Prepare electron-builder.yml file for updater test
if: ${{ env.CUT_RELEASE_PR == 'true' }}
if: ${{ env.IS_RELEASE == 'true' }}
run: |
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/updater-test"' electron-builder.yml
- uses: actions/upload-artifact@v3
if: ${{ env.CUT_RELEASE_PR == 'true' }}
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: prepared-files-updater-test
path: |
@ -109,32 +101,24 @@ jobs:
platform: linux
runs-on: ${{ matrix.os }}
env:
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
name: prepared-files
- name: Copy prepared files
run: |
ls -R prepared-files
cp prepared-files/package.json package.json
cp prepared-files/electron-builder.yml electron-builder.yml
cp prepared-files/src/wasm-lib/pkg/wasm_lib_bg.wasm public
mkdir src/wasm-lib/pkg
cp prepared-files/src/wasm-lib/pkg/wasm_lib* src/wasm-lib/pkg
cp prepared-files/release-notes.md release-notes.md
- uses: actions/download-artifact@v3
if: ${{ github.event_name == 'schedule' }}
name: prepared-files-nightly
- name: Copy updated electron-builder.yml file for nightly build
if: ${{ github.event_name == 'schedule' }}
run: |
ls -R prepared-files-nightly
cp prepared-files-nightly/electron-builder.yml electron-builder.yml
cp prepared-files/assets/icon.ico assets/icon.ico
cp prepared-files/assets/icon.png assets/icon.png
- name: Sync node version and setup cache
uses: actions/setup-node@v4
@ -147,7 +131,7 @@ jobs:
- run: yarn tronb:vite
- name: Prepare certificate and variables (Windows only)
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'windows-2022' }}
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: |
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
cat /d/Certificate_pkcs12.p12
@ -162,7 +146,7 @@ jobs:
shell: bash
- name: Setup certicate with SSM KSP (Windows only)
if: ${{ env.BUILD_RELEASE == 'true' && matrix.os == 'windows-2022' }}
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
run: |
curl -X GET https://one.digicert.com/signingmanager/api-ui/v1/releases/smtools-windows-x64.msi/download -H "x-api-key:%SM_API_KEY%" -o smtools-windows-x64.msi
msiexec /i smtools-windows-x64.msi /quiet /qn
@ -173,13 +157,13 @@ jobs:
shell: cmd
- name: Build the app (debug)
if: ${{ env.BUILD_RELEASE == 'false' }}
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
# electron-builder doesn't have a concept of release vs debug,
# this is just not doing any codesign or release yml generation
run: yarn electron-builder --config
- name: Build the app (release)
if: ${{ env.BUILD_RELEASE == 'true' }}
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
env:
PUBLISH_FOR_PULL_REQUEST: true
APPLE_ID: ${{ secrets.APPLE_ID }}
@ -196,7 +180,7 @@ jobs:
- name: List artifacts in out/
run: ls -R out
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: out-arm64-${{ matrix.platform }}
# first two will pick both Zoo Modeling App-$VERSION-arm64-win.exe and Zoo Modeling App-$VERSION-win.exe
@ -206,7 +190,7 @@ jobs:
out/*-arm64-mac.*
out/*-arm64-linux.*
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: out-x64-${{ matrix.platform }}
path: |
@ -214,27 +198,29 @@ jobs:
out/*-x64-mac.*
out/*-x86_64-linux.*
- uses: actions/upload-artifact@v3
if: ${{ env.BUILD_RELEASE == 'true' }}
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
with:
name: out-yml
name: out-yml-${{ matrix.platform }}
path: |
out/latest*.yml
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
- uses: actions/download-artifact@v3
if: ${{ env.CUT_RELEASE_PR == 'true' }}
# The steps below are for updater-test builds, only on release
- uses: actions/download-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
name: prepared-files-updater-test
- name: Copy updated electron-builder.yml file for updater test
if: ${{ env.CUT_RELEASE_PR == 'true' }}
if: ${{ env.IS_RELEASE == 'true' }}
run: |
ls -R prepared-files-updater-test
cp prepared-files-updater-test/electron-builder.yml electron-builder.yml
- name: Build the app (updater-test)
if: ${{ env.CUT_RELEASE_PR == 'true' }}
if: ${{ env.IS_RELEASE == 'true' }}
env:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
@ -247,8 +233,8 @@ jobs:
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
run: yarn electron-builder --config --publish always
- uses: actions/upload-artifact@v3
if: ${{ env.CUT_RELEASE_PR == 'true' }}
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-arm64-${{ matrix.platform }}
path: |
@ -256,8 +242,8 @@ jobs:
out/*-arm64-mac.dmg
out/*-arm64-linux.AppImage
- uses: actions/upload-artifact@v3
if: ${{ env.CUT_RELEASE_PR == 'true' }}
- uses: actions/upload-artifact@v4
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: updater-test-x64-${{ matrix.platform }}
path: |
@ -266,58 +252,69 @@ jobs:
out/*-x86_64-linux.AppImage
publish-apps-release:
upload-apps-release:
runs-on: ubuntu-22.04
permissions:
contents: write
if: ${{ github.event_name == 'release' || github.event_name == 'schedule' }}
needs: [prepare-files, build-apps]
if: ${{ github.ref_type == 'tag' || github.event_name == 'schedule' }}
env:
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
VERSION: ${{ github.event_name == 'schedule' && needs.prepare-files.outputs.version || format('v{0}', needs.prepare-files.outputs.version) }}
PUB_DATE: ${{ github.event_name == 'release' && github.event.release.created_at || github.event.repository.updated_at }}
BUCKET_DIR: ${{ github.event_name == 'schedule' && 'dl.kittycad.io/releases/modeling-app/nightly' || 'dl.kittycad.io/releases/modeling-app' }}
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }}
needs: [prepare-files, build-apps]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-arm64-win
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-x64-win
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-yml-win
path: out
- uses: actions/download-artifact@v4
with:
name: out-arm64-mac
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-x64-mac
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-yml-mac
path: out
- uses: actions/download-artifact@v4
with:
name: out-arm64-linux
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-x64-linux
path: out
- uses: actions/download-artifact@v3
- uses: actions/download-artifact@v4
with:
name: out-yml
name: out-yml-linux
path: out
- name: Generate the download static endpoint
env:
NOTES: ${{ needs.prepare-files.outputs.notes }}
PUB_DATE: ${{ github.event.repository.updated_at }}
WEBSITE_DIR: ${{ github.event_name == 'schedule' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
URL_CODED_NAME: ${{ github.event_name == 'schedule' && 'Zoo%20Modeling%20App%20%28Nightly%29' || 'Zoo%20Modeling%20App' }}
run: |
RELEASE_DIR=https://${WEBSITE_DIR}
jq --null-input \
@ -354,78 +351,44 @@ jobs:
"url": $linux_x64_url
}
}
}' > last_download.json
cat last_download.json
}' > out/last_download.json
cat out/last_download.json
- uses: actions/upload-artifact@v4
with:
name: out-download-json
path: out/last_download.json
- name: List artifacts
run: "ls -R out"
- name: Authenticate to Google Cloud
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: 'google-github-actions/auth@v2.1.7'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
- name: Set up Google Cloud SDK
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: google-github-actions/setup-gcloud@v2.1.2
with:
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
- name: Upload release files to public bucket
- name: Upload nightly files to public bucket
if: ${{ env.IS_NIGHTLY == 'true' }}
uses: google-github-actions/upload-cloud-storage@v2.2.1
with:
path: out
glob: 'Zoo*'
glob: '*'
parent: false
destination: ${{ env.BUCKET_DIR }}
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
- name: Upload update endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.2.1
with:
path: out
glob: 'latest*'
parent: false
destination: ${{ env.BUCKET_DIR }}
- name: Upload download endpoint to public bucket
uses: google-github-actions/upload-cloud-storage@v2.2.1
with:
path: last_download.json
destination: ${{ env.BUCKET_DIR }}
- name: Upload release files to Github
if: ${{ github.event_name == 'release' }}
- name: Create draft release
uses: softprops/action-gh-release@v2
if: ${{ env.IS_RELEASE == 'true' }}
with:
name: ${{ env.VERSION }}
tag_name: ${{ env.VERSION }}
draft: true
generate_release_notes: true
files: 'out/Zoo*'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: |
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
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
announce_release:
needs: [publish-apps-release]
runs-on: ubuntu-22.04
if: github.event_name == 'release'
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Announce Release
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_VERSION: ${{ github.event.release.tag_name }}
RELEASE_BODY: ${{ github.event.release.body}}
run: python public/announce_release.py

View File

@ -1,37 +0,0 @@
name: Create Release
on:
push:
branches:
- main
jobs:
create-release:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: read
if: contains(github.event.head_commit.message, 'Cut release v')
steps:
- uses: actions/github-script@v7
name: Read Cut release PR info and create release
with:
script: |
const { owner, repo } = context.repo
const pulls = await github.rest.repos.listPullRequestsAssociatedWithCommit({
owner,
repo,
commit_sha: context.sha,
})
const { title, body } = pulls.data[0]
const version = title.split('Cut release ')[1]
const result = await github.rest.repos.createRelease({
owner,
repo,
body,
tag_name: version,
name: version,
draft: true,
})
console.log(result)

View File

@ -0,0 +1,158 @@
name: publish-apps-release
on:
release:
types: [published]
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
publish-apps-release:
runs-on: ubuntu-22.04
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- name: Find tag workflow id
id: tag_workflow_id
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
id=$(gh run ls --repo kittycad/modeling-app -w build-apps.yml --branch ${{ github.event.release.tag_name }} --json databaseId | jq '.[0].databaseId')
echo "id=$id" >> "$GITHUB_OUTPUT"
- uses: actions/download-artifact@v4
with:
name: out-arm64-win
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-x64-win
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-yml-win
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-arm64-mac
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-x64-mac
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-yml-mac
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-arm64-linux
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-x64-linux
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-yml-linux
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- uses: actions/download-artifact@v4
with:
name: out-download-json
path: out
run-id: ${{ steps.tag_workflow_id.outputs.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: List artifacts
run: ls -R out
- name: Override release notes
env:
NOTES: ${{ github.event.release.body }}
run: yarn files:set-notes
- name: Authenticate to Google Cloud
uses: 'google-github-actions/auth@v2.1.7'
with:
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
- name: Set up Google Cloud SDK
uses: google-github-actions/setup-gcloud@v2.1.2
with:
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
- name: Upload release files to public bucket
uses: google-github-actions/upload-cloud-storage@v2.2.1
with:
path: out
glob: '*'
parent: false
destination: 'dl.kittycad.io/releases/modeling-app'
- name: Invalidate bucket cache on latest*.yml and last_download.json files
run: |
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
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest-mac.yml" --async
gcloud compute url-maps invalidate-cdn-cache dl-url-map --path="/releases/modeling-app/latest.yml" --async
announce_release:
needs: [publish-apps-release]
runs-on: ubuntu-22.04
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests
- name: Announce Release
env:
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
RELEASE_VERSION: ${{ github.event.release.tag_name }}
RELEASE_BODY: ${{ github.event.release.body }}
run: python public/announce_release.py

View File

@ -128,45 +128,39 @@ Before you submit a contribution PR to this repo, please ensure that:
## Release a new version
#### 1. Bump the versions by running `./make-release.sh`
#### 1. Create a 'Cut release $VERSION' issue
The `./make-release.sh` script has git commands to pull main but to be sure you can run the following git commands to have a fresh `main` locally.
It will be used to document changelog discussions and release testing.
https://github.com/KittyCAD/modeling-app/issues/new
#### 2. Push a new tag
Create a new tag and push it to the repo (eg. `v0.28.0` for `$VERSION`)
```
git branch -D main
git checkout main
git pull origin
./make-release.sh
# Copy within the back ticks and paste the stdout of the change log
git push --set-upstream origin <branch name created from ./make-release.sh>
VERSION=$(./scripts/semantic-release.sh)
git tag $VERSION
git push origin --tags
```
That will create the branch with the updated json files for you:
- run `./make-release.sh` or `./make-release.sh patch` for a patch update;
- run `./make-release.sh minor` for minor; or
- run `./make-release.sh major` for major.
This will trigger the `build-apps` workflow, set the version, build & sign the apps, and generate release files as well as updater-test artifacts.
After it runs you should just need the push the branch and open a PR.
#### 2. Create a Cut Release PR
When you open the PR copy the change log from the output of the `./make-release.sh` script into the description of the PR.
**Important:** Pull request title needs to be prefixed with `Cut release v` to build in release mode and a few other things to test in the best context possible, the intent would be for instance to have `Cut release v1.2.3` for the `v1.2.3` release candidate.
The PR may then serve as a place to discuss the human-readable changelog and extra QA. The `make-release.sh` tool suggests a changelog for you too to be used as PR description, just make sure to delete lines that are not user facing.
Once the workflow succeeds, a draft release will be created at https://github.com/KittyCAD/modeling-app/releases.
#### 3. Manually test artifacts from the Cut Release PR
##### Release builds
The release builds can be found under the `out-{platform}` zip, at the very bottom of the `build-publish-apps` summary page for each commit on this branch.
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.).
Manually test against this [list](https://github.com/KittyCAD/modeling-app/issues/3588) across Windows, MacOS, Linux and posting results as comments in the Cut Release PR.
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.
##### Updater-test builds
The other `build-publish-apps` output in Cut Release PRs is `updater-test-{platform}`. As we don't have a way to test this fully automatically, we have a semi-automated process. For macOS, Windows, and Linux, download the corresponding updater-test artifact file, install the app, run it, expect an updater prompt to a dummy v0.255.255, install it and check that the app comes back at that version.
The other `build-apps` output in the release `build-apps` workflow (triggered by 2.) is `updater-test-{arch}-{platform}`. It's a semi-automated process: for macOS, Windows, and Linux, download the corresponding updater-test artifact file, install the app, run it, expect an updater prompt to a dummy v0.255.255, install it and check that the app comes back at that version.
The only difference with these builds is that they point to a different update location on the release bucket, with this dummy v0.255.255 always available. This helps ensuring that the version we release will be able to update to the next one available.
@ -182,18 +176,15 @@ If the prompt doesn't show up, start the app in command line to grab the electro
./Zoo Modeling App-{version}-{arch}-linux.AppImage
```
#### 4. Merge the Cut Release PR
#### 4. Publish the release
This will kick the `create-release` action, that creates a _Draft_ release out of this Cut Release PR merge after less than a minute, with the new version as title and Cut Release PR as description.
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.
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.
#### 5. Publish the release
#### 5. Close the issue
Head over to https://github.com/KittyCAD/modeling-app/releases, the draft release corresponding to the merged Cut Release PR should show up at the top as _Draft_. Click on it, verify the content, and hit _Publish_.
#### 6. Profit
A new Action kicks in at https://github.com/KittyCAD/modeling-app/actions, which can be found under `release` event filter.
If everything is well and the release is out to the public, the issue tracking the release shall be closed.
## Fuzzing the parser
@ -442,3 +433,9 @@ PS: for the debug panel, the following JSON is useful for snapping the camera
## 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`.

BIN
assets/icon-nightly.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
assets/icon-nightly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

Binary file not shown.

View File

@ -36,9 +36,9 @@ myAngle = -120
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([8, 0], %)
|> angledLine({ angle: abs(myAngle), length: 5 }, %)
|> angledLine({ angle = abs(myAngle), length = 5 }, %)
|> line([-5, 0], %)
|> angledLine({ angle: myAngle, length: 5 }, %)
|> angledLine({ angle = myAngle, length = 5 }, %)
|> close(%)
baseExtrusion = extrude(5, sketch001)

View File

@ -34,8 +34,8 @@ acos(num: number) -> number
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({
angle: toDegrees(acos(0.5)),
length: 10
angle = toDegrees(acos(0.5)),
length = 10
}, %)
|> line([5, 0], %)
|> lineTo([12, 0], %)

View File

@ -33,8 +33,8 @@ sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([1, 2], %, $seg01)
|> angledLine({
angle: angleToMatchLengthY(seg01, 15, %),
length: 5
angle = angleToMatchLengthY(seg01, 15, %),
length = 5
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -32,7 +32,7 @@ angledLine(data: AngledLineData, sketch: Sketch, tag?: TagDeclarator) -> Sketch
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> yLineTo(15, %)
|> angledLine({ angle: 30, length: 15 }, %)
|> angledLine({ angle = 30, length = 15 }, %)
|> line([8, -10], %)
|> yLineTo(0, %)
|> close(%)

View File

@ -31,8 +31,8 @@ angledLineOfXLength(data: AngledLineData, sketch: Sketch, tag?: TagDeclarator) -
```js
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLineOfXLength({ angle: 45, length: 10 }, %, $edge1)
|> angledLineOfXLength({ angle: -15, length: 20 }, %, $edge2)
|> angledLineOfXLength({ angle = 45, length = 10 }, %, $edge1)
|> angledLineOfXLength({ angle = -15, length = 20 }, %, $edge2)
|> line([0, -5], %)
|> close(%, $edge3)

View File

@ -32,9 +32,9 @@ angledLineOfYLength(data: AngledLineData, sketch: Sketch, tag?: TagDeclarator) -
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> angledLineOfYLength({ angle: 45, length: 10 }, %)
|> angledLineOfYLength({ angle = 45, length = 10 }, %)
|> line([0, 10], %)
|> angledLineOfYLength({ angle: 135, length: 10 }, %)
|> angledLineOfYLength({ angle = 135, length = 10 }, %)
|> line([-10, 0], %)
|> line([0, -30], %)

View File

@ -35,9 +35,9 @@ exampleSketch = startSketchOn('XZ')
|> lineTo([-10, 10], %, $lineToIntersect)
|> lineTo([0, 20], %)
|> angledLineThatIntersects({
angle: 80,
intersectTag: lineToIntersect,
offset: 10
angle = 80,
intersectTag = lineToIntersect,
offset = 10
}, %)
|> close(%)

View File

@ -31,7 +31,7 @@ angledLineToX(data: AngledLineToData, sketch: Sketch, tag?: TagDeclarator) -> Sk
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLineToX({ angle: 30, to: 10 }, %)
|> angledLineToX({ angle = 30, to = 10 }, %)
|> line([0, 10], %)
|> line([-10, 0], %)
|> close(%)

View File

@ -31,9 +31,9 @@ angledLineToY(data: AngledLineToData, sketch: Sketch, tag?: TagDeclarator) -> Sk
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLineToY({ angle: 60, to: 20 }, %)
|> angledLineToY({ angle = 60, to = 20 }, %)
|> line([-20, 0], %)
|> angledLineToY({ angle: 70, to: 10 }, %)
|> angledLineToY({ angle = 70, to = 10 }, %)
|> close(%)
example = extrude(10, exampleSketch)

View File

@ -35,9 +35,9 @@ exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> arc({
angleStart: 0,
angleEnd: 280,
radius: 16
angleStart = 0,
angleEnd = 280,
radius = 16
}, %)
|> close(%)
example = extrude(10, exampleSketch)

View File

@ -31,7 +31,7 @@ arcTo(data: ArcToData, sketch: Sketch, tag?: TagDeclarator) -> Sketch
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> arcTo({ end: [10, 0], interior: [5, 5] }, %)
|> arcTo({ end = [10, 0], interior = [5, 5] }, %)
|> close(%)
example = extrude(10, exampleSketch)
```

View File

@ -34,8 +34,8 @@ asin(num: number) -> number
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({
angle: toDegrees(asin(0.5)),
length: 20
angle = toDegrees(asin(0.5)),
length = 20
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -34,8 +34,8 @@ atan(num: number) -> number
sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({
angle: toDegrees(atan(1.25)),
length: 20
angle = toDegrees(atan(1.25)),
length = 20
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -33,9 +33,9 @@ exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([0, 10], %)
|> bezierCurve({
to: [10, 10],
control1: [5, 0],
control2: [5, 10]
to = [10, 10],
control1 = [5, 0],
control2 = [5, 10]
}, %)
|> lineTo([10, 0], %)
|> close(%)

View File

@ -44,8 +44,8 @@ mountingPlateSketch = startSketchOn("XY")
mountingPlate = extrude(thickness, mountingPlateSketch)
|> chamfer({
length: chamferLength,
tags: [
length = chamferLength,
tags = [
getNextAdjacentEdge(edge1),
getNextAdjacentEdge(edge2),
getNextAdjacentEdge(edge3),
@ -58,7 +58,7 @@ mountingPlate = extrude(thickness, mountingPlateSketch)
```js
// Sketch on the face of a chamfer.
fn cube = (pos, scale) => {
fn cube(pos, scale) {
sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> line([0, scale], %)
@ -72,8 +72,8 @@ part001 = cube([0, 0], 20)
|> close(%, $line1)
|> extrude(20, %)
|> chamfer({
length: 10,
tags: [getOppositeEdge(line1)]
length = 10,
tags = [getOppositeEdge(line1)]
}, %, $chamfer1) // We tag the chamfer to reference it later.
sketch001 = startSketchOn(part001, chamfer1)

View File

@ -30,7 +30,7 @@ circle(data: CircleData, sketch_surface_or_group: SketchOrSurface, tag?: TagDecl
```js
exampleSketch = startSketchOn("-XZ")
|> circle({ center: [0, 0], radius: 10 }, %)
|> circle({ center = [0, 0], radius = 10 }, %)
example = extrude(5, exampleSketch)
```
@ -44,7 +44,7 @@ exampleSketch = startSketchOn("XZ")
|> line([0, 30], %)
|> line([-30, 0], %)
|> close(%)
|> hole(circle({ center: [0, 15], radius: 5 }, %), %)
|> hole(circle({ center = [0, 15], radius = 5 }, %), %)
example = extrude(5, exampleSketch)
```

View File

@ -34,8 +34,8 @@ cos(num: number) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 30,
length: 3 / cos(toRadians(30))
angle = 30,
length = 3 / cos(toRadians(30))
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -28,7 +28,7 @@ e() -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 30, length: 2 * e() ^ 2 }, %)
|> angledLine({ angle = 30, length = 2 * e() ^ 2 }, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -32,16 +32,16 @@ example = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> arc({
angleStart: 120,
angleEnd: 0,
radius: 5
angleStart = 120,
angleEnd = 0,
radius = 5
}, %)
|> line([5, 0], %)
|> line([0, 10], %)
|> bezierCurve({
control1: [-10, 0],
control2: [2, 10],
to: [-5, 10]
control1 = [-10, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> line([-5, -2], %)
|> close(%)
@ -54,16 +54,16 @@ example = startSketchOn('XZ')
exampleSketch = startSketchOn('XZ')
|> startProfileAt([-10, 0], %)
|> arc({
angleStart: 120,
angleEnd: -60,
radius: 5
angleStart = 120,
angleEnd = -60,
radius = 5
}, %)
|> line([10, 0], %)
|> line([5, 0], %)
|> bezierCurve({
control1: [-3, 0],
control2: [2, 10],
to: [-5, 10]
control1 = [-3, 0],
control2 = [2, 10],
to = [-5, 10]
}, %)
|> line([-4, 10], %)
|> line([-5, -2], %)

View File

@ -43,8 +43,8 @@ mountingPlateSketch = startSketchOn("XY")
mountingPlate = extrude(thickness, mountingPlateSketch)
|> fillet({
radius: filletRadius,
tags: [
radius = filletRadius,
tags = [
getNextAdjacentEdge(edge1),
getNextAdjacentEdge(edge2),
getNextAdjacentEdge(edge3),
@ -70,9 +70,9 @@ mountingPlateSketch = startSketchOn("XY")
mountingPlate = extrude(thickness, mountingPlateSketch)
|> fillet({
radius: filletRadius,
tolerance: 0.000001,
tags: [
radius = filletRadius,
tolerance = 0.000001,
tags = [
getNextAdjacentEdge(edge1),
getNextAdjacentEdge(edge2),
getNextAdjacentEdge(edge3),

View File

@ -30,16 +30,16 @@ getNextAdjacentEdge(tag: TagIdentifier) -> Uuid
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> angledLine({ angle: 120, length: 10 }, %)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %)
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%)
example = extrude(5, exampleSketch)
|> fillet({
radius: 3,
tags: [getNextAdjacentEdge(referenceEdge)]
radius = 3,
tags = [getNextAdjacentEdge(referenceEdge)]
}, %)
```

View File

@ -30,16 +30,16 @@ getOppositeEdge(tag: TagIdentifier) -> Uuid
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> angledLine({ angle: 120, length: 10 }, %)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %)
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%)
example = extrude(5, exampleSketch)
|> fillet({
radius: 3,
tags: [getOppositeEdge(referenceEdge)]
radius = 3,
tags = [getOppositeEdge(referenceEdge)]
}, %)
```

View File

@ -30,16 +30,16 @@ getPreviousAdjacentEdge(tag: TagIdentifier) -> Uuid
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line([10, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> angledLine({ angle: 120, length: 10 }, %)
|> angledLine({ angle = 60, length = 10 }, %)
|> angledLine({ angle = 120, length = 10 }, %)
|> line([-10, 0], %)
|> angledLine({ angle: 240, length: 10 }, %, $referenceEdge)
|> angledLine({ angle = 240, length = 10 }, %, $referenceEdge)
|> close(%)
example = extrude(5, exampleSketch)
|> fillet({
radius: 3,
tags: [getPreviousAdjacentEdge(referenceEdge)]
radius = 3,
tags = [getPreviousAdjacentEdge(referenceEdge)]
}, %)
```

View File

@ -29,12 +29,12 @@ helix(data: HelixData, solid: Solid) -> Solid
```js
part001 = startSketchOn('XY')
|> circle({ center: [5, 5], radius: 10 }, %)
|> circle({ center = [5, 5], radius = 10 }, %)
|> extrude(10, %)
|> helix({
angleStart: 0,
ccw: true,
revolutions: 16
angleStart = 0,
ccw = true,
revolutions = 16
}, %)
```

File diff suppressed because one or more lines are too long

View File

@ -68,15 +68,15 @@ case = startSketchOn('-XZ')
thing1 = startSketchOn(case, 'end')
|> circle({
center: [-size / 2, -size / 2],
radius: 25
center = [-size / 2, -size / 2],
radius = 25
}, %)
|> extrude(50, %)
thing2 = startSketchOn(case, 'end')
|> circle({
center: [size / 2, -size / 2],
radius: 25
center = [size / 2, -size / 2],
radius = 25
}, %)
|> extrude(50, %)

File diff suppressed because one or more lines are too long

View File

@ -74,6 +74,7 @@ layout: manual
* [`patternLinear2d`](kcl/patternLinear2d)
* [`patternLinear3d`](kcl/patternLinear3d)
* [`patternTransform`](kcl/patternTransform)
* [`patternTransform2d`](kcl/patternTransform2d)
* [`pi`](kcl/pi)
* [`polar`](kcl/polar)
* [`polygon`](kcl/polygon)
@ -85,6 +86,7 @@ layout: manual
* [`reduce`](kcl/reduce)
* [`rem`](kcl/rem)
* [`revolve`](kcl/revolve)
* [`round`](kcl/round)
* [`segAng`](kcl/segAng)
* [`segEnd`](kcl/segEnd)
* [`segEndX`](kcl/segEndX)
@ -100,6 +102,7 @@ layout: manual
* [`startSketchAt`](kcl/startSketchAt)
* [`startSketchOn`](kcl/startSketchOn)
* [`tan`](kcl/tan)
* [`tangentToEnd`](kcl/tangentToEnd)
* [`tangentialArc`](kcl/tangentialArc)
* [`tangentialArcTo`](kcl/tangentialArcTo)
* [`tangentialArcToRelative`](kcl/tangentialArcToRelative)

View File

@ -6,12 +6,10 @@ layout: manual
Convert a number to an integer.
Callers should use floor(), ceil(), or other rounding function first if they care about how numbers with fractional parts are converted. If the number has a fractional part, it's truncated, moving the number towards zero.
If the number is NaN or has a magnitude, either positive or negative, that is too large to fit into the internal integer representation, the result is a runtime error.
DEPRECATED use floor(), ceil(), or round().
```js
int(num: number) -> i64
int(num: number) -> number
```
### Tags
@ -27,7 +25,7 @@ int(num: number) -> i64
### Returns
`i64`
`number`
### Examples
@ -37,10 +35,10 @@ n = int(ceil(5 / 2))
assertEqual(n, 3, 0.0001, "5/2 = 2.5, rounded up makes 3")
// Draw n cylinders.
startSketchOn('XZ')
|> circle({ center: [0, 0], radius: 2 }, %)
|> circle({ center = [0, 0], radius = 2 }, %)
|> extrude(5, %)
|> patternTransform(n, (id) => {
return { translate: [4 * id, 0, 0] }
|> patternTransform(n, fn(id) {
return { translate = [4 * id, 0, 0] }
}, %)
```

View File

@ -60,10 +60,10 @@ squareSketch = startSketchOn('XY')
|> close(%)
circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|> circle({ center: [0, 100], radius: 20 }, %)
|> circle({ center = [0, 100], radius = 20 }, %)
loft([
squareSketch,
@ -85,10 +85,10 @@ squareSketch = startSketchOn('XY')
|> close(%)
circleSketch0 = startSketchOn(offsetPlane('XY', 75))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
circleSketch1 = startSketchOn(offsetPlane('XY', 150))
|> circle({ center: [0, 100], radius: 20 }, %)
|> circle({ center = [0, 100], radius = 20 }, %)
loft([
squareSketch,
@ -97,17 +97,17 @@ loft([
], {
// This can be set to override the automatically determined
// topological base curve, which is usually the first section encountered.
baseCurveIndex: 0,
baseCurveIndex = 0,
// Attempt to approximate rational curves (such as arcs) using a bezier.
// This will remove banding around interpolations between arcs and non-arcs.
// It may produce errors in other scenarios Over time, this field won't be necessary.
bezApproximateRational: false,
bezApproximateRational = false,
// Tolerance for the loft operation.
tolerance: 0.000001,
tolerance = 0.000001,
// Degree of the interpolation. Must be greater than zero.
// For example, use 2 for quadratic, or 3 for cubic interpolation in
// the V direction. This defaults to 2, if not specified.
vDegree: 2
vDegree = 2
})
```

View File

@ -29,9 +29,9 @@ map(array: [KclValue], map_fn: FunctionParam) -> [KclValue]
```js
r = 10 // radius
fn drawCircle = (id) => {
fn drawCircle(id) {
return startSketchOn("XY")
|> circle({ center: [id * 2 * r, 0], radius: r }, %)
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
}
// Call `drawCircle`, passing in each element of the array.
@ -45,9 +45,9 @@ circles = map([1..3], drawCircle)
```js
r = 10 // radius
// Call `map`, using an anonymous function instead of a named one.
circles = map([1..3], (id) => {
circles = map([1..3], (id) {
return startSketchOn("XY")
|> circle({ center: [id * 2 * r, 0], radius: r }, %)
|> circle({ center = [id * 2 * r, 0], radius = r }, %)
})
```

View File

@ -34,8 +34,8 @@ max(args: [number]) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 70,
length: max(15, 31, 4, 13, 22)
angle = 70,
length = max(15, 31, 4, 13, 22)
}, %)
|> line([20, 0], %)
|> close(%)

View File

@ -34,8 +34,8 @@ min(args: [number]) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 70,
length: min(15, 31, 4, 13, 22)
angle = 70,
length = min(15, 31, 4, 13, 22)
}, %)
|> line([20, 0], %)
|> close(%)

View File

@ -41,7 +41,7 @@ sketch001 = startSketchOn('XZ')
|> line([-8, -3], %)
|> line([9, -1], %)
|> line([-19, -0], %)
|> mirror2d({ axis: 'Y' }, %)
|> mirror2d({ axis = 'Y' }, %)
example = extrude(10, sketch001)
```
@ -54,7 +54,7 @@ sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 8.5], %)
|> line([20, -8.5], %)
|> line([-20, -8.5], %)
|> mirror2d({ axis: 'Y' }, %)
|> mirror2d({ axis = 'Y' }, %)
example = extrude(10, sketch001)
```
@ -71,7 +71,7 @@ sketch001 = startSketchOn('XZ')
|> startProfileAt([0, 8.5], %)
|> line([20, -8.5], %)
|> line([-20, -8.5], %)
|> mirror2d({ axis: edge001 }, %)
|> mirror2d({ axis = edge001 }, %)
example = extrude(10, sketch001)
```
@ -85,8 +85,11 @@ sketch001 = startSketchOn('XZ')
|> line([20, -8.5], %)
|> line([-20, -8.5], %)
|> mirror2d({
axis: {
custom: { axis: [0.0, 1.0], origin: [0.0, 0.0] }
axis = {
custom = {
axis = [0.0, 1.0],
origin = [0.0, 0.0]
}
}
}, %)

View File

@ -38,7 +38,7 @@ squareSketch = startSketchOn('XY')
|> close(%)
circleSketch = startSketchOn(offsetPlane('XY', 150))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
loft([squareSketch, circleSketch])
```
@ -56,7 +56,7 @@ squareSketch = startSketchOn('XZ')
|> close(%)
circleSketch = startSketchOn(offsetPlane('XZ', 150))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
loft([squareSketch, circleSketch])
```
@ -74,7 +74,7 @@ squareSketch = startSketchOn('YZ')
|> close(%)
circleSketch = startSketchOn(offsetPlane('YZ', 150))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
loft([squareSketch, circleSketch])
```
@ -92,7 +92,7 @@ squareSketch = startSketchOn('-XZ')
|> close(%)
circleSketch = startSketchOn(offsetPlane('-XZ', -150))
|> circle({ center: [0, 100], radius: 50 }, %)
|> circle({ center = [0, 100], radius = 50 }, %)
loft([squareSketch, circleSketch])
```
@ -103,7 +103,7 @@ loft([squareSketch, circleSketch])
// A circle on the XY plane
startSketchOn("XY")
|> startProfileAt([0, 0], %)
|> circle({ radius: 10, center: [0, 0] }, %)
|> circle({ radius = 10, center = [0, 0] }, %)
// Triangle on the plane 4 units above
startSketchOn(offsetPlane("XY", 4))

View File

@ -35,10 +35,10 @@ exampleSketch = startSketchOn('XZ')
|> line([0, -5], %)
|> close(%)
|> patternCircular2d({
center: [0, 0],
instances: 13,
arcDegrees: 360,
rotateDuplicates: true
center = [0, 0],
instances = 13,
arcDegrees = 360,
rotateDuplicates = true
}, %)
example = extrude(1, exampleSketch)

View File

@ -29,15 +29,15 @@ patternCircular3d(data: CircularPattern3dData, solid_set: SolidSet) -> [Solid]
```js
exampleSketch = startSketchOn('XZ')
|> circle({ center: [0, 0], radius: 1 }, %)
|> circle({ center = [0, 0], radius = 1 }, %)
example = extrude(-5, exampleSketch)
|> patternCircular3d({
axis: [1, -1, 0],
center: [10, -20, 0],
instances: 11,
arcDegrees: 360,
rotateDuplicates: true
axis = [1, -1, 0],
center = [10, -20, 0],
instances = 11,
arcDegrees = 360,
rotateDuplicates = true
}, %)
```

View File

@ -29,11 +29,11 @@ patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet) -> [Sketch]
```js
exampleSketch = startSketchOn('XZ')
|> circle({ center: [0, 0], radius: 1 }, %)
|> circle({ center = [0, 0], radius = 1 }, %)
|> patternLinear2d({
axis: [1, 0],
instances: 7,
distance: 4
axis = [1, 0],
instances = 7,
distance = 4
}, %)
example = extrude(1, exampleSketch)

View File

@ -37,9 +37,9 @@ exampleSketch = startSketchOn('XZ')
example = extrude(1, exampleSketch)
|> patternLinear3d({
axis: [1, 0, 1],
instances: 7,
distance: 6
axis = [1, 0, 1],
instances = 7,
distance = 6
}, %)
```

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

@ -29,7 +29,7 @@ polar(data: PolarCoordsData) -> [number]
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> line(polar({ angle: 30, length: 5 }), %, $thing)
|> line(polar({ angle = 30, length = 5 }), %, $thing)
|> line([0, 5], %)
|> line([segEndX(thing), 0], %)
|> line([-20, 10], %)

View File

@ -32,10 +32,10 @@ polygon(data: PolygonData, sketch_surface_or_group: SketchOrSurface, tag?: TagDe
// Create a regular hexagon inscribed in a circle of radius 10
hex = startSketchOn('XY')
|> polygon({
radius: 10,
numSides: 6,
center: [0, 0],
inscribed: true
radius = 10,
numSides = 6,
center = [0, 0],
inscribed = true
}, %)
example = extrude(5, hex)
@ -47,10 +47,10 @@ example = extrude(5, hex)
// Create a square circumscribed around a circle of radius 5
square = startSketchOn('XY')
|> polygon({
radius: 5.0,
numSides: 4,
center: [10, 10],
inscribed: false
radius = 5.0,
numSides = 4,
center = [10, 10],
inscribed = false
}, %)
example = extrude(5, square)
```

View File

@ -34,7 +34,7 @@ pow(num: number, pow: number) -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: pow(5, 2) }, %)
|> angledLine({ angle = 50, length = pow(5, 2) }, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -29,10 +29,10 @@ profileStart(sketch: Sketch) -> [number]
```js
sketch001 = startSketchOn('XY')
|> startProfileAt([5, 2], %)
|> angledLine({ angle: 120, length: 50 }, %, $seg01)
|> angledLine({ angle = 120, length = 50 }, %, $seg01)
|> angledLine({
angle: segAng(seg01) + 120,
length: 50
angle = segAng(seg01) + 120,
length = 50
}, %)
|> lineTo(profileStart(%), %)
|> close(%)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -30,14 +30,14 @@ reduce(array: [KclValue], start: KclValue, reduce_fn: FunctionParam) -> KclValue
```js
// This function adds two numbers.
fn add = (a, b) => {
fn add(a, b) {
return a + b
}
// This function adds an array of numbers.
// It uses the `reduce` function, to call the `add` function on every
// element of the `arr` parameter. The starting value is 0.
fn sum = (arr) => {
fn sum(arr) {
return reduce(arr, 0, add)
}
@ -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
// named function outside.
arr = [1, 2, 3]
sum = reduce(arr, 0, (i, result_so_far) => {
sum = reduce(arr, 0, (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
// 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.
stepAngle = 1 / 10 * tau()
@ -84,7 +84,7 @@ fn decagon = (radius) => {
// Use a `reduce` to draw the remaining decagon sides.
// 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.
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) => {
fullDecagon = reduce([1..10], startOfDecagonSketch, (i, partialDecagon) {
// Draw one edge of the decagon.
x = cos(stepAngle * i) * radius
y = sin(stepAngle * i) * radius

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

46
docs/kcl/round.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -36,9 +36,12 @@ cube = startSketchAt([0, 0])
|> close(%)
|> extrude(5, %)
fn cylinder = (radius, tag) => {
fn cylinder(radius, tag) {
return startSketchAt([0, 0])
|> circle({ radius: radius, center: segEnd(tag) }, %)
|> circle({
radius = radius,
center = segEnd(tag)
}, %)
|> extrude(radius, %)
}

View File

@ -29,9 +29,9 @@ segLen(tag: TagIdentifier) -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 60, length: 10 }, %, $thing)
|> tangentialArc({ offset: -120, radius: 5 }, %)
|> angledLine({ angle: -60, length: segLen(thing) }, %)
|> angledLine({ angle = 60, length = 10 }, %, $thing)
|> tangentialArc({ offset = -120, radius = 5 }, %)
|> angledLine({ angle = -60, length = segLen(thing) }, %)
|> close(%)
example = extrude(5, exampleSketch)

View File

@ -36,11 +36,11 @@ cube = startSketchAt([0, 0])
|> close(%)
|> extrude(5, %)
fn cylinder = (radius, tag) => {
fn cylinder(radius, tag) {
return startSketchAt([0, 0])
|> circle({
radius: radius,
center: segStart(tag)
radius = radius,
center = segStart(tag)
}, %)
|> extrude(radius, %)
}

File diff suppressed because one or more lines are too long

View File

@ -34,8 +34,8 @@ sin(num: number) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 50,
length: 15 / sin(toDegrees(135))
angle = 50,
length = 15 / sin(toDegrees(135))
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -33,7 +33,7 @@ sqrt(num: number) -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: sqrt(2500) }, %)
|> angledLine({ angle = 50, length = sqrt(2500) }, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -101,7 +101,7 @@ exampleSketch = startSketchOn('XY')
|> line([-2, 0], %)
|> close(%)
example = revolve({ axis: 'y', angle: 180 }, exampleSketch)
example = revolve({ axis = 'y', angle = 180 }, exampleSketch)
exampleSketch002 = startSketchOn(example, 'end')
|> startProfileAt([4.5, -5], %)
@ -117,11 +117,11 @@ example002 = extrude(5, exampleSketch002)
```js
a1 = startSketchOn({
plane: {
origin: { x: 0, y: 0, z: 0 },
xAxis: { x: 1, y: 0, z: 0 },
yAxis: { x: 0, y: 1, z: 0 },
zAxis: { x: 0, y: 0, z: 1 }
plane = {
origin = { x = 0, y = 0, z = 0 },
xAxis = { x = 1, y = 0, z = 0 },
yAxis = { x = 0, y = 1, z = 0 },
zAxis = { x = 0, y = 0, z = 1 }
}
})
|> startProfileAt([0, 0], %)

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ tan(num: number) -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 50 * tan(1 / 2) }, %)
|> angledLine({ angle = 50, length = 50 * tan(1 / 2) }, %)
|> yLineTo(0, %)
|> close(%)

105
docs/kcl/tangentToEnd.md Normal file

File diff suppressed because one or more lines are too long

View File

@ -31,9 +31,9 @@ tangentialArc(data: TangentialArcData, sketch: Sketch, tag?: TagDeclarator) -> S
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> tangentialArc({ radius: 10, offset: -120 }, %)
|> angledLine({ angle: -60, length: 10 }, %)
|> angledLine({ angle = 60, length = 10 }, %)
|> tangentialArc({ radius = 10, offset = -120 }, %)
|> angledLine({ angle = -60, length = 10 }, %)
|> close(%)
example = extrude(10, exampleSketch)

View File

@ -31,7 +31,7 @@ tangentialArcTo(to: [number], sketch: Sketch, tag?: TagDeclarator) -> Sketch
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 60, length: 10 }, %)
|> angledLine({ angle = 60, length = 10 }, %)
|> tangentialArcTo([15, 15], %)
|> line([10, -15], %)
|> close(%)

View File

@ -31,7 +31,7 @@ tangentialArcToRelative(delta: [number], sketch: Sketch, tag?: TagDeclarator) ->
```js
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 45, length: 10 }, %)
|> angledLine({ angle = 45, length = 10 }, %)
|> tangentialArcToRelative([0, -10], %)
|> line([-10, 0], %)
|> close(%)

View File

@ -28,7 +28,7 @@ tau() -> number
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 10 * tau() }, %)
|> angledLine({ angle = 50, length = 10 * tau() }, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -34,8 +34,8 @@ toDegrees(num: number) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 50,
length: 70 * cos(toDegrees(pi() / 4))
angle = 50,
length = 70 * cos(toDegrees(pi() / 4))
}, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -34,8 +34,8 @@ toRadians(num: number) -> number
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({
angle: 50,
length: 70 * cos(toRadians(45))
angle = 50,
length = 70 * cos(toRadians(45))
}, %)
|> yLineTo(0, %)
|> close(%)

View 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:
```
myObj = {a: 0, b: "thing"}
myObj = { a = 0, b = "thing" }
```
We support two different ways of getting properties from objects, you can call
@ -90,12 +90,12 @@ startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
@ -120,18 +120,18 @@ However if the code was written like this:
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
@ -148,26 +148,29 @@ For example the following code works.
```
fn rect = (origin) => {
return startSketchOn('XZ')
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001, %) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001, %),
-segLen(rectangleSegmentA001, %)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
|> startProfileAt(origin, %)
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|> angledLine([
segAng(rectangleSegmentA001) - 90,
196.99
], %, $rectangleSegmentB001)
|> angledLine([
segAng(rectangleSegmentA001),
-segLen(rectangleSegmentA001)
], %, $rectangleSegmentC001)
|> lineTo([profileStartX(%), profileStartY(%)], %)
|> close(%)
}
rect([0, 0])
myRect = rect([20, 0])
myRect
myRect
|> 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

View File

@ -85,7 +85,6 @@ layout: manual
| `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 |

View File

@ -16,7 +16,7 @@ Data for a circular pattern on a 2D sketch.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `instances` |[`Uint`](/docs/kcl/types/Uint)| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `instances` |`integer`| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |

View File

@ -16,7 +16,7 @@ Data for a circular pattern on a 3D model.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `instances` |[`Uint`](/docs/kcl/types/Uint)| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `instances` |`integer`| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `axis` |`[number, number, number]`| The axis around which to make the pattern. This is a 3D vector. | No |
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |

View File

@ -125,7 +125,6 @@ An expression can be evaluated to yield a single KCL value.
| `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 |

View File

@ -16,7 +16,7 @@ Data for a linear pattern on a 2D sketch.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `instances` |[`Uint`](/docs/kcl/types/Uint)| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `instances` |`integer`| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `distance` |`number`| The distance between each repetition. This can also be referred to as spacing. | No |
| `axis` |`[number, number]`| The axis of the pattern. This is a 2D vector. | No |

View File

@ -16,7 +16,7 @@ Data for a linear pattern on a 3D model.
| Property | Type | Description | Required |
|----------|------|-------------|----------|
| `instances` |[`Uint`](/docs/kcl/types/Uint)| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `instances` |`integer`| The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | No |
| `distance` |`number`| The distance between each repetition. This can also be referred to as spacing. | No |
| `axis` |`[number, number, number]`| The axis of the pattern. | No |

View File

@ -11,16 +11,6 @@ layout: manual
**This schema accepts any of the following:**
**Type:** `integer` (`int64`)
----
**Type:** `number` (`double`)

View File

@ -11,23 +11,6 @@ layout: manual
**This schema accepts exactly one of the following:**
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 |
|----------|------|-------------|----------|
| `type` |enum: `shebang`| | No |
| `value` |`string`| | No |
----
An inline comment. Here are examples: `1 + 1 // This is an inline comment`. `1 + 1 /* Here's another */`.
**Type:** `object`

View File

@ -91,7 +91,7 @@ a complete arc
| `type` |enum: `Circle`| | No |
| `center` |`[number, number]`| the arc's center | No |
| `radius` |`number`| the arc's radius | No |
| `ccw` |`boolean`| arc's direction | No |
| `ccw` |`boolean`| arc's direction This is used to compute the tangential angle. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
@ -177,6 +177,7 @@ A circular arc, not necessarily tangential to the current point.
| `type` |enum: `Arc`| | No |
| `center` |`[number, number]`| Center of the circle that this arc is drawn on. | No |
| `radius` |`number`| Radius of the circle that this arc is drawn on. | No |
| `ccw` |`boolean`| True if the arc is counterclockwise. | No |
| `from` |`[number, number]`| The from point. | No |
| `to` |`[number, number]`| The to point. | No |
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |

View File

@ -18,6 +18,7 @@ A KCL program top level, or function body.
|----------|------|-------------|----------|
| `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 |

23
docs/kcl/types/Shebang.md Normal file
View File

@ -0,0 +1,23 @@
---
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 |

View File

@ -32,10 +32,10 @@ xLine(length: number, sketch: Sketch, tag?: TagDeclarator) -> Sketch
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> xLine(15, %)
|> angledLine({ angle: 80, length: 15 }, %)
|> angledLine({ angle = 80, length = 15 }, %)
|> line([8, -10], %)
|> xLine(10, %)
|> angledLine({ angle: 120, length: 30 }, %)
|> angledLine({ angle = 120, length = 30 }, %)
|> xLine(-15, %)
|> close(%)

View File

@ -32,10 +32,10 @@ xLineTo(to: number, sketch: Sketch, tag?: TagDeclarator) -> Sketch
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> xLineTo(15, %)
|> angledLine({ angle: 80, length: 15 }, %)
|> angledLine({ angle = 80, length = 15 }, %)
|> line([8, -10], %)
|> xLineTo(40, %)
|> angledLine({ angle: 135, length: 30 }, %)
|> angledLine({ angle = 135, length = 30 }, %)
|> xLineTo(10, %)
|> close(%)

View File

@ -32,7 +32,7 @@ yLine(length: number, sketch: Sketch, tag?: TagDeclarator) -> Sketch
exampleSketch = startSketchOn('XZ')
|> startProfileAt([0, 0], %)
|> yLine(15, %)
|> angledLine({ angle: 30, length: 15 }, %)
|> angledLine({ angle = 30, length = 15 }, %)
|> line([8, -10], %)
|> yLine(-5, %)
|> close(%)

View File

@ -31,7 +31,7 @@ yLineTo(to: number, sketch: Sketch, tag?: TagDeclarator) -> Sketch
```js
exampleSketch = startSketchOn("XZ")
|> startProfileAt([0, 0], %)
|> angledLine({ angle: 50, length: 45 }, %)
|> angledLine({ angle = 50, length = 45 }, %)
|> yLineTo(0, %)
|> close(%)

View File

@ -81,7 +81,7 @@ test.describe('Command bar tests', () => {
await page.keyboard.press('Enter') // submit
await page.waitForTimeout(100)
await expect(page.locator('.cm-activeLine')).toContainText(
`fillet({ radius: ${KCL_DEFAULT_LENGTH}, tags: [seg01] }, %)`
`fillet({ radius = ${KCL_DEFAULT_LENGTH}, tags = [seg01] }, %)`
)
})

View File

@ -35,7 +35,7 @@ export class CmdBarFixture {
}
private _serialiseCmdBar = async (): Promise<CmdBarSerialised> => {
const reviewForm = await this.page.locator('#review-form')
const reviewForm = this.page.locator('#review-form')
const getHeaderArgs = async () => {
const inputs = await this.page.getByTestId('cmd-bar-input-tab').all()
const entries = await Promise.all(

View File

@ -28,6 +28,7 @@ type SceneSerialised = {
type ClickHandler = (clickParams?: 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 DragFromHandler = (
dragParams: mouseDragFromParams
@ -67,7 +68,7 @@ export class SceneFixture {
x: number,
y: number,
{ steps }: { steps: number } = { steps: 20 }
): [ClickHandler, MoveHandler] =>
): [ClickHandler, MoveHandler, DblClickHandler] =>
[
(clickParams?: mouseParams) => {
if (clickParams?.pixelDiff) {
@ -89,6 +90,16 @@ export class SceneFixture {
}
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
makeDragHelpers = (
x: number,

View File

@ -6,6 +6,7 @@ export class ToolbarFixture {
public page: Page
extrudeButton!: Locator
offsetPlaneButton!: Locator
startSketchBtn!: Locator
lineBtn!: Locator
rectangleBtn!: Locator
@ -25,6 +26,7 @@ export class ToolbarFixture {
reConstruct = (page: Page) => {
this.page = page
this.extrudeButton = page.getByTestId('extrude')
this.offsetPlaneButton = page.getByTestId('plane-offset')
this.startSketchBtn = page.getByTestId('sketch')
this.lineBtn = page.getByTestId('line')
this.rectangleBtn = page.getByTestId('corner-rectangle')

View File

@ -33,7 +33,7 @@ test(
await test.step('check code model connection works and that button is still enable once circle is selected ', async () => {
await moveToCircle()
const circleSnippet =
'circle({ center: [318.33, 168.1], radius: 182.8 }, %)'
'circle({ center = [318.33, 168.1], radius = 182.8 }, %)'
await editor.expectState({
activeLines: [
"constsketch002=startSketchOn('XZ')"
@ -184,7 +184,7 @@ test.describe('verify sketch on chamfer works', () => {
cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer({length:30,tags:[
chamfer({length = 30,tags = [
seg01,
getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02),
@ -215,8 +215,8 @@ test.describe('verify sketch on chamfer works', () => {
segAng(rectangleSegmentA001) - 90,
217.26
], %, $seg01)chamfer({
length: 30,
tags: [
length = 30,
tags = [
seg01,
getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02)
@ -243,8 +243,8 @@ test.describe('verify sketch on chamfer works', () => {
cameraPos: { x: -6200, y: 1500, z: 6200 },
cameraTarget: { x: 8300, y: 1100, z: 4800 },
beforeChamferSnippet: `angledLine([0, 268.43], %, $rectangleSegmentA001)chamfer({
length: 30,
tags: [
length = 30,
tags = [
getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02)
]
@ -270,8 +270,8 @@ test.describe('verify sketch on chamfer works', () => {
cameraPos: { x: -1100, y: -7700, z: 1600 },
cameraTarget: { x: 1450, y: 670, z: 4000 },
beforeChamferSnippet: `chamfer({
length: 30,
tags: [getNextAdjacentEdge(yo)]
length = 30,
tags = [getNextAdjacentEdge(yo)]
}, %)`,
afterChamferSelectSnippet:
'sketch005 = startSketchOn(extrude001, seg06)',
@ -308,17 +308,17 @@ test.describe('verify sketch on chamfer works', () => {
|> close(%)
extrude001 = extrude(100, sketch001)
|> chamfer({
length: 30,
tags: [getOppositeEdge(seg01)]
length = 30,
tags = [getOppositeEdge(seg01)]
}, %, $seg03)
|> chamfer({ length: 30, tags: [seg01] }, %, $seg04)
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|> chamfer({
length: 30,
tags: [getNextAdjacentEdge(seg02)]
length = 30,
tags = [getNextAdjacentEdge(seg02)]
}, %, $seg05)
|> chamfer({
length: 30,
tags: [getNextAdjacentEdge(yo)]
length = 30,
tags = [getNextAdjacentEdge(yo)]
}, %, $seg06)
sketch005 = startSketchOn(extrude001, seg06)
|> startProfileAt([-59.83,19.69], %)
@ -403,7 +403,7 @@ test.describe('verify sketch on chamfer works', () => {
cameraPos: { x: 16020, y: -2000, z: 10500 },
cameraTarget: { x: -150, y: -4500, z: -80 },
beforeChamferSnippet: `angledLine([segAng(rectangleSegmentA001)-90,217.26],%,$seg01)
chamfer({length:30,tags:[
chamfer({length=30,tags=[
seg01,
getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02),
@ -441,12 +441,12 @@ test.describe('verify sketch on chamfer works', () => {
|> close(%)
extrude001 = extrude(100, sketch001)
chamf = chamfer({
length: 30,
tags: [getOppositeEdge(seg01)]
length = 30,
tags = [getOppositeEdge(seg01)]
}, extrude001, $seg03)
|> chamfer({
length: 30,
tags: [
length = 30,
tags = [
seg01,
getNextAdjacentEdge(yo),
getNextAdjacentEdge(seg02)
@ -576,3 +576,129 @@ 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 ({
app,
scene,
editor,
toolbar,
cmdBar,
}) => {
await app.initialise()
// One dumb hardcoded screen pixel value
const testPoint = { x: 700, y: 150 }
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
await test.step(`Look for the blue of the XZ plane`, async () => {
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
})
await test.step(`Go through the command bar flow`, async () => {
await toolbar.offsetPlaneButton.click()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'plane',
currentArgValue: '',
headerArguments: { Plane: '', Distance: '' },
highlightedHeaderArg: 'plane',
commandName: 'Offset plane',
})
await clickOnXzPlane()
await cmdBar.expectState({
stage: 'arguments',
currentArgKey: 'distance',
currentArgValue: '5',
headerArguments: { Plane: '1 plane', Distance: '' },
highlightedHeaderArg: 'distance',
commandName: 'Offset plane',
})
await cmdBar.progressCmdBar()
})
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
await editor.expectEditor.toContain(expectedOutput)
await editor.expectState({
diagnostics: [],
activeLines: [expectedOutput],
highlightedCode: '',
})
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
})
})

View File

@ -391,7 +391,7 @@ test.describe('Sketch tests', () => {
const dragPX = 40
await page
.getByText('circle({ center: [4.61, -5.01], radius: 8 }, %)')
.getByText('circle({ center = [4.61, -5.01], radius = 8 }, %)')
.click()
await expect(
page.getByRole('button', { name: 'Edit Sketch' })

View File

@ -77,27 +77,27 @@ part001 = startSketchOn('-XZ')
|> yLine(baseHeight, %)
|> xLine(baseLen, %)
|> angledLineToY({
angle: topAng,
to: totalHeightHalf,
angle = topAng,
to = totalHeightHalf,
}, %, $seg04)
|> xLineTo(totalLen, %, $seg03)
|> yLine(-armThick, %, $seg01)
|> angledLineThatIntersects({
angle: HALF_TURN,
offset: -armThick,
intersectTag: seg04
angle = HALF_TURN,
offset = -armThick,
intersectTag = seg04
}, %)
|> angledLineToY([segAng(seg04, %) + 180, ZERO], %)
|> angledLineToY({
angle: -bottomAng,
to: -totalHeightHalf - armThick,
angle = -bottomAng,
to = -totalHeightHalf - armThick,
}, %, $seg02)
|> xLineTo(segEndX(seg03, %) + 0, %)
|> yLine(-segLen(seg01, %), %)
|> angledLineThatIntersects({
angle: HALF_TURN,
offset: -armThick,
intersectTag: seg02
angle = HALF_TURN,
offset = -armThick,
intersectTag = seg02
}, %)
|> angledLineToY([segAng(seg02, %) + 180, -baseHeight], %)
|> xLineTo(ZERO, %)
@ -592,7 +592,7 @@ test(
})
await expect(page.locator('.cm-content')).toHaveText(
`sketch001 = startSketchOn('XZ')
|> circle({ center: [14.44, -2.44], radius: 1 }, %)`
|> circle({ center = [14.44, -2.44], radius = 1 }, %)`
)
}
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

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