Compare commits
44 Commits
kurt-web-a
...
nicboone8-
Author | SHA1 | Date | |
---|---|---|---|
1dad2c208d | |||
2b0ced179a | |||
caea13fe91 | |||
2a02e0eacf | |||
c2f6ce065d | |||
b3bdc35da2 | |||
8fe4f67a29 | |||
1f58f31cf3 | |||
c6b1d11700 | |||
2ef84382a6 | |||
ff5f10c9cc | |||
939c2c77b0 | |||
f34d3ab8bc | |||
61ae2c9bcd | |||
31ec0184a1 | |||
62c4546658 | |||
18959c8585 | |||
e036ad37ad | |||
f7dafed7a4 | |||
383b38c2d2 | |||
e0025f7fad | |||
2a13888c54 | |||
1443f3ab39 | |||
bf87c23ea8 | |||
5d23b0e487 | |||
df6c81b0b4 | |||
5f1f579d4b | |||
9a549ff379 | |||
851ea28bd3 | |||
ff15c7b9db | |||
f304577d5d | |||
b03b0d3b53 | |||
dd4d0f6d98 | |||
1cd742df5d | |||
6460ed8ea8 | |||
5c51b27f29 | |||
77690b4419 | |||
6996670020 | |||
1fd4e93091 | |||
a1ac029333 | |||
29cf16d744 | |||
9b3afccf53 | |||
231ca0fa35 | |||
4608c02442 |
38
.github/ISSUE_TEMPLATE/release.md
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
---
|
||||
name: Release
|
||||
about: Create a new release for the Zoo Design Studio
|
||||
title: "Cut release v1.?.?"
|
||||
labels: [release]
|
||||
---
|
||||
|
||||
> Instructions: https://github.com/KittyCAD/modeling-app/blob/main/CONTRIBUTING.md#shipping-releases
|
||||
|
||||
---
|
||||
|
||||
# Manual Checklist
|
||||
|
||||
Release builds URL: ???
|
||||
|
||||
## Windows via ???
|
||||
|
||||
* [ ] Download the release build for this platform
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
|
||||
## macOS via ???
|
||||
|
||||
* [ ] Download the release build for this platform
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
||||
|
||||
## Linux via ???
|
||||
|
||||
* [ ] Download the release build for this platform
|
||||
* [ ] Confirm the application opens (dismiss the updater)
|
||||
* [ ] Create a project with a basic Text-to-CAD prompt
|
||||
* [ ] Confirm the result is viewable in an engine stream
|
||||
* [ ] Open the application again and confirm the updater can downgrade
|
71
.github/workflows/build-apps.yml
vendored
@ -10,7 +10,7 @@ on:
|
||||
|
||||
env:
|
||||
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
|
||||
IS_NIGHTLY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
IS_STAGING: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
@ -91,14 +91,14 @@ jobs:
|
||||
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||
run: "npm run build:wasm"
|
||||
|
||||
- name: Set nightly version, product name, release notes, and icons
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
- name: Set staging version, product name, release notes, and icons
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
run: |
|
||||
COMMIT=$(git rev-parse --short HEAD)
|
||||
DATE=$(date +'%-y.%-m.%-d')
|
||||
export VERSION=$DATE-main.$COMMIT
|
||||
npm run files:set-version
|
||||
npm run files:flip-to-nightly
|
||||
npm run files:flip-to-staging
|
||||
|
||||
- name: Set release version
|
||||
if: ${{ env.IS_RELEASE == 'true' }}
|
||||
@ -130,13 +130,14 @@ jobs:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-14
|
||||
platform: mac
|
||||
- os: windows-2022
|
||||
platform: win
|
||||
- os: namespace-profile-macos-6-cores
|
||||
platform: macos
|
||||
- os: namespace-profile-windows-4-cores
|
||||
platform: windows
|
||||
- os: ubuntu-22.04
|
||||
platform: linux
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: build-apps (${{ matrix.platform }})
|
||||
env:
|
||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||
steps:
|
||||
@ -166,14 +167,14 @@ jobs:
|
||||
- run: npm install
|
||||
|
||||
- name: Prepare certificate and variables (Windows only)
|
||||
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
|
||||
if: ${{ (env.IS_RELEASE == 'true' || env.IS_STAGING == 'true') && matrix.platform == 'windows' }}
|
||||
run: |
|
||||
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
|
||||
cat /d/Certificate_pkcs12.p12
|
||||
echo "${{secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /c/Certificate_pkcs12.p12
|
||||
cat /c/Certificate_pkcs12.p12
|
||||
echo "::set-output name=version::${GITHUB_REF#refs/tags/v}"
|
||||
echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV"
|
||||
echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV"
|
||||
echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
|
||||
echo "SM_CLIENT_CERT_FILE=C:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
|
||||
echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV"
|
||||
echo "C:\Program Files (x86)\Windows Kits\10\App Certification Kit" >> $GITHUB_PATH
|
||||
echo "C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" >> $GITHUB_PATH
|
||||
@ -181,7 +182,7 @@ jobs:
|
||||
shell: bash
|
||||
|
||||
- name: Setup certicate with SSM KSP (Windows only)
|
||||
if: ${{ (env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true') && matrix.os == 'windows-2022' }}
|
||||
if: ${{ (env.IS_RELEASE == 'true' || env.IS_STAGING == 'true') && matrix.platform == 'windows' }}
|
||||
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
|
||||
@ -191,7 +192,7 @@ jobs:
|
||||
smksp_cert_sync.exe
|
||||
smctl windows certsync
|
||||
# This last line `smctl windows certsync` was added after windows codesign failures started happening
|
||||
# with nightly-v25.4.10. It looks like `smksp_cert_sync.exe` used to do the sync to the local cert store,
|
||||
# with staging-v25.4.10. It looks like `smksp_cert_sync.exe` used to do the sync to the local cert store,
|
||||
# but stopped doing it overnight. This extra call that I randomly got from this azure-related doc page
|
||||
# https://docs.digicert.com/en/digicert-keylocker/code-signing/sign-with-third-party-signing-tools/windows-applications/sign-azure-apps-with-signtool-using-ksp-library.html#sync-certificates--windows-only--618365
|
||||
# seems to be doing that extra sync that we need for scripts/sign-win.js to work.
|
||||
@ -199,13 +200,13 @@ jobs:
|
||||
shell: cmd
|
||||
|
||||
- name: Build the app (debug)
|
||||
if: ${{ env.IS_RELEASE == 'false' && env.IS_NIGHTLY == 'false' }}
|
||||
if: ${{ env.IS_RELEASE == 'false' && env.IS_STAGING == 'false' }}
|
||||
# electron-builder doesn't have a concept of release vs debug,
|
||||
# this is just not doing any codesign or release yml generation, and points to dev infra
|
||||
run: npm run tronb:package:dev
|
||||
|
||||
- name: Build the app (release)
|
||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_STAGING == 'true' }}
|
||||
env:
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
@ -215,7 +216,7 @@ jobs:
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
CSC_KEYCHAIN: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
WINDOWS_CERTIFICATE_THUMBPRINT: ${{ secrets.WINDOWS_CERTIFICATE_THUMBPRINT }}
|
||||
run: npm run tronb:package:prod
|
||||
run: npm run tronb:package:${{ env.IS_STAGING == 'true' && 'dev' || 'prod' }}
|
||||
|
||||
- name: List artifacts in out/
|
||||
run: ls -R out
|
||||
@ -239,20 +240,20 @@ jobs:
|
||||
out/*-x86_64-linux.*
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_NIGHTLY == 'true' }}
|
||||
if: ${{ env.IS_RELEASE == 'true' || env.IS_STAGING == 'true' }}
|
||||
with:
|
||||
name: out-yml-${{ matrix.platform }}
|
||||
path: |
|
||||
out/latest*.yml
|
||||
|
||||
# TODO: add the 'Build for Mac TestFlight (nightly)' stage back
|
||||
# TODO: add the 'Build for Mac TestFlight' stage back
|
||||
|
||||
|
||||
upload-apps-release:
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: write
|
||||
# Equivalent to IS_RELEASE || IS_NIGHTLY (but we can't access those env vars here)
|
||||
# Equivalent to IS_RELEASE || IS_STAGING (but we can't access those env vars here)
|
||||
if: ${{ (github.ref_type == 'tag' && startsWith(github.ref_name, 'v')) || (github.event_name == 'push' && github.ref == 'refs/heads/main') }}
|
||||
env:
|
||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||
@ -263,32 +264,32 @@ jobs:
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-arm64-win
|
||||
name: out-arm64-windows
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-x64-win
|
||||
name: out-x64-windows
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-yml-win
|
||||
name: out-yml-windows
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-arm64-mac
|
||||
name: out-arm64-macos
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-x64-mac
|
||||
name: out-x64-macos
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-yml-mac
|
||||
name: out-yml-macos
|
||||
path: out
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
@ -310,8 +311,8 @@ jobs:
|
||||
env:
|
||||
NOTES: ${{ needs.prepare-files.outputs.notes }}
|
||||
PUB_DATE: ${{ github.event.repository.updated_at }}
|
||||
WEBSITE_DIR: ${{ env.IS_NIGHTLY == 'true' && 'dl.zoo.dev/releases/modeling-app/nightly' || 'dl.zoo.dev/releases/modeling-app' }}
|
||||
URL_CODED_NAME: ${{ env.IS_NIGHTLY == 'true' && 'Zoo%20Design%20Studio%20%28Nightly%29' || 'Zoo%20Design%20Studio' }}
|
||||
WEBSITE_DIR: ${{ env.IS_STAGING == 'true' && 'dl.zoo.dev/releases/modeling-app/staging' || 'dl.zoo.dev/releases/modeling-app' }}
|
||||
URL_CODED_NAME: ${{ env.IS_STAGING == 'true' && 'Zoo%20Design%20Studio%20%28Staging%29' || 'Zoo%20Design%20Studio' }}
|
||||
run: |
|
||||
RELEASE_DIR=https://${WEBSITE_DIR}
|
||||
jq --null-input \
|
||||
@ -360,26 +361,26 @@ jobs:
|
||||
run: "ls -R out"
|
||||
|
||||
- name: Authenticate to Google Cloud
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: 'google-github-actions/auth@v2.1.8'
|
||||
with:
|
||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||
|
||||
- name: Set up Google Cloud SDK
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: google-github-actions/setup-gcloud@v2.1.4
|
||||
with:
|
||||
project_id: ${{ env.GOOGLE_CLOUD_PROJECT_ID }}
|
||||
|
||||
- name: Upload nightly files to public bucket
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
- name: Upload staging files to public bucket
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
uses: google-github-actions/upload-cloud-storage@v2.2.2
|
||||
with:
|
||||
path: out
|
||||
glob: '*'
|
||||
parent: false
|
||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||
destination: 'dl.kittycad.io/releases/modeling-app/staging'
|
||||
|
||||
- name: Invalidate bucket cache on latest*.yml and last_download.json files
|
||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||
run: npm run files:invalidate-bucket:nightly
|
||||
if: ${{ env.IS_STAGING == 'true' }}
|
||||
run: npm run files:invalidate-bucket:staging
|
||||
|
20
.github/workflows/e2e-tests.yml
vendored
@ -177,16 +177,16 @@ jobs:
|
||||
TARGET: web
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
if: ${{ !cancelled() }}
|
||||
with:
|
||||
name: playwright-report-ubuntu-snapshot-${{ github.sha }}
|
||||
name: playwright-report-snapshot-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
overwrite: true
|
||||
|
||||
- name: Check diff
|
||||
if: ${{ github.ref != 'refs/heads/main' }}
|
||||
if: ${{ always() && github.ref != 'refs/heads/main' }}
|
||||
shell: bash
|
||||
id: git-check
|
||||
run: |
|
||||
@ -197,7 +197,7 @@ jobs:
|
||||
fi
|
||||
|
||||
- name: Commit changes
|
||||
if: ${{ steps.git-check.outputs.modified == 'true' }}
|
||||
if: ${{ always() && steps.git-check.outputs.modified == 'true' }}
|
||||
shell: bash
|
||||
run: |
|
||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||
@ -220,7 +220,7 @@ jobs:
|
||||
include:
|
||||
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||
- os: namespace-profile-macos-8-cores
|
||||
- os: windows-latest-8-cores
|
||||
- os: namespace-profile-windows-8-cores
|
||||
runs-on: ${{ matrix.os }}
|
||||
name: e2e:web (${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }})
|
||||
env:
|
||||
@ -293,6 +293,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: ${{ !cancelled() && (success() || failure()) }}
|
||||
with:
|
||||
name: playwright-report-web-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
@ -304,7 +305,6 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# TODO: enable namespace-profile-windows-latest once available
|
||||
include:
|
||||
- os: "runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64"
|
||||
shardIndex: 1
|
||||
@ -336,10 +336,10 @@ jobs:
|
||||
- os: namespace-profile-macos-8-cores
|
||||
shardIndex: 2
|
||||
shardTotal: 2
|
||||
- os: windows-latest-8-cores
|
||||
- os: namespace-profile-windows-8-cores
|
||||
shardIndex: 1
|
||||
shardTotal: 2
|
||||
- os: windows-latest-8-cores
|
||||
- os: namespace-profile-windows-8-cores
|
||||
shardIndex: 2
|
||||
shardTotal: 2
|
||||
runs-on: ${{ matrix.os }}
|
||||
@ -418,7 +418,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
name: test-results-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: test-results/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
@ -427,7 +427,7 @@ jobs:
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: always()
|
||||
with:
|
||||
name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
name: playwright-report-desktop-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||
path: playwright-report/
|
||||
include-hidden-files: true
|
||||
retention-days: 30
|
||||
|
12
.github/workflows/publish-apps-release.yml
vendored
@ -31,42 +31,42 @@ jobs:
|
||||
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: out-arm64-win
|
||||
name: out-arm64-windows
|
||||
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
|
||||
name: out-x64-windows
|
||||
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
|
||||
name: out-yml-windows
|
||||
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
|
||||
name: out-arm64-macos
|
||||
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
|
||||
name: out-x64-macos
|
||||
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
|
||||
name: out-yml-macos
|
||||
path: out
|
||||
run-id: ${{ steps.tag_workflow_id.outputs.id }}
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
@ -251,7 +251,8 @@ Before you submit a contribution PR to this repo, please ensure that:
|
||||
|
||||
#### 1. Create a 'Cut release $VERSION' issue
|
||||
|
||||
It will be used to document changelog discussions and release testing.
|
||||
Use the **Release** issue template.
|
||||
This will be used to facilitate changelog discussions and release testing.
|
||||
|
||||
https://github.com/KittyCAD/modeling-app/issues/new
|
||||
|
||||
@ -270,27 +271,9 @@ The workflow should be listed right away [in this list](https://github.com/Kitty
|
||||
|
||||
#### 3. Manually test artifacts
|
||||
|
||||
##### 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 step 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 issue.
|
||||
|
||||
A prompt should show up asking for a downgrade to the last release version. Running through that at the end of testing
|
||||
and making sure the current release candidate has the ability to be updated to what electron-updater points to is critical,
|
||||
but what is actually being downloaded and installed isn't.
|
||||
If the prompt doesn't show up, start the app in command line to grab the electron-updater logs. This is likely an issue with the current build that needs addressing.
|
||||
|
||||
```
|
||||
# Windows (PowerShell)
|
||||
& 'C:\Program Files\Zoo Design Studio\Zoo Design Studio.exe'
|
||||
|
||||
# macOS
|
||||
/Applications/Zoo\ Modeling\ App.app/Contents/MacOS/Zoo\ Modeling\ App
|
||||
|
||||
# Linux
|
||||
./Zoo Design Studio-{version}-{arch}-linux.AppImage
|
||||
```
|
||||
Assign someone to each section of the manual checklist generated by the issue template.
|
||||
|
||||
#### 4. Bump the KCL version
|
||||
|
||||
|
Before Width: | Height: | Size: 119 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 259 KiB After Width: | Height: | Size: 259 KiB |
@ -46,3 +46,7 @@ KCL has no support for area, volume, or other higher dimension units. When inter
|
||||
## Explicit conversions
|
||||
|
||||
You might sometimes need to convert from one unit to another for some calculation. You can do this implicitly when calling a function (see above), but if you can't or don't want to, then you can use the explicit conversion functions in the [`std::units`](/docs/kcl-std/modules/std-units) module.
|
||||
|
||||
KCL cannot know about changes to units caused by arithmetic. For example, you may intend for `10in * 25.4` to be the value `254mm` (i.e., `10in` in mm), however, the result of that computation in KCL is `254in`. It is always better to rely on automatic conversion or to use the explicit conversion functions, where possible.
|
||||
|
||||
Converting between degrees and radians using π ([`PI`](/docs/kcl-std/consts/std-math-PI) in KCL) is especially prone to this error and so the `PI` constant always requires specifying units of any computation it is used with. E.g., `radius = (circumference / (2 * PI)): number(mm)`.
|
||||
|
@ -4,8 +4,6 @@ excerpt: "Project specific settings for the app. These live in `project.toml` in
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# Project Settings
|
||||
|
||||
Project specific settings for the app. These live in `project.toml` in the base of the project directory. Updating the settings for the project in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file.
|
||||
|
||||
## Project Configuration Structure
|
||||
@ -184,4 +182,4 @@ color = 240.0
|
||||
# Use inches as the default measurement unit
|
||||
base_unit = "in"
|
||||
|
||||
```
|
||||
```
|
||||
|
@ -4,8 +4,6 @@ excerpt: "User specific settings for the app. These live in `user.toml` in the a
|
||||
layout: manual
|
||||
---
|
||||
|
||||
# User Settings
|
||||
|
||||
User specific settings for the app. These live in `user.toml` in the app's configuration directory. Updating the settings in the app will update this file automatically. Do not edit this file manually, as it may be overwritten by the app. Manual edits can cause corruption of the settings file.
|
||||
|
||||
## User Configuration Structure
|
||||
@ -234,4 +232,4 @@ base_unit = "mm"
|
||||
# Disable text wrapping in the editor
|
||||
text_wrapping = false
|
||||
|
||||
```
|
||||
```
|
||||
|
@ -1,33 +0,0 @@
|
||||
---
|
||||
title: "KCL Constants"
|
||||
excerpt: "Documentation for the KCL constants."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
|
||||
### `std`
|
||||
|
||||
- [`END`](/docs/kcl/consts/std-END)
|
||||
- [`START`](/docs/kcl/consts/std-START)
|
||||
- [`X`](/docs/kcl/consts/std-X)
|
||||
- [`XY`](/docs/kcl/consts/std-XY)
|
||||
- [`XZ`](/docs/kcl/consts/std-XZ)
|
||||
- [`Y`](/docs/kcl/consts/std-Y)
|
||||
- [`YZ`](/docs/kcl/consts/std-YZ)
|
||||
- [`Z`](/docs/kcl/consts/std-Z)
|
||||
|
||||
### `std::math`
|
||||
|
||||
- [`E`](/docs/kcl/consts/std-math-E)
|
||||
- [`PI`](/docs/kcl/consts/std-math-PI)
|
||||
- [`TAU`](/docs/kcl/consts/std-math-TAU)
|
||||
|
||||
### `std::turns`
|
||||
|
||||
- [`HALF_TURN`](/docs/kcl/consts/std-turns-HALF_TURN)
|
||||
- [`QUARTER_TURN`](/docs/kcl/consts/std-turns-QUARTER_TURN)
|
||||
- [`THREE_QUARTER_TURN`](/docs/kcl/consts/std-turns-THREE_QUARTER_TURN)
|
||||
- [`ZERO`](/docs/kcl/consts/std-turns-ZERO)
|
||||
|
@ -18,7 +18,7 @@ E: number = 2.71828182845904523536028747135266250_
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 30, length = 2 * E ^ 2)
|
||||
|> angledLine(angle = 30deg, length = 2 * E ^ 2)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -16,8 +16,8 @@ However, `PI` is nearly always used for converting between different units - usu
|
||||
from radians. Therefore, `PI` is treated a bit specially by KCL and always has unknown units. This
|
||||
means that if you use `PI`, you will need to give KCL some extra information about the units of numbers.
|
||||
Usually you should use type ascription on the result of calculations, e.g., `(2 * PI): number(rad)`.
|
||||
You might prefer to use `units::toRadians` or `units::toDegrees` to convert between angles with
|
||||
different units.
|
||||
It is better to use `units::toRadians` or `units::toDegrees` to convert between angles with
|
||||
different units where possible.
|
||||
|
||||
### Examples
|
||||
|
||||
|
@ -18,7 +18,7 @@ TAU: number = 6.28318530717958647692528676655900577_
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = 10 * TAU)
|
||||
|> angledLine(angle = 50deg, length = 10 * TAU)
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
No turn, zero degrees/radians.
|
||||
|
||||
```kcl
|
||||
turns::ZERO: number = 0
|
||||
turns::ZERO
|
||||
```
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@ abs(@input: number): number
|
||||
### Examples
|
||||
|
||||
```kcl
|
||||
myAngle = -120
|
||||
myAngle = -120deg
|
||||
|
||||
sketch001 = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the cosine of a number.
|
||||
|
||||
```kcl
|
||||
cos(@num: number(Angle)): number(_)
|
||||
cos(@num: number(Angle)): number
|
||||
```
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ cos(@num: number(Angle)): number(_)
|
||||
|
||||
### Returns
|
||||
|
||||
[`number(_)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -29,7 +29,7 @@ cos(@num: number(Angle)): number(_)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 30, length = 3 / cos(30deg))
|
||||
|> angledLine(angle = 30deg, length = 3 / cos(30deg))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -29,7 +29,7 @@ max(@input: [number; 1+]): number
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 70, length = max([15, 31, 4, 13, 22]))
|
||||
|> angledLine(angle = 70deg, length = max([15, 31, 4, 13, 22]))
|
||||
|> line(end = [20, 0])
|
||||
|> close()
|
||||
|
||||
|
@ -29,7 +29,7 @@ min(@input: [number; 1+]): number
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 70, length = min([15, 31, 4, 13, 22]))
|
||||
|> angledLine(angle = 70deg, length = min([15, 31, 4, 13, 22]))
|
||||
|> line(end = [20, 0])
|
||||
|> close()
|
||||
|
||||
|
@ -33,7 +33,7 @@ polar(
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = polar(angle = 30, length = 5), tag = $thing)
|
||||
|> line(end = polar(angle = 30deg, length = 5), tag = $thing)
|
||||
|> line(end = [0, 5])
|
||||
|> line(end = [segEndX(thing), 0])
|
||||
|> line(end = [-20, 10])
|
||||
|
@ -33,7 +33,7 @@ pow(
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = pow(5, exp = 2))
|
||||
|> angledLine(angle = 50deg, length = pow(5, exp = 2))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the sine of a number.
|
||||
|
||||
```kcl
|
||||
sin(@num: number(Angle)): number(_)
|
||||
sin(@num: number(Angle)): number
|
||||
```
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ sin(@num: number(Angle)): number(_)
|
||||
|
||||
### Returns
|
||||
|
||||
[`number(_)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -29,7 +29,7 @@ sin(@num: number(Angle)): number(_)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = 15 / sin(135deg))
|
||||
|> angledLine(angle = 50deg, length = 15 / sin(135deg))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -29,7 +29,7 @@ sqrt(@input: number): number
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = sqrt(2500))
|
||||
|> angledLine(angle = 50deg, length = sqrt(2500))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -8,7 +8,7 @@ layout: manual
|
||||
Compute the tangent of a number.
|
||||
|
||||
```kcl
|
||||
tan(@num: number(Angle)): number(_)
|
||||
tan(@num: number(Angle)): number
|
||||
```
|
||||
|
||||
|
||||
@ -21,7 +21,7 @@ tan(@num: number(Angle)): number(_)
|
||||
|
||||
### Returns
|
||||
|
||||
[`number(_)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
||||
|
||||
|
||||
### Examples
|
||||
@ -29,7 +29,7 @@ tan(@num: number(Angle)): number(_)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = 50 * tan((1 / 2): number(rad)))
|
||||
|> angledLine(angle = 50deg, length = 50 * tan((1 / 2): number(rad)))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -46,7 +46,7 @@ angledLine(
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> yLine(endAbsolute = 15)
|
||||
|> angledLine(angle = 30, length = 15)
|
||||
|> angledLine(angle = 30deg, length = 15)
|
||||
|> line(end = [8, -10])
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
@ -42,7 +42,7 @@ exampleSketch = startSketchOn(XZ)
|
||||
|> line(endAbsolute = [5, 10])
|
||||
|> line(endAbsolute = [-10, 10], tag = $lineToIntersect)
|
||||
|> line(endAbsolute = [0, 20])
|
||||
|> angledLineThatIntersects(angle = 80, intersectTag = lineToIntersect, offset = 10)
|
||||
|> angledLineThatIntersects(angle = 80deg, intersectTag = lineToIntersect, offset = 10)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
@ -53,7 +53,7 @@ for to construct your shape, you're likely looking for tangentialArc.
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0])
|
||||
|> arc(angleStart = 0, angleEnd = 280, radius = 16)
|
||||
|> arc(angleStart = 0, angleEnd = 280deg, radius = 16)
|
||||
|> close()
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
||||
|
@ -43,7 +43,7 @@ extruded in the same direction.
|
||||
example = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0])
|
||||
|> arc(angleStart = 120, angleEnd = 0, radius = 5)
|
||||
|> arc(angleStart = 120deg, angleEnd = 0, radius = 5)
|
||||
|> line(end = [5, 0])
|
||||
|> line(end = [0, 10])
|
||||
|> bezierCurve(control1 = [-10, 0], control2 = [2, 10], end = [-5, 10])
|
||||
@ -58,7 +58,7 @@ example = startSketchOn(XZ)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [-10, 0])
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc(angleStart = 120deg, angleEnd = -60deg, radius = 5)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
@ -75,7 +75,7 @@ example = extrude(exampleSketch, length = 10)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [-10, 0])
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc(angleStart = 120deg, angleEnd = -60deg, radius = 5)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
@ -92,7 +92,7 @@ example = extrude(exampleSketch, length = 20, symmetric = true)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [-10, 0])
|
||||
|> arc(angleStart = 120, angleEnd = -60, radius = 5)
|
||||
|> arc(angleStart = 120deg, angleEnd = -60deg, radius = 5)
|
||||
|> line(end = [10, 0])
|
||||
|> line(end = [5, 0])
|
||||
|> bezierCurve(control1 = [-3, 0], control2 = [2, 10], end = [-5, 10])
|
||||
|
53
docs/kcl-std/functions/std-sketch-extrudeTwist.md
Normal file
@ -30,10 +30,10 @@ getNextAdjacentEdge(@edge: tag): Edge
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0])
|
||||
|> angledLine(angle = 60, length = 10)
|
||||
|> angledLine(angle = 120, length = 10)
|
||||
|> angledLine(angle = 60deg, length = 10)
|
||||
|> angledLine(angle = 120deg, length = 10)
|
||||
|> line(end = [-10, 0])
|
||||
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|
||||
|> angledLine(angle = 240deg, length = 10, tag = $referenceEdge)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -30,10 +30,10 @@ getOppositeEdge(@edge: tag): Edge
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0])
|
||||
|> angledLine(angle = 60, length = 10)
|
||||
|> angledLine(angle = 120, length = 10)
|
||||
|> angledLine(angle = 60deg, length = 10)
|
||||
|> angledLine(angle = 120deg, length = 10)
|
||||
|> line(end = [-10, 0])
|
||||
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|
||||
|> angledLine(angle = 240deg, length = 10, tag = $referenceEdge)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -30,10 +30,10 @@ getPreviousAdjacentEdge(@edge: tag): Edge
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> line(end = [10, 0])
|
||||
|> angledLine(angle = 60, length = 10)
|
||||
|> angledLine(angle = 120, length = 10)
|
||||
|> angledLine(angle = 60deg, length = 10)
|
||||
|> angledLine(angle = 120deg, length = 10)
|
||||
|> line(end = [-10, 0])
|
||||
|> angledLine(angle = 240, length = 10, tag = $referenceEdge)
|
||||
|> angledLine(angle = 240deg, length = 10, tag = $referenceEdge)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -43,11 +43,11 @@ a = 10
|
||||
b = 14
|
||||
startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> involuteCircular(startRadius = a, endRadius = b, angle = 60)
|
||||
|> involuteCircular(startRadius = a, endRadius = b, angle = 60deg)
|
||||
|> involuteCircular(
|
||||
startRadius = a,
|
||||
endRadius = b,
|
||||
angle = 60,
|
||||
angle = 60deg,
|
||||
reverse = true,
|
||||
)
|
||||
|
||||
|
@ -30,7 +30,7 @@ profileStart(@profile: Sketch): Point2d
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [5, 2])
|
||||
|> angledLine(angle = 120, length = 50, tag = $seg01)
|
||||
|> angledLine(angle = segAng(seg01) + 120, length = 50)
|
||||
|> angledLine(angle = segAng(seg01) + 120deg, length = 50)
|
||||
|> line(end = profileStart(%))
|
||||
|> close()
|
||||
|> extrude(length = 20)
|
||||
|
@ -30,8 +30,8 @@ profileStartX(@profile: Sketch): number(Length)
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [5, 2])
|
||||
|> angledLine(angle = -26.6, length = 50)
|
||||
|> angledLine(angle = 90, length = 50)
|
||||
|> angledLine(angle = 30, endAbsoluteX = profileStartX(%))
|
||||
|> angledLine(angle = 90deg, length = 50)
|
||||
|> angledLine(angle = 30deg, endAbsoluteX = profileStartX(%))
|
||||
|
||||
```
|
||||
|
||||
|
@ -29,8 +29,8 @@ profileStartY(@profile: Sketch): number(Length)
|
||||
```kcl
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> startProfile(at = [5, 2])
|
||||
|> angledLine(angle = -60, length = 14)
|
||||
|> angledLine(angle = 30, endAbsoluteY = profileStartY(%))
|
||||
|> angledLine(angle = -60deg, length = 14)
|
||||
|> angledLine(angle = 30deg, endAbsoluteY = profileStartY(%))
|
||||
|
||||
```
|
||||
|
||||
|
@ -62,7 +62,7 @@ part001 = startSketchOn(XY)
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = Y) // default angle is 360
|
||||
|> revolve(axis = Y) // default angle is 360deg
|
||||
|
||||
```
|
||||
|
||||
@ -72,7 +72,7 @@ part001 = startSketchOn(XY)
|
||||
// A donut shape.
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [15, 0], radius = 5)
|
||||
|> revolve(angle = 360, axis = Y)
|
||||
|> revolve(angle = 360deg, axis = Y)
|
||||
|
||||
```
|
||||
|
||||
@ -89,7 +89,7 @@ part001 = startSketchOn(XY)
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = Y, angle = 180)
|
||||
|> revolve(axis = Y, angle = 180deg)
|
||||
|
||||
```
|
||||
|
||||
@ -106,7 +106,7 @@ part001 = startSketchOn(XY)
|
||||
|> line(end = [0, -5.5])
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|> revolve(axis = Y, angle = 180)
|
||||
|> revolve(axis = Y, angle = 180deg)
|
||||
|
||||
part002 = startSketchOn(part001, face = END)
|
||||
|> startProfile(at = [4.5, -5])
|
||||
@ -131,7 +131,7 @@ box = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(box, face = END)
|
||||
|> circle(center = [10, 10], radius = 4)
|
||||
|> revolve(angle = -90, axis = Y)
|
||||
|> revolve(angle = -90deg, axis = Y)
|
||||
|
||||
```
|
||||
|
||||
@ -148,7 +148,7 @@ box = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(box, face = END)
|
||||
|> circle(center = [10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = getOppositeEdge(revolveAxis))
|
||||
|> revolve(angle = 90deg, axis = getOppositeEdge(revolveAxis))
|
||||
|
||||
```
|
||||
|
||||
@ -165,7 +165,7 @@ box = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(box, face = END)
|
||||
|> circle(center = [10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = getOppositeEdge(revolveAxis), tolerance = 0.0001)
|
||||
|> revolve(angle = 90deg, axis = getOppositeEdge(revolveAxis), tolerance = 0.0001)
|
||||
|
||||
```
|
||||
|
||||
@ -229,7 +229,7 @@ profile001 = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [-10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = revolveAxis)
|
||||
|> revolve(angle = 90deg, axis = revolveAxis)
|
||||
|
||||
```
|
||||
|
||||
@ -246,7 +246,7 @@ profile001 = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [-10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = revolveAxis)
|
||||
|> revolve(angle = 90deg, axis = revolveAxis)
|
||||
|
||||
```
|
||||
|
||||
@ -263,7 +263,7 @@ profile001 = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [-10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = revolveAxis, symmetric = true)
|
||||
|> revolve(angle = 90deg, axis = revolveAxis, symmetric = true)
|
||||
|
||||
```
|
||||
|
||||
@ -280,7 +280,7 @@ profile001 = startSketchOn(XY)
|
||||
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [-10, 10], radius = 4)
|
||||
|> revolve(angle = 90, axis = revolveAxis, bidirectionalAngle = 50)
|
||||
|> revolve(angle = 90deg, axis = revolveAxis, bidirectionalAngle = 50)
|
||||
|
||||
```
|
||||
|
||||
|
@ -30,8 +30,8 @@ segLen(@tag: tag): number(Length)
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 60, length = 10, tag = $thing)
|
||||
|> tangentialArc(angle = -120, radius = 5)
|
||||
|> angledLine(angle = -60, length = segLen(thing))
|
||||
|> tangentialArc(angle = -120deg, radius = 5)
|
||||
|> angledLine(angle = -60deg, length = segLen(thing))
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 5)
|
||||
|
@ -156,7 +156,7 @@ exampleSketch = startSketchOn(XY)
|
||||
|> line(end = [-2, 0])
|
||||
|> close()
|
||||
|
||||
example = revolve(exampleSketch, axis = Y, angle = 180)
|
||||
example = revolve(exampleSketch, axis = Y, angle = 180deg)
|
||||
|
||||
exampleSketch002 = startSketchOn(example, face = END)
|
||||
|> startProfile(at = [4.5, -5])
|
||||
@ -189,7 +189,7 @@ exampleSketch = startSketchOn(XY)
|
||||
example = revolve(
|
||||
exampleSketch,
|
||||
axis = Y,
|
||||
angle = 180,
|
||||
angle = 180deg,
|
||||
tagEnd = $end01,
|
||||
)
|
||||
|
||||
|
@ -54,9 +54,9 @@ swept along the same path.
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -101,7 +101,7 @@ springSketch = startSketchOn(XZ)
|
||||
sketch001 = startSketchOn(XY)
|
||||
rectangleSketch = startProfile(sketch001, at = [-200, 23.86])
|
||||
|> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
@ -111,7 +111,7 @@ circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
sketch002 = startSketchOn(YZ)
|
||||
sweepPath = startProfile(sketch002, at = [0, 0])
|
||||
|> yLine(length = 231.81)
|
||||
|> tangentialArc(radius = 80, angle = -90)
|
||||
|> tangentialArc(radius = 80, angle = -90deg)
|
||||
|> xLine(length = 384.93)
|
||||
|
||||
sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
@ -130,7 +130,7 @@ circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
sketch002 = startSketchOn(YZ)
|
||||
sweepPath = startProfile(sketch002, at = [0, 0])
|
||||
|> yLine(length = 231.81)
|
||||
|> tangentialArc(radius = 80, angle = -90)
|
||||
|> tangentialArc(radius = 80, angle = -90deg)
|
||||
|> xLine(length = 384.93)
|
||||
|
||||
sweep(circleSketch, path = sweepPath, sectional = true)
|
||||
|
@ -47,7 +47,7 @@ for 'angle' degrees along the imaginary circle.
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 45, length = 10)
|
||||
|> angledLine(angle = 45deg, length = 10)
|
||||
|> tangentialArc(end = [0, -10])
|
||||
|> line(end = [-10, 0])
|
||||
|> close()
|
||||
@ -61,7 +61,7 @@ example = extrude(exampleSketch, length = 10)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 60, length = 10)
|
||||
|> angledLine(angle = 60deg, length = 10)
|
||||
|> tangentialArc(endAbsolute = [15, 15])
|
||||
|> line(end = [10, -15])
|
||||
|> close()
|
||||
@ -75,9 +75,9 @@ example = extrude(exampleSketch, length = 10)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 60, length = 10)
|
||||
|> tangentialArc(radius = 10, angle = -120)
|
||||
|> angledLine(angle = -60, length = 10)
|
||||
|> angledLine(angle = 60deg, length = 10)
|
||||
|> tangentialArc(radius = 10, angle = -120deg)
|
||||
|> angledLine(angle = -60deg, length = 10)
|
||||
|> close()
|
||||
|
||||
example = extrude(exampleSketch, length = 10)
|
||||
|
@ -38,10 +38,10 @@ xLine(
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> xLine(length = 15)
|
||||
|> angledLine(angle = 80, length = 15)
|
||||
|> angledLine(angle = 80deg, length = 15)
|
||||
|> line(end = [8, -10])
|
||||
|> xLine(length = 10)
|
||||
|> angledLine(angle = 120, length = 30)
|
||||
|> angledLine(angle = 120deg, length = 30)
|
||||
|> xLine(length = -15)
|
||||
|> close()
|
||||
|
||||
|
@ -38,7 +38,7 @@ yLine(
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> yLine(length = 15)
|
||||
|> angledLine(angle = 30, length = 15)
|
||||
|> angledLine(angle = 30deg, length = 15)
|
||||
|> line(end = [8, -10])
|
||||
|> yLine(length = -5)
|
||||
|> close()
|
||||
|
@ -55,7 +55,7 @@ example = extrude(exampleSketch, length = 5)
|
||||
// Add color to a revolved solid.
|
||||
sketch001 = startSketchOn(XY)
|
||||
|> circle(center = [15, 0], radius = 5)
|
||||
|> revolve(angle = 360, axis = Y)
|
||||
|> revolve(angle = 360deg, axis = Y)
|
||||
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)
|
||||
|
||||
```
|
||||
@ -196,9 +196,9 @@ example = extrude(exampleSketch, length = 1)
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
pipeHole = startSketchOn(XY)
|
||||
|
@ -50,7 +50,7 @@ Its properties are:
|
||||
|
||||
- `rotation.axis` (a 3D point, defaults to the Z axis)
|
||||
|
||||
- `rotation.angle` (number of degrees)
|
||||
- `rotation.angle`
|
||||
|
||||
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||
|
||||
@ -135,7 +135,7 @@ fn transform(@i) {
|
||||
pow(0.9, exp = i)
|
||||
],
|
||||
// Turn by 15 degrees each time.
|
||||
rotation = { angle = 15 * i, origin = "local" }
|
||||
rotation = { angle = 15deg * i, origin = "local" }
|
||||
}
|
||||
}
|
||||
|
||||
@ -171,7 +171,7 @@ fn transform(@i) {
|
||||
return {
|
||||
translate = [0, 0, -i * width],
|
||||
rotation = {
|
||||
angle = 90 * i,
|
||||
angle = 90deg * i,
|
||||
// Rotate around the overall scene's origin.
|
||||
origin = "global"
|
||||
}
|
||||
@ -219,7 +219,7 @@ fn transform(@i) {
|
||||
// Transform functions can return multiple transforms. They'll be applied in order.
|
||||
return [
|
||||
{ translate = [30 * i, 0, 0] },
|
||||
{ rotation = { angle = 45 * i } }
|
||||
{ rotation = { angle = 45deg * i } }
|
||||
]
|
||||
}
|
||||
startSketchOn(XY)
|
||||
|
@ -72,9 +72,9 @@ rotation.
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -98,9 +98,9 @@ sweepSketch = startSketchOn(XY)
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -124,9 +124,9 @@ sweepSketch = startSketchOn(XY)
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -137,7 +137,7 @@ sweepSketch = startSketchOn(XY)
|
||||
|> circle(center = [0, 0], radius = 2)
|
||||
|> subtract2d(tool = pipeHole)
|
||||
|> sweep(path = sweepPath)
|
||||
|> rotate(axis = Z, angle = 90)
|
||||
|> rotate(axis = Z, angle = 90deg)
|
||||
|
||||
```
|
||||
|
||||
@ -150,7 +150,7 @@ sweepSketch = startSketchOn(XY)
|
||||
import "tests/inputs/cube.sldprt" as cube
|
||||
|
||||
cube
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 9)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 9deg)
|
||||
|
||||
```
|
||||
|
||||
@ -163,9 +163,9 @@ cube
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -176,7 +176,7 @@ sweepSketch = startSketchOn(XY)
|
||||
|> circle(center = [0, 0], radius = 2)
|
||||
|> subtract2d(tool = pipeHole)
|
||||
|> sweep(path = sweepPath)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 90)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 90deg)
|
||||
|
||||
```
|
||||
|
||||
@ -199,13 +199,13 @@ circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
sketch002 = startSketchOn(YZ)
|
||||
sweepPath = startProfile(sketch002, at = [0, 0])
|
||||
|> yLine(length = 231.81)
|
||||
|> tangentialArc(radius = 80, angle = -90)
|
||||
|> tangentialArc(radius = 80, angle = -90deg)
|
||||
|> xLine(length = 384.93)
|
||||
|
||||
parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
|
||||
// Rotate the sweeps.
|
||||
rotate(parts, axis = [0, 0, 1.0], angle = 90)
|
||||
rotate(parts, axis = [0, 0, 1.0], angle = 90deg)
|
||||
|
||||
```
|
||||
|
||||
@ -228,7 +228,7 @@ profile001 = square()
|
||||
|
||||
profile002 = square()
|
||||
|> translate(x = 0, y = 0, z = 20)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45deg)
|
||||
|
||||
loft([profile001, profile002])
|
||||
|
||||
|
@ -54,9 +54,9 @@ look like the model moves and gets bigger at the same time. Say you have a squar
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -93,7 +93,7 @@ cube
|
||||
sketch001 = startSketchOn(XY)
|
||||
rectangleSketch = startProfile(sketch001, at = [-200, 23.86])
|
||||
|> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
@ -103,7 +103,7 @@ circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
sketch002 = startSketchOn(YZ)
|
||||
sweepPath = startProfile(sketch002, at = [0, 0])
|
||||
|> yLine(length = 231.81)
|
||||
|> tangentialArc(radius = 80, angle = -90)
|
||||
|> tangentialArc(radius = 80, angle = -90deg)
|
||||
|> xLine(length = 384.93)
|
||||
|
||||
parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
|
@ -47,9 +47,9 @@ and then rotate it using the `rotate` function to create a loft.
|
||||
sweepPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0.05, 0.05])
|
||||
|> line(end = [0, 7])
|
||||
|> tangentialArc(angle = 90, radius = 5)
|
||||
|> tangentialArc(angle = 90deg, radius = 5)
|
||||
|> line(end = [-3, 0])
|
||||
|> tangentialArc(angle = -90, radius = 5)
|
||||
|> tangentialArc(angle = -90deg, radius = 5)
|
||||
|> line(end = [0, 7])
|
||||
|
||||
// Create a hole for the pipe.
|
||||
@ -91,7 +91,7 @@ cube
|
||||
sketch001 = startSketchOn(XY)
|
||||
rectangleSketch = startProfile(sketch001, at = [-200, 23.86])
|
||||
|> angledLine(angle = 0, length = 73.47, tag = $rectangleSegmentA001)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001) - 90deg, length = 50.61)
|
||||
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
@ -101,7 +101,7 @@ circleSketch = circle(sketch001, center = [200, -30.29], radius = 32.63)
|
||||
sketch002 = startSketchOn(YZ)
|
||||
sweepPath = startProfile(sketch002, at = [0, 0])
|
||||
|> yLine(length = 231.81)
|
||||
|> tangentialArc(radius = 80, angle = -90)
|
||||
|> tangentialArc(radius = 80, angle = -90deg)
|
||||
|> xLine(length = 384.93)
|
||||
|
||||
parts = sweep([rectangleSketch, circleSketch], path = sweepPath)
|
||||
@ -162,7 +162,7 @@ profile001 = square()
|
||||
|
||||
profile002 = square()
|
||||
|> translate(z = 20)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45)
|
||||
|> rotate(axis = [0, 0, 1.0], angle = 45deg)
|
||||
|
||||
loft([profile001, profile002])
|
||||
|
||||
|
@ -29,7 +29,7 @@ units::toDegrees(@num: number(Angle)): number(deg)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = 70 * cos(units::toDegrees((PI / 4): number(rad))))
|
||||
|> angledLine(angle = 50deg, length = 70 * cos(units::toDegrees((PI / 4): number(rad))))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -29,7 +29,7 @@ units::toRadians(@num: number(Angle)): number(rad)
|
||||
```kcl
|
||||
exampleSketch = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> angledLine(angle = 50, length = 70 * cos(units::toRadians(45)))
|
||||
|> angledLine(angle = 50deg, length = 70 * cos(units::toRadians(45deg)))
|
||||
|> yLine(endAbsolute = 0)
|
||||
|> close()
|
||||
|
||||
|
@ -55,6 +55,7 @@ layout: manual
|
||||
* [`circleThreePoint`](/docs/kcl-std/functions/std-sketch-circleThreePoint)
|
||||
* [`close`](/docs/kcl-std/functions/std-sketch-close)
|
||||
* [`extrude`](/docs/kcl-std/functions/std-sketch-extrude)
|
||||
* [`extrudeTwist`](/docs/kcl-std/functions/std-sketch-extrudeTwist)
|
||||
* [`getCommonEdge`](/docs/kcl-std/functions/std-sketch-getCommonEdge)
|
||||
* [`getNextAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
|
@ -20,6 +20,7 @@ This module contains functions for creating and manipulating sketches, and makin
|
||||
* [`circleThreePoint`](/docs/kcl-std/functions/std-sketch-circleThreePoint)
|
||||
* [`close`](/docs/kcl-std/functions/std-sketch-close)
|
||||
* [`extrude`](/docs/kcl-std/functions/std-sketch-extrude)
|
||||
* [`extrudeTwist`](/docs/kcl-std/functions/std-sketch-extrudeTwist)
|
||||
* [`getCommonEdge`](/docs/kcl-std/functions/std-sketch-getCommonEdge)
|
||||
* [`getNextAdjacentEdge`](/docs/kcl-std/functions/std-sketch-getNextAdjacentEdge)
|
||||
* [`getOppositeEdge`](/docs/kcl-std/functions/std-sketch-getOppositeEdge)
|
||||
|
@ -17,7 +17,7 @@ startSketchOn(XZ)
|
||||
|> startProfile(at = origin)
|
||||
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|
||||
|> angledLine(
|
||||
angle = segAng(rectangleSegmentA001) - 90,
|
||||
angle = segAng(rectangleSegmentA001) - 90deg,
|
||||
length = 196.99,
|
||||
tag = $rectangleSegmentB001,
|
||||
)
|
||||
@ -80,7 +80,7 @@ fn rect(origin) {
|
||||
|> startProfile(at = origin)
|
||||
|> angledLine(angle = 0, length = 191.26, tag = $rectangleSegmentA001)
|
||||
|> angledLine(
|
||||
angle = segAng(rectangleSegmentA001) - 90,
|
||||
angle = segAng(rectangleSegmentA001) - 90deg,
|
||||
length = 196.99
|
||||
tag = $rectangleSegmentB001,
|
||||
)
|
||||
|
@ -302,7 +302,7 @@ test.describe('Command bar tests', () => {
|
||||
|
||||
// Assert that the an alternative variable name is chosen,
|
||||
// since the default variable name is already in use (distance)
|
||||
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||
await cmdBar.variableCheckbox.click()
|
||||
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||
'length001'
|
||||
)
|
||||
|
@ -281,10 +281,7 @@ test.describe('Feature Tree pane', () => {
|
||||
|
||||
await test.step('Add a named constant for distance argument and submit', async () => {
|
||||
await expect(cmdBar.currentArgumentInput).toBeVisible()
|
||||
const addVariableButton = page.getByRole('button', {
|
||||
name: 'Create new variable',
|
||||
})
|
||||
await addVariableButton.click()
|
||||
await cmdBar.variableCheckbox.click()
|
||||
await cmdBar.progressCmdBar()
|
||||
await cmdBar.expectState({
|
||||
stage: 'review',
|
||||
|
@ -172,6 +172,10 @@ export class CmdBarFixture {
|
||||
return this.page.getByTestId('cmd-bar-arg-value')
|
||||
}
|
||||
|
||||
get variableCheckbox() {
|
||||
return this.page.getByTestId('cmd-bar-variable-checkbox')
|
||||
}
|
||||
|
||||
get cmdOptions() {
|
||||
return this.page.getByTestId('cmd-bar-option')
|
||||
}
|
||||
@ -191,7 +195,7 @@ export class CmdBarFixture {
|
||||
* Clicks the Create new variable button for kcl input
|
||||
*/
|
||||
createNewVariable = async () => {
|
||||
await this.page.getByRole('button', { name: 'Create new variable' }).click()
|
||||
await this.variableCheckbox.click()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -3807,7 +3807,7 @@ sketch002 = startSketchOn(extrude001, face = rectangleSegmentA001)
|
||||
stage: 'arguments',
|
||||
})
|
||||
await page.keyboard.insertText(newAngle)
|
||||
await page.getByRole('button', { name: 'Create new variable' }).click()
|
||||
await cmdBar.variableCheckbox.click()
|
||||
await expect(page.getByPlaceholder('Variable name')).toHaveValue(
|
||||
'angle001'
|
||||
)
|
||||
|
@ -170,7 +170,7 @@ test(
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
const crypticErrorText =
|
||||
'tag requires a value with type `tag`, but found string'
|
||||
'tag requires a value with type `tag`, but found a value with type `string`.'
|
||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||
|
||||
// black pixel means the scene has been cleared.
|
||||
@ -369,7 +369,7 @@ test(
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
const crypticErrorText =
|
||||
'tag requires a value with type `tag`, but found string'
|
||||
'tag requires a value with type `tag`, but found a value with type `string`.'
|
||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||
|
||||
// black pixel means the scene has been cleared.
|
||||
@ -408,7 +408,7 @@ test(
|
||||
// error text on hover
|
||||
await page.hover('.cm-lint-marker-error')
|
||||
const crypticErrorText =
|
||||
'tag requires a value with type `tag`, but found string'
|
||||
'tag requires a value with type `tag`, but found a value with type `string`.'
|
||||
await expect(page.getByText(crypticErrorText).first()).toBeVisible()
|
||||
}
|
||||
)
|
||||
|
@ -1,45 +1,54 @@
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
async function navigateAndClickOpenInDesktopApp(
|
||||
page: Page,
|
||||
codeLength: number
|
||||
) {
|
||||
const code = Array(codeLength).fill('0').join('')
|
||||
const targetURL = `?create-file=true&browser=test&code=${code}&ask-open-desktop=true`
|
||||
expect(targetURL.length).toEqual(codeLength + 58)
|
||||
await page.goto(page.url() + targetURL)
|
||||
expect(page.url()).toContain(targetURL)
|
||||
const button = page.getByRole('button', { name: 'Open in desktop app' })
|
||||
await button.click()
|
||||
}
|
||||
|
||||
function getToastError(page: Page) {
|
||||
return page.getByText('The URL is too long to open in the desktop app')
|
||||
}
|
||||
|
||||
const isWindows =
|
||||
navigator.platform === 'Windows' || navigator.platform === 'Win32'
|
||||
test.describe('Share link tests', () => {
|
||||
;[
|
||||
{
|
||||
codeLength: 1000,
|
||||
showsErrorOnWindows: false,
|
||||
},
|
||||
{
|
||||
codeLength: 2000,
|
||||
showsErrorOnWindows: true,
|
||||
},
|
||||
].forEach(({ codeLength, showsErrorOnWindows }) => {
|
||||
test(
|
||||
`Open in desktop app with ${codeLength}-long code ${isWindows && showsErrorOnWindows ? 'shows error' : "doesn't show error"}`,
|
||||
{ tag: ['@web'] },
|
||||
async ({ page }) => {
|
||||
if (process.env.TARGET !== 'web') {
|
||||
// This test is web-only
|
||||
// TODO: re-enable on CI as part of a new @web test suite
|
||||
return
|
||||
}
|
||||
test(
|
||||
`Open in desktop app with 2000-long code works non-Windows`,
|
||||
{ tag: ['@web', '@macos', '@linux'] },
|
||||
async ({ page }) => {
|
||||
test.skip(process.platform === 'win32')
|
||||
const codeLength = 2000
|
||||
await navigateAndClickOpenInDesktopApp(page, codeLength)
|
||||
await expect(getToastError(page)).not.toBeVisible()
|
||||
}
|
||||
)
|
||||
|
||||
const code = Array(codeLength).fill('0').join('')
|
||||
const targetURL = `?create-file=true&browser=test&code=${code}&ask-open-desktop=true`
|
||||
expect(targetURL.length).toEqual(codeLength + 58)
|
||||
await page.goto(page.url() + targetURL)
|
||||
expect(page.url()).toContain(targetURL)
|
||||
const button = page.getByRole('button', { name: 'Open in desktop app' })
|
||||
await button.click()
|
||||
const toastError = page.getByText(
|
||||
'The URL is too long to open in the desktop app on Windows'
|
||||
)
|
||||
if (isWindows && showsErrorOnWindows) {
|
||||
await expect(toastError).toBeVisible()
|
||||
} else {
|
||||
await expect(toastError).not.toBeVisible()
|
||||
// TODO: check if we could verify the deep link dialog shows up
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
test(
|
||||
`Open in desktop app with 1000-long code works on Windows`,
|
||||
{ tag: ['@web', '@windows'] },
|
||||
async ({ page }) => {
|
||||
test.skip(process.platform !== 'win32')
|
||||
const codeLength = 1000
|
||||
await navigateAndClickOpenInDesktopApp(page, codeLength)
|
||||
await expect(getToastError(page)).not.toBeVisible()
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
`Open in desktop app with 2000-long code doesn't work on Windows`,
|
||||
{ tag: ['@web', '@windows'] },
|
||||
async ({ page }) => {
|
||||
test.skip(process.platform !== 'win32')
|
||||
const codeLength = 2000
|
||||
await navigateAndClickOpenInDesktopApp(page, codeLength)
|
||||
await expect(getToastError(page)).toBeVisible()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
@ -3433,6 +3433,71 @@ profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
||||
).toBeVisible()
|
||||
})
|
||||
})
|
||||
test('Will exit out of sketch mode when all code is nuked', async ({
|
||||
page,
|
||||
context,
|
||||
homePage,
|
||||
scene,
|
||||
editor,
|
||||
toolbar,
|
||||
cmdBar,
|
||||
}) => {
|
||||
const initialCode = `myVar1 = 5
|
||||
myVar2 = 6
|
||||
|
||||
sketch001 = startSketchOn(XZ)
|
||||
profile001 = startProfile(sketch001, at = [106.68, 89.77])
|
||||
|> line(end = [132.34, 157.8])
|
||||
|> line(end = [67.65, -460.55], tag = $seg01)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
extrude001 = extrude(profile001, length = 500)
|
||||
sketch002 = startSketchOn(extrude001, face = seg01)
|
||||
profile002 = startProfile(sketch002, at = [83.39, 329.15])
|
||||
|> angledLine(angle = 0, length = 119.61, tag = $rectangleSegmentA001)
|
||||
|> angledLine(length = 156.54, angle = -28)
|
||||
|> angledLine(
|
||||
angle = -151,
|
||||
length = 116.27,
|
||||
)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
profile003 = startProfile(sketch002, at = [-201.08, 254.17])
|
||||
|> line(end = [103.55, 33.32])
|
||||
|> line(end = [48.8, -153.54])`
|
||||
|
||||
await context.addInitScript((initialCode) => {
|
||||
localStorage.setItem('persistCode', initialCode)
|
||||
}, initialCode)
|
||||
|
||||
await homePage.goToModelingScene()
|
||||
await scene.connectionEstablished()
|
||||
await scene.settled(cmdBar)
|
||||
const expectSketchOriginToBeDrawn = async () => {
|
||||
await scene.expectPixelColor(TEST_COLORS.WHITE, { x: 672, y: 193 }, 15)
|
||||
}
|
||||
|
||||
await test.step('Open feature tree and edit second sketch', async () => {
|
||||
await toolbar.openFeatureTreePane()
|
||||
const sketchButton = await toolbar.getFeatureTreeOperation('Sketch', 1)
|
||||
await sketchButton.dblclick()
|
||||
await page.waitForTimeout(700) // Wait for engine animation
|
||||
await expectSketchOriginToBeDrawn()
|
||||
})
|
||||
|
||||
await test.step('clear editor content while in sketch mode', async () => {
|
||||
await editor.replaceCode('', '')
|
||||
await page.waitForTimeout(100)
|
||||
await expect(
|
||||
page.getByText('Unable to maintain sketch mode')
|
||||
).toBeVisible()
|
||||
await scene.expectPixelColorNotToBe(
|
||||
TEST_COLORS.WHITE,
|
||||
{ x: 672, y: 193 },
|
||||
15
|
||||
)
|
||||
})
|
||||
})
|
||||
test('empty draft sketch is cleaned up properly', async ({
|
||||
scene,
|
||||
toolbar,
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 46 KiB |
@ -1198,3 +1198,174 @@ export async function enableConsoleLogEverything({
|
||||
console.log(`[Main] ${msg.type()}: ${msg.text()}`)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a pan touch gesture from the center of an element.
|
||||
*
|
||||
* Adapted from Playwright docs: https://playwright.dev/docs/touch-events
|
||||
*/
|
||||
export async function panFromCenter(
|
||||
locator: Locator,
|
||||
deltaX = 0,
|
||||
deltaY = 0,
|
||||
steps = 5
|
||||
) {
|
||||
const { centerX, centerY } = await locator.evaluate((target: HTMLElement) => {
|
||||
const bounds = target.getBoundingClientRect()
|
||||
const centerX = bounds.left + bounds.width / 2
|
||||
const centerY = bounds.top + bounds.height / 2
|
||||
return { centerX, centerY }
|
||||
})
|
||||
|
||||
// Providing only clientX and clientY as the app only cares about those.
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX,
|
||||
clientY: centerY,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchstart', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
|
||||
for (let j = 1; j <= steps; j++) {
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX + (deltaX * j) / steps,
|
||||
clientY: centerY + (deltaY * j) / steps,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchmove', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
}
|
||||
|
||||
await locator.dispatchEvent('touchend')
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a 2-finger pan touch gesture from the center of an element.
|
||||
* with {touchSpacing} pixels between.
|
||||
*
|
||||
* Adapted from Playwright docs: https://playwright.dev/docs/touch-events
|
||||
*/
|
||||
export async function panTwoFingerFromCenter(
|
||||
locator: Locator,
|
||||
deltaX = 0,
|
||||
deltaY = 0,
|
||||
steps = 5,
|
||||
spacingX = 20
|
||||
) {
|
||||
const { centerX, centerY } = await locator.evaluate((target: HTMLElement) => {
|
||||
const bounds = target.getBoundingClientRect()
|
||||
const centerX = bounds.left + bounds.width / 2
|
||||
const centerY = bounds.top + bounds.height / 2
|
||||
return { centerX, centerY }
|
||||
})
|
||||
|
||||
// Providing only clientX and clientY as the app only cares about those.
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX,
|
||||
clientY: centerY,
|
||||
},
|
||||
{
|
||||
identifier: 1,
|
||||
clientX: centerX + spacingX,
|
||||
clientY: centerY,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchstart', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
|
||||
for (let j = 1; j <= steps; j++) {
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX + (deltaX * j) / steps,
|
||||
clientY: centerY + (deltaY * j) / steps,
|
||||
},
|
||||
{
|
||||
identifier: 1,
|
||||
clientX: centerX + spacingX + (deltaX * j) / steps,
|
||||
clientY: centerY + (deltaY * j) / steps,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchmove', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
}
|
||||
|
||||
await locator.dispatchEvent('touchend')
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate a pinch touch gesture from the center of an element.
|
||||
* Touch points are set horizontally from each other, separated by {startDistance} pixels.
|
||||
*/
|
||||
export async function pinchFromCenter(
|
||||
locator: Locator,
|
||||
startDistance = 100,
|
||||
delta = 0,
|
||||
steps = 5
|
||||
) {
|
||||
const { centerX, centerY } = await locator.evaluate((target: HTMLElement) => {
|
||||
const bounds = target.getBoundingClientRect()
|
||||
const centerX = bounds.left + bounds.width / 2
|
||||
const centerY = bounds.top + bounds.height / 2
|
||||
return { centerX, centerY }
|
||||
})
|
||||
|
||||
// Providing only clientX and clientY as the app only cares about those.
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX - startDistance / 2,
|
||||
clientY: centerY,
|
||||
},
|
||||
{
|
||||
identifier: 1,
|
||||
clientX: centerX + startDistance / 2,
|
||||
clientY: centerY,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchstart', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
|
||||
for (let i = 1; i <= steps; i++) {
|
||||
const touches = [
|
||||
{
|
||||
identifier: 0,
|
||||
clientX: centerX - startDistance / 2 + (delta * i) / steps,
|
||||
clientY: centerY,
|
||||
},
|
||||
{
|
||||
identifier: 1,
|
||||
clientX: centerX + startDistance / 2 + (delta * i) / steps,
|
||||
clientY: centerY,
|
||||
},
|
||||
]
|
||||
await locator.dispatchEvent('touchmove', {
|
||||
touches,
|
||||
changedTouches: touches,
|
||||
targetTouches: touches,
|
||||
})
|
||||
}
|
||||
|
||||
await locator.dispatchEvent('touchend')
|
||||
}
|
||||
|
146
e2e/playwright/testing-camera-movement-touch.spec.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { getUtils } from '@e2e/playwright/test-utils'
|
||||
import { expect, test } from '@e2e/playwright/zoo-test'
|
||||
import { type Page } from '@playwright/test'
|
||||
import type { SceneFixture } from '@e2e/playwright/fixtures/sceneFixture'
|
||||
|
||||
test.use({
|
||||
hasTouch: true,
|
||||
})
|
||||
test.describe('Testing Camera Movement (Touch Only)', () => {
|
||||
/**
|
||||
* DUPLICATED FROM `testing-camera-movement.spec.ts`, might need to become a util.
|
||||
*
|
||||
* hack that we're implemented our own retry instead of using retries built into playwright.
|
||||
* however each of these camera drags can be flaky, because of udp
|
||||
* and so putting them together means only one needs to fail to make this test extra flaky.
|
||||
* this way we can retry within the test
|
||||
* We could break them out into separate tests, but the longest past of the test is waiting
|
||||
* for the stream to start, so it can be good to bundle related things together.
|
||||
*/
|
||||
const _bakeInRetries = async ({
|
||||
mouseActions,
|
||||
afterPosition,
|
||||
beforePosition,
|
||||
retryCount = 0,
|
||||
page,
|
||||
scene,
|
||||
}: {
|
||||
mouseActions: () => Promise<void>
|
||||
beforePosition: [number, number, number]
|
||||
afterPosition: [number, number, number]
|
||||
retryCount?: number
|
||||
page: Page
|
||||
scene: SceneFixture
|
||||
}) => {
|
||||
const acceptableCamError = 5
|
||||
const u = await getUtils(page)
|
||||
|
||||
await test.step('Set up initial camera position', async () =>
|
||||
await scene.moveCameraTo({
|
||||
x: beforePosition[0],
|
||||
y: beforePosition[1],
|
||||
z: beforePosition[2],
|
||||
}))
|
||||
|
||||
await test.step('Do actions and watch for changes', async () =>
|
||||
u.doAndWaitForImageDiff(async () => {
|
||||
await mouseActions()
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await u.closeDebugPanel()
|
||||
await page.waitForTimeout(100)
|
||||
}, 300))
|
||||
|
||||
await u.openAndClearDebugPanel()
|
||||
await expect(page.getByTestId('cam-x-position')).toBeAttached()
|
||||
|
||||
const vals = await Promise.all([
|
||||
page.getByTestId('cam-x-position').inputValue(),
|
||||
page.getByTestId('cam-y-position').inputValue(),
|
||||
page.getByTestId('cam-z-position').inputValue(),
|
||||
])
|
||||
const errors = vals.map((v, i) => Math.abs(Number(v) - afterPosition[i]))
|
||||
let shouldRetry = false
|
||||
|
||||
if (errors.some((e) => e > acceptableCamError)) {
|
||||
if (retryCount > 2) {
|
||||
console.log('xVal', vals[0], 'xError', errors[0])
|
||||
console.log('yVal', vals[1], 'yError', errors[1])
|
||||
console.log('zVal', vals[2], 'zError', errors[2])
|
||||
|
||||
throw new Error('Camera position not as expected', {
|
||||
cause: {
|
||||
vals,
|
||||
errors,
|
||||
},
|
||||
})
|
||||
}
|
||||
shouldRetry = true
|
||||
}
|
||||
if (shouldRetry) {
|
||||
await _bakeInRetries({
|
||||
mouseActions,
|
||||
afterPosition: afterPosition,
|
||||
beforePosition: beforePosition,
|
||||
retryCount: retryCount + 1,
|
||||
page,
|
||||
scene,
|
||||
})
|
||||
}
|
||||
}
|
||||
// test(
|
||||
// 'Touch camera controls',
|
||||
// {
|
||||
// tag: '@web',
|
||||
// },
|
||||
// async ({ page, homePage, scene, cmdBar }) => {
|
||||
// const u = await getUtils(page)
|
||||
// const camInitialPosition: [number, number, number] = [0, 85, 85]
|
||||
//
|
||||
// await homePage.goToModelingScene()
|
||||
// await scene.settled(cmdBar)
|
||||
// const stream = page.getByTestId('stream')
|
||||
//
|
||||
// await u.openAndClearDebugPanel()
|
||||
// await u.closeKclCodePanel()
|
||||
//
|
||||
// await test.step('Orbit', async () => {
|
||||
// await bakeInRetries({
|
||||
// mouseActions: async () => {
|
||||
// await panFromCenter(stream, 200, 200)
|
||||
// await page.waitForTimeout(200)
|
||||
// },
|
||||
// afterPosition: [19, 85, 85],
|
||||
// beforePosition: camInitialPosition,
|
||||
// page,
|
||||
// scene,
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// await test.step('Pan', async () => {
|
||||
// await bakeInRetries({
|
||||
// mouseActions: async () => {
|
||||
// await panTwoFingerFromCenter(stream, 200, 200)
|
||||
// await page.waitForTimeout(200)
|
||||
// },
|
||||
// afterPosition: [19, 85, 85],
|
||||
// beforePosition: camInitialPosition,
|
||||
// page,
|
||||
// scene,
|
||||
// })
|
||||
// })
|
||||
//
|
||||
// await test.step('Zoom', async () => {
|
||||
// await bakeInRetries({
|
||||
// mouseActions: async () => {
|
||||
// await pinchFromCenter(stream, 300, -100, 5)
|
||||
// },
|
||||
// afterPosition: [0, 118, 118],
|
||||
// beforePosition: camInitialPosition,
|
||||
// page,
|
||||
// scene,
|
||||
// })
|
||||
// })
|
||||
// }
|
||||
// )
|
||||
})
|
18
package-lock.json
generated
@ -47,6 +47,7 @@
|
||||
"diff": "^7.0.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
@ -93,6 +94,7 @@
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@types/diff": "^7.0.2",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/hammerjs": "^2.0.46",
|
||||
"@types/isomorphic-fetch": "^0.0.39",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/minimist": "^1.2.5",
|
||||
@ -7491,6 +7493,13 @@
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/hammerjs": {
|
||||
"version": "2.0.46",
|
||||
"resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz",
|
||||
"integrity": "sha512-ynRvcq6wvqexJ9brDMS4BnBLzmr0e14d6ZJTEShTBWKymQiHwlAyGu0ZPEFI2Fh1U53F7tN9ufClWM5KvqkKOw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/http-cache-semantics": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
|
||||
@ -15174,6 +15183,15 @@
|
||||
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/hammerjs": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/hammerjs/-/hammerjs-2.0.8.tgz",
|
||||
"integrity": "sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "17.4.4",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-17.4.4.tgz",
|
||||
|
@ -49,6 +49,7 @@
|
||||
"diff": "^7.0.0",
|
||||
"electron-updater": "^6.6.2",
|
||||
"fuse.js": "^7.1.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"html2canvas-pro": "^1.5.8",
|
||||
"isomorphic-fetch": "^3.0.0",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
@ -115,10 +116,10 @@
|
||||
"circular-deps:diff:nodejs": "npm run circular-deps:diff || node ./scripts/diff.js",
|
||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||
"files:flip-to-nightly:windows": "powershell -ExecutionPolicy Bypass -File ./scripts/flip-files-to-nightly.ps1",
|
||||
"files:flip-to-staging": "./scripts/flip-files-to-staging.sh",
|
||||
"files:flip-to-staging:windows": "powershell -ExecutionPolicy Bypass -File ./scripts/flip-files-to-staging.ps1",
|
||||
"files:invalidate-bucket": "./scripts/invalidate-files-bucket.sh",
|
||||
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
||||
"files:invalidate-bucket:staging": "./scripts/invalidate-files-bucket.sh --staging",
|
||||
"postinstall": "electron-rebuild",
|
||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||
"tron:start": "electron-forge start",
|
||||
@ -168,6 +169,7 @@
|
||||
"@testing-library/react": "^15.0.7",
|
||||
"@types/diff": "^7.0.2",
|
||||
"@types/electron": "^1.6.10",
|
||||
"@types/hammerjs": "^2.0.46",
|
||||
"@types/isomorphic-fetch": "^0.0.39",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/minimist": "^1.2.5",
|
||||
|
@ -109,6 +109,8 @@ When you submit a PR to add or modify KCL samples, images will be generated and
|
||||
[](makeup-mirror/main.kcl)
|
||||
#### [mounting-plate](mounting-plate/main.kcl) ([screenshot](screenshots/mounting-plate.png))
|
||||
[](mounting-plate/main.kcl)
|
||||
#### [mug](mug/main.kcl) ([screenshot](screenshots/mug.png))
|
||||
[](mug/main.kcl)
|
||||
#### [multi-axis-robot](multi-axis-robot/main.kcl) ([screenshot](screenshots/multi-axis-robot.png))
|
||||
[](multi-axis-robot/main.kcl)
|
||||
#### [pdu-faceplate](pdu-faceplate/main.kcl) ([screenshot](screenshots/pdu-faceplate.png))
|
||||
@ -139,6 +141,8 @@ When you submit a PR to add or modify KCL samples, images will be generated and
|
||||
[](socket-head-cap-screw/main.kcl)
|
||||
#### [spinning-highrise-tower](spinning-highrise-tower/main.kcl) ([screenshot](screenshots/spinning-highrise-tower.png))
|
||||
[](spinning-highrise-tower/main.kcl)
|
||||
#### [spool](spool/main.kcl) ([screenshot](screenshots/spool.png))
|
||||
[](spool/main.kcl)
|
||||
#### [spur-gear](spur-gear/main.kcl) ([screenshot](screenshots/spur-gear.png))
|
||||
[](spur-gear/main.kcl)
|
||||
#### [spur-reduction-gearset](spur-reduction-gearset/main.kcl) ([screenshot](screenshots/spur-reduction-gearset.png))
|
||||
@ -147,6 +151,8 @@ When you submit a PR to add or modify KCL samples, images will be generated and
|
||||
[](surgical-drill-guide/main.kcl)
|
||||
#### [t-slot-rail](t-slot-rail/main.kcl) ([screenshot](screenshots/t-slot-rail.png))
|
||||
[](t-slot-rail/main.kcl)
|
||||
#### [teapot](teapot/main.kcl) ([screenshot](screenshots/teapot.png))
|
||||
[](teapot/main.kcl)
|
||||
#### [telemetry-antenna](telemetry-antenna/main.kcl) ([screenshot](screenshots/telemetry-antenna.png))
|
||||
[](telemetry-antenna/main.kcl)
|
||||
#### [thermal-block-insert](thermal-block-insert/main.kcl) ([screenshot](screenshots/thermal-block-insert.png))
|
||||
|
@ -1,4 +1,4 @@
|
||||
// PC Fan
|
||||
// Axial Fan
|
||||
// A small axial fan, used to push or draw airflow over components to remove excess heat
|
||||
|
||||
// Set units
|
||||
|
@ -11,7 +11,7 @@ centerHoleDiameter = 1 + 3 / 4
|
||||
plateThickness = 0.375
|
||||
|
||||
// Check that the plate is thick enough to countersink a hole
|
||||
// assertGreaterThan(plateThickness, boltDiameter, "This plate is not thick enough for the necessary countersink dimensions")
|
||||
assert(plateThickness, isGreaterThan = boltDiameter, error = "This plate is not thick enough for the necessary countersink dimensions")
|
||||
|
||||
// A bit of math to calculate the tangent line between the two diameters
|
||||
r1 = centerHoleDiameter / 2 * 1.5 + .35
|
||||
|
@ -3,7 +3,7 @@
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "axial-fan/main.kcl",
|
||||
"multipleFiles": true,
|
||||
"title": "PC Fan",
|
||||
"title": "Axial Fan",
|
||||
"description": "A small axial fan, used to push or draw airflow over components to remove excess heat",
|
||||
"files": [
|
||||
"fan-housing.kcl",
|
||||
@ -447,6 +447,16 @@
|
||||
"main.kcl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "mug/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "Mug",
|
||||
"description": "A cup with straight sides and a handle, typically used for hot drinks",
|
||||
"files": [
|
||||
"main.kcl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "multi-axis-robot/main.kcl",
|
||||
@ -612,6 +622,16 @@
|
||||
"main.kcl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "spool/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "Spool",
|
||||
"description": "A spool is a winding device for storing large batches of filament, wire, tape, etc",
|
||||
"files": [
|
||||
"main.kcl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "spur-gear/main.kcl",
|
||||
@ -647,7 +667,17 @@
|
||||
"pathFromProjectDirectoryToFirstFile": "t-slot-rail/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "T-Slotted Framing Rail",
|
||||
"description": "A T-slotted framing rail, or T-slot extrusion, is a rectangular or square aluminum profile with a \"T\" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.",
|
||||
"description": "A T-slotted framing rail, commonly known as an 80/20 rail, is a rectangular or square aluminum extrusion with a \"T\" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.",
|
||||
"files": [
|
||||
"main.kcl"
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "main.kcl",
|
||||
"pathFromProjectDirectoryToFirstFile": "teapot/main.kcl",
|
||||
"multipleFiles": false,
|
||||
"title": "Teapot",
|
||||
"description": "A vessel with a spout and a handle used for serving tea and boiling water on a stovetop",
|
||||
"files": [
|
||||
"main.kcl"
|
||||
]
|
||||
|
81
public/kcl-samples/mug/main.kcl
Normal file
@ -0,0 +1,81 @@
|
||||
// Mug
|
||||
// A cup with straight sides and a handle, typically used for hot drinks
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
// Define parameters
|
||||
mugDiameter = 80
|
||||
mugHeight = 95
|
||||
wallThickness = 6
|
||||
handleWidth = 18
|
||||
handleThickness = 5
|
||||
|
||||
// Revolve the outer perimeter of the mug body. This will be used to subtract material from the handle, so that it fits snug to the body
|
||||
tools = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, mugDiameter * 0.04])
|
||||
|> xLine(length = mugDiameter * 0.1)
|
||||
|> tangentialArc(endAbsolute = [mugDiameter * 0.3, mugDiameter * 0.01])
|
||||
|> arc(angleStart = -180, angleEnd = -90, radius = mugDiameter * 0.01)
|
||||
|> xLine(length = mugDiameter * 0.025)
|
||||
|> arc(angleStart = -90, angleEnd = 0, radius = mugDiameter * 0.01)
|
||||
|> xLine(endAbsolute = mugDiameter * 0.35)
|
||||
|> tangentialArc(angle = 90, radius = mugDiameter * 0.15)
|
||||
|> yLine(endAbsolute = mugHeight - (wallThickness / 2))
|
||||
|> tangentialArc(angle = 90, diameter = wallThickness)
|
||||
|> xLine(endAbsolute = profileStartX())
|
||||
|> yLine(endAbsolute = profileStartY())
|
||||
|> close()
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Draw the path of the mug handle
|
||||
handlePath = startSketchOn(XZ)
|
||||
|> startProfile(at = [mugDiameter / 3, mugHeight * 0.25])
|
||||
|> angledLine(angle = -5, length = mugDiameter / 3)
|
||||
|> tangentialArc(angle = 90, radius = mugHeight * 0.15)
|
||||
|> tangentialArc(end = [0, mugHeight * 0.425])
|
||||
|> tangentialArc(angle = 90, radius = mugHeight * 0.15, tag = $seg02)
|
||||
|> angledLine(angle = tangentToEnd(seg02), endAbsoluteX = profileStartX())
|
||||
|
||||
// Sweep the handle profile along the path
|
||||
handle = startSketchOn(offsetPlane(YZ, offset = mugDiameter / 3))
|
||||
|> startProfile(at = [
|
||||
-handleWidth / 2,
|
||||
mugHeight * 0.25 + handleThickness / 2
|
||||
])
|
||||
|> arc(
|
||||
interiorAbsolute = [
|
||||
0,
|
||||
profileStartY() + handleThickness / 4
|
||||
],
|
||||
endAbsolute = [handleWidth / 2, profileStartY()],
|
||||
)
|
||||
|> tangentialArc(end = [0, -handleThickness])
|
||||
|> tangentialArc(end = [-handleWidth, 0])
|
||||
|> tangentialArc(endAbsolute = profileStart())
|
||||
|> close()
|
||||
|> sweep(path = handlePath)
|
||||
|> subtract(tools)
|
||||
|
||||
// Create the body of the mug
|
||||
mug = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, mugDiameter * 0.04])
|
||||
|> xLine(length = mugDiameter * 0.1)
|
||||
|> tangentialArc(endAbsolute = [mugDiameter * 0.3, mugDiameter * 0.01])
|
||||
|> arc(angleStart = -180, angleEnd = -90, radius = mugDiameter * 0.01)
|
||||
|> xLine(length = mugDiameter * 0.025)
|
||||
|> arc(angleStart = -90, angleEnd = 0, radius = mugDiameter * 0.01)
|
||||
|> xLine(endAbsolute = mugDiameter * 0.35)
|
||||
|> tangentialArc(angle = 90, radius = mugDiameter * 0.15)
|
||||
|> yLine(endAbsolute = mugHeight - (wallThickness / 2))
|
||||
|> tangentialArc(angle = 180, diameter = wallThickness)
|
||||
|> yLine(endAbsolute = mugDiameter * 0.15)
|
||||
|> tangentialArc(angle = -90, radius = lastSegX() * 0.2)
|
||||
|> xLine(endAbsolute = profileStartX())
|
||||
|> yLine(endAbsolute = profileStartY())
|
||||
|> close()
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Join the mug and handle, then add an appearance
|
||||
[handle, mug]
|
||||
|> appearance(color = "#9b1212", metalness = 40, roughness = 30)
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
BIN
public/kcl-samples/screenshots/mug.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
public/kcl-samples/screenshots/spool.png
Normal file
After Width: | Height: | Size: 155 KiB |
BIN
public/kcl-samples/screenshots/teapot.png
Normal file
After Width: | Height: | Size: 128 KiB |
68
public/kcl-samples/spool/main.kcl
Normal file
@ -0,0 +1,68 @@
|
||||
// Spool
|
||||
// A spool is a winding device for storing large batches of filament, wire, tape, etc
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
// Define shaft parameters
|
||||
hubDiameter = 2in
|
||||
spoolDiameter = 305
|
||||
spoolThickness = 267
|
||||
coreDiameter = 150
|
||||
webThickness = 10
|
||||
stockDiameter = 5
|
||||
|
||||
// Sketch the revolved profile of one side of the spool
|
||||
spoolBase = startSketchOn(XY)
|
||||
|> startProfile(at = [0, coreDiameter / 2])
|
||||
|> xLine(length = spoolThickness / 2)
|
||||
|> yLine(endAbsolute = spoolDiameter / 2 - webThickness)
|
||||
|> tangentialArc(angle = -270, radius = webThickness)
|
||||
|> yLine(endAbsolute = hubDiameter / 2, tag = $seg01)
|
||||
|> xLine(endAbsolute = profileStartX())
|
||||
|> yLine(endAbsolute = profileStartY())
|
||||
|> close()
|
||||
|> revolve(axis = X)
|
||||
|> appearance(color = "#010101", metalness = 10, roughness = 70)
|
||||
|
||||
// Create a function that draws an indent on the outer faces of the spool
|
||||
fn score(radius, length, depth) {
|
||||
scoreFn = startSketchOn(spoolBase, face = seg01)
|
||||
|> startProfile(at = [0, radius])
|
||||
|> arc(angleStart = 90, angleEnd = 55, radius = profileStartY(%))
|
||||
|> angledLine(angle = 55, length = length)
|
||||
|> arc(
|
||||
%,
|
||||
angleStart = 55,
|
||||
angleEnd = 90,
|
||||
radius = profileStartY(%) + length,
|
||||
)
|
||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||
|> close()
|
||||
|> patternCircular2d(instances = 8, center = [0, 0])
|
||||
|> extrude(length = -depth)
|
||||
|
||||
return scoreFn
|
||||
}
|
||||
|
||||
// Circular pattern indents around each side face of the spool
|
||||
scoreSketch01 = score(radius = hubDiameter / 2 + webThickness, length = (coreDiameter - hubDiameter) / 2 - (2 * webThickness), depth = spoolThickness / 2)
|
||||
scoreSketch02 = score(radius = coreDiameter / 2, length = (spoolDiameter - coreDiameter) / 2 - (webThickness * 3), depth = webThickness / 2)
|
||||
// Mirror the spool base piece across the Y axis
|
||||
|> patternCircular3d(instances = 2, axis = [0, 1, 0], center = [0, 0, 0])
|
||||
|
||||
// Model a wound coil on the spool diameter
|
||||
hel01 = helix(
|
||||
revolutions = spoolThickness / stockDiameter / 1.5,
|
||||
angleStart = 0,
|
||||
radius = (coreDiameter + stockDiameter) / 2,
|
||||
axis = X,
|
||||
length = spoolThickness,
|
||||
)
|
||||
|
||||
// Sweep and color the coil
|
||||
stock = startSketchOn(XZ)
|
||||
|> circle(center = [0, -(coreDiameter + stockDiameter) / 2], diameter = stockDiameter)
|
||||
|> sweep(path = hel01)
|
||||
|> translate(x = -spoolThickness / 2)
|
||||
|> appearance(color = "#f2471c", metalness = 70, roughness = 30)
|
@ -1,5 +1,5 @@
|
||||
// T-Slotted Framing Rail
|
||||
// A T-slotted framing rail, or T-slot extrusion, is a rectangular or square aluminum profile with a "T" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.
|
||||
// A T-slotted framing rail, commonly known as an 80/20 rail, is a rectangular or square aluminum extrusion with a "T" shaped slot along one or more sides. These slots allow for easy attachment of various hardware components like brackets, connectors, and fasteners, making it a versatile and customizable framing system.
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = in, kclVersion = 1.0)
|
||||
|
106
public/kcl-samples/teapot/main.kcl
Normal file
@ -0,0 +1,106 @@
|
||||
// Teapot
|
||||
// A vessel with a spout and a handle used for serving tea and boiling water on a stovetop
|
||||
|
||||
// Set units
|
||||
@settings(defaultLengthUnit = mm)
|
||||
|
||||
// Define parameters
|
||||
teapotHeight = 130
|
||||
beltlineDiameter = 160
|
||||
wallThickness = 6
|
||||
outletHeight = 26
|
||||
spoutDiameter = 12
|
||||
|
||||
// Sketch the path of the teapot spout
|
||||
spoutPath = startSketchOn(XZ)
|
||||
|> startProfile(at = [-beltlineDiameter / 2.5, outletHeight])
|
||||
|> xLine(length = -15.05)
|
||||
|> tangentialArc(angle = -110, radius = 30, tag = $seg01)
|
||||
|> angledLine(angle = tangentToEnd(seg01), length = 16.84)
|
||||
|> tangentialArc(angle = 100, radius = 30)
|
||||
|
||||
// Sweep a hollow spout
|
||||
spout = startSketchOn(offsetPlane(YZ, offset = -beltlineDiameter / 2.5))
|
||||
|> circle(center = [0, outletHeight], diameter = spoutDiameter)
|
||||
|> subtract2d(tool = circle(center = [0, outletHeight], diameter = spoutDiameter * 0.8))
|
||||
|> sweep(path = spoutPath)
|
||||
|
||||
// Model the perimeter of the teapot body, to be used to trim the handle so that it fits snug to the body
|
||||
tools = startSketchOn(YZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> xLine(length = beltlineDiameter / 5)
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 2.1,
|
||||
teapotHeight / 2.5
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 3,
|
||||
teapotHeight / 1.1
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [beltlineDiameter / 4, teapotHeight])
|
||||
|> xLine(endAbsolute = profileStartX(%))
|
||||
|> line(endAbsolute = profileStart(%))
|
||||
|> close()
|
||||
|> revolve(axis = Y)
|
||||
|
||||
// Sketch the path of the handle
|
||||
handlePath = startSketchOn(XZ)
|
||||
|> startProfile(at = [0, outletHeight])
|
||||
|> xLine(endAbsolute = 76)
|
||||
|> tangentialArc(end = [12.98, 6.64])
|
||||
|> tangentialArc(end = [28.39, 63.11])
|
||||
|> tangentialArc(end = [-10.27, 14.8])
|
||||
|> tangentialArc(end = [-48.01, 1.81], tag = $seg02)
|
||||
|> angledLine(angle = tangentToEnd(seg02), endAbsoluteX = 0)
|
||||
|
||||
// Sweep the handle
|
||||
handle = startSketchOn(YZ)
|
||||
|> startProfile(at = [-10, outletHeight + 3.5])
|
||||
|> arc(interiorAbsolute = [0, outletHeight + 5], endAbsolute = [10, profileStartY(%)])
|
||||
|> tangentialArc(end = [0, -7])
|
||||
|> tangentialArc(end = [-20, 0])
|
||||
|> tangentialArc(endAbsolute = profileStart())
|
||||
|> close()
|
||||
|> sweep(path = handlePath)
|
||||
|> subtract(tools)
|
||||
|
||||
// Create a cutout in the body for the spout outlet
|
||||
spoutHole = startSketchOn(YZ)
|
||||
|> circle(center = [0, outletHeight], diameter = spoutDiameter)
|
||||
|> extrude(length = -beltlineDiameter / 2)
|
||||
|
||||
// Model the body of the teapot
|
||||
body = startSketchOn(YZ)
|
||||
|> startProfile(at = [0, 0])
|
||||
|> xLine(length = beltlineDiameter / 5)
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 2.1,
|
||||
teapotHeight / 2.5
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 3,
|
||||
teapotHeight / 1.1
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [beltlineDiameter / 4, teapotHeight])
|
||||
|> tangentialArc(angle = 190, diameter = wallThickness)
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 3 - wallThickness,
|
||||
teapotHeight / 1.1
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 2.1 - wallThickness,
|
||||
teapotHeight / 2.5
|
||||
])
|
||||
|> tangentialArc(endAbsolute = [
|
||||
beltlineDiameter / 5,
|
||||
profileStartY() + wallThickness
|
||||
])
|
||||
|> xLine(endAbsolute = profileStartX(%))
|
||||
|> line(endAbsolute = profileStart(%))
|
||||
|> close()
|
||||
|> revolve(axis = Y)
|
||||
|> subtract(tools = [spoutHole])
|
||||
|
||||
// Join each component of the teapot and add an appearance
|
||||
[body, handle, spout]
|
||||
|> appearance(color = "#1f9896", metalness = 40, roughness = 30)
|
24
rust/Cargo.lock
generated
@ -1792,7 +1792,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1803,7 +1803,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-derive-docs"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -1812,7 +1812,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-directory-test-macro"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"convert_case",
|
||||
"proc-macro2",
|
||||
@ -1822,7 +1822,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1843,7 +1843,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@ -1863,7 +1863,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"approx 0.5.1",
|
||||
@ -1940,7 +1940,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-python-bindings"
|
||||
version = "0.3.80"
|
||||
version = "0.3.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"kcl-lib",
|
||||
@ -1955,7 +1955,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-test-server"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"hyper 0.14.32",
|
||||
@ -1968,7 +1968,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-to-core"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -1982,7 +1982,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-wasm-lib"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bson",
|
||||
@ -2049,9 +2049,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad-modeling-cmds"
|
||||
version = "0.2.121"
|
||||
version = "0.2.123"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94ba95c22493d79ec8a1faab963d8903f6de0e373efedf2bc3bb76a0ddbab036"
|
||||
checksum = "b8f3c1b4b4ddb9aa336a09933f2550f9882552e321187b7bcff47f006379c3aa"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
|
@ -36,7 +36,7 @@ dashmap = { version = "6.1.0" }
|
||||
http = "1"
|
||||
indexmap = "2.9.0"
|
||||
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.120", features = ["ts-rs", "websocket"] }
|
||||
kittycad-modeling-cmds = { version = "0.2.123", features = ["ts-rs", "websocket"] }
|
||||
lazy_static = "1.5.0"
|
||||
miette = "7.5.0"
|
||||
pyo3 = { version = "0.24.1" }
|
||||
|
@ -1,7 +1,7 @@
|
||||
|
||||
[package]
|
||||
name = "kcl-bumper"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
edition = "2021"
|
||||
repository = "https://github.com/KittyCAD/modeling-api"
|
||||
rust-version = "1.76"
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-derive-docs"
|
||||
description = "A tool for generating documentation from Rust derive macros"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -117,6 +117,7 @@ pub const TEST_NAMES: &[&str] = &[
|
||||
"std-sketch-getOppositeEdge-0",
|
||||
"std-sketch-getPreviousAdjacentEdge-0",
|
||||
"std-sketch-getNextAdjacentEdge-0",
|
||||
"std-sketch-extrudeTwist-0",
|
||||
"std-sketch-extrude-0",
|
||||
"std-sketch-extrude-1",
|
||||
"std-sketch-extrude-2",
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-directory-test-macro"
|
||||
description = "A tool for generating tests from a directory of kcl files"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "kcl-language-server-release"
|
||||
version = "0.1.80"
|
||||
version = "0.1.81"
|
||||
edition = "2021"
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
publish = false
|
||||
|
@ -2,7 +2,7 @@
|
||||
name = "kcl-language-server"
|
||||
description = "A language server for KCL."
|
||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language implementation and tools"
|
||||
version = "0.2.80"
|
||||
version = "0.2.81"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
repository = "https://github.com/KittyCAD/modeling-app"
|
||||
|
@ -300,12 +300,6 @@ extrude001 = extrude(profile001, length = 4)
|
||||
let first = &result.first().unwrap().2;
|
||||
let second = &result.last().unwrap().2;
|
||||
|
||||
assert!(
|
||||
first.artifact_commands.len() < second.artifact_commands.len(),
|
||||
"Second should have all the artifact commands of the first, plus more. first={:?}, second={:?}",
|
||||
first.artifact_commands.len(),
|
||||
second.artifact_commands.len()
|
||||
);
|
||||
assert!(
|
||||
first.artifact_graph.len() < second.artifact_graph.len(),
|
||||
"Second should have all the artifacts of the first, plus more. first={:?}, second={:?}",
|
||||
|
@ -1230,7 +1230,10 @@ secondSketch = startSketchOn(part001, face = '')
|
||||
let result = execute_and_snapshot(code, None).await;
|
||||
let err = result.unwrap_err();
|
||||
let err = err.as_kcl_error().unwrap();
|
||||
assert_eq!(err.message(), "face requires a value with type `tag`, but found string");
|
||||
assert_eq!(
|
||||
err.message(),
|
||||
"face requires a value with type `tag`, but found a value with type `string`."
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -1962,7 +1965,7 @@ someFunction('INVALID')
|
||||
let err = err.as_kcl_error().unwrap();
|
||||
assert_eq!(
|
||||
err.message(),
|
||||
"The input argument of `startSketchOn` requires a value with type `Solid | Plane`, but found string"
|
||||
"The input argument of `startSketchOn` requires a value with type `Solid` or a value with type `Plane` (`Solid | Plane`), but found a value with type `string`."
|
||||
);
|
||||
assert_eq!(
|
||||
err.source_ranges(),
|
||||
@ -2087,7 +2090,7 @@ async fn kcl_test_better_type_names() {
|
||||
},
|
||||
None => todo!(),
|
||||
};
|
||||
assert_eq!(err, "This function expected the input argument to be one or more Solids or imported geometry but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
||||
assert_eq!(err, "This function expected the input argument to be one or more Solids or ImportedGeometry but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
|
@ -508,8 +508,9 @@ impl EngineManager for EngineConnection {
|
||||
.await?;
|
||||
|
||||
// Wait for the response.
|
||||
let response_timeout = 300;
|
||||
let current_time = std::time::Instant::now();
|
||||
while current_time.elapsed().as_secs() < 60 {
|
||||
while current_time.elapsed().as_secs() < response_timeout {
|
||||
let guard = self.socket_health.read().await;
|
||||
if *guard == SocketHealth::Inactive {
|
||||
// Check if we have any pending errors.
|
||||
|
@ -135,8 +135,10 @@ pub struct KclErrorWithOutputs {
|
||||
pub non_fatal: Vec<CompilationError>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub operations: Vec<Operation>,
|
||||
// TODO: Remove this field. Doing so breaks the ts-rs output for some
|
||||
// reason.
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_commands: Vec<ArtifactCommand>,
|
||||
pub _artifact_commands: Vec<ArtifactCommand>,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub artifact_graph: ArtifactGraph,
|
||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||
@ -162,7 +164,7 @@ impl KclErrorWithOutputs {
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands,
|
||||
_artifact_commands: artifact_commands,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph,
|
||||
filenames,
|
||||
@ -177,7 +179,7 @@ impl KclErrorWithOutputs {
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: Default::default(),
|
||||
_artifact_commands: Default::default(),
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: Default::default(),
|
||||
filenames: Default::default(),
|
||||
@ -781,6 +783,7 @@ impl Severity {
|
||||
pub enum Tag {
|
||||
Deprecated,
|
||||
Unnecessary,
|
||||
UnknownNumericUnits,
|
||||
None,
|
||||
}
|
||||
|
||||
|
@ -45,26 +45,6 @@ pub struct ArtifactCommand {
|
||||
pub command: ModelingCmd,
|
||||
}
|
||||
|
||||
impl PartialOrd for ArtifactCommand {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
// Order by the source range.
|
||||
let range = self.range.cmp(&other.range);
|
||||
if range != std::cmp::Ordering::Equal {
|
||||
return Some(range);
|
||||
}
|
||||
#[cfg(test)]
|
||||
{
|
||||
// If the ranges are equal, order by the serde variant.
|
||||
Some(
|
||||
crate::variant_name::variant_name(&self.command)
|
||||
.cmp(&crate::variant_name::variant_name(&other.command)),
|
||||
)
|
||||
}
|
||||
#[cfg(not(test))]
|
||||
self.cmd_id.partial_cmp(&other.cmd_id)
|
||||
}
|
||||
}
|
||||
|
||||
pub type DummyPathToNode = Vec<()>;
|
||||
|
||||
fn serialize_dummy_path_to_node<S>(_path_to_node: &DummyPathToNode, serializer: S) -> Result<S::Ok, S::Error>
|
||||
@ -190,6 +170,7 @@ pub struct Sweep {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum SweepSubType {
|
||||
Extrusion,
|
||||
ExtrusionTwist,
|
||||
Revolve,
|
||||
RevolveAboutEdge,
|
||||
Loft,
|
||||
@ -1102,11 +1083,13 @@ fn artifacts_to_update(
|
||||
return Ok(return_arr);
|
||||
}
|
||||
ModelingCmd::Extrude(kcmc::Extrude { target, .. })
|
||||
| ModelingCmd::TwistExtrude(kcmc::TwistExtrude { target, .. })
|
||||
| ModelingCmd::Revolve(kcmc::Revolve { target, .. })
|
||||
| ModelingCmd::RevolveAboutEdge(kcmc::RevolveAboutEdge { target, .. })
|
||||
| ModelingCmd::Sweep(kcmc::Sweep { target, .. }) => {
|
||||
let sub_type = match cmd {
|
||||
ModelingCmd::Extrude(_) => SweepSubType::Extrusion,
|
||||
ModelingCmd::TwistExtrude(_) => SweepSubType::ExtrusionTwist,
|
||||
ModelingCmd::Revolve(_) => SweepSubType::Revolve,
|
||||
ModelingCmd::RevolveAboutEdge(_) => SweepSubType::RevolveAboutEdge,
|
||||
ModelingCmd::Sweep(_) => SweepSubType::Sweep,
|
||||
|
@ -111,8 +111,6 @@ impl GlobalState {
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
operations: self.exec_state.artifacts.operations,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_commands: self.exec_state.artifacts.commands,
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
artifact_graph: self.exec_state.artifacts.graph,
|
||||
errors: self.exec_state.errors,
|
||||
default_planes: ctx.engine.get_default_planes().read().await.clone(),
|
||||
|
@ -2,7 +2,9 @@ use indexmap::IndexMap;
|
||||
use serde::Serialize;
|
||||
|
||||
use super::{types::NumericType, ArtifactId, KclValue};
|
||||
use crate::{ModuleId, NodePath, SourceRange};
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
use crate::parsing::ast::types::{Node, Program};
|
||||
use crate::{parsing::ast::types::ItemVisibility, ModuleId, NodePath, SourceRange};
|
||||
|
||||
/// A CAD modeling operation for display in the feature tree, AKA operations
|
||||
/// timeline.
|
||||
@ -26,6 +28,20 @@ pub enum Operation {
|
||||
is_error: bool,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
VariableDeclaration {
|
||||
/// The variable name.
|
||||
name: String,
|
||||
/// The value of the variable.
|
||||
value: OpKclValue,
|
||||
/// The visibility modifier of the variable, e.g. `export`. `Default`
|
||||
/// means there is no visibility modifier.
|
||||
visibility: ItemVisibility,
|
||||
/// The node path of the operation in the source code.
|
||||
node_path: NodePath,
|
||||
/// The source range of the operation in the source code.
|
||||
source_range: SourceRange,
|
||||
},
|
||||
#[serde(rename_all = "camelCase")]
|
||||
GroupBegin {
|
||||
/// The details of the group.
|
||||
group: Group,
|
||||
@ -37,32 +53,36 @@ pub enum Operation {
|
||||
GroupEnd,
|
||||
}
|
||||
|
||||
/// A way for sorting the operations in the timeline. This is used to sort
|
||||
/// operations in the timeline and to determine the order of operations.
|
||||
/// We use this for the multi-threaded snapshotting, so that we can have deterministic
|
||||
/// output.
|
||||
impl PartialOrd for Operation {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(match (self, other) {
|
||||
(Self::StdLibCall { source_range: a, .. }, Self::StdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::StdLibCall { source_range: a, .. }, Self::GroupBegin { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::StdLibCall { .. }, Self::GroupEnd) => std::cmp::Ordering::Less,
|
||||
(Self::GroupBegin { source_range: a, .. }, Self::GroupBegin { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::GroupBegin { source_range: a, .. }, Self::StdLibCall { source_range: b, .. }) => a.cmp(b),
|
||||
(Self::GroupBegin { .. }, Self::GroupEnd) => std::cmp::Ordering::Less,
|
||||
(Self::GroupEnd, Self::StdLibCall { .. }) => std::cmp::Ordering::Greater,
|
||||
(Self::GroupEnd, Self::GroupBegin { .. }) => std::cmp::Ordering::Greater,
|
||||
(Self::GroupEnd, Self::GroupEnd) => std::cmp::Ordering::Equal,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
/// If the variant is `StdLibCall`, set the `is_error` field.
|
||||
pub(crate) fn set_std_lib_call_is_error(&mut self, is_err: bool) {
|
||||
match self {
|
||||
Self::StdLibCall { ref mut is_error, .. } => *is_error = is_err,
|
||||
Self::GroupBegin { .. } | Self::GroupEnd => {}
|
||||
Self::VariableDeclaration { .. } | Self::GroupBegin { .. } | Self::GroupEnd => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "artifact-graph")]
|
||||
pub(crate) fn fill_node_paths(&mut self, program: &Node<Program>, cached_body_items: usize) {
|
||||
match self {
|
||||
Operation::StdLibCall {
|
||||
node_path,
|
||||
source_range,
|
||||
..
|
||||
}
|
||||
| Operation::VariableDeclaration {
|
||||
node_path,
|
||||
source_range,
|
||||
..
|
||||
}
|
||||
| Operation::GroupBegin {
|
||||
node_path,
|
||||
source_range,
|
||||
..
|
||||
} => {
|
||||
node_path.fill_placeholder(program, cached_body_items, *source_range);
|
||||
}
|
||||
Operation::GroupEnd => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|