Compare commits
39 Commits
more-gears
...
delete-net
Author | SHA1 | Date | |
---|---|---|---|
dac3c0c224 | |||
99cb6a6179 | |||
13dbfdfaa4 | |||
8603f5c53a | |||
d5cc9e8386 | |||
533e17466b | |||
01c7b69f50 | |||
47feae3bd9 | |||
e7ecd655c4 | |||
d9e538c6ea | |||
e297f8286f | |||
b3bc90bbe4 | |||
95a02cbcd7 | |||
a049768f1c | |||
818d9a0d77 | |||
1a8f80a7dc | |||
566c9eaf10 | |||
e6485c2da1 | |||
0479edd36a | |||
87c1e92134 | |||
8f950ac1b0 | |||
78e4f43708 | |||
270436f404 | |||
57e61632a9 | |||
884191b8bb | |||
21b92f5f13 | |||
5599a75dbd | |||
3a06ae6e34 | |||
22857d77e9 | |||
1a325d0b29 | |||
1240b23080 | |||
8445080d7a | |||
bbe89f56a7 | |||
86e8bcfe0b | |||
21ccf129d6 | |||
dfc4b7d0c5 | |||
17b1120a27 | |||
2b509a515b | |||
97594b9a9e |
40
.github/ci-cd-scripts/upload-results.sh
vendored
@ -1,13 +1,41 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
BRANCH="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
|
if [ -z "${TAB_API_URL:-}" ] || [ -z "${TAB_API_KEY:-}" ]; then
|
||||||
COMMIT="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
curl --request POST \
|
project="https://github.com/KittyCAD/modeling-app"
|
||||||
|
branch="${GITHUB_HEAD_REF:-${GITHUB_REF_NAME:-}}"
|
||||||
|
commit="${CI_COMMIT_SHA:-${GITHUB_SHA:-}}"
|
||||||
|
|
||||||
|
echo "Uploading batch results"
|
||||||
|
curl --silent --request POST \
|
||||||
--header "X-API-Key: ${TAB_API_KEY}" \
|
--header "X-API-Key: ${TAB_API_KEY}" \
|
||||||
--form "project=https://github.com/KittyCAD/modeling-app" \
|
--form "project=${project}" \
|
||||||
--form "branch=${BRANCH}" \
|
--form "branch=${branch}" \
|
||||||
--form "commit=${COMMIT}" \
|
--form "commit=${commit}" \
|
||||||
--form "tests=@test-results/junit.xml" \
|
--form "tests=@test-results/junit.xml" \
|
||||||
|
--form "CI_COMMIT_SHA=${CI_COMMIT_SHA:-}" \
|
||||||
|
--form "CI_PR_NUMBER=${CI_PR_NUMBER:-}" \
|
||||||
|
--form "GITHUB_BASE_REF=${GITHUB_BASE_REF:-}" \
|
||||||
|
--form "GITHUB_EVENT_NAME=${GITHUB_EVENT_NAME:-}" \
|
||||||
|
--form "GITHUB_HEAD_REF=${GITHUB_HEAD_REF:-}" \
|
||||||
|
--form "GITHUB_REF_NAME=${GITHUB_REF_NAME:-}" \
|
||||||
|
--form "GITHUB_REF=${GITHUB_REF:-}" \
|
||||||
|
--form "GITHUB_SHA=${GITHUB_SHA:-}" \
|
||||||
|
--form "GITHUB_WORKFLOW=${GITHUB_WORKFLOW:-}" \
|
||||||
|
--form "RUNNER_ARCH=${RUNNER_ARCH:-}" \
|
||||||
${TAB_API_URL}/api/results/bulk
|
${TAB_API_URL}/api/results/bulk
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Sharing updated report"
|
||||||
|
curl --silent --request POST \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
--header "X-API-Key: ${TAB_API_KEY}" \
|
||||||
|
--data "{
|
||||||
|
\"project\": \"${project}\",
|
||||||
|
\"branch\": \"${branch}\",
|
||||||
|
\"commit\": \"${commit}\"
|
||||||
|
}" \
|
||||||
|
${TAB_API_URL}/api/share
|
||||||
|
21
.github/workflows/build-apps.yml
vendored
@ -7,11 +7,10 @@ on:
|
|||||||
- main
|
- main
|
||||||
tags:
|
tags:
|
||||||
- 'v[0-9]+.[0-9]+.[0-9]+'
|
- 'v[0-9]+.[0-9]+.[0-9]+'
|
||||||
- 'nightly-v[0-9]+.[0-9]+.[0-9]+'
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
|
IS_RELEASE: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'v') }}
|
||||||
IS_NIGHTLY: ${{ github.ref_type == 'tag' && startsWith(github.ref_name, 'nightly-v') }}
|
IS_NIGHTLY: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
@ -95,7 +94,9 @@ jobs:
|
|||||||
- name: Set nightly version, product name, release notes, and icons
|
- name: Set nightly version, product name, release notes, and icons
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
run: |
|
run: |
|
||||||
export VERSION=${GITHUB_REF_NAME#nightly-v}
|
COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
DATE=$(date +'%-y.%-m.%-d')
|
||||||
|
export VERSION=$DATE-main.$COMMIT
|
||||||
npm run files:set-version
|
npm run files:set-version
|
||||||
npm run files:flip-to-nightly
|
npm run files:flip-to-nightly
|
||||||
|
|
||||||
@ -306,7 +307,8 @@ jobs:
|
|||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-22.04
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
if: ${{ github.ref_type == 'tag' }}
|
# Equivalent to IS_RELEASE || IS_NIGHTLY (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:
|
env:
|
||||||
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
VERSION_NO_V: ${{ needs.prepare-files.outputs.version }}
|
||||||
VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }}
|
VERSION: ${{ format('v{0}', needs.prepare-files.outputs.version) }}
|
||||||
@ -412,17 +414,6 @@ jobs:
|
|||||||
- name: List artifacts
|
- name: List artifacts
|
||||||
run: "ls -R out"
|
run: "ls -R out"
|
||||||
|
|
||||||
- name: Set more complete nightly release notes
|
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
|
||||||
run: |
|
|
||||||
# Note: preferred going this way instead of a full clone in the checkout step,
|
|
||||||
# see https://github.com/actions/checkout/issues/1471
|
|
||||||
git fetch --prune --unshallow --tags
|
|
||||||
export TAG="nightly-${VERSION}"
|
|
||||||
export PREVIOUS_TAG=$(git tag --list --sort=-committerdate "nightly-v[0-9]*" | head -n2 | tail -n1)
|
|
||||||
export NOTES=$(./scripts/get-nightly-changelog.sh)
|
|
||||||
npm run files:set-notes
|
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
if: ${{ env.IS_NIGHTLY == 'true' }}
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
uses: 'google-github-actions/auth@v2.1.8'
|
uses: 'google-github-actions/auth@v2.1.8'
|
||||||
|
2
.github/workflows/cargo-test.yml
vendored
@ -188,6 +188,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
TAB_API_URL: ${{ secrets.TAB_API_URL }}
|
||||||
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
TAB_API_KEY: ${{ secrets.TAB_API_KEY }}
|
||||||
|
CI_COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
|
||||||
|
CI_PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||||
run-wasm-tests:
|
run-wasm-tests:
|
||||||
name: Run wasm tests
|
name: Run wasm tests
|
||||||
strategy:
|
strategy:
|
||||||
|
47
.github/workflows/check-exampleKcl.yml
vendored
@ -1,47 +0,0 @@
|
|||||||
name: Check Onboarding KCL
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize]
|
|
||||||
paths:
|
|
||||||
- 'src/lib/exampleKcl.ts'
|
|
||||||
- 'public/kcl-samples/bracket/main.kcl'
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
issues: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
comment:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Comment on PR
|
|
||||||
uses: actions/github-script@v7
|
|
||||||
with:
|
|
||||||
script: |
|
|
||||||
const message = '`public/kcl-samples/bracket/main.kcl` or `src/lib/exampleKcl.ts` has been updated in this PR, please review and update the `src/routes/onboarding`, if needed.';
|
|
||||||
const issue_number = context.payload.pull_request.number;
|
|
||||||
const owner = context.repo.owner;
|
|
||||||
const repo = context.repo.repo;
|
|
||||||
|
|
||||||
const { data: comments } = await github.rest.issues.listComments({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number
|
|
||||||
});
|
|
||||||
|
|
||||||
const commentExists = comments.some(comment => comment.body === message);
|
|
||||||
|
|
||||||
if (!commentExists) {
|
|
||||||
// Post a comment on the PR
|
|
||||||
await github.rest.issues.createComment({
|
|
||||||
owner,
|
|
||||||
repo,
|
|
||||||
issue_number,
|
|
||||||
body: message,
|
|
||||||
});
|
|
||||||
}
|
|
114
.github/workflows/e2e-tests.yml
vendored
@ -18,73 +18,13 @@ permissions:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
conditions:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
outputs:
|
|
||||||
significant: ${{ steps.path-changes.outputs.significant }}
|
|
||||||
should-run: ${{ steps.should-run.outputs.should-run }}
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Fetch the base branch
|
|
||||||
if: ${{ github.event_name == 'pull_request' }}
|
|
||||||
run: git fetch origin ${{ github.base_ref }} --depth=1
|
|
||||||
|
|
||||||
- name: Check for path changes
|
|
||||||
id: path-changes
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Manual runs or push should run all tests.
|
|
||||||
if [[ ${{ github.event_name }} != 'pull_request' ]]; then
|
|
||||||
echo "significant=true" >> $GITHUB_OUTPUT
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
changed_files=$(git diff --name-only origin/${{ github.base_ref }})
|
|
||||||
echo "$changed_files"
|
|
||||||
if grep -Evq '^README.md|^public/kcl-samples/|^rust/kcl-lib/tests/kcl_samples/' <<< "$changed_files" ; then
|
|
||||||
echo "significant=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "significant=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Should run
|
|
||||||
id: should-run
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
# We should run when this is a scheduled run or if there are
|
|
||||||
# significant changes in the diff.
|
|
||||||
if [[ ${{ github.event_name }} == 'schedule' || ${{ steps.path-changes.outputs.significant }} == 'true' ]]; then
|
|
||||||
echo "should-run=true" >> $GITHUB_OUTPUT
|
|
||||||
else
|
|
||||||
echo "should-run=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Display conditions
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
# For debugging purposes
|
|
||||||
set -euo pipefail
|
|
||||||
echo "GITHUB_REF: $GITHUB_REF"
|
|
||||||
echo "GITHUB_HEAD_REF: $GITHUB_HEAD_REF"
|
|
||||||
echo "GITHUB_BASE_REF: $GITHUB_BASE_REF"
|
|
||||||
echo "significant: ${{ steps.path-changes.outputs.significant }}"
|
|
||||||
echo "should-run: ${{ steps.should-run.outputs.should-run }}"
|
|
||||||
|
|
||||||
prepare-wasm:
|
prepare-wasm:
|
||||||
# separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
|
# separate job on Ubuntu to build or fetch the wasm blob once on the fastest runner
|
||||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||||
needs: conditions
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
|
|
||||||
- id: filter
|
- id: filter
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
name: Check for Rust changes
|
name: Check for Rust changes
|
||||||
uses: dorny/paths-filter@v3
|
uses: dorny/paths-filter@v3
|
||||||
with:
|
with:
|
||||||
@ -93,18 +33,16 @@ jobs:
|
|||||||
- 'rust/**'
|
- 'rust/**'
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Download Wasm Cache
|
- name: Download Wasm Cache
|
||||||
id: download-wasm
|
id: download-wasm
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
if: ${{ github.event_name != 'schedule' && steps.filter.outputs.rust == 'false' }}
|
||||||
uses: dawidd6/action-download-artifact@v7
|
uses: dawidd6/action-download-artifact@v7
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
@ -116,7 +54,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Build WASM condition
|
- name: Build WASM condition
|
||||||
id: wasm
|
id: wasm
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: |
|
run: |
|
||||||
set -euox pipefail
|
set -euox pipefail
|
||||||
# Build wasm if this is a scheduled run, there are Rust changes, or
|
# Build wasm if this is a scheduled run, there are Rust changes, or
|
||||||
@ -128,35 +65,34 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Use correct Rust toolchain
|
- name: Use correct Rust toolchain
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
[ -e rust-toolchain.toml ] || cp rust/rust-toolchain.toml ./
|
||||||
|
|
||||||
- name: Install rust
|
- name: Install rust
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
cache: false # Configured below.
|
cache: false # Configured below.
|
||||||
|
|
||||||
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
- uses: taiki-e/install-action@d4635f2de61c8b8104d59cd4aede2060638378cc
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
with:
|
with:
|
||||||
tool: wasm-pack
|
tool: wasm-pack
|
||||||
|
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
uses: Swatinem/rust-cache@v2
|
uses: Swatinem/rust-cache@v2
|
||||||
with:
|
with:
|
||||||
workspaces: './rust'
|
workspaces: './rust'
|
||||||
|
|
||||||
- name: Build Wasm
|
- name: Build Wasm
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && steps.wasm.outputs.should-build-wasm == 'true' }}
|
if: ${{ steps.wasm.outputs.should-build-wasm == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: npm run build:wasm
|
run: npm run build:wasm
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
with:
|
with:
|
||||||
name: prepared-wasm
|
name: prepared-wasm
|
||||||
path: |
|
path: |
|
||||||
@ -165,10 +101,9 @@ jobs:
|
|||||||
snapshots:
|
snapshots:
|
||||||
name: playwright:snapshots:ubuntu
|
name: playwright:snapshots:ubuntu
|
||||||
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
runs-on: runs-on=${{ github.run_id }}/family=i7ie.2xlarge/image=ubuntu22-full-x64
|
||||||
needs: [conditions, prepare-wasm]
|
needs: [prepare-wasm]
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/create-github-app-token@v1
|
- uses: actions/create-github-app-token@v1
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
id: app-token
|
id: app-token
|
||||||
with:
|
with:
|
||||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
||||||
@ -176,16 +111,13 @@ jobs:
|
|||||||
owner: ${{ github.repository_owner }}
|
owner: ${{ github.repository_owner }}
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
with:
|
with:
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
name: prepared-wasm
|
name: prepared-wasm
|
||||||
|
|
||||||
- name: Copy prepared wasm
|
- name: Copy prepared wasm
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: |
|
run: |
|
||||||
ls -R prepared-wasm
|
ls -R prepared-wasm
|
||||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||||
@ -193,18 +125,15 @@ jobs:
|
|||||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
id: deps-install
|
id: deps-install
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Cache Playwright Browsers
|
- name: Cache Playwright Browsers
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@ -212,15 +141,12 @@ jobs:
|
|||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
|
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm run playwright install --with-deps
|
run: npm run playwright install --with-deps
|
||||||
|
|
||||||
- name: build web
|
- name: build web
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm run tronb:vite:dev
|
run: npm run tronb:vite:dev
|
||||||
|
|
||||||
- name: Run ubuntu/chrome snapshots
|
- name: Run ubuntu/chrome snapshots
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
uses: nick-fields/retry@v3.0.2
|
uses: nick-fields/retry@v3.0.2
|
||||||
with:
|
with:
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -236,7 +162,7 @@ jobs:
|
|||||||
TARGET: web
|
TARGET: web
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
with:
|
with:
|
||||||
name: playwright-report-ubuntu-snapshot-${{ github.sha }}
|
name: playwright-report-ubuntu-snapshot-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
@ -245,7 +171,7 @@ jobs:
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- name: Check for changes
|
- name: Check for changes
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && github.ref != 'refs/heads/main' }}
|
if: ${{ github.ref != 'refs/heads/main' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
id: git-check
|
id: git-check
|
||||||
run: |
|
run: |
|
||||||
@ -257,7 +183,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Commit changes, if any
|
- name: Commit changes, if any
|
||||||
# TODO: find a more reliable way to detect visual changes
|
# TODO: find a more reliable way to detect visual changes
|
||||||
if: ${{ false && needs.conditions.outputs.should-run == 'true' && steps.git-check.outputs.modified == 'true' }}
|
if: ${{ false && steps.git-check.outputs.modified == 'true' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
git add e2e/playwright/snapshot-tests.spec.ts-snapshots e2e/playwright/snapshots
|
||||||
@ -272,7 +198,7 @@ jobs:
|
|||||||
git push origin ${{ github.head_ref }}
|
git push origin ${{ github.head_ref }}
|
||||||
|
|
||||||
electron:
|
electron:
|
||||||
needs: [conditions, prepare-wasm]
|
needs: [prepare-wasm]
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
env:
|
env:
|
||||||
OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
|
OS_NAME: ${{ contains(matrix.os, 'ubuntu') && 'ubuntu' || (contains(matrix.os, 'windows') && 'windows' || 'macos') }}
|
||||||
@ -315,14 +241,11 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
name: prepared-wasm
|
name: prepared-wasm
|
||||||
|
|
||||||
- name: Copy prepared wasm
|
- name: Copy prepared wasm
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: |
|
run: |
|
||||||
ls -R prepared-wasm
|
ls -R prepared-wasm
|
||||||
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
cp prepared-wasm/kcl_wasm_lib_bg.wasm public
|
||||||
@ -330,18 +253,15 @@ jobs:
|
|||||||
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
cp prepared-wasm/kcl_wasm_lib* rust/kcl-wasm-lib/pkg
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
with:
|
with:
|
||||||
node-version-file: '.nvmrc'
|
node-version-file: '.nvmrc'
|
||||||
cache: 'npm'
|
cache: 'npm'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
id: deps-install
|
id: deps-install
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
- name: Cache Playwright Browsers
|
- name: Cache Playwright Browsers
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
uses: actions/cache@v4
|
uses: actions/cache@v4
|
||||||
with:
|
with:
|
||||||
path: |
|
path: |
|
||||||
@ -349,22 +269,20 @@ jobs:
|
|||||||
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
|
key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }}
|
||||||
|
|
||||||
- name: Install Playwright Browsers
|
- name: Install Playwright Browsers
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm run playwright install --with-deps
|
run: npm run playwright install --with-deps
|
||||||
|
|
||||||
- name: Build web
|
- name: Build web
|
||||||
if: needs.conditions.outputs.should-run == 'true'
|
|
||||||
run: npm run tronb:vite:dev
|
run: npm run tronb:vite:dev
|
||||||
|
|
||||||
- name: Start Vector
|
- name: Start Vector
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !contains(matrix.os, 'windows') }}
|
if: ${{ !contains(matrix.os, 'windows') }}
|
||||||
run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
|
run: .github/ci-cd-scripts/start-vector-${{ env.OS_NAME }}.sh
|
||||||
env:
|
env:
|
||||||
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
|
GH_ACTIONS_AXIOM_TOKEN: ${{ secrets.GH_ACTIONS_AXIOM_TOKEN }}
|
||||||
OS_NAME: ${{ env.OS_NAME }}
|
OS_NAME: ${{ env.OS_NAME }}
|
||||||
|
|
||||||
- uses: actions/download-artifact@v4
|
- uses: actions/download-artifact@v4
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && (success() || failure()) }}
|
if: ${{ !cancelled() && (success() || failure()) }}
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
@ -372,7 +290,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Run playwright/electron flow (with retries)
|
- name: Run playwright/electron flow (with retries)
|
||||||
id: retry
|
id: retry
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && !cancelled() && steps.deps-install.outcome == 'success' }}
|
if: ${{ !cancelled() && steps.deps-install.outcome == 'success' }}
|
||||||
uses: nick-fields/retry@v3.0.2
|
uses: nick-fields/retry@v3.0.2
|
||||||
with:
|
with:
|
||||||
shell: bash
|
shell: bash
|
||||||
@ -389,7 +307,7 @@ jobs:
|
|||||||
TARGET: desktop
|
TARGET: desktop
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: test-results-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: test-results/
|
path: test-results/
|
||||||
@ -398,7 +316,7 @@ jobs:
|
|||||||
overwrite: true
|
overwrite: true
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
if: ${{ needs.conditions.outputs.should-run == 'true' && always() }}
|
if: always()
|
||||||
with:
|
with:
|
||||||
name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
name: playwright-report-${{ env.OS_NAME }}-${{ matrix.shardIndex }}-${{ github.sha }}
|
||||||
path: playwright-report/
|
path: playwright-report/
|
||||||
|
8
.github/workflows/static-analysis.yml
vendored
@ -213,7 +213,13 @@ jobs:
|
|||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: npm run playwright install chromium --with-deps
|
run: npm run playwright install chromium --with-deps
|
||||||
|
|
||||||
- name: run unit tests for kcl samples
|
- name: Download internal KCL samples
|
||||||
|
run: git clone --depth=1 https://x-access-token:${{ secrets.GH_PAT_KCL_SAMPLES_INTERNAL }}@github.com/KittyCAD/kcl-samples-internal public/kcl-samples/internal
|
||||||
|
|
||||||
|
- name: Regenerate KCL samples manifest
|
||||||
|
run: cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||||
|
|
||||||
|
- name: Check public and internal KCL samples
|
||||||
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
if: ${{ github.event_name != 'release' && github.event_name != 'schedule' }}
|
||||||
run: npm run test:unit:kcl-samples
|
run: npm run test:unit:kcl-samples
|
||||||
env:
|
env:
|
||||||
|
39
.github/workflows/tag-nightly.yml
vendored
@ -1,39 +0,0 @@
|
|||||||
name: tag-nightly
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
on:
|
|
||||||
schedule:
|
|
||||||
- cron: '0 4 * * *'
|
|
||||||
# Daily at 04:00 AM UTC
|
|
||||||
# Will checkout the last commit from the default branch (main as of 2023-10-04)
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
tag-nightly:
|
|
||||||
runs-on: ubuntu-22.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/create-github-app-token@v1
|
|
||||||
id: app-token
|
|
||||||
with:
|
|
||||||
app-id: ${{ secrets.MODELING_APP_GH_APP_ID }}
|
|
||||||
private-key: ${{ secrets.MODELING_APP_GH_APP_PRIVATE_KEY }}
|
|
||||||
owner: ${{ github.repository_owner }}
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ steps.app-token.outputs.token }}
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version-file: '.nvmrc'
|
|
||||||
|
|
||||||
- run: npm install
|
|
||||||
|
|
||||||
- name: Push tag
|
|
||||||
run: |
|
|
||||||
VERSION_NO_V=$(date +'%-y.%-m.%-d')
|
|
||||||
TAG="nightly-v$VERSION_NO_V"
|
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git tag $TAG
|
|
||||||
git push origin tag $TAG
|
|
10
Makefile
@ -23,6 +23,7 @@ endif
|
|||||||
install: node_modules/.package-lock.json $(CARGO) $(WASM_PACK) ## Install dependencies
|
install: node_modules/.package-lock.json $(CARGO) $(WASM_PACK) ## Install dependencies
|
||||||
|
|
||||||
node_modules/.package-lock.json: package.json package-lock.json
|
node_modules/.package-lock.json: package.json package-lock.json
|
||||||
|
npm prune
|
||||||
npm install
|
npm install
|
||||||
|
|
||||||
$(CARGO):
|
$(CARGO):
|
||||||
@ -43,15 +44,15 @@ endif
|
|||||||
# BUILD
|
# BUILD
|
||||||
|
|
||||||
CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*)
|
CARGO_SOURCES := rust/.cargo/config.toml $(wildcard rust/Cargo.*) $(wildcard rust/**/Cargo.*)
|
||||||
|
KCL_SOURCES := $(wildcard public/kcl-samples/**/*.kcl)
|
||||||
RUST_SOURCES := $(wildcard rust/**/*.rs)
|
RUST_SOURCES := $(wildcard rust/**/*.rs)
|
||||||
|
|
||||||
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
REACT_SOURCES := $(wildcard src/*.tsx) $(wildcard src/**/*.tsx)
|
||||||
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
TYPESCRIPT_SOURCES := tsconfig.* $(wildcard src/*.ts) $(wildcard src/**/*.ts)
|
||||||
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
VITE_SOURCES := $(wildcard vite.*) $(wildcard vite/**/*.tsx)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
build: install public/kcl_wasm_lib_bg.wasm .vite/build/main.js
|
build: install public/kcl_wasm_lib_bg.wasm public/kcl-samples/manifest.json .vite/build/main.js
|
||||||
|
|
||||||
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
|
public/kcl_wasm_lib_bg.wasm: $(CARGO_SOURCES) $(RUST_SOURCES)
|
||||||
ifdef WINDOWS
|
ifdef WINDOWS
|
||||||
@ -60,6 +61,9 @@ else
|
|||||||
npm run build:wasm:dev
|
npm run build:wasm:dev
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
public/kcl-samples/manifest.json: $(KCL_SOURCES)
|
||||||
|
cd rust/kcl-lib && EXPECTORATE=overwrite cargo test generate_manifest
|
||||||
|
|
||||||
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
.vite/build/main.js: $(REACT_SOURCES) $(TYPESCRIPT_SOURCES) $(VITE_SOURCES)
|
||||||
npm run tronb:vite:dev
|
npm run tronb:vite:dev
|
||||||
|
|
||||||
@ -107,8 +111,10 @@ test: test-unit test-e2e
|
|||||||
.PHONY: test-unit
|
.PHONY: test-unit
|
||||||
test-unit: install ## Run the unit tests
|
test-unit: install ## Run the unit tests
|
||||||
npm run test:rust
|
npm run test:rust
|
||||||
|
npm run test:unit:components
|
||||||
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
|
@ curl -fs localhost:3000 >/dev/null || ( echo "Error: localhost:3000 not available, 'make run-web' first" && exit 1 )
|
||||||
npm run test:unit
|
npm run test:unit
|
||||||
|
npm run test:unit:kcl-samples
|
||||||
|
|
||||||
.PHONY: test-e2e
|
.PHONY: test-e2e
|
||||||
test-e2e: test-e2e-$(TARGET)
|
test-e2e: test-e2e-$(TARGET)
|
||||||
|
32
docs/kcl-lang/arrays.md
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
---
|
||||||
|
title: "Arrays and ranges"
|
||||||
|
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Arrays are sequences of values.
|
||||||
|
|
||||||
|
Arrays can be written out as *array literals* using a sequence of expressions surrounded by square brackets, e.g., `['hello', 'world']` is an array of strings, `[x, x + 1, x + 2]` is an array of numbers (assuming `x` is a number), `[]` is an empty array, and `['hello', 42, true]` is a mixed array.
|
||||||
|
|
||||||
|
A value in an array can be accessed by indexing using square brackets where the index is a number, for example, `arr[0]`, `arr[42]`, `arr[i]` (where `arr` is an array and `i` is a (whole) number).
|
||||||
|
|
||||||
|
There are some useful functions for working with arrays in the standard library, see [std::array](/docs/kcl-std/modules/std-array) for details.
|
||||||
|
|
||||||
|
## Array types
|
||||||
|
|
||||||
|
Arrays have their own types: `[T]` where `T` is the type of the elements of the array, for example, `[string]` means an array of `string`s and `[any]` means an array of any values.
|
||||||
|
|
||||||
|
Array types can also include length information: `[T; n]` denotes an array of length `n` (where `n` is a number literal) and `[T; 1+]` denotes an array whose length is at least one (i.e., a non-empty array). E.g., `[string; 1+]` and `[number(mm); 3]` are valid array types.
|
||||||
|
|
||||||
|
## Ranges
|
||||||
|
|
||||||
|
Ranges are a succinct way to create an array of sequential numbers. The syntax is `[start .. end]` where `start` and `end` evaluate to whole numbers (integers). Ranges are inclusive of the start and end. The end must be greater than the start. Examples:
|
||||||
|
|
||||||
|
```kcl,norun
|
||||||
|
[0..3] // [0, 1, 2, 3]
|
||||||
|
[3..10] // [3, 4, 5, 6, 7, 8, 9, 10]
|
||||||
|
x = 2
|
||||||
|
[x..x+1] // [2, 3]
|
||||||
|
```
|
||||||
|
|
||||||
|
The units of the start and end numbers must be the same and the result inherits those units.
|
99
docs/kcl-lang/foreign-imports.md
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
---
|
||||||
|
title: "Importing geometry from other CAD systems"
|
||||||
|
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
`import` can also be used to import files from other CAD systems. The format of the statement is the
|
||||||
|
same as for KCL files. You can only import the whole file, not items from it. E.g.,
|
||||||
|
|
||||||
|
```norun
|
||||||
|
import "tests/inputs/cube.obj"
|
||||||
|
|
||||||
|
// Use `cube` just like a KCL object.
|
||||||
|
```
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
import "tests/inputs/cube.sldprt" as cube
|
||||||
|
|
||||||
|
// Use `cube` just like a KCL object.
|
||||||
|
```
|
||||||
|
|
||||||
|
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
|
||||||
|
unit of measurement is millimeters. Alternatively you may specify the unit
|
||||||
|
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
@(lengthUnit = ft, coords = opengl)
|
||||||
|
import "tests/inputs/cube.obj"
|
||||||
|
```
|
||||||
|
|
||||||
|
When importing a GLTF file, the bin file will be imported as well.
|
||||||
|
|
||||||
|
Import paths are relative to the current project directory. Imports currently only work when
|
||||||
|
using the native Design Studio, not in the browser.
|
||||||
|
|
||||||
|
### Supported values
|
||||||
|
|
||||||
|
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
|
||||||
|
'+' support customising the length unit and coordinate system).
|
||||||
|
|
||||||
|
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
|
||||||
|
|
||||||
|
Coordinate systems:
|
||||||
|
|
||||||
|
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
|
||||||
|
- `opengl`, forward: +Z, up: +Y, handedness: right
|
||||||
|
- `vulkan`, forward: +Z, up: -Y, handedness: left
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Performance deep‑dive for foreign‑file imports
|
||||||
|
|
||||||
|
Parallelized foreign‑file imports now let you overlap file reads, initialization,
|
||||||
|
and rendering. To maximize throughput, you need to understand the three distinct
|
||||||
|
stages—reading, initializing (background render start), and invocation (blocking)
|
||||||
|
—and structure your code to defer blocking operations until the end.
|
||||||
|
|
||||||
|
### Foreign import execution stages
|
||||||
|
|
||||||
|
1. **Import (Read / Initialization) Stage**
|
||||||
|
```kcl
|
||||||
|
import "tests/inputs/cube.step" as cube
|
||||||
|
```
|
||||||
|
- Reads the file from disk and makes its API available.
|
||||||
|
- Starts engine rendering but **does not block** your script.
|
||||||
|
- This kick‑starts the render pipeline while you keep executing other code.
|
||||||
|
|
||||||
|
2. **Invocation (Blocking) Stage**
|
||||||
|
```kcl
|
||||||
|
import "tests/inputs/cube.step" as cube
|
||||||
|
|
||||||
|
cube
|
||||||
|
|> translate(z=10) // ← blocks here only
|
||||||
|
```
|
||||||
|
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
|
||||||
|
|
||||||
|
### Best practices
|
||||||
|
|
||||||
|
#### 1. Defer blocking calls
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
|
||||||
|
|
||||||
|
|
||||||
|
// --- perform other operations and calculations here ---
|
||||||
|
|
||||||
|
|
||||||
|
cube
|
||||||
|
|> translate(z=10) // 2) Blocks only here
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. Split heavy work into separate modules
|
||||||
|
|
||||||
|
Place computationally expensive or IO‑heavy work into its own module so it can render in parallel while `main.kcl` continues.
|
||||||
|
|
||||||
|
#### Future improvements
|
||||||
|
|
||||||
|
Upcoming releases will auto‑analyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.
|
||||||
|
|
@ -5,7 +5,7 @@ layout: manual
|
|||||||
---
|
---
|
||||||
|
|
||||||
This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains
|
This is a reference for KCL. If you are learning KCL, you may prefer the [guide]() which explains
|
||||||
things in a more tutorial fashion.
|
things in a more tutorial fashion. See also our documentation of the [standard library](/docs/kcl-std).
|
||||||
|
|
||||||
## Topics
|
## Topics
|
||||||
|
|
||||||
@ -14,7 +14,9 @@ things in a more tutorial fashion.
|
|||||||
* [Values and types](/docs/kcl-lang/types)
|
* [Values and types](/docs/kcl-lang/types)
|
||||||
* [Numeric types and units](/docs/kcl-lang/numeric)
|
* [Numeric types and units](/docs/kcl-lang/numeric)
|
||||||
* [Functions](/docs/kcl-lang/functions)
|
* [Functions](/docs/kcl-lang/functions)
|
||||||
* [Projects, modules, and imports](/docs/kcl-lang/modules)
|
* [Arrays and ranges](/docs/kcl-lang/arrays)
|
||||||
|
* [Projects and modules](/docs/kcl-lang/modules)
|
||||||
* [Attributes](/docs/kcl-lang/attributes)
|
* [Attributes](/docs/kcl-lang/attributes)
|
||||||
|
* [Importing geometry from other CAD systems](/docs/kcl-lang/foreign-imports)
|
||||||
* [Settings](/docs/kcl-lang/settings)
|
* [Settings](/docs/kcl-lang/settings)
|
||||||
* [Known Issues](/docs/kcl-lang/known-issues)
|
* [Known Issues](/docs/kcl-lang/known-issues)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
title: "Modules"
|
title: "Projects and modules"
|
||||||
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
|
excerpt: "Documentation of the KCL language for the Zoo Design Studio."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
@ -264,102 +264,3 @@ cube
|
|||||||
clone(cube)
|
clone(cube)
|
||||||
|> translate(x=20)
|
|> translate(x=20)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Importing files from other CAD systems
|
|
||||||
|
|
||||||
`import` can also be used to import files from other CAD systems. The format of the statement is the
|
|
||||||
same as for KCL files. You can only import the whole file, not items from it. E.g.,
|
|
||||||
|
|
||||||
```norun
|
|
||||||
import "tests/inputs/cube.obj"
|
|
||||||
|
|
||||||
// Use `cube` just like a KCL object.
|
|
||||||
```
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
import "tests/inputs/cube.sldprt" as cube
|
|
||||||
|
|
||||||
// Use `cube` just like a KCL object.
|
|
||||||
```
|
|
||||||
|
|
||||||
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
|
|
||||||
unit of measurement is millimeters. Alternatively you may specify the unit
|
|
||||||
by using an attribute. Likewise, you can also specify a coordinate system. E.g.,
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
@(lengthUnit = ft, coords = opengl)
|
|
||||||
import "tests/inputs/cube.obj"
|
|
||||||
```
|
|
||||||
|
|
||||||
When importing a GLTF file, the bin file will be imported as well.
|
|
||||||
|
|
||||||
Import paths are relative to the current project directory. Imports currently only work when
|
|
||||||
using the native Design Studio, not in the browser.
|
|
||||||
|
|
||||||
### Supported values
|
|
||||||
|
|
||||||
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
|
|
||||||
'+' support customising the length unit and coordinate system).
|
|
||||||
|
|
||||||
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
|
|
||||||
|
|
||||||
Coordinate systems:
|
|
||||||
|
|
||||||
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
|
|
||||||
- `opengl`, forward: +Z, up: +Y, handedness: right
|
|
||||||
- `vulkan`, forward: +Z, up: -Y, handedness: left
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Performance deep‑dive for foreign‑file imports
|
|
||||||
|
|
||||||
Parallelized foreign‑file imports now let you overlap file reads, initialization,
|
|
||||||
and rendering. To maximize throughput, you need to understand the three distinct
|
|
||||||
stages—reading, initializing (background render start), and invocation (blocking)
|
|
||||||
—and structure your code to defer blocking operations until the end.
|
|
||||||
|
|
||||||
### Foreign import execution stages
|
|
||||||
|
|
||||||
1. **Import (Read / Initialization) Stage**
|
|
||||||
```kcl
|
|
||||||
import "tests/inputs/cube.step" as cube
|
|
||||||
```
|
|
||||||
- Reads the file from disk and makes its API available.
|
|
||||||
- Starts engine rendering but **does not block** your script.
|
|
||||||
- This kick‑starts the render pipeline while you keep executing other code.
|
|
||||||
|
|
||||||
2. **Invocation (Blocking) Stage**
|
|
||||||
```kcl
|
|
||||||
import "tests/inputs/cube.step" as cube
|
|
||||||
|
|
||||||
cube
|
|
||||||
|> translate(z=10) // ← blocks here only
|
|
||||||
```
|
|
||||||
- Any method call (e.g., `translate`, `scale`, `rotate`) waits for the background render to finish before applying transformations.
|
|
||||||
|
|
||||||
### Best practices
|
|
||||||
|
|
||||||
#### 1. Defer blocking calls
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
import "tests/inputs/cube.step" as cube // 1) Read / Background render starts
|
|
||||||
|
|
||||||
|
|
||||||
// --- perform other operations and calculations here ---
|
|
||||||
|
|
||||||
|
|
||||||
cube
|
|
||||||
|> translate(z=10) // 2) Blocks only here
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Split heavy work into separate modules
|
|
||||||
|
|
||||||
Place computationally expensive or IO‑heavy work into its own module so it can render in parallel while `main.kcl` continues.
|
|
||||||
|
|
||||||
#### Future improvements
|
|
||||||
|
|
||||||
Upcoming releases will auto‑analyse dependencies and only block when truly necessary. Until then, explicit deferral will give you the best performance.
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +28,8 @@ Any of the suffixes described above can be used meaning that values with that ty
|
|||||||
|
|
||||||
You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness).
|
You can also use `number(Length)`, `number(Angle)`, or `number(Count)`. These types mean a number with any length, angle, or unitless (count) units, respectively (note that `number(_)` and `number(Count)` are equivalent since there is only one kind of unitless-ness).
|
||||||
|
|
||||||
|
Using just `number` means accepting any kind of number, even where the units are unknown by KCL.
|
||||||
|
|
||||||
|
|
||||||
## Function calls
|
## Function calls
|
||||||
|
|
||||||
|
@ -19,18 +19,6 @@ myBool = false
|
|||||||
|
|
||||||
Currently you cannot redeclare a constant.
|
Currently you cannot redeclare a constant.
|
||||||
|
|
||||||
## Arrays
|
|
||||||
|
|
||||||
An array is defined with `[]` braces. What is inside the brackets can
|
|
||||||
be of any type. For example, the following is completely valid:
|
|
||||||
|
|
||||||
```
|
|
||||||
myArray = ["thing", 2, false]
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to get a value from an array you can use the index like so:
|
|
||||||
`myArray[0]`.
|
|
||||||
|
|
||||||
|
|
||||||
## Objects
|
## Objects
|
||||||
|
|
||||||
@ -40,8 +28,8 @@ An object is defined with `{}` braces. Here is an example object:
|
|||||||
myObj = { a = 0, b = "thing" }
|
myObj = { a = 0, b = "thing" }
|
||||||
```
|
```
|
||||||
|
|
||||||
We support two different ways of getting properties from objects, you can call
|
To get the property of an object, you can call `myObj.a`, which in the above
|
||||||
`myObj.a` or `myObj["a"]` both work.
|
example returns 0.
|
||||||
|
|
||||||
## `ImportedGeometry`
|
## `ImportedGeometry`
|
||||||
|
|
||||||
|
@ -22,14 +22,14 @@ This will work on any solid, including extruded solids, revolved solids, and she
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solids` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) | The solid(s) whose appearance is being set | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) | The solid(s) whose appearance is being set | Yes |
|
||||||
| `color` | [`string`](/docs/kcl-std/types/std-types-string) | Color of the new material, a hex string like '#ff0000' | Yes |
|
| `color` | [`string`](/docs/kcl-std/types/std-types-string) | Color of the new material, a hex string like '#ff0000' | Yes |
|
||||||
| `metalness` | [`number`](/docs/kcl-std/types/std-types-number) | Metalness of the new material, a percentage like 95.7. | No |
|
| `metalness` | [`number`](/docs/kcl-std/types/std-types-number) | Metalness of the new material, a percentage like 95.7. | No |
|
||||||
| `roughness` | [`number`](/docs/kcl-std/types/std-types-number) | Roughness of the new material, a percentage like 95.7. | No |
|
| `roughness` | [`number`](/docs/kcl-std/types/std-types-number) | Roughness of the new material, a percentage like 95.7. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) - Data for a solid or an imported geometry.
|
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) - Data for a solid or an imported geometry.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
@ -11,7 +11,13 @@ The value of `pi`, Archimedes’ constant (π).
|
|||||||
PI: number(_?) = 3.14159265358979323846264338327950288_?
|
PI: number(_?) = 3.14159265358979323846264338327950288_?
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`PI` is a number and is technically a ratio, so you might expect it to have type `number(_)`.
|
||||||
|
However, `PI` is nearly always used for converting between different units - usually degrees to or
|
||||||
|
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.
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ You can provide more than one sketch to extrude, and they will all be extruded i
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | Which sketch or sketches should be extruded | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) | Which sketch or sketches should be extruded | Yes |
|
||||||
| `length` | [`number`](/docs/kcl-std/types/std-types-number) | How far to extrude the given sketches | Yes |
|
| `length` | [`number`](/docs/kcl-std/types/std-types-number) | How far to extrude the given sketches | Yes |
|
||||||
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the | No |
|
| `symmetric` | [`bool`](/docs/kcl-std/types/std-types-bool) | If true, the extrusion will happen symmetrically around the sketch. Otherwise, the extrusion will happen on only one side of the sketch. | No |
|
||||||
| `bidirectionalLength` | [`number`](/docs/kcl-std/types/std-types-number) | If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored. | No |
|
| `bidirectionalLength` | [`number`](/docs/kcl-std/types/std-types-number) | If specified, will also extrude in the opposite direction to 'distance' to the specified distance. If 'symmetric' is true, this value is ignored. | No |
|
||||||
| `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the extrusion, i.e. the original sketch | No |
|
| `tagStart` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the start of the extrusion, i.e. the original sketch | No |
|
||||||
| `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch | No |
|
| `tagEnd` | [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator) | A named tag for the face at the end of the extrusion, i.e. the new face created by extruding the original sketch | No |
|
||||||
|
38
docs/kcl-std/functions/std-math-legAngX.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "legAngX"
|
||||||
|
subtitle: "Function in std::math"
|
||||||
|
excerpt: "Compute the angle of the given leg for x."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Compute the angle of the given leg for x.
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legAngX(
|
||||||
|
hypotenuse: number(Length),
|
||||||
|
leg: number(Length),
|
||||||
|
): number(deg)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
|
||||||
|
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||||
|
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legAngX(hypotenuse = 5, leg = 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
38
docs/kcl-std/functions/std-math-legAngY.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "legAngY"
|
||||||
|
subtitle: "Function in std::math"
|
||||||
|
excerpt: "Compute the angle of the given leg for y."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Compute the angle of the given leg for y.
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legAngY(
|
||||||
|
hypotenuse: number(Length),
|
||||||
|
leg: number(Length),
|
||||||
|
): number(deg)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
|
||||||
|
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||||
|
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legAngY(hypotenuse = 5, leg = 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
38
docs/kcl-std/functions/std-math-legLen.md
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
---
|
||||||
|
title: "legLen"
|
||||||
|
subtitle: "Function in std::math"
|
||||||
|
excerpt: "Compute the length of the given leg."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Compute the length of the given leg.
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legLen(
|
||||||
|
hypotenuse: number(Length),
|
||||||
|
leg: number(Length),
|
||||||
|
): number(deg)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### Arguments
|
||||||
|
|
||||||
|
| Name | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `hypotenuse` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse. | Yes |
|
||||||
|
| `leg` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side). | Yes |
|
||||||
|
|
||||||
|
### Returns
|
||||||
|
|
||||||
|
[`number(deg)`](/docs/kcl-std/types/std-types-number) - A number.
|
||||||
|
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
```kcl
|
||||||
|
legLen(hypotenuse = 5, leg = 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Convert a number to centimeters from its current units.
|
Convert a number to centimeters from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toCentimeters(@num: number(cm)): number(cm)
|
units::toCentimeters(@num: number(Length)): number(cm)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toCentimeters(@num: number(cm)): number(cm)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(cm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Converts a number to degrees from its current units.
|
Converts a number to degrees from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toDegrees(@num: number(deg)): number(deg)
|
units::toDegrees(@num: number(Angle)): number(deg)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toDegrees(@num: number(deg)): number(deg)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(deg)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Convert a number to feet from its current units.
|
Convert a number to feet from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toFeet(@num: number(ft)): number(ft)
|
units::toFeet(@num: number(Length)): number(ft)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toFeet(@num: number(ft)): number(ft)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(ft)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Convert a number to inches from its current units.
|
Convert a number to inches from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toInches(@num: number(in)): number(in)
|
units::toInches(@num: number(Length)): number(in)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toInches(@num: number(in)): number(in)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(in)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Convert a number to meters from its current units.
|
Convert a number to meters from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toMeters(@num: number(m)): number(m)
|
units::toMeters(@num: number(Length)): number(m)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toMeters(@num: number(m)): number(m)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(m)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Convert a number to millimeters from its current units.
|
Convert a number to millimeters from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toMillimeters(@num: number(mm)): number(mm)
|
units::toMillimeters(@num: number(Length)): number(mm)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toMillimeters(@num: number(mm)): number(mm)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(mm)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Converts a number to radians from its current units.
|
Converts a number to radians from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toRadians(@num: number(rad)): number(rad)
|
units::toRadians(@num: number(Angle)): number(rad)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toRadians(@num: number(rad)): number(rad)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(rad)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Angle)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ layout: manual
|
|||||||
Converts a number to yards from its current units.
|
Converts a number to yards from its current units.
|
||||||
|
|
||||||
```kcl
|
```kcl
|
||||||
units::toYards(@num: number(yd)): number(yd)
|
units::toYards(@num: number(Length)): number(yd)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ units::toYards(@num: number(yd)): number(yd)
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `num` | [`number(yd)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
| `num` | [`number(Length)`](/docs/kcl-std/types/std-types-number) | A number. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -12,15 +12,15 @@ layout: manual
|
|||||||
* [`appearance`](/docs/kcl-std/appearance)
|
* [`appearance`](/docs/kcl-std/appearance)
|
||||||
* [`assert`](/docs/kcl-std/assert)
|
* [`assert`](/docs/kcl-std/assert)
|
||||||
* [`assertIs`](/docs/kcl-std/assertIs)
|
* [`assertIs`](/docs/kcl-std/assertIs)
|
||||||
* [`clone`](/docs/kcl-std/clone)
|
* [`clone`](/docs/kcl-std/functions/std-clone)
|
||||||
* [`helix`](/docs/kcl-std/functions/std-helix)
|
* [`helix`](/docs/kcl-std/functions/std-helix)
|
||||||
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
||||||
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
||||||
* [**std::array**](/docs/kcl-std/modules/std-array)
|
* [**std::array**](/docs/kcl-std/modules/std-array)
|
||||||
* [`map`](/docs/kcl-std/map)
|
* [`map`](/docs/kcl-std/functions/std-array-map)
|
||||||
* [`pop`](/docs/kcl-std/pop)
|
* [`pop`](/docs/kcl-std/functions/std-array-pop)
|
||||||
* [`push`](/docs/kcl-std/push)
|
* [`push`](/docs/kcl-std/functions/std-array-push)
|
||||||
* [`reduce`](/docs/kcl-std/reduce)
|
* [`reduce`](/docs/kcl-std/functions/std-array-reduce)
|
||||||
* [**std::math**](/docs/kcl-std/modules/std-math)
|
* [**std::math**](/docs/kcl-std/modules/std-math)
|
||||||
* [`abs`](/docs/kcl-std/functions/std-math-abs)
|
* [`abs`](/docs/kcl-std/functions/std-math-abs)
|
||||||
* [`acos`](/docs/kcl-std/functions/std-math-acos)
|
* [`acos`](/docs/kcl-std/functions/std-math-acos)
|
||||||
@ -30,9 +30,9 @@ layout: manual
|
|||||||
* [`ceil`](/docs/kcl-std/functions/std-math-ceil)
|
* [`ceil`](/docs/kcl-std/functions/std-math-ceil)
|
||||||
* [`cos`](/docs/kcl-std/functions/std-math-cos)
|
* [`cos`](/docs/kcl-std/functions/std-math-cos)
|
||||||
* [`floor`](/docs/kcl-std/functions/std-math-floor)
|
* [`floor`](/docs/kcl-std/functions/std-math-floor)
|
||||||
* [`legAngX`](/docs/kcl-std/legAngX)
|
* [`legAngX`](/docs/kcl-std/functions/std-math-legAngX)
|
||||||
* [`legAngY`](/docs/kcl-std/legAngY)
|
* [`legAngY`](/docs/kcl-std/functions/std-math-legAngY)
|
||||||
* [`legLen`](/docs/kcl-std/legLen)
|
* [`legLen`](/docs/kcl-std/functions/std-math-legLen)
|
||||||
* [`ln`](/docs/kcl-std/functions/std-math-ln)
|
* [`ln`](/docs/kcl-std/functions/std-math-ln)
|
||||||
* [`log`](/docs/kcl-std/functions/std-math-log)
|
* [`log`](/docs/kcl-std/functions/std-math-log)
|
||||||
* [`log10`](/docs/kcl-std/functions/std-math-log10)
|
* [`log10`](/docs/kcl-std/functions/std-math-log10)
|
||||||
@ -140,12 +140,13 @@ See also the [types overview](/docs/kcl-lang/types)
|
|||||||
|
|
||||||
* [**Primitive types**](/docs/kcl-lang/types)
|
* [**Primitive types**](/docs/kcl-lang/types)
|
||||||
* [`End`](/docs/kcl-lang/types#End)
|
* [`End`](/docs/kcl-lang/types#End)
|
||||||
* [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry)
|
* [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry)
|
||||||
* [`Start`](/docs/kcl-lang/types#Start)
|
* [`Start`](/docs/kcl-lang/types#Start)
|
||||||
* [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator)
|
* [`TagDeclarator`](/docs/kcl-lang/types#TagDeclarator)
|
||||||
* [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier)
|
* [`TagIdentifier`](/docs/kcl-lang/types#TagIdentifier)
|
||||||
* [`any`](/docs/kcl-std/types/std-types-any)
|
* [`any`](/docs/kcl-std/types/std-types-any)
|
||||||
* [`bool`](/docs/kcl-std/types/std-types-bool)
|
* [`bool`](/docs/kcl-std/types/std-types-bool)
|
||||||
|
* [`fn`](/docs/kcl-std/types/std-types-fn)
|
||||||
* [`number`](/docs/kcl-std/types/std-types-number)
|
* [`number`](/docs/kcl-std/types/std-types-number)
|
||||||
* [`string`](/docs/kcl-std/types/std-types-string)
|
* [`string`](/docs/kcl-std/types/std-types-string)
|
||||||
* [`tag`](/docs/kcl-std/types/std-types-tag)
|
* [`tag`](/docs/kcl-std/types/std-types-tag)
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
title: "legAngX"
|
|
||||||
subtitle: "Function in std::math"
|
|
||||||
excerpt: "Compute the angle of the given leg for x."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Compute the angle of the given leg for x.
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legAngX(
|
|
||||||
hypotenuse: number,
|
|
||||||
leg: number,
|
|
||||||
): number
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Arguments
|
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
|
|
||||||
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
|
||||||
|
|
||||||
### Returns
|
|
||||||
|
|
||||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legAngX(hypotenuse = 5, leg = 3)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
title: "legAngY"
|
|
||||||
subtitle: "Function in std::math"
|
|
||||||
excerpt: "Compute the angle of the given leg for y."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Compute the angle of the given leg for y.
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legAngY(
|
|
||||||
hypotenuse: number,
|
|
||||||
leg: number,
|
|
||||||
): number
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Arguments
|
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
|
|
||||||
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
|
||||||
|
|
||||||
### Returns
|
|
||||||
|
|
||||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legAngY(hypotenuse = 5, leg = 3)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
|||||||
---
|
|
||||||
title: "legLen"
|
|
||||||
subtitle: "Function in std::math"
|
|
||||||
excerpt: "Compute the length of the given leg."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
Compute the length of the given leg.
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legLen(
|
|
||||||
hypotenuse: number,
|
|
||||||
leg: number,
|
|
||||||
): number
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Arguments
|
|
||||||
|
|
||||||
| Name | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `hypotenuse` | [`number`](/docs/kcl-std/types/std-types-number) | The length of the triangle's hypotenuse | Yes |
|
|
||||||
| `leg` | [`number`](/docs/kcl-std/types/std-types-number) | The length of one of the triangle's legs (i.e. non-hypotenuse side) | Yes |
|
|
||||||
|
|
||||||
### Returns
|
|
||||||
|
|
||||||
[`number`](/docs/kcl-std/types/std-types-number) - A number.
|
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```kcl
|
|
||||||
legLen(hypotenuse = 5, leg = 3)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -12,8 +12,8 @@ Functions for manipulating arrays of values.
|
|||||||
|
|
||||||
## Functions and constants
|
## Functions and constants
|
||||||
|
|
||||||
* [`map`](/docs/kcl-std/map)
|
* [`map`](/docs/kcl-std/functions/std-array-map)
|
||||||
* [`pop`](/docs/kcl-std/pop)
|
* [`pop`](/docs/kcl-std/functions/std-array-pop)
|
||||||
* [`push`](/docs/kcl-std/push)
|
* [`push`](/docs/kcl-std/functions/std-array-push)
|
||||||
* [`reduce`](/docs/kcl-std/reduce)
|
* [`reduce`](/docs/kcl-std/functions/std-array-reduce)
|
||||||
|
|
||||||
|
@ -23,9 +23,9 @@ Functions for mathematical operations and some useful constants.
|
|||||||
* [`ceil`](/docs/kcl-std/functions/std-math-ceil)
|
* [`ceil`](/docs/kcl-std/functions/std-math-ceil)
|
||||||
* [`cos`](/docs/kcl-std/functions/std-math-cos)
|
* [`cos`](/docs/kcl-std/functions/std-math-cos)
|
||||||
* [`floor`](/docs/kcl-std/functions/std-math-floor)
|
* [`floor`](/docs/kcl-std/functions/std-math-floor)
|
||||||
* [`legAngX`](/docs/kcl-std/legAngX)
|
* [`legAngX`](/docs/kcl-std/functions/std-math-legAngX)
|
||||||
* [`legAngY`](/docs/kcl-std/legAngY)
|
* [`legAngY`](/docs/kcl-std/functions/std-math-legAngY)
|
||||||
* [`legLen`](/docs/kcl-std/legLen)
|
* [`legLen`](/docs/kcl-std/functions/std-math-legLen)
|
||||||
* [`ln`](/docs/kcl-std/functions/std-math-ln)
|
* [`ln`](/docs/kcl-std/functions/std-math-ln)
|
||||||
* [`log`](/docs/kcl-std/functions/std-math-log)
|
* [`log`](/docs/kcl-std/functions/std-math-log)
|
||||||
* [`log10`](/docs/kcl-std/functions/std-math-log10)
|
* [`log10`](/docs/kcl-std/functions/std-math-log10)
|
||||||
|
@ -18,6 +18,7 @@ Types can (optionally) be used to describe a function's arguments and returned v
|
|||||||
* [`Edge`](/docs/kcl-std/types/std-types-Edge)
|
* [`Edge`](/docs/kcl-std/types/std-types-Edge)
|
||||||
* [`Face`](/docs/kcl-std/types/std-types-Face)
|
* [`Face`](/docs/kcl-std/types/std-types-Face)
|
||||||
* [`Helix`](/docs/kcl-std/types/std-types-Helix)
|
* [`Helix`](/docs/kcl-std/types/std-types-Helix)
|
||||||
|
* [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry)
|
||||||
* [`Plane`](/docs/kcl-std/types/std-types-Plane)
|
* [`Plane`](/docs/kcl-std/types/std-types-Plane)
|
||||||
* [`Point2d`](/docs/kcl-std/types/std-types-Point2d)
|
* [`Point2d`](/docs/kcl-std/types/std-types-Point2d)
|
||||||
* [`Point3d`](/docs/kcl-std/types/std-types-Point3d)
|
* [`Point3d`](/docs/kcl-std/types/std-types-Point3d)
|
||||||
@ -25,6 +26,7 @@ Types can (optionally) be used to describe a function's arguments and returned v
|
|||||||
* [`Solid`](/docs/kcl-std/types/std-types-Solid)
|
* [`Solid`](/docs/kcl-std/types/std-types-Solid)
|
||||||
* [`any`](/docs/kcl-std/types/std-types-any)
|
* [`any`](/docs/kcl-std/types/std-types-any)
|
||||||
* [`bool`](/docs/kcl-std/types/std-types-bool)
|
* [`bool`](/docs/kcl-std/types/std-types-bool)
|
||||||
|
* [`fn`](/docs/kcl-std/types/std-types-fn)
|
||||||
* [`number`](/docs/kcl-std/types/std-types-number)
|
* [`number`](/docs/kcl-std/types/std-types-number)
|
||||||
* [`string`](/docs/kcl-std/types/std-types-string)
|
* [`string`](/docs/kcl-std/types/std-types-string)
|
||||||
* [`tag`](/docs/kcl-std/types/std-types-tag)
|
* [`tag`](/docs/kcl-std/types/std-types-tag)
|
||||||
|
@ -11,6 +11,8 @@ Contains frequently used constants, functions for interacting with the KittyCAD
|
|||||||
|
|
||||||
The standard library is organised into modules (listed below), but most things are always available in KCL programs.
|
The standard library is organised into modules (listed below), but most things are always available in KCL programs.
|
||||||
|
|
||||||
|
You might also want the [KCL language reference](/docs/kcl-lang) or the [KCL guide]().
|
||||||
|
|
||||||
## Modules
|
## Modules
|
||||||
|
|
||||||
* [`array`](/docs/kcl-std/modules/std-array)
|
* [`array`](/docs/kcl-std/modules/std-array)
|
||||||
@ -35,7 +37,7 @@ The standard library is organised into modules (listed below), but most things a
|
|||||||
* [`appearance`](/docs/kcl-std/appearance)
|
* [`appearance`](/docs/kcl-std/appearance)
|
||||||
* [`assert`](/docs/kcl-std/assert)
|
* [`assert`](/docs/kcl-std/assert)
|
||||||
* [`assertIs`](/docs/kcl-std/assertIs)
|
* [`assertIs`](/docs/kcl-std/assertIs)
|
||||||
* [`clone`](/docs/kcl-std/clone)
|
* [`clone`](/docs/kcl-std/functions/std-clone)
|
||||||
* [`helix`](/docs/kcl-std/functions/std-helix)
|
* [`helix`](/docs/kcl-std/functions/std-helix)
|
||||||
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
* [`offsetPlane`](/docs/kcl-std/functions/std-offsetPlane)
|
||||||
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
* [`patternLinear2d`](/docs/kcl-std/patternLinear2d)
|
||||||
|
@ -43,7 +43,7 @@ When rotating a part around an axis, you specify the axis of rotation and the an
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) | The solid, sketch, or set of solids or sketches to rotate. | Yes |
|
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) | The solid, sketch, or set of solids or sketches to rotate. | Yes |
|
||||||
| `roll` | [`number`](/docs/kcl-std/types/std-types-number) | The roll angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
| `roll` | [`number`](/docs/kcl-std/types/std-types-number) | The roll angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
||||||
| `pitch` | [`number`](/docs/kcl-std/types/std-types-number) | The pitch angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
| `pitch` | [`number`](/docs/kcl-std/types/std-types-number) | The pitch angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
||||||
| `yaw` | [`number`](/docs/kcl-std/types/std-types-number) | The yaw angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
| `yaw` | [`number`](/docs/kcl-std/types/std-types-number) | The yaw angle in degrees. Must be between -360 and 360. Default is 0 if not given. | No |
|
||||||
@ -53,7 +53,7 @@ When rotating a part around an axis, you specify the axis of rotation and the an
|
|||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
@ -29,7 +29,7 @@ If you want to apply the transform in global space, set `global` to `true`. The
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) | The solid, sketch, or set of solids or sketches to scale. | Yes |
|
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) | The solid, sketch, or set of solids or sketches to scale. | Yes |
|
||||||
| `x` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the x axis. Default is 1 if not provided. | No |
|
| `x` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the x axis. Default is 1 if not provided. | No |
|
||||||
| `y` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the y axis. Default is 1 if not provided. | No |
|
| `y` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the y axis. Default is 1 if not provided. | No |
|
||||||
| `z` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the z axis. Default is 1 if not provided. | No |
|
| `z` | [`number`](/docs/kcl-std/types/std-types-number) | The scale factor for the z axis. Default is 1 if not provided. | No |
|
||||||
@ -37,7 +37,7 @@ If you want to apply the transform in global space, set `global` to `true`. The
|
|||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
35694
docs/kcl-std/std.json
@ -25,7 +25,7 @@ Translate is really useful for sketches if you want to move a sketch and then ro
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) | The solid, sketch, or set of solids or sketches to move. | Yes |
|
| `objects` | [`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) | The solid, sketch, or set of solids or sketches to move. | Yes |
|
||||||
| `x` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided. | No |
|
| `x` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the x axis. Defaults to 0 if not provided. | No |
|
||||||
| `y` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided. | No |
|
| `y` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the y axis. Defaults to 0 if not provided. | No |
|
||||||
| `z` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided. | No |
|
| `z` | [`number`](/docs/kcl-std/types/std-types-number) | The amount to move the solid or sketch along the z axis. Defaults to 0 if not provided. | No |
|
||||||
@ -33,7 +33,7 @@ Translate is really useful for sketches if you want to move a sketch and then ro
|
|||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-lang/types#ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
[`[Solid]`](/docs/kcl-std/types/std-types-Solid) or [`[Sketch]`](/docs/kcl-std/types/std-types-Sketch) or [`ImportedGeometry`](/docs/kcl-std/types/std-types-ImportedGeometry) - Data for a solid, sketch, or an imported geometry.
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
13
docs/kcl-std/types/std-types-ImportedGeometry.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: "ImportedGeometry"
|
||||||
|
subtitle: "Type in std::types"
|
||||||
|
excerpt: "Represents geometry which is defined using some other CAD system and imported into KCL."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Represents geometry which is defined using some other CAD system and imported into KCL.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -7,8 +7,25 @@ layout: manual
|
|||||||
|
|
||||||
An abstract plane.
|
An abstract plane.
|
||||||
|
|
||||||
A plane has a position and orientation in space defined by its origin and axes. A plane can be used
|
A plane has a position and orientation in space defined by its origin and axes. A plane is abstract
|
||||||
to sketch on.
|
in the sense that it is not part of the objects being drawn. A plane can be used to sketch on.
|
||||||
|
|
||||||
|
A plane can be created in several ways:
|
||||||
|
- you can use one of the default planes, e.g., `XY`.
|
||||||
|
- you can use `offsetPlane` to create a new plane offset from an existing one, e.g., `offsetPlane(XY, offset = 150)`.
|
||||||
|
- you can use negation to create a plane from an existing one which is identical but has an opposite normal
|
||||||
|
e.g., `-XY`.
|
||||||
|
- you can define an entirely custom plane, e.g.,
|
||||||
|
|
||||||
|
```js
|
||||||
|
myXY = {
|
||||||
|
origin = { x = 0, y = 0, z = 0 },
|
||||||
|
xAxis = { x = 1, y = 0, z = 0 },
|
||||||
|
yAxis = { x = 0, y = 1, z = 0 },
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Any object with appropriate `origin`, `xAxis`, and `yAxis` fields can be used as a plane.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
13
docs/kcl-std/types/std-types-fn.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
title: "fn"
|
||||||
|
subtitle: "Type in std::types"
|
||||||
|
excerpt: "The type of any function in KCL."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
The type of any function in KCL.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -549,48 +549,6 @@ extrude002 = extrude(profile002, length = 150)
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
|
||||||
`Network health indicator only appears in modeling view`,
|
|
||||||
{ tag: '@electron' },
|
|
||||||
async ({ context, page }, testInfo) => {
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('cylinder-inches.kcl'),
|
|
||||||
path.join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
|
||||||
const u = await getUtils(page)
|
|
||||||
|
|
||||||
// Locators
|
|
||||||
const projectsHeading = page.getByRole('heading', {
|
|
||||||
name: 'Projects',
|
|
||||||
})
|
|
||||||
const projectLink = page.getByRole('link', { name: 'bracket' })
|
|
||||||
const networkHealthIndicator = page.getByTestId('network-toggle')
|
|
||||||
|
|
||||||
await test.step('Check the home page', async () => {
|
|
||||||
await expect(projectsHeading).toBeVisible()
|
|
||||||
await expect(projectLink).toBeVisible()
|
|
||||||
await expect(networkHealthIndicator).not.toBeVisible()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Open the project', async () => {
|
|
||||||
await projectLink.click()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Check the modeling view', async () => {
|
|
||||||
await expect(networkHealthIndicator).toBeVisible()
|
|
||||||
await expect(networkHealthIndicator).toContainText('Problem')
|
|
||||||
await u.waitForPageLoad()
|
|
||||||
await expect(networkHealthIndicator).toContainText('Connected')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
test(`View gizmo stays visible even when zoomed out all the way`, async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
@ -841,6 +799,40 @@ washer = extrude(washerSketch, length = thicknessMax)`
|
|||||||
await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)')
|
await editor.expectEditor.toContain('@settings(defaultLengthUnit = yd)')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Exiting existing sketch without editing should not delete it', async ({
|
||||||
|
page,
|
||||||
|
editor,
|
||||||
|
homePage,
|
||||||
|
context,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const testDir = path.join(dir, 'test')
|
||||||
|
await fsp.mkdir(testDir, { recursive: true })
|
||||||
|
await fsp.writeFile(
|
||||||
|
path.join(testDir, 'main.kcl'),
|
||||||
|
`s1 = startSketchOn(XY)
|
||||||
|
|> startProfile(at = [0, 25])
|
||||||
|
|> xLine(endAbsolute = -15 + 1.5)
|
||||||
|
s2 = startSketchOn(XY)
|
||||||
|
|> startProfile(at = [25, 0])
|
||||||
|
|> yLine(endAbsolute = -15 + 1.5)`,
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await homePage.openProject('test')
|
||||||
|
await scene.settled(cmdBar)
|
||||||
|
await toolbar.waitForFeatureTreeToBeBuilt()
|
||||||
|
await toolbar.editSketch(1)
|
||||||
|
await page.waitForTimeout(1000) // Just hang out for a second
|
||||||
|
await toolbar.exitSketch()
|
||||||
|
|
||||||
|
await editor.expectEditor.toContain('s2 = startSketchOn(XY)')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page) {
|
||||||
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 56 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 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: 30 KiB After Width: | Height: | Size: 30 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 132 KiB |
Before Width: | Height: | Size: 116 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 64 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 67 KiB |
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
@ -905,7 +905,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Go into the project that was created from Text to CAD
|
// Go into the project that was created from Text to CAD
|
||||||
await page.getByText(projectName).click()
|
await homePage.openProject(projectName)
|
||||||
|
|
||||||
await expect(page.getByTestId('app-header-project-name')).toBeVisible()
|
await expect(page.getByTestId('app-header-project-name')).toBeVisible()
|
||||||
await expect(page.getByTestId('app-header-project-name')).toContainText(
|
await expect(page.getByTestId('app-header-project-name')).toContainText(
|
||||||
@ -951,7 +951,7 @@ test.describe('Mocked Text-to-CAD API tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
// Go into the project that was created from Text to CAD
|
// Go into the project that was created from Text to CAD
|
||||||
await page.getByText(projectName).click()
|
await homePage.openProject(projectName)
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Accept' }).click()
|
await page.getByRole('button', { name: 'Accept' }).click()
|
||||||
|
|
||||||
|
@ -107,7 +107,6 @@
|
|||||||
"check": "biome check ./src ./e2e ./packages/codemirror-lsp-client/src ./rust/kcl-language-server/client/src",
|
"check": "biome check ./src ./e2e ./packages/codemirror-lsp-client/src ./rust/kcl-language-server/client/src",
|
||||||
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./scripts/get-latest-wasm-bundle.sh",
|
||||||
"fetch:wasm:windows": "powershell -ExecutionPolicy Bypass -File ./scripts/get-latest-wasm-bundle.ps1",
|
"fetch:wasm:windows": "powershell -ExecutionPolicy Bypass -File ./scripts/get-latest-wasm-bundle.ps1",
|
||||||
"fetch:samples": "rm -rf public/kcl-samples* && curl -L -o public/kcl-samples.zip https://github.com/KittyCAD/kcl-samples/archive/refs/heads/achalmers/kw-args-xylineto.zip && unzip -o public/kcl-samples.zip -d public && mv public/kcl-samples-* public/kcl-samples",
|
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./rust/kcl-wasm-lib/pkg/kcl_wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||||
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src rust/kcl-language-server/client/src",
|
||||||
@ -123,7 +122,6 @@
|
|||||||
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
"files:invalidate-bucket:nightly": "./scripts/invalidate-files-bucket.sh --nightly",
|
||||||
"postinstall": "electron-rebuild",
|
"postinstall": "electron-rebuild",
|
||||||
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
"generate:machine-api": "npx openapi-typescript ./openapi/machine-api.json -o src/lib/machine-api.d.ts",
|
||||||
"generate:samples-manifest": "cd public/kcl-samples && node generate-manifest.js",
|
|
||||||
"tron:start": "electron-forge start",
|
"tron:start": "electron-forge start",
|
||||||
"chrome:test": "PLATFORM=web NODE_ENV=development playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert=@snapshot",
|
"chrome:test": "PLATFORM=web NODE_ENV=development playwright test --config=playwright.config.ts --project='Google Chrome' --grep-invert=@snapshot",
|
||||||
"tronb:vite:dev": "vite build -c vite.main.config.ts -m development && vite build -c vite.preload.config.ts -m development && vite build -c vite.renderer.config.ts -m development",
|
"tronb:vite:dev": "vite build -c vite.main.config.ts -m development && vite build -c vite.preload.config.ts -m development && vite build -c vite.renderer.config.ts -m development",
|
||||||
|
25
rust/Cargo.lock
generated
@ -1815,7 +1815,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1826,7 +1826,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"Inflector",
|
"Inflector",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -1845,7 +1845,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1854,7 +1854,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
version = "0.2.68"
|
version = "0.2.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1875,7 +1875,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1895,7 +1895,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.68"
|
version = "0.2.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1959,6 +1959,7 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
|
"walkdir",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
@ -1970,7 +1971,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-python-bindings"
|
name = "kcl-python-bindings"
|
||||||
version = "0.3.68"
|
version = "0.3.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
@ -1985,7 +1986,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-test-server"
|
name = "kcl-test-server"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"hyper 0.14.32",
|
"hyper 0.14.32",
|
||||||
@ -1998,7 +1999,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-to-core"
|
name = "kcl-to-core"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2012,7 +2013,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-wasm-lib"
|
name = "kcl-wasm-lib"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bson",
|
"bson",
|
||||||
@ -2042,9 +2043,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.36"
|
version = "0.3.37"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a345fd2a4cb16205f32bd1aa41715045830c59d78c59927fca6580e2a651ac9"
|
checksum = "b48a9698d68c791df76aa020b596c324177a614e38ff3dd67eedd04db76e222f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -35,7 +35,7 @@ console_error_panic_hook = "0.1.7"
|
|||||||
dashmap = { version = "6.1.0" }
|
dashmap = { version = "6.1.0" }
|
||||||
http = "1"
|
http = "1"
|
||||||
indexmap = "2.9.0"
|
indexmap = "2.9.0"
|
||||||
kittycad = { version = "0.3.36", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.37", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.117", features = ["ts-rs", "websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.117", features = ["ts-rs", "websocket"] }
|
||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
miette = "7.5.0"
|
miette = "7.5.0"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "kcl-bumper"
|
name = "kcl-bumper"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/KittyCAD/modeling-api"
|
repository = "https://github.com/KittyCAD/modeling-api"
|
||||||
rust-version = "1.76"
|
rust-version = "1.76"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-derive-docs"
|
name = "kcl-derive-docs"
|
||||||
description = "A tool for generating documentation from Rust derive macros"
|
description = "A tool for generating documentation from Rust derive macros"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-directory-test-macro"
|
name = "kcl-directory-test-macro"
|
||||||
description = "A tool for generating tests from a directory of kcl files"
|
description = "A tool for generating tests from a directory of kcl files"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-language-server-release"
|
name = "kcl-language-server-release"
|
||||||
version = "0.1.68"
|
version = "0.1.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
publish = false
|
publish = false
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
name = "kcl-language-server"
|
name = "kcl-language-server"
|
||||||
description = "A language server for KCL."
|
description = "A language server for KCL."
|
||||||
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
authors = ["KittyCAD Inc <kcl@kittycad.io>"]
|
||||||
version = "0.2.68"
|
version = "0.2.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.68"
|
version = "0.2.69"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -85,6 +85,7 @@ tynm = "0.1.10"
|
|||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.4", features = ["serde"] }
|
||||||
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
||||||
validator = { version = "0.20.0", features = ["derive"] }
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
|
walkdir = "2.5.0"
|
||||||
web-time = "1.1"
|
web-time = "1.1"
|
||||||
winnow = "=0.6.24"
|
winnow = "=0.6.24"
|
||||||
zip = { workspace = true }
|
zip = { workspace = true }
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
use kcl_lib::{bust_cache, ExecError, ExecOutcome};
|
use kcl_lib::{bust_cache, ExecError, ExecOutcome};
|
||||||
use kcmc::{each_cmd as mcmd, ModelingCmd};
|
use kcmc::{each_cmd as mcmd, ModelingCmd};
|
||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Variation<'a> {
|
struct Variation<'a> {
|
||||||
@ -247,8 +248,11 @@ extrude(profile001, length = 100)"#
|
|||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
result.first().unwrap();
|
let first = result.first().unwrap();
|
||||||
result.last().unwrap();
|
let last = result.last().unwrap();
|
||||||
|
|
||||||
|
assert!(first.1 == last.1, "The images should be the same");
|
||||||
|
assert_eq!(first.2, last.2, "The outcomes should be the same");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "artifact-graph")]
|
#[cfg(feature = "artifact-graph")]
|
||||||
@ -550,3 +554,64 @@ extrude(profile001, length = 100)
|
|||||||
"The outcomes artifact graphs should be different"
|
"The outcomes artifact graphs should be different"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_cache_multi_file_same_code_dont_reexecute_settings_only_change() {
|
||||||
|
let code = r#"import "toBeImported.kcl" as importedCube
|
||||||
|
|
||||||
|
importedCube
|
||||||
|
|
||||||
|
sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = startProfile(sketch001, at = [-134.53, -56.17])
|
||||||
|
|> angledLine(angle = 0, length = 79.05, tag = $rectangleSegmentA001)
|
||||||
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 76.28)
|
||||||
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001), tag = $seg01)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||||
|
|> close()
|
||||||
|
extrude001 = extrude(profile001, length = 100)
|
||||||
|
sketch003 = startSketchOn(extrude001, face = seg02)
|
||||||
|
sketch002 = startSketchOn(extrude001, face = seg01)
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let other_file = (
|
||||||
|
std::path::PathBuf::from("toBeImported.kcl"),
|
||||||
|
r#"sketch001 = startSketchOn(XZ)
|
||||||
|
profile001 = startProfile(sketch001, at = [281.54, 305.81])
|
||||||
|
|> angledLine(angle = 0, length = 123.43, tag = $rectangleSegmentA001)
|
||||||
|
|> angledLine(angle = segAng(rectangleSegmentA001) - 90, length = 85.99)
|
||||||
|
|> angledLine(angle = segAng(rectangleSegmentA001), length = -segLen(rectangleSegmentA001))
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
extrude(profile001, length = 100)"#
|
||||||
|
.to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = cache_test(
|
||||||
|
"multi_file_same_code_dont_reexecute_settings_only_change",
|
||||||
|
vec![
|
||||||
|
Variation {
|
||||||
|
code,
|
||||||
|
other_files: vec![other_file.clone()],
|
||||||
|
settings: &kcl_lib::ExecutorSettings {
|
||||||
|
show_grid: false,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Variation {
|
||||||
|
code,
|
||||||
|
other_files: vec![other_file],
|
||||||
|
settings: &kcl_lib::ExecutorSettings {
|
||||||
|
show_grid: true,
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let first = result.first().unwrap();
|
||||||
|
let last = result.last().unwrap();
|
||||||
|
|
||||||
|
assert!(first.1 != last.1, "The images should be different for the grid");
|
||||||
|
assert_eq!(first.2, last.2, "The outcomes should be the same");
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 109 KiB |
@ -16,7 +16,7 @@ use crate::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Types with special handling.
|
// Types with special handling.
|
||||||
const SPECIAL_TYPES: [&str; 5] = ["TagDeclarator", "TagIdentifier", "Start", "End", "ImportedGeometry"];
|
const SPECIAL_TYPES: [&str; 4] = ["TagDeclarator", "TagIdentifier", "Start", "End"];
|
||||||
|
|
||||||
const TYPE_REWRITES: [(&str, &str); 11] = [
|
const TYPE_REWRITES: [(&str, &str); 11] = [
|
||||||
("TagNode", "TagDeclarator"),
|
("TagNode", "TagDeclarator"),
|
||||||
@ -380,6 +380,21 @@ fn generate_function_from_kcl(
|
|||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &example_name))
|
.filter_map(|(index, example)| generate_example(index, &example.0, &example.1, &example_name))
|
||||||
.collect();
|
.collect();
|
||||||
|
let args = function.args.iter().map(|arg| {
|
||||||
|
let docs = arg.docs.clone();
|
||||||
|
if let Some(docs) = &docs {
|
||||||
|
// We deliberately truncate to one line in the template so that if we are using the docs
|
||||||
|
// from the type, then we only take the summary. However, if there's a newline in the
|
||||||
|
// arg docs, then they would get truncated unintentionally.
|
||||||
|
assert!(!docs.contains('\n'), "Arg docs will get truncated");
|
||||||
|
};
|
||||||
|
json!({
|
||||||
|
"name": arg.name,
|
||||||
|
"type_": arg.ty,
|
||||||
|
"description": docs.or_else(|| arg.ty.as_ref().and_then(|t| super::docs_for_type(t, kcl_std))).unwrap_or_default(),
|
||||||
|
"required": arg.kind.required(),
|
||||||
|
})
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
let data = json!({
|
let data = json!({
|
||||||
"name": function.preferred_name,
|
"name": function.preferred_name,
|
||||||
@ -389,14 +404,7 @@ fn generate_function_from_kcl(
|
|||||||
"deprecated": function.properties.deprecated,
|
"deprecated": function.properties.deprecated,
|
||||||
"fn_signature": function.preferred_name.clone() + &function.fn_signature(),
|
"fn_signature": function.preferred_name.clone() + &function.fn_signature(),
|
||||||
"examples": examples,
|
"examples": examples,
|
||||||
"args": function.args.iter().map(|arg| {
|
"args": args,
|
||||||
json!({
|
|
||||||
"name": arg.name,
|
|
||||||
"type_": arg.ty,
|
|
||||||
"description": arg.docs.clone().or_else(|| arg.ty.as_ref().and_then(|t| super::docs_for_type(t, kcl_std))).unwrap_or_default(),
|
|
||||||
"required": arg.kind.required(),
|
|
||||||
})
|
|
||||||
}).collect::<Vec<_>>(),
|
|
||||||
"return_value": function.return_type.as_ref().map(|t| {
|
"return_value": function.return_type.as_ref().map(|t| {
|
||||||
json!({
|
json!({
|
||||||
"type_": t,
|
"type_": t,
|
||||||
@ -666,6 +674,8 @@ fn cleanup_type_string(input: &str, fmt_for_text: bool) -> String {
|
|||||||
|
|
||||||
if fmt_for_text && ty.starts_with("number") {
|
if fmt_for_text && ty.starts_with("number") {
|
||||||
format!("[{prefix}{ty}{suffix}](/docs/kcl-std/types/std-types-number)")
|
format!("[{prefix}{ty}{suffix}](/docs/kcl-std/types/std-types-number)")
|
||||||
|
} else if fmt_for_text && ty.starts_with("fn") {
|
||||||
|
format!("[{prefix}{ty}{suffix}](/docs/kcl-std/types/std-types-fn)")
|
||||||
} else if fmt_for_text && SPECIAL_TYPES.contains(&ty) {
|
} else if fmt_for_text && SPECIAL_TYPES.contains(&ty) {
|
||||||
format!("[{prefix}{ty}{suffix}](/docs/kcl-lang/types#{ty})")
|
format!("[{prefix}{ty}{suffix}](/docs/kcl-lang/types#{ty})")
|
||||||
} else if fmt_for_text && DECLARED_TYPES.contains(&ty) {
|
} else if fmt_for_text && DECLARED_TYPES.contains(&ty) {
|
||||||
|
@ -626,6 +626,8 @@ impl FnData {
|
|||||||
pub(super) fn to_autocomplete_snippet(&self) -> String {
|
pub(super) fn to_autocomplete_snippet(&self) -> String {
|
||||||
if self.name == "loft" {
|
if self.name == "loft" {
|
||||||
return "loft([${0:sketch000}, ${1:sketch001}])".to_owned();
|
return "loft([${0:sketch000}, ${1:sketch001}])".to_owned();
|
||||||
|
} else if self.name == "clone" {
|
||||||
|
return "clone(${0:part001})".to_owned();
|
||||||
} else if self.name == "hole" {
|
} else if self.name == "hole" {
|
||||||
return "hole(${0:holeSketch}, ${1:%})".to_owned();
|
return "hole(${0:holeSketch}, ${1:%})".to_owned();
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ use std::{
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use kcl_doc::ModData;
|
use kcl_doc::ModData;
|
||||||
|
use parse_display::Display;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_lsp::lsp_types::{
|
use tower_lsp::lsp_types::{
|
||||||
@ -18,15 +19,27 @@ use tower_lsp::lsp_types::{
|
|||||||
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
MarkupKind, ParameterInformation, ParameterLabel, SignatureHelp, SignatureInformation,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::execution::{types::NumericType, Sketch};
|
||||||
execution::{types::NumericType, Sketch},
|
|
||||||
std::Primitive,
|
|
||||||
};
|
|
||||||
|
|
||||||
// These types are declared in (KCL) std.
|
// These types are declared in (KCL) std.
|
||||||
const DECLARED_TYPES: [&str; 15] = [
|
const DECLARED_TYPES: [&str; 17] = [
|
||||||
"any", "number", "string", "tag", "bool", "Sketch", "Solid", "Plane", "Helix", "Face", "Edge", "Point2d",
|
"any",
|
||||||
"Point3d", "Axis2d", "Axis3d",
|
"number",
|
||||||
|
"string",
|
||||||
|
"tag",
|
||||||
|
"bool",
|
||||||
|
"Sketch",
|
||||||
|
"Solid",
|
||||||
|
"Plane",
|
||||||
|
"Helix",
|
||||||
|
"Face",
|
||||||
|
"Edge",
|
||||||
|
"Point2d",
|
||||||
|
"Point3d",
|
||||||
|
"Axis2d",
|
||||||
|
"Axis3d",
|
||||||
|
"ImportedGeometry",
|
||||||
|
"fn",
|
||||||
];
|
];
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
lazy_static::lazy_static! {
|
||||||
@ -38,6 +51,21 @@ lazy_static::lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The primitive types that can be used in a KCL file.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Serialize, JsonSchema, Display)]
|
||||||
|
#[serde(rename_all = "lowercase")]
|
||||||
|
#[display(style = "lowercase")]
|
||||||
|
enum Primitive {
|
||||||
|
/// A boolean value.
|
||||||
|
Bool,
|
||||||
|
/// A number value.
|
||||||
|
Number,
|
||||||
|
/// A string value.
|
||||||
|
String,
|
||||||
|
/// A uuid value.
|
||||||
|
Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@ -167,6 +195,7 @@ impl StdLibFnArg {
|
|||||||
pub fn description(&self, kcl_std: Option<&ModData>) -> Option<String> {
|
pub fn description(&self, kcl_std: Option<&ModData>) -> Option<String> {
|
||||||
// Check if we explicitly gave this stdlib arg a description.
|
// Check if we explicitly gave this stdlib arg a description.
|
||||||
if !self.description.is_empty() {
|
if !self.description.is_empty() {
|
||||||
|
assert!(!self.description.contains('\n'), "Arg docs will get truncated");
|
||||||
return Some(self.description.clone());
|
return Some(self.description.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -533,8 +562,6 @@ pub trait StdLibFn: std::fmt::Debug + Send + Sync {
|
|||||||
fn to_autocomplete_snippet(&self) -> Result<String> {
|
fn to_autocomplete_snippet(&self) -> Result<String> {
|
||||||
if self.name() == "loft" {
|
if self.name() == "loft" {
|
||||||
return Ok("loft([${0:sketch000}, ${1:sketch001}])".to_string());
|
return Ok("loft([${0:sketch000}, ${1:sketch001}])".to_string());
|
||||||
} else if self.name() == "clone" {
|
|
||||||
return Ok("clone(${0:part001})".to_string());
|
|
||||||
} else if self.name() == "union" {
|
} else if self.name() == "union" {
|
||||||
return Ok("union([${0:extrude001}, ${1:extrude002}])".to_string());
|
return Ok("union([${0:extrude001}, ${1:extrude002}])".to_string());
|
||||||
} else if self.name() == "subtract" {
|
} else if self.name() == "subtract" {
|
||||||
@ -701,7 +728,7 @@ pub fn get_description_string_from_schema(schema: &schemars::schema::RootSchema)
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_primitive(schema: &schemars::schema::Schema) -> Result<Option<Primitive>> {
|
fn is_primitive(schema: &schemars::schema::Schema) -> Result<Option<Primitive>> {
|
||||||
match schema {
|
match schema {
|
||||||
schemars::schema::Schema::Object(o) => {
|
schemars::schema::Schema::Object(o) => {
|
||||||
if o.enum_values.is_some() {
|
if o.enum_values.is_some() {
|
||||||
@ -1007,9 +1034,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_autocomplete_snippet_map() {
|
fn get_autocomplete_snippet_map() {
|
||||||
let map_fn: Box<dyn StdLibFn> = Box::new(crate::std::array::Map);
|
let data = kcl_doc::walk_prelude();
|
||||||
let snippet = map_fn.to_autocomplete_snippet().unwrap();
|
let DocData::Fn(map_fn) = data.find_by_name("map").unwrap() else {
|
||||||
assert_eq!(snippet, r#"map(${0:[0..9]})"#);
|
panic!();
|
||||||
|
};
|
||||||
|
let snippet = map_fn.to_autocomplete_snippet();
|
||||||
|
assert_eq!(snippet, r#"map()"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1129,8 +1159,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::literal_string_with_formatting_args)]
|
#[allow(clippy::literal_string_with_formatting_args)]
|
||||||
fn get_autocomplete_snippet_clone() {
|
fn get_autocomplete_snippet_clone() {
|
||||||
let clone_fn: Box<dyn StdLibFn> = Box::new(crate::std::clone::Clone);
|
let data = kcl_doc::walk_prelude();
|
||||||
let snippet = clone_fn.to_autocomplete_snippet().unwrap();
|
let DocData::Fn(clone_fn) = data.find_by_name("clone").unwrap() else {
|
||||||
|
panic!();
|
||||||
|
};
|
||||||
|
let snippet = clone_fn.to_autocomplete_snippet();
|
||||||
assert_eq!(snippet, r#"clone(${0:part001})"#);
|
assert_eq!(snippet, r#"clone(${0:part001})"#);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +559,7 @@ impl From<KclError> for pyo3::PyErr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// An error which occurred during parsing, etc.
|
/// An error which occurred during parsing, etc.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, ts_rs::TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, ts_rs::TS, PartialEq, Eq)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
pub struct CompilationError {
|
pub struct CompilationError {
|
||||||
#[serde(rename = "sourceRange")]
|
#[serde(rename = "sourceRange")]
|
||||||
|
@ -118,7 +118,6 @@ pub(super) async fn get_changed_program(old: CacheInformation<'_>, new: CacheInf
|
|||||||
// We know they have the same imports because the ast is the same.
|
// We know they have the same imports because the ast is the same.
|
||||||
// If we have no imports, we can skip this.
|
// If we have no imports, we can skip this.
|
||||||
if !old.ast.has_import_statements() {
|
if !old.ast.has_import_statements() {
|
||||||
println!("No imports, no need to check.");
|
|
||||||
return CacheResult::NoAction(reapply_settings);
|
return CacheResult::NoAction(reapply_settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +278,7 @@ impl From<&KclValue> for OpKclValue {
|
|||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
},
|
},
|
||||||
KclValue::String { value, .. } => Self::String { value: value.clone() },
|
KclValue::String { value, .. } => Self::String { value: value.clone() },
|
||||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
|
||||||
let value = value.iter().map(Self::from).collect();
|
let value = value.iter().map(Self::from).collect();
|
||||||
Self::Array { value }
|
Self::Array { value }
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,13 @@ use crate::{
|
|||||||
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
|
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
|
||||||
TagIdentifier,
|
TagIdentifier,
|
||||||
},
|
},
|
||||||
|
fmt,
|
||||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||||
parsing::ast::types::{
|
parsing::ast::types::{
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryOperator,
|
||||||
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility,
|
BinaryPart, BodyItem, CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector,
|
||||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef, ObjectExpression,
|
ItemVisibility, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, Node, NodeRef,
|
||||||
PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
ObjectExpression, PipeExpression, Program, TagDeclarator, Type, UnaryExpression, UnaryOperator,
|
||||||
},
|
},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
std::{
|
std::{
|
||||||
@ -707,17 +708,25 @@ impl ExecutorContext {
|
|||||||
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
Expr::AscribedExpression(expr) => {
|
Expr::AscribedExpression(expr) => expr.get_result(exec_state, self).await?,
|
||||||
let result = self
|
|
||||||
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
|
|
||||||
.await?;
|
|
||||||
apply_ascription(&result, &expr.ty, exec_state, expr.into())?
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(item)
|
Ok(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Node<AscribedExpression> {
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn get_result(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
|
let metadata = Metadata {
|
||||||
|
source_range: SourceRange::from(self),
|
||||||
|
};
|
||||||
|
let result = ctx
|
||||||
|
.execute_expr(&self.expr, exec_state, &metadata, &[], StatementKind::Expression)
|
||||||
|
.await?;
|
||||||
|
apply_ascription(&result, &self.ty, exec_state, self.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn apply_ascription(
|
fn apply_ascription(
|
||||||
value: &KclValue,
|
value: &KclValue,
|
||||||
ty: &Node<Type>,
|
ty: &Node<Type>,
|
||||||
@ -758,6 +767,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.get_result(exec_state, ctx).await,
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(exec_state),
|
||||||
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
BinaryPart::IfExpression(e) => e.get_result(exec_state, ctx).await,
|
||||||
|
BinaryPart::AscribedExpression(e) => e.get_result(exec_state, ctx).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -867,11 +877,7 @@ impl Node<MemberExpression> {
|
|||||||
source_ranges: vec![self.clone().into()],
|
source_ranges: vec![self.clone().into()],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
(
|
(KclValue::HomArray { value: arr, .. }, Property::UInt(index), _) => {
|
||||||
KclValue::MixedArray { value: arr, .. } | KclValue::HomArray { value: arr, .. },
|
|
||||||
Property::UInt(index),
|
|
||||||
_,
|
|
||||||
) => {
|
|
||||||
let value_of_arr = arr.get(index);
|
let value_of_arr = arr.get(index);
|
||||||
if let Some(value) = value_of_arr {
|
if let Some(value) = value_of_arr {
|
||||||
Ok(value.to_owned())
|
Ok(value.to_owned())
|
||||||
@ -882,7 +888,7 @@ impl Node<MemberExpression> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(KclValue::MixedArray { .. } | KclValue::HomArray { .. }, p, _) => {
|
(KclValue::HomArray { .. }, p, _) => {
|
||||||
let t = p.type_name();
|
let t = p.type_name();
|
||||||
let article = article_for(t);
|
let article = article_for(t);
|
||||||
Err(KclError::Semantic(KclErrorDetails {
|
Err(KclError::Semantic(KclErrorDetails {
|
||||||
@ -1170,7 +1176,7 @@ impl Node<UnaryExpression> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let direction = match direction {
|
let direction = match direction {
|
||||||
KclValue::MixedArray { value: values, meta } => {
|
KclValue::Tuple { value: values, meta } => {
|
||||||
let values = values
|
let values = values
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| match v {
|
.map(|v| match v {
|
||||||
@ -1183,7 +1189,7 @@ impl Node<UnaryExpression> {
|
|||||||
})
|
})
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
KclValue::MixedArray {
|
KclValue::Tuple {
|
||||||
value: values,
|
value: values,
|
||||||
meta: meta.clone(),
|
meta: meta.clone(),
|
||||||
}
|
}
|
||||||
@ -1551,7 +1557,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => {
|
||||||
for v in value {
|
for v in value {
|
||||||
update_memory_for_tags_of_geometry(v, exec_state)?;
|
update_memory_for_tags_of_geometry(v, exec_state)?;
|
||||||
}
|
}
|
||||||
@ -1595,9 +1601,9 @@ impl Node<ArrayExpression> {
|
|||||||
results.push(value);
|
results.push(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(KclValue::MixedArray {
|
Ok(KclValue::HomArray {
|
||||||
value: results,
|
value: results,
|
||||||
meta: vec![self.into()],
|
ty: RuntimeType::Primitive(PrimitiveType::Any),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1606,7 +1612,7 @@ impl Node<ArrayRangeExpression> {
|
|||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
pub async fn execute(&self, exec_state: &mut ExecState, ctx: &ExecutorContext) -> Result<KclValue, KclError> {
|
||||||
let metadata = Metadata::from(&self.start_element);
|
let metadata = Metadata::from(&self.start_element);
|
||||||
let start = ctx
|
let start_val = ctx
|
||||||
.execute_expr(
|
.execute_expr(
|
||||||
&self.start_element,
|
&self.start_element,
|
||||||
exec_state,
|
exec_state,
|
||||||
@ -1615,19 +1621,30 @@ impl Node<ArrayRangeExpression> {
|
|||||||
StatementKind::Expression,
|
StatementKind::Expression,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let start = start.as_int().ok_or(KclError::Semantic(KclErrorDetails {
|
let (start, start_ty) = start_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
|
||||||
source_ranges: vec![self.into()],
|
source_ranges: vec![self.into()],
|
||||||
message: format!("Expected int but found {}", start.human_friendly_type()),
|
message: format!("Expected int but found {}", start_val.human_friendly_type()),
|
||||||
}))?;
|
}))?;
|
||||||
let metadata = Metadata::from(&self.end_element);
|
let metadata = Metadata::from(&self.end_element);
|
||||||
let end = ctx
|
let end_val = ctx
|
||||||
.execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
|
.execute_expr(&self.end_element, exec_state, &metadata, &[], StatementKind::Expression)
|
||||||
.await?;
|
.await?;
|
||||||
let end = end.as_int().ok_or(KclError::Semantic(KclErrorDetails {
|
let (end, end_ty) = end_val.as_int_with_ty().ok_or(KclError::Semantic(KclErrorDetails {
|
||||||
source_ranges: vec![self.into()],
|
source_ranges: vec![self.into()],
|
||||||
message: format!("Expected int but found {}", end.human_friendly_type()),
|
message: format!("Expected int but found {}", end_val.human_friendly_type()),
|
||||||
}))?;
|
}))?;
|
||||||
|
|
||||||
|
if start_ty != end_ty {
|
||||||
|
let start = start_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: start_ty });
|
||||||
|
let start = fmt::human_display_number(start.n, start.ty);
|
||||||
|
let end = end_val.as_ty_f64().unwrap_or(TyF64 { n: 0.0, ty: end_ty });
|
||||||
|
let end = fmt::human_display_number(end.n, end.ty);
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges: vec![self.into()],
|
||||||
|
message: format!("Range start and end must be of the same type, but found {start} and {end}"),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
if end < start {
|
if end < start {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
source_ranges: vec![self.into()],
|
source_ranges: vec![self.into()],
|
||||||
@ -1644,16 +1661,17 @@ impl Node<ArrayRangeExpression> {
|
|||||||
let meta = vec![Metadata {
|
let meta = vec![Metadata {
|
||||||
source_range: self.into(),
|
source_range: self.into(),
|
||||||
}];
|
}];
|
||||||
Ok(KclValue::MixedArray {
|
|
||||||
|
Ok(KclValue::HomArray {
|
||||||
value: range
|
value: range
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|num| KclValue::Number {
|
.map(|num| KclValue::Number {
|
||||||
value: num as f64,
|
value: num as f64,
|
||||||
ty: NumericType::count(),
|
ty: start_ty.clone(),
|
||||||
meta: meta.clone(),
|
meta: meta.clone(),
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
meta,
|
ty: RuntimeType::Primitive(PrimitiveType::Number(start_ty)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1868,7 +1886,7 @@ fn type_check_params_kw(
|
|||||||
arg.value = arg
|
arg.value = arg
|
||||||
.value
|
.value
|
||||||
.coerce(
|
.coerce(
|
||||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).map_err(|e| KclError::Semantic(e.into()))?,
|
||||||
exec_state,
|
exec_state,
|
||||||
)
|
)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
@ -1946,7 +1964,8 @@ fn type_check_params_kw(
|
|||||||
arg.value = arg
|
arg.value = arg
|
||||||
.value
|
.value
|
||||||
.coerce(
|
.coerce(
|
||||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
|
||||||
|
.map_err(|e| KclError::Semantic(e.into()))?,
|
||||||
exec_state,
|
exec_state,
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@ -2125,7 +2144,8 @@ impl FunctionSource {
|
|||||||
arg.value = arg
|
arg.value = arg
|
||||||
.value
|
.value
|
||||||
.coerce(
|
.coerce(
|
||||||
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range).unwrap(),
|
&RuntimeType::from_parsed(ty.inner.clone(), exec_state, arg.source_range)
|
||||||
|
.map_err(|e| KclError::Semantic(e.into()))?,
|
||||||
exec_state,
|
exec_state,
|
||||||
)
|
)
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
@ -2235,9 +2255,10 @@ mod test {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
exec::UnitType,
|
||||||
execution::{memory::Stack, parse_execute, ContextType},
|
execution::{memory::Stack, parse_execute, ContextType},
|
||||||
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
||||||
ExecutorSettings,
|
ExecutorSettings, UnitLen,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2391,6 +2412,7 @@ p = {
|
|||||||
yAxis = { x = 0, y = 1, z = 0 },
|
yAxis = { x = 0, y = 1, z = 0 },
|
||||||
zAxis = { x = 0, y = 0, z = 1 }
|
zAxis = { x = 0, y = 0, z = 1 }
|
||||||
}: Plane
|
}: Plane
|
||||||
|
arr1 = [42]: [number(cm)]
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = parse_execute(program).await.unwrap();
|
let result = parse_execute(program).await.unwrap();
|
||||||
@ -2401,6 +2423,24 @@ p = {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
KclValue::Plane { .. }
|
KclValue::Plane { .. }
|
||||||
));
|
));
|
||||||
|
let arr1 = mem
|
||||||
|
.memory
|
||||||
|
.get_from("arr1", result.mem_env, SourceRange::default(), 0)
|
||||||
|
.unwrap();
|
||||||
|
if let KclValue::HomArray { value, ty } = arr1 {
|
||||||
|
assert_eq!(value.len(), 1, "Expected Vec with specific length: found {:?}", value);
|
||||||
|
assert_eq!(*ty, RuntimeType::known_length(UnitLen::Cm));
|
||||||
|
// Compare, ignoring meta.
|
||||||
|
if let KclValue::Number { value, ty, .. } = &value[0] {
|
||||||
|
// Converted from mm to cm.
|
||||||
|
assert_eq!(*value, 4.2);
|
||||||
|
assert_eq!(*ty, NumericType::Known(UnitType::Length(UnitLen::Cm)));
|
||||||
|
} else {
|
||||||
|
panic!("Expected a number; found {:?}", value[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
panic!("Expected HomArray; found {arr1:?}");
|
||||||
|
}
|
||||||
|
|
||||||
let program = r#"
|
let program = r#"
|
||||||
a = 42: string
|
a = 42: string
|
||||||
@ -2419,6 +2459,28 @@ a = 42: Plane
|
|||||||
.unwrap_err()
|
.unwrap_err()
|
||||||
.to_string()
|
.to_string()
|
||||||
.contains("could not coerce number value to type Plane"));
|
.contains("could not coerce number value to type Plane"));
|
||||||
|
|
||||||
|
let program = r#"
|
||||||
|
arr = [0]: [string]
|
||||||
|
"#;
|
||||||
|
let result = parse_execute(program).await;
|
||||||
|
let err = result.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
err.to_string()
|
||||||
|
.contains("could not coerce array (list) value to type [string]"),
|
||||||
|
"Expected error but found {err:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
let program = r#"
|
||||||
|
mixedArr = [0, "a"]: [number(mm)]
|
||||||
|
"#;
|
||||||
|
let result = parse_execute(program).await;
|
||||||
|
let err = result.unwrap_err();
|
||||||
|
assert!(
|
||||||
|
err.to_string()
|
||||||
|
.contains("could not coerce array (list) value to type [number(mm)]"),
|
||||||
|
"Expected error but found {err:?}"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -2638,4 +2700,19 @@ sketch001 = startSketchOn(XY)
|
|||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn ascription_in_binop() {
|
||||||
|
let ast = r#"foo = tan(0): number(rad) - 4deg"#;
|
||||||
|
parse_execute(ast).await.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn neg_sqrt() {
|
||||||
|
let ast = r#"bad = sqrt(-2)"#;
|
||||||
|
|
||||||
|
let e = parse_execute(ast).await.unwrap_err();
|
||||||
|
// Make sure we get a useful error message and not an engine error.
|
||||||
|
assert!(e.message().contains("sqrt"), "Error message: '{}'", e.message());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1239,6 +1239,20 @@ impl Path {
|
|||||||
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
[TyF64::new(p[0], ty.clone()), TyF64::new(p[1], ty)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The path segment start point and its type.
|
||||||
|
pub fn start_point_components(&self) -> ([f64; 2], NumericType) {
|
||||||
|
let p = &self.get_base().from;
|
||||||
|
let ty: NumericType = self.get_base().units.into();
|
||||||
|
(*p, ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The path segment end point and its type.
|
||||||
|
pub fn end_point_components(&self) -> ([f64; 2], NumericType) {
|
||||||
|
let p = &self.get_base().to;
|
||||||
|
let ty: NumericType = self.get_base().units.into();
|
||||||
|
(*p, ty)
|
||||||
|
}
|
||||||
|
|
||||||
/// Length of this path segment, in cartesian plane.
|
/// Length of this path segment, in cartesian plane.
|
||||||
pub fn length(&self) -> TyF64 {
|
pub fn length(&self) -> TyF64 {
|
||||||
let n = match self {
|
let n = match self {
|
||||||
|
@ -48,7 +48,7 @@ pub enum KclValue {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
},
|
},
|
||||||
MixedArray {
|
Tuple {
|
||||||
value: Vec<KclValue>,
|
value: Vec<KclValue>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
@ -197,7 +197,7 @@ impl From<KclValue> for Vec<SourceRange> {
|
|||||||
KclValue::Bool { meta, .. } => to_vec_sr(&meta),
|
KclValue::Bool { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Number { meta, .. } => to_vec_sr(&meta),
|
KclValue::Number { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
|
KclValue::Tuple { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
||||||
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
||||||
@ -228,7 +228,7 @@ impl From<&KclValue> for Vec<SourceRange> {
|
|||||||
KclValue::Number { meta, .. } => to_vec_sr(meta),
|
KclValue::Number { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::String { meta, .. } => to_vec_sr(meta),
|
KclValue::String { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
|
KclValue::Tuple { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
||||||
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
||||||
@ -252,7 +252,7 @@ impl KclValue {
|
|||||||
KclValue::Bool { value: _, meta } => meta.clone(),
|
KclValue::Bool { value: _, meta } => meta.clone(),
|
||||||
KclValue::Number { meta, .. } => meta.clone(),
|
KclValue::Number { meta, .. } => meta.clone(),
|
||||||
KclValue::String { value: _, meta } => meta.clone(),
|
KclValue::String { value: _, meta } => meta.clone(),
|
||||||
KclValue::MixedArray { value: _, meta } => meta.clone(),
|
KclValue::Tuple { value: _, meta } => meta.clone(),
|
||||||
KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
|
KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
|
||||||
KclValue::Object { value: _, meta } => meta.clone(),
|
KclValue::Object { value: _, meta } => meta.clone(),
|
||||||
KclValue::TagIdentifier(x) => x.meta.clone(),
|
KclValue::TagIdentifier(x) => x.meta.clone(),
|
||||||
@ -307,7 +307,7 @@ impl KclValue {
|
|||||||
} => "number(Angle)",
|
} => "number(Angle)",
|
||||||
KclValue::Number { .. } => "number",
|
KclValue::Number { .. } => "number",
|
||||||
KclValue::String { .. } => "string (text)",
|
KclValue::String { .. } => "string (text)",
|
||||||
KclValue::MixedArray { .. } => "mixed array (list)",
|
KclValue::Tuple { .. } => "tuple (list)",
|
||||||
KclValue::HomArray { .. } => "array (list)",
|
KclValue::HomArray { .. } => "array (list)",
|
||||||
KclValue::Object { .. } => "object",
|
KclValue::Object { .. } => "object",
|
||||||
KclValue::Module { .. } => "module",
|
KclValue::Module { .. } => "module",
|
||||||
@ -373,7 +373,7 @@ impl KclValue {
|
|||||||
|
|
||||||
/// Put the point into a KCL value.
|
/// Put the point into a KCL value.
|
||||||
pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
|
pub fn from_point2d(p: [f64; 2], ty: NumericType, meta: Vec<Metadata>) -> Self {
|
||||||
Self::MixedArray {
|
Self::Tuple {
|
||||||
value: vec![
|
value: vec![
|
||||||
Self::Number {
|
Self::Number {
|
||||||
value: p[0],
|
value: p[0],
|
||||||
@ -404,6 +404,13 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_int_with_ty(&self) -> Option<(i64, NumericType)> {
|
||||||
|
match self {
|
||||||
|
KclValue::Number { value, ty, .. } => crate::try_f64_to_i64(*value).map(|i| (i, ty.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_object(&self) -> Option<&KclObjectFields> {
|
pub fn as_object(&self) -> Option<&KclObjectFields> {
|
||||||
if let KclValue::Object { value, meta: _ } = &self {
|
if let KclValue::Object { value, meta: _ } = &self {
|
||||||
Some(value)
|
Some(value)
|
||||||
@ -430,7 +437,7 @@ impl KclValue {
|
|||||||
|
|
||||||
pub fn as_array(&self) -> Option<&[KclValue]> {
|
pub fn as_array(&self) -> Option<&[KclValue]> {
|
||||||
match self {
|
match self {
|
||||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => Some(value),
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } => Some(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,7 +609,7 @@ impl KclValue {
|
|||||||
KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
|
KclValue::TagDeclarator(tag) => Some(format!("${}", tag.name)),
|
||||||
KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
|
KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
|
||||||
// TODO better Array and Object stringification
|
// TODO better Array and Object stringification
|
||||||
KclValue::MixedArray { .. } => Some("[...]".to_owned()),
|
KclValue::Tuple { .. } => Some("[...]".to_owned()),
|
||||||
KclValue::HomArray { .. } => Some("[...]".to_owned()),
|
KclValue::HomArray { .. } => Some("[...]".to_owned()),
|
||||||
KclValue::Object { .. } => Some("{ ... }".to_owned()),
|
KclValue::Object { .. } => Some("{ ... }".to_owned()),
|
||||||
KclValue::Module { .. }
|
KclValue::Module { .. }
|
||||||
|
@ -64,7 +64,7 @@ pub mod typed_path;
|
|||||||
pub(crate) mod types;
|
pub(crate) mod types;
|
||||||
|
|
||||||
/// Outcome of executing a program. This is used in TS.
|
/// Outcome of executing a program. This is used in TS.
|
||||||
#[derive(Debug, Clone, Serialize, ts_rs::TS)]
|
#[derive(Debug, Clone, Serialize, ts_rs::TS, PartialEq)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct ExecOutcome {
|
pub struct ExecOutcome {
|
||||||
@ -385,6 +385,7 @@ impl ExecutorContext {
|
|||||||
let (ws, _headers) = client
|
let (ws, _headers) = client
|
||||||
.modeling()
|
.modeling()
|
||||||
.commands_ws(
|
.commands_ws(
|
||||||
|
None,
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
if settings.enable_ssao {
|
if settings.enable_ssao {
|
||||||
@ -653,13 +654,21 @@ impl ExecutorContext {
|
|||||||
keys.sort();
|
keys.sort();
|
||||||
for key in keys {
|
for key in keys {
|
||||||
let (_, id, _, _) = &new_universe[key];
|
let (_, id, _, _) = &new_universe[key];
|
||||||
let old_source = old_state.get_source(*id);
|
if let (Some(source0), Some(source1)) =
|
||||||
let new_source = new_exec_state.get_source(*id);
|
(old_state.get_source(*id), new_exec_state.get_source(*id))
|
||||||
if old_source != new_source {
|
{
|
||||||
|
if source0.source != source1.source {
|
||||||
clear_scene = true;
|
clear_scene = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !clear_scene {
|
||||||
|
// Return early we don't need to clear the scene.
|
||||||
|
let outcome = old_state.to_exec_outcome(result_env).await;
|
||||||
|
return Ok(outcome);
|
||||||
|
}
|
||||||
|
|
||||||
(
|
(
|
||||||
clear_scene,
|
clear_scene,
|
||||||
@ -1932,7 +1941,7 @@ a = []
|
|||||||
notArray = !a";
|
notArray = !a";
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parse_execute(code5).await.unwrap_err().message(),
|
parse_execute(code5).await.unwrap_err().message(),
|
||||||
"Cannot apply unary operator ! to non-boolean value: mixed array (list)",
|
"Cannot apply unary operator ! to non-boolean value: array (list)",
|
||||||
);
|
);
|
||||||
|
|
||||||
let code6 = "
|
let code6 = "
|
||||||
|
@ -28,6 +28,10 @@ pub enum RuntimeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RuntimeType {
|
impl RuntimeType {
|
||||||
|
pub fn any() -> Self {
|
||||||
|
RuntimeType::Primitive(PrimitiveType::Any)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn edge() -> Self {
|
pub fn edge() -> Self {
|
||||||
RuntimeType::Primitive(PrimitiveType::Edge)
|
RuntimeType::Primitive(PrimitiveType::Edge)
|
||||||
}
|
}
|
||||||
@ -72,6 +76,10 @@ impl RuntimeType {
|
|||||||
RuntimeType::Primitive(PrimitiveType::Tag)
|
RuntimeType::Primitive(PrimitiveType::Tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tag_identifier() -> Self {
|
||||||
|
RuntimeType::Primitive(PrimitiveType::TagId)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bool() -> Self {
|
pub fn bool() -> Self {
|
||||||
RuntimeType::Primitive(PrimitiveType::Boolean)
|
RuntimeType::Primitive(PrimitiveType::Boolean)
|
||||||
}
|
}
|
||||||
@ -162,13 +170,20 @@ impl RuntimeType {
|
|||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<Self, CompilationError> {
|
) -> Result<Self, CompilationError> {
|
||||||
Ok(match value {
|
Ok(match value {
|
||||||
|
AstPrimitiveType::Any => RuntimeType::Primitive(PrimitiveType::Any),
|
||||||
AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
|
AstPrimitiveType::String => RuntimeType::Primitive(PrimitiveType::String),
|
||||||
AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
|
AstPrimitiveType::Boolean => RuntimeType::Primitive(PrimitiveType::Boolean),
|
||||||
AstPrimitiveType::Number(suffix) => RuntimeType::Primitive(PrimitiveType::Number(
|
AstPrimitiveType::Number(suffix) => {
|
||||||
NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
|
let ty = match suffix {
|
||||||
)),
|
NumericSuffix::None => NumericType::Any,
|
||||||
|
_ => NumericType::from_parsed(suffix, &exec_state.mod_local.settings),
|
||||||
|
};
|
||||||
|
RuntimeType::Primitive(PrimitiveType::Number(ty))
|
||||||
|
}
|
||||||
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
|
AstPrimitiveType::Named(name) => Self::from_alias(&name.name, exec_state, source_range)?,
|
||||||
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
AstPrimitiveType::Tag => RuntimeType::Primitive(PrimitiveType::Tag),
|
||||||
|
AstPrimitiveType::ImportedGeometry => RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||||
|
AstPrimitiveType::Function(_) => RuntimeType::Primitive(PrimitiveType::Function),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,7 +218,7 @@ impl RuntimeType {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" or "),
|
.join(" or "),
|
||||||
RuntimeType::Tuple(tys) => format!(
|
RuntimeType::Tuple(tys) => format!(
|
||||||
"an array with values of types ({})",
|
"a tuple with values of types ({})",
|
||||||
tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
|
tys.iter().map(Self::human_friendly_type).collect::<Vec<_>>().join(", ")
|
||||||
),
|
),
|
||||||
RuntimeType::Object(_) => format!("an object with fields {}", self),
|
RuntimeType::Object(_) => format!("an object with fields {}", self),
|
||||||
@ -215,6 +230,7 @@ impl RuntimeType {
|
|||||||
use RuntimeType::*;
|
use RuntimeType::*;
|
||||||
|
|
||||||
match (self, sup) {
|
match (self, sup) {
|
||||||
|
(_, Primitive(PrimitiveType::Any)) => true,
|
||||||
(Primitive(t1), Primitive(t2)) => t1.subtype(t2),
|
(Primitive(t1), Primitive(t2)) => t1.subtype(t2),
|
||||||
(Array(t1, l1), Array(t2, l2)) => t1.subtype(t2) && l1.subtype(*l2),
|
(Array(t1, l1), Array(t2, l2)) => t1.subtype(t2) && l1.subtype(*l2),
|
||||||
(Tuple(t1), Tuple(t2)) => t1.len() == t2.len() && t1.iter().zip(t2).all(|(t1, t2)| t1.subtype(t2)),
|
(Tuple(t1), Tuple(t2)) => t1.len() == t2.len() && t1.iter().zip(t2).all(|(t1, t2)| t1.subtype(t2)),
|
||||||
@ -265,7 +281,7 @@ impl RuntimeType {
|
|||||||
.map(|t| t.display_multiple())
|
.map(|t| t.display_multiple())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" or "),
|
.join(" or "),
|
||||||
RuntimeType::Tuple(_) => "arrays".to_owned(),
|
RuntimeType::Tuple(_) => "tuples".to_owned(),
|
||||||
RuntimeType::Object(_) => format!("objects with fields {self}"),
|
RuntimeType::Object(_) => format!("objects with fields {self}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,7 +298,7 @@ impl fmt::Display for RuntimeType {
|
|||||||
},
|
},
|
||||||
RuntimeType::Tuple(ts) => write!(
|
RuntimeType::Tuple(ts) => write!(
|
||||||
f,
|
f,
|
||||||
"[{}]",
|
"({})",
|
||||||
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
|
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
|
||||||
),
|
),
|
||||||
RuntimeType::Union(ts) => write!(
|
RuntimeType::Union(ts) => write!(
|
||||||
@ -333,10 +349,13 @@ impl ArrayLen {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum PrimitiveType {
|
pub enum PrimitiveType {
|
||||||
|
Any,
|
||||||
Number(NumericType),
|
Number(NumericType),
|
||||||
String,
|
String,
|
||||||
Boolean,
|
Boolean,
|
||||||
Tag,
|
Tag,
|
||||||
|
// Annoyingly some functions only want a TagIdentifier, not any kind of tag.
|
||||||
|
TagId,
|
||||||
Sketch,
|
Sketch,
|
||||||
Solid,
|
Solid,
|
||||||
Plane,
|
Plane,
|
||||||
@ -346,11 +365,13 @@ pub enum PrimitiveType {
|
|||||||
Axis2d,
|
Axis2d,
|
||||||
Axis3d,
|
Axis3d,
|
||||||
ImportedGeometry,
|
ImportedGeometry,
|
||||||
|
Function,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveType {
|
impl PrimitiveType {
|
||||||
fn display_multiple(&self) -> String {
|
fn display_multiple(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
|
PrimitiveType::Any => "any values".to_owned(),
|
||||||
PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
|
PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
|
||||||
PrimitiveType::Number(_) => "numbers".to_owned(),
|
PrimitiveType::Number(_) => "numbers".to_owned(),
|
||||||
PrimitiveType::String => "strings".to_owned(),
|
PrimitiveType::String => "strings".to_owned(),
|
||||||
@ -364,13 +385,17 @@ impl PrimitiveType {
|
|||||||
PrimitiveType::Axis2d => "2d axes".to_owned(),
|
PrimitiveType::Axis2d => "2d axes".to_owned(),
|
||||||
PrimitiveType::Axis3d => "3d axes".to_owned(),
|
PrimitiveType::Axis3d => "3d axes".to_owned(),
|
||||||
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
|
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
|
||||||
|
PrimitiveType::Function => "functions".to_owned(),
|
||||||
PrimitiveType::Tag => "tags".to_owned(),
|
PrimitiveType::Tag => "tags".to_owned(),
|
||||||
|
PrimitiveType::TagId => "tag identifiers".to_owned(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subtype(&self, other: &PrimitiveType) -> bool {
|
fn subtype(&self, other: &PrimitiveType) -> bool {
|
||||||
match (self, other) {
|
match (self, other) {
|
||||||
|
(_, PrimitiveType::Any) => true,
|
||||||
(PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
|
(PrimitiveType::Number(n1), PrimitiveType::Number(n2)) => n1.subtype(n2),
|
||||||
|
(PrimitiveType::TagId, PrimitiveType::Tag) => true,
|
||||||
(t1, t2) => t1 == t2,
|
(t1, t2) => t1 == t2,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -379,6 +404,7 @@ impl PrimitiveType {
|
|||||||
impl fmt::Display for PrimitiveType {
|
impl fmt::Display for PrimitiveType {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
|
PrimitiveType::Any => write!(f, "any"),
|
||||||
PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
|
PrimitiveType::Number(NumericType::Known(unit)) => write!(f, "number({unit})"),
|
||||||
PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
|
PrimitiveType::Number(NumericType::Unknown) => write!(f, "number(unknown units)"),
|
||||||
PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number(default units)"),
|
PrimitiveType::Number(NumericType::Default { .. }) => write!(f, "number(default units)"),
|
||||||
@ -386,6 +412,7 @@ impl fmt::Display for PrimitiveType {
|
|||||||
PrimitiveType::String => write!(f, "string"),
|
PrimitiveType::String => write!(f, "string"),
|
||||||
PrimitiveType::Boolean => write!(f, "bool"),
|
PrimitiveType::Boolean => write!(f, "bool"),
|
||||||
PrimitiveType::Tag => write!(f, "tag"),
|
PrimitiveType::Tag => write!(f, "tag"),
|
||||||
|
PrimitiveType::TagId => write!(f, "tag identifier"),
|
||||||
PrimitiveType::Sketch => write!(f, "Sketch"),
|
PrimitiveType::Sketch => write!(f, "Sketch"),
|
||||||
PrimitiveType::Solid => write!(f, "Solid"),
|
PrimitiveType::Solid => write!(f, "Solid"),
|
||||||
PrimitiveType::Plane => write!(f, "Plane"),
|
PrimitiveType::Plane => write!(f, "Plane"),
|
||||||
@ -395,6 +422,7 @@ impl fmt::Display for PrimitiveType {
|
|||||||
PrimitiveType::Axis3d => write!(f, "Axis3d"),
|
PrimitiveType::Axis3d => write!(f, "Axis3d"),
|
||||||
PrimitiveType::Helix => write!(f, "Helix"),
|
PrimitiveType::Helix => write!(f, "Helix"),
|
||||||
PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
|
PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
|
||||||
|
PrimitiveType::Function => write!(f, "function"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -663,6 +691,7 @@ impl NumericType {
|
|||||||
// We don't have enough information to coerce.
|
// We don't have enough information to coerce.
|
||||||
(Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
|
(Unknown, _) => Err(CoercionError::from(val).with_explicit(self.example_ty().unwrap_or("mm".to_owned()))),
|
||||||
(_, Unknown) => Err(val.into()),
|
(_, Unknown) => Err(val.into()),
|
||||||
|
|
||||||
(Any, _) => Ok(KclValue::Number {
|
(Any, _) => Ok(KclValue::Number {
|
||||||
value: *value,
|
value: *value,
|
||||||
ty: self.clone(),
|
ty: self.clone(),
|
||||||
@ -994,9 +1023,9 @@ impl KclValue {
|
|||||||
self_ty.subtype(ty)
|
self_ty.subtype(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Coerce `self` to a new value which has `ty` as it's closest supertype.
|
/// Coerce `self` to a new value which has `ty` as its closest supertype.
|
||||||
///
|
///
|
||||||
/// If the result is Some, then:
|
/// If the result is Ok, then:
|
||||||
/// - result.principal_type().unwrap().subtype(ty)
|
/// - result.principal_type().unwrap().subtype(ty)
|
||||||
///
|
///
|
||||||
/// If self.principal_type() == ty then result == self
|
/// If self.principal_type() == ty then result == self
|
||||||
@ -1016,10 +1045,11 @@ impl KclValue {
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
) -> Result<KclValue, CoercionError> {
|
) -> Result<KclValue, CoercionError> {
|
||||||
let value = match self {
|
let value = match self {
|
||||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
|
||||||
_ => self,
|
_ => self,
|
||||||
};
|
};
|
||||||
match ty {
|
match ty {
|
||||||
|
PrimitiveType::Any => Ok(value.clone()),
|
||||||
PrimitiveType::Number(ty) => ty.coerce(value),
|
PrimitiveType::Number(ty) => ty.coerce(value),
|
||||||
PrimitiveType::String => match value {
|
PrimitiveType::String => match value {
|
||||||
KclValue::String { .. } => Ok(value.clone()),
|
KclValue::String { .. } => Ok(value.clone()),
|
||||||
@ -1159,6 +1189,14 @@ impl KclValue {
|
|||||||
KclValue::ImportedGeometry { .. } => Ok(value.clone()),
|
KclValue::ImportedGeometry { .. } => Ok(value.clone()),
|
||||||
_ => Err(self.into()),
|
_ => Err(self.into()),
|
||||||
},
|
},
|
||||||
|
PrimitiveType::Function => match value {
|
||||||
|
KclValue::Function { .. } => Ok(value.clone()),
|
||||||
|
_ => Err(self.into()),
|
||||||
|
},
|
||||||
|
PrimitiveType::TagId => match value {
|
||||||
|
KclValue::TagIdentifier { .. } => Ok(value.clone()),
|
||||||
|
_ => Err(self.into()),
|
||||||
|
},
|
||||||
PrimitiveType::Tag => match value {
|
PrimitiveType::Tag => match value {
|
||||||
KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => {
|
KclValue::TagDeclarator { .. } | KclValue::TagIdentifier { .. } | KclValue::Uuid { .. } => {
|
||||||
Ok(value.clone())
|
Ok(value.clone())
|
||||||
@ -1178,41 +1216,49 @@ impl KclValue {
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
allow_shrink: bool,
|
allow_shrink: bool,
|
||||||
) -> Result<KclValue, CoercionError> {
|
) -> Result<KclValue, CoercionError> {
|
||||||
if len.satisfied(1, false).is_some() && self.has_type(ty) {
|
|
||||||
return Ok(KclValue::HomArray {
|
|
||||||
value: vec![self.clone()],
|
|
||||||
ty: ty.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
match self {
|
match self {
|
||||||
KclValue::HomArray { value, ty: aty } => {
|
KclValue::HomArray { value, ty: aty, .. } => {
|
||||||
|
let satisfied_len = len.satisfied(value.len(), allow_shrink);
|
||||||
|
|
||||||
if aty.subtype(ty) {
|
if aty.subtype(ty) {
|
||||||
len.satisfied(value.len(), allow_shrink)
|
// If the element type is a subtype of the target type and
|
||||||
|
// the length constraint is satisfied, we can just return
|
||||||
|
// the values unchanged, only adjusting the length. The new
|
||||||
|
// array element type should preserve its type because the
|
||||||
|
// target type oftentimes includes an unknown type as a way
|
||||||
|
// to say that the caller doesn't care.
|
||||||
|
return satisfied_len
|
||||||
.map(|len| KclValue::HomArray {
|
.map(|len| KclValue::HomArray {
|
||||||
value: value[..len].to_vec(),
|
value: value[..len].to_vec(),
|
||||||
ty: aty.clone(),
|
ty: aty.clone(),
|
||||||
})
|
})
|
||||||
.ok_or(self.into())
|
.ok_or(self.into());
|
||||||
} else {
|
}
|
||||||
Err(self.into())
|
|
||||||
|
// Ignore the array type, and coerce the elements of the array.
|
||||||
|
if let Some(satisfied_len) = satisfied_len {
|
||||||
|
let value_result = value
|
||||||
|
.iter()
|
||||||
|
.take(satisfied_len)
|
||||||
|
.map(|v| v.coerce(ty, exec_state))
|
||||||
|
.collect::<Result<Vec<_>, _>>();
|
||||||
|
|
||||||
|
if let Ok(value) = value_result {
|
||||||
|
// We were able to coerce all the elements.
|
||||||
|
return Ok(KclValue::HomArray { value, ty: ty.clone() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KclValue::MixedArray { value, .. } => {
|
|
||||||
// Check if we have a nested homogeneous array that we can flatten.
|
// As a last resort, try to flatten the array.
|
||||||
let mut values = Vec::new();
|
let mut values = Vec::new();
|
||||||
for item in value {
|
for item in value {
|
||||||
if let KclValue::HomArray {
|
if let KclValue::HomArray { value: inner_value, .. } = item {
|
||||||
ty: inner_ty,
|
// Flatten elements.
|
||||||
value: inner_value,
|
for item in inner_value {
|
||||||
} = item
|
values.push(item.coerce(ty, exec_state)?);
|
||||||
{
|
|
||||||
if inner_ty.subtype(ty) {
|
|
||||||
values.extend(inner_value.iter().cloned());
|
|
||||||
} else {
|
|
||||||
values.push(item.clone());
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
values.push(item.clone());
|
values.push(item.coerce(ty, exec_state)?);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1220,9 +1266,22 @@ impl KclValue {
|
|||||||
.satisfied(values.len(), allow_shrink)
|
.satisfied(values.len(), allow_shrink)
|
||||||
.ok_or(CoercionError::from(self))?;
|
.ok_or(CoercionError::from(self))?;
|
||||||
|
|
||||||
let value = values[..len]
|
assert!(len <= values.len());
|
||||||
|
values.truncate(len);
|
||||||
|
|
||||||
|
Ok(KclValue::HomArray {
|
||||||
|
value: values,
|
||||||
|
ty: ty.clone(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
KclValue::Tuple { value, .. } => {
|
||||||
|
let len = len
|
||||||
|
.satisfied(value.len(), allow_shrink)
|
||||||
|
.ok_or(CoercionError::from(self))?;
|
||||||
|
let value = value
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.coerce(ty, exec_state))
|
.map(|item| item.coerce(ty, exec_state))
|
||||||
|
.take(len)
|
||||||
.collect::<Result<Vec<_>, _>>()?;
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
Ok(KclValue::HomArray { value, ty: ty.clone() })
|
Ok(KclValue::HomArray { value, ty: ty.clone() })
|
||||||
@ -1231,28 +1290,32 @@ impl KclValue {
|
|||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
}),
|
}),
|
||||||
|
_ if len.satisfied(1, false).is_some() => Ok(KclValue::HomArray {
|
||||||
|
value: vec![self.coerce(ty, exec_state)?],
|
||||||
|
ty: ty.clone(),
|
||||||
|
}),
|
||||||
_ => Err(self.into()),
|
_ => Err(self.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
fn coerce_to_tuple_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Result<KclValue, CoercionError> {
|
||||||
match self {
|
match self {
|
||||||
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
|
KclValue::Tuple { value, .. } | KclValue::HomArray { value, .. } if value.len() == tys.len() => {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
for (i, t) in tys.iter().enumerate() {
|
for (i, t) in tys.iter().enumerate() {
|
||||||
result.push(value[i].coerce(t, exec_state)?);
|
result.push(value[i].coerce(t, exec_state)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(KclValue::MixedArray {
|
Ok(KclValue::Tuple {
|
||||||
value: result,
|
value: result,
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::MixedArray {
|
KclValue::KclNone { meta, .. } if tys.is_empty() => Ok(KclValue::Tuple {
|
||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
meta: meta.clone(),
|
meta: meta.clone(),
|
||||||
}),
|
}),
|
||||||
value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::MixedArray {
|
value if tys.len() == 1 && value.has_type(&tys[0]) => Ok(KclValue::Tuple {
|
||||||
value: vec![value.clone()],
|
value: vec![value.clone()],
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
}),
|
}),
|
||||||
@ -1312,18 +1375,16 @@ impl KclValue {
|
|||||||
KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
|
KclValue::Face { .. } => Some(RuntimeType::Primitive(PrimitiveType::Face)),
|
||||||
KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
|
KclValue::Helix { .. } => Some(RuntimeType::Primitive(PrimitiveType::Helix)),
|
||||||
KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
|
KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
|
||||||
KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
|
KclValue::Tuple { value, .. } => Some(RuntimeType::Tuple(
|
||||||
value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
|
value.iter().map(|v| v.principal_type()).collect::<Option<Vec<_>>>()?,
|
||||||
)),
|
)),
|
||||||
KclValue::HomArray { ty, value, .. } => {
|
KclValue::HomArray { ty, value, .. } => {
|
||||||
Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
|
Some(RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(value.len())))
|
||||||
}
|
}
|
||||||
KclValue::TagIdentifier(_) | KclValue::TagDeclarator(_) | KclValue::Uuid { .. } => {
|
KclValue::TagIdentifier(_) => Some(RuntimeType::Primitive(PrimitiveType::TagId)),
|
||||||
Some(RuntimeType::Primitive(PrimitiveType::Tag))
|
KclValue::TagDeclarator(_) | KclValue::Uuid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Tag)),
|
||||||
}
|
KclValue::Function { .. } => Some(RuntimeType::Primitive(PrimitiveType::Function)),
|
||||||
KclValue::Function { .. } | KclValue::Module { .. } | KclValue::KclNone { .. } | KclValue::Type { .. } => {
|
KclValue::Module { .. } | KclValue::KclNone { .. } | KclValue::Type { .. } => None,
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1348,7 +1409,7 @@ mod test {
|
|||||||
value: "hello".to_owned(),
|
value: "hello".to_owned(),
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
},
|
},
|
||||||
KclValue::MixedArray {
|
KclValue::Tuple {
|
||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
},
|
},
|
||||||
@ -1417,6 +1478,26 @@ mod test {
|
|||||||
let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
|
let aty1 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::Known(1));
|
||||||
let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::NonEmpty);
|
let aty0 = RuntimeType::Array(Box::new(ty.clone()), ArrayLen::NonEmpty);
|
||||||
|
|
||||||
|
match v {
|
||||||
|
KclValue::Tuple { .. } | KclValue::HomArray { .. } => {
|
||||||
|
// These will not get wrapped if possible.
|
||||||
|
assert_coerce_results(
|
||||||
|
v,
|
||||||
|
&aty,
|
||||||
|
&KclValue::HomArray {
|
||||||
|
value: vec![],
|
||||||
|
ty: ty.clone(),
|
||||||
|
},
|
||||||
|
&mut exec_state,
|
||||||
|
);
|
||||||
|
// Coercing an empty tuple or array to an array of length 1
|
||||||
|
// should fail.
|
||||||
|
v.coerce(&aty1, &mut exec_state).unwrap_err();
|
||||||
|
// Coercing an empty tuple or array to an array that's
|
||||||
|
// non-empty should fail.
|
||||||
|
v.coerce(&aty0, &mut exec_state).unwrap_err();
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
assert_coerce_results(
|
assert_coerce_results(
|
||||||
v,
|
v,
|
||||||
&aty,
|
&aty,
|
||||||
@ -1450,13 +1531,15 @@ mod test {
|
|||||||
assert_coerce_results(
|
assert_coerce_results(
|
||||||
v,
|
v,
|
||||||
&tty,
|
&tty,
|
||||||
&KclValue::MixedArray {
|
&KclValue::Tuple {
|
||||||
value: vec![v.clone()],
|
value: vec![v.clone()],
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
},
|
},
|
||||||
&mut exec_state,
|
&mut exec_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for v in &values[1..] {
|
for v in &values[1..] {
|
||||||
// Not a subtype
|
// Not a subtype
|
||||||
@ -1503,7 +1586,7 @@ mod test {
|
|||||||
assert_coerce_results(
|
assert_coerce_results(
|
||||||
&none,
|
&none,
|
||||||
&tty,
|
&tty,
|
||||||
&KclValue::MixedArray {
|
&KclValue::Tuple {
|
||||||
value: Vec::new(),
|
value: Vec::new(),
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
},
|
},
|
||||||
@ -1634,7 +1717,7 @@ mod test {
|
|||||||
],
|
],
|
||||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
||||||
};
|
};
|
||||||
let mixed1 = KclValue::MixedArray {
|
let mixed1 = KclValue::Tuple {
|
||||||
value: vec![
|
value: vec![
|
||||||
KclValue::Number {
|
KclValue::Number {
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
@ -1649,7 +1732,7 @@ mod test {
|
|||||||
],
|
],
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
};
|
};
|
||||||
let mixed2 = KclValue::MixedArray {
|
let mixed2 = KclValue::Tuple {
|
||||||
value: vec![
|
value: vec![
|
||||||
KclValue::Number {
|
KclValue::Number {
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
@ -1739,7 +1822,7 @@ mod test {
|
|||||||
],
|
],
|
||||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
||||||
};
|
};
|
||||||
let mixed0 = KclValue::MixedArray {
|
let mixed0 = KclValue::Tuple {
|
||||||
value: vec![],
|
value: vec![],
|
||||||
meta: Vec::new(),
|
meta: Vec::new(),
|
||||||
};
|
};
|
||||||
@ -2156,7 +2239,7 @@ d = cos(30)
|
|||||||
async fn coerce_nested_array() {
|
async fn coerce_nested_array() {
|
||||||
let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock().await);
|
let mut exec_state = ExecState::new(&crate::ExecutorContext::new_mock().await);
|
||||||
|
|
||||||
let mixed1 = KclValue::MixedArray {
|
let mixed1 = KclValue::HomArray {
|
||||||
value: vec![
|
value: vec![
|
||||||
KclValue::Number {
|
KclValue::Number {
|
||||||
value: 0.0,
|
value: 0.0,
|
||||||
@ -2184,7 +2267,7 @@ d = cos(30)
|
|||||||
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
ty: RuntimeType::Primitive(PrimitiveType::Number(NumericType::count())),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
meta: Vec::new(),
|
ty: RuntimeType::any(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Principal types
|
// Principal types
|
||||||
|
@ -150,6 +150,7 @@ impl BinaryPart {
|
|||||||
unary_expression.get_hover_value_for_position(pos, code, opts)
|
unary_expression.get_hover_value_for_position(pos, code, opts)
|
||||||
}
|
}
|
||||||
BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code, opts),
|
BinaryPart::IfExpression(e) => e.get_hover_value_for_position(pos, code, opts),
|
||||||
|
BinaryPart::AscribedExpression(e) => e.expr.get_hover_value_for_position(pos, code, opts),
|
||||||
BinaryPart::MemberExpression(member_expression) => {
|
BinaryPart::MemberExpression(member_expression) => {
|
||||||
member_expression.get_hover_value_for_position(pos, code, opts)
|
member_expression.get_hover_value_for_position(pos, code, opts)
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use sha2::{Digest as DigestTrait, Sha256};
|
|||||||
|
|
||||||
use crate::parsing::ast::types::{
|
use crate::parsing::ast::types::{
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryPart, BodyItem,
|
Annotation, ArrayExpression, ArrayRangeExpression, AscribedExpression, BinaryExpression, BinaryPart, BodyItem,
|
||||||
CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression, Identifier, IfExpression,
|
CallExpressionKw, DefaultParamVal, ElseIf, Expr, ExpressionStatement, FunctionExpression, FunctionType, Identifier,
|
||||||
ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression, Literal,
|
IfExpression, ImportItem, ImportSelector, ImportStatement, ItemVisibility, KclNone, LabelledExpression, Literal,
|
||||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty, Parameter,
|
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Name, ObjectExpression, ObjectProperty, Parameter,
|
||||||
PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type, TypeDeclaration,
|
PipeExpression, PipeSubstitution, PrimitiveType, Program, ReturnStatement, TagDeclarator, Type, TypeDeclaration,
|
||||||
UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
|
UnaryExpression, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
@ -162,6 +162,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
BinaryPart::UnaryExpression(ue) => ue.compute_digest(),
|
||||||
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
BinaryPart::MemberExpression(me) => me.compute_digest(),
|
||||||
BinaryPart::IfExpression(e) => e.compute_digest(),
|
BinaryPart::IfExpression(e) => e.compute_digest(),
|
||||||
|
BinaryPart::AscribedExpression(e) => e.compute_digest(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,17 +226,35 @@ impl PrimitiveType {
|
|||||||
pub fn compute_digest(&mut self) -> Digest {
|
pub fn compute_digest(&mut self) -> Digest {
|
||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
match self {
|
match self {
|
||||||
|
PrimitiveType::Any => hasher.update(b"any"),
|
||||||
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
|
PrimitiveType::Named(id) => hasher.update(id.compute_digest()),
|
||||||
PrimitiveType::String => hasher.update(b"string"),
|
PrimitiveType::String => hasher.update(b"string"),
|
||||||
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
PrimitiveType::Number(suffix) => hasher.update(suffix.digestable_id()),
|
||||||
PrimitiveType::Boolean => hasher.update(b"bool"),
|
PrimitiveType::Boolean => hasher.update(b"bool"),
|
||||||
PrimitiveType::Tag => hasher.update(b"tag"),
|
PrimitiveType::Tag => hasher.update(b"tag"),
|
||||||
|
PrimitiveType::ImportedGeometry => hasher.update(b"ImportedGeometry"),
|
||||||
|
PrimitiveType::Function(f) => hasher.update(f.compute_digest()),
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher.finalize().into()
|
hasher.finalize().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FunctionType {
|
||||||
|
compute_digest!(|slf, hasher| {
|
||||||
|
if let Some(u) = &mut slf.unnamed_arg {
|
||||||
|
hasher.update(u.compute_digest());
|
||||||
|
}
|
||||||
|
slf.named_args.iter_mut().for_each(|(a, t)| {
|
||||||
|
a.compute_digest();
|
||||||
|
t.compute_digest();
|
||||||
|
});
|
||||||
|
if let Some(r) = &mut slf.return_type {
|
||||||
|
hasher.update(r.compute_digest());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
impl Parameter {
|
impl Parameter {
|
||||||
compute_digest!(|slf, hasher| {
|
compute_digest!(|slf, hasher| {
|
||||||
hasher.update(slf.identifier.compute_digest());
|
hasher.update(slf.identifier.compute_digest());
|
||||||
|
@ -52,6 +52,7 @@ impl BinaryPart {
|
|||||||
BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
|
BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
|
||||||
BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
|
BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
|
||||||
BinaryPart::IfExpression(e) => e.module_id,
|
BinaryPart::IfExpression(e) => e.module_id,
|
||||||
|
BinaryPart::AscribedExpression(e) => e.module_id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|