Compare commits

...

24 Commits

Author SHA1 Message Date
182865014e try 2024-04-25 10:05:22 +10:00
2452eede0b omg 2024-04-25 09:59:51 +10:00
98442b9ec2 tweak 2024-04-25 09:55:52 +10:00
fb1c8036f6 try debug again 2024-04-25 09:50:22 +10:00
2918612d4b trying shit 2024-04-25 09:47:40 +10:00
abbd065c2c typo 2024-04-25 09:45:00 +10:00
23e29b024f trigger ci 2024-04-25 09:41:31 +10:00
807adac371 more tweaks 2024-04-25 09:36:36 +10:00
03eb8dca32 debug code 2024-04-25 09:35:22 +10:00
e3358f8251 more tweaks 2024-04-25 09:26:26 +10:00
49ea3991b2 another tweak 2024-04-25 09:20:14 +10:00
f32f0e2717 debug 2024-04-25 09:09:00 +10:00
0363e4f4e0 tweak 2024-04-25 08:59:53 +10:00
5e60dbd5e8 speed up playw tests by skipping wasm:build 2024-04-25 08:57:25 +10:00
379f154a5c more parallel playw tests (#2179) 2024-04-25 07:00:41 +10:00
60c4969322 Adding dark edge lines in light mode + enabling SSAO (#2219)
* adding dark edge lines in light mode + enabling SSAO

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* Rerun CI

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@zoo.dev>
Co-authored-by: Frank Noirot <frank@kittycad.io>
2024-04-24 16:59:25 -04:00
cc6dee8ad4 Make it impossible to crash app while extruding (#2224)
Co-authored-by: Kurt Hutten <k.hutten@protonmail.ch>
2024-04-24 16:34:56 -04:00
2fc7c0d5fd fix gizmo (#2221) 2024-04-24 20:09:39 +00:00
bf2dcd808f Bump tauri-plugin-http from 2.0.0-beta.5 to 2.0.0-beta.6 in /src-tauri (#2222)
Bumps [tauri-plugin-http](https://github.com/tauri-apps/plugins-workspace) from 2.0.0-beta.5 to 2.0.0-beta.6.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.0.0-beta.5...fs-v2.0.0-beta.6)

---
updated-dependencies:
- dependency-name: tauri-plugin-http
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-24 05:54:18 -04:00
ee21e486d4 math u32 fix (#2218)
* math fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* empty

* A snapshot a day keeps the bugs away! 📷🐛 (OS: ubuntu)

* empty

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-04-23 22:59:12 +00:00
b5a3eb9e9c Bump tauri-build from 2.0.0-beta.12 to 2.0.0-beta.13 in /src-tauri (#2204)
Bumps [tauri-build](https://github.com/tauri-apps/tauri) from 2.0.0-beta.12 to 2.0.0-beta.13.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-build-v2.0.0-beta.12...tauri-build-v2.0.0-beta.13)

---
updated-dependencies:
- dependency-name: tauri-build
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 20:57:23 +00:00
c85645c9f2 Bump tauri-plugin-dialog from 2.0.0-beta.5 to 2.0.0-beta.6 in /src-tauri (#2203)
Bumps [tauri-plugin-dialog](https://github.com/tauri-apps/plugins-workspace) from 2.0.0-beta.5 to 2.0.0-beta.6.
- [Release notes](https://github.com/tauri-apps/plugins-workspace/releases)
- [Commits](https://github.com/tauri-apps/plugins-workspace/compare/fs-v2.0.0-beta.5...fs-v2.0.0-beta.6)

---
updated-dependencies:
- dependency-name: tauri-plugin-dialog
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 18:48:00 +00:00
cfa4dd2e33 Bump kittycad-modeling-cmds from 0.2.20 to 0.2.21 in /src/wasm-lib (#2199)
Bumps [kittycad-modeling-cmds](https://github.com/KittyCAD/modeling-api) from 0.2.20 to 0.2.21.
- [Commits](https://github.com/KittyCAD/modeling-api/compare/kittycad-modeling-cmds-0.2.20...kittycad-modeling-cmds-0.2.21)

---
updated-dependencies:
- dependency-name: kittycad-modeling-cmds
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-23 17:55:03 +00:00
c620f7269c Patterns fixes (2d) (#2197)
* updates

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* patterns fixes

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix docs

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* better autocomplete

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* fix

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
2024-04-23 17:31:20 +00:00
46 changed files with 5576 additions and 2439 deletions

View File

@ -1,5 +1,6 @@
VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands VITE_KC_API_WS_MODELING_URL=wss://api.dev.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.dev.zoo.dev VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
VITE_KC_WASM_OVERRIDE_URL=""
VITE_KC_SKIP_AUTH=false VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=5000 VITE_KC_CONNECTION_TIMEOUT_MS=5000

View File

@ -1,5 +1,6 @@
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
VITE_KC_API_BASE_URL=https://api.zoo.dev VITE_KC_API_BASE_URL=https://api.zoo.dev
VITE_KC_SITE_BASE_URL=https://zoo.dev VITE_KC_SITE_BASE_URL=https://zoo.dev
VITE_KC_WASM_OVERRIDE_URL=""
VITE_KC_SKIP_AUTH=false VITE_KC_SKIP_AUTH=false
VITE_KC_CONNECTION_TIMEOUT_MS=15000 VITE_KC_CONNECTION_TIMEOUT_MS=15000

View File

@ -14,9 +14,31 @@ permissions:
pull-requests: write pull-requests: write
jobs: jobs:
check-wasm-lib-changes:
runs-on: ubuntu-latest
outputs:
url: ${{ steps.set-output.outputs.url }}
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0 # Fetches all history for all branches and tags
- name: Check for changes in src/wasm-lib
id: set-output
run: |
if git diff --quiet origin/main...HEAD -- src/wasm-lib; then
echo "url=https://app.zoo.dev" >> $GITHUB_OUTPUT
echo "No changes detected in src/wasm-lib"
else
echo "Changes detected in src/wasm-lib"
echo "url=" >> $GITHUB_OUTPUT
fi
playwright-ubuntu: playwright-ubuntu:
timeout-minutes: 60 timeout-minutes: 60
runs-on: ubuntu-latest-8-cores runs-on: ubuntu-latest-8-cores
needs: check-wasm-lib-changes
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
@ -28,13 +50,19 @@ jobs:
run: yarn run: yarn
- name: Install Playwright Browsers - name: Install Playwright Browsers
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
- name: Print WASM Lib Changes URL
run: |
echo "WASM Lib Changes URL: ${{ needs.check-wasm-lib-changes.outputs.url }}"
- name: Setup Rust - name: Setup Rust
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Cache wasm - name: Cache wasm
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: build wasm - name: build wasm
if: ${{ needs.check-wasm-lib-changes.outputs.url }} == ''
run: yarn build:wasm run: yarn build:wasm
- name: build web - name: build web
run: yarn build:local run: yarn build:local
@ -44,6 +72,7 @@ jobs:
CI: true CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }} snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
@ -79,6 +108,7 @@ jobs:
env: env:
CI: true CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
@ -89,6 +119,7 @@ jobs:
playwright-macos: playwright-macos:
timeout-minutes: 60 timeout-minutes: 60
runs-on: macos-14 runs-on: macos-14
needs: check-wasm-lib-changes
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: actions/setup-node@v4 - uses: actions/setup-node@v4
@ -100,12 +131,15 @@ jobs:
- name: Install Playwright Browsers - name: Install Playwright Browsers
run: yarn playwright install --with-deps run: yarn playwright install --with-deps
- name: Setup Rust - name: Setup Rust
if: needs.check-wasm-lib-changes.outputs.url == ''
uses: dtolnay/rust-toolchain@stable uses: dtolnay/rust-toolchain@stable
- name: Cache wasm - name: Cache wasm
if: needs.check-wasm-lib-changes.outputs.url == ''
uses: Swatinem/rust-cache@v2 uses: Swatinem/rust-cache@v2
with: with:
workspaces: './src/wasm-lib' workspaces: './src/wasm-lib'
- name: build wasm - name: build wasm
if: needs.check-wasm-lib-changes.outputs.url == ''
run: yarn build:wasm run: yarn build:wasm
- name: build web - name: build web
run: yarn build:local run: yarn build:local
@ -116,6 +150,7 @@ jobs:
env: env:
CI: true CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }} token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
WASM_OVERRIDE: ${{ steps.check-wasm-lib-changes.outputs.url }}
- uses: actions/upload-artifact@v3 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:

View File

@ -9,7 +9,7 @@ Extrudes by a given amount.
```js ```js
extrude(length: number, sketch_group: SketchGroup) -> ExtrudeGroup extrude(length: number, sketch_group_set: SketchGroupSet) -> ExtrudeGroupSet
``` ```
### Examples ### Examples
@ -29,7 +29,7 @@ startSketchOn('XY')
### Arguments ### Arguments
* `length`: `number` (REQUIRED) * `length`: `number` (REQUIRED)
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) * `sketch_group_set`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED)
```js ```js
{ {
// The plane id or face id of the sketch group. // The plane id or face id of the sketch group.
@ -110,6 +110,7 @@ startSketchOn('XY')
// The to point. // The to point.
to: [number, number], to: [number, number],
}, },
type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
// The from point. // The from point.
@ -193,12 +194,15 @@ startSketchOn('XY')
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "sketchGroups",
} }
``` ```
### Returns ### Returns
`ExtrudeGroup` - An extrude group is a collection of extrude surfaces. `ExtrudeGroupSet` - A extrude group or a group of extrude groups.
```js ```js
{ {
// The id of the extrusion end cap // The id of the extrusion end cap
@ -278,6 +282,7 @@ startSketchOn('XY')
}], }],
// The id of the extrusion start cap // The id of the extrusion start cap
startCapId: uuid, startCapId: uuid,
type: "extrudeGroup",
// The extrude surfaces. // The extrude surfaces.
value: [{ value: [{
// The face id for the extrude plane. // The face id for the extrude plane.
@ -327,6 +332,9 @@ startSketchOn('XY')
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "extrudeGroups",
} }
``` ```

View File

@ -9,7 +9,7 @@ A linear pattern on a 2D sketch.
```js ```js
patternLinear2d(data: LinearPattern2dData, sketch_group: SketchGroup) -> [SketchGroup] patternLinear2d(data: LinearPattern2dData, sketch_group_set: SketchGroupSet) -> [SketchGroup]
``` ```
### Examples ### Examples
@ -39,7 +39,7 @@ const part = startSketchOn('XY')
repetitions: number, repetitions: number,
} }
``` ```
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths. (REQUIRED) * `sketch_group_set`: `SketchGroupSet` - A sketch group or a group of sketch groups. (REQUIRED)
```js ```js
{ {
// The plane id or face id of the sketch group. // The plane id or face id of the sketch group.
@ -120,6 +120,7 @@ const part = startSketchOn('XY')
// The to point. // The to point.
to: [number, number], to: [number, number],
}, },
type: "sketchGroup",
// The paths in the sketch group. // The paths in the sketch group.
value: [{ value: [{
// The from point. // The from point.
@ -203,6 +204,9 @@ const part = startSketchOn('XY')
y: number, y: number,
z: number, z: number,
}, },
} |
{
type: "sketchGroups",
} }
``` ```

File diff suppressed because it is too large Load Diff

View File

@ -1035,6 +1035,7 @@ const part001 = startSketchOn('-XZ')
}) })
test('Can add multiple sketches', async ({ page }) => { test('Can add multiple sketches', async ({ page }) => {
test.skip(process.platform === 'darwin', 'Can add multiple sketches')
const u = getUtils(page) const u = getUtils(page)
await page.setViewportSize({ width: 1200, height: 500 }) await page.setViewportSize({ width: 1200, height: 500 })
const PUR = 400 / 37.5 //pixeltoUnitRatio const PUR = 400 / 37.5 //pixeltoUnitRatio

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

@ -7,6 +7,7 @@ export const TEST_SETTINGS = {
theme: Themes.Dark, theme: Themes.Dark,
onboardingStatus: 'dismissed', onboardingStatus: 'dismissed',
projectDirectory: '', projectDirectory: '',
enableSSAO: false,
}, },
modeling: { modeling: {
defaultUnit: 'in', defaultUnit: 'in',

View File

@ -6,7 +6,7 @@ import { PNG } from 'pngjs'
async function waitForPageLoad(page: Page) { async function waitForPageLoad(page: Page) {
// wait for 'Loading stream...' spinner // wait for 'Loading stream...' spinner
// await page.getByTestId('loading-stream').waitFor() await page.getByTestId('loading-stream').waitFor()
// wait for all spinners to be gone // wait for all spinners to be gone
await page.getByTestId('loading').waitFor({ state: 'detached' }) await page.getByTestId('loading').waitFor({ state: 'detached' })

View File

@ -18,7 +18,7 @@ export default defineConfig({
/* Retry on CI only */ /* Retry on CI only */
retries: process.env.CI ? 3 : 0, retries: process.env.CI ? 3 : 0,
/* Opt out of parallel tests on CI. */ /* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : 1, workers: process.env.CI ? 2 : 1,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */ /* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html', reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
@ -72,7 +72,7 @@ export default defineConfig({
/* Run your local dev server before starting the tests */ /* Run your local dev server before starting the tests */
webServer: { webServer: {
command: 'yarn serve', command: 'VITE_KC_WASM_OVERRIDE_URL=$WASM_OVERRIDE yarn serve',
// url: 'http://127.0.0.1:3000', // url: 'http://127.0.0.1:3000',
reuseExistingServer: !process.env.CI, reuseExistingServer: !process.env.CI,
}, },

36
src-tauri/Cargo.lock generated
View File

@ -1592,7 +1592,7 @@ version = "0.18.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro-crate 2.0.2", "proc-macro-crate 2.0.2",
"proc-macro-error", "proc-macro-error",
"proc-macro2", "proc-macro2",
@ -1735,6 +1735,12 @@ version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.2.6" version = "0.2.6"
@ -4322,7 +4328,7 @@ version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"rustversion", "rustversion",
@ -4411,7 +4417,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2" checksum = "e5fa6fb9ee296c0dc2df41a656ca7948546d061958115ddb0bcaae43ad0d17d2"
dependencies = [ dependencies = [
"cfg-expr", "cfg-expr",
"heck", "heck 0.4.1",
"pkg-config", "pkg-config",
"toml 0.7.8", "toml 0.7.8",
"version-compare", "version-compare",
@ -4508,7 +4514,7 @@ dependencies = [
"getrandom 0.2.14", "getrandom 0.2.14",
"glob", "glob",
"gtk", "gtk",
"heck", "heck 0.4.1",
"http 1.1.0", "http 1.1.0",
"jni", "jni",
"libc", "libc",
@ -4543,15 +4549,15 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.0.0-beta.12" version = "2.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33de24aabe2b9c340d67005800cb6dd40aac5283126a42896fc8eec0b87cbe45" checksum = "abcf98a9b4527567c3e5ca9723431d121e001c2145651b3fa044d22b5e025a7e"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
"dirs-next", "dirs-next",
"glob", "glob",
"heck", "heck 0.5.0",
"json-patch", "json-patch",
"schemars", "schemars",
"semver", "semver",
@ -4596,7 +4602,7 @@ version = "2.0.0-beta.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b096f63f2724a1280ae0f5a34d0731de18ca18305e2ef6e5e9a39bb2710e8a85" checksum = "b096f63f2724a1280ae0f5a34d0731de18ca18305e2ef6e5e9a39bb2710e8a85"
dependencies = [ dependencies = [
"heck", "heck 0.4.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.48", "syn 2.0.48",
@ -4623,9 +4629,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.0.0-beta.5" version = "2.0.0-beta.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db4476c824a1488a52f4672d2b419a71fbf3dc97249013ef3c2c08fae2a23b71" checksum = "87caf6f2b704b0d27b4c64ef1fdd1f6ef97e2f5293216e230ad1efe61de54131"
dependencies = [ dependencies = [
"dunce", "dunce",
"log", "log",
@ -4660,9 +4666,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-http" name = "tauri-plugin-http"
version = "2.0.0-beta.5" version = "2.0.0-beta.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f27b2c90ed5473e1c068f523927fa0024212bc3a3f3a47c2a9c0b10b4b2e3ee4" checksum = "b7c32962a2e2141b3bc034e5c04f363635965e59435794b6bdcf97a027f0925a"
dependencies = [ dependencies = [
"data-url", "data-url",
"http 1.1.0", "http 1.1.0",
@ -4800,16 +4806,16 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "2.0.0-beta.12" version = "2.0.0-beta.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "760ac613d7f0de95067bcbcbcea175fe1df88fc4ab59c7f0b2cc2d01dc16a199" checksum = "d4709765385f035338ecc330f3fba753b8ee283c659c235da9768949cdb25469"
dependencies = [ dependencies = [
"brotli", "brotli",
"cargo_metadata", "cargo_metadata",
"ctor", "ctor",
"dunce", "dunce",
"glob", "glob",
"heck", "heck 0.5.0",
"html5ever", "html5ever",
"infer", "infer",
"json-patch", "json-patch",

View File

@ -12,7 +12,7 @@ rust-version = "1.70"
# 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
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.0.0-beta.12", features = [] } tauri-build = { version = "2.0.0-beta.13", features = [] }
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
@ -21,9 +21,9 @@ oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0" serde_json = "1.0"
tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] } tauri = { version = "2.0.0-beta.15", features = [ "devtools", "unstable"] }
tauri-plugin-dialog = { version = "2.0.0-beta.5" } tauri-plugin-dialog = { version = "2.0.0-beta.6" }
tauri-plugin-fs = { version = "2.0.0-beta.6" } tauri-plugin-fs = { version = "2.0.0-beta.6" }
tauri-plugin-http = { version = "2.0.0-beta.5" } tauri-plugin-http = { version = "2.0.0-beta.6" }
tauri-plugin-os = { version = "2.0.0-beta.2" } tauri-plugin-os = { version = "2.0.0-beta.2" }
tauri-plugin-process = { version = "2.0.0-beta.2" } tauri-plugin-process = { version = "2.0.0-beta.2" }
tauri-plugin-shell = { version = "2.0.0-beta.2" } tauri-plugin-shell = { version = "2.0.0-beta.2" }

View File

@ -3,13 +3,12 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
import { useKclContext } from 'lang/KclProvider' import { useKclContext } from 'lang/KclProvider'
import { CommandArgument } from 'lib/commandTypes' import { CommandArgument } from 'lib/commandTypes'
import { import {
ResolvedSelectionType,
canSubmitSelectionArg, canSubmitSelectionArg,
getSelectionType, getSelectionType,
getSelectionTypeDisplayText, getSelectionTypeDisplayText,
} from 'lib/selections' } from 'lib/selections'
import { modelingMachine } from 'machines/modelingMachine' import { modelingMachine } from 'machines/modelingMachine'
import { useEffect, useRef, useState } from 'react' import { useCallback, useEffect, useRef, useState } from 'react'
import { useHotkeys } from 'react-hotkeys-hook' import { useHotkeys } from 'react-hotkeys-hook'
import { StateFrom } from 'xstate' import { StateFrom } from 'xstate'
@ -30,13 +29,13 @@ function CommandBarSelectionInput({
const { commandBarState, commandBarSend } = useCommandsContext() const { commandBarState, commandBarSend } = useCommandsContext()
const [hasSubmitted, setHasSubmitted] = useState(false) const [hasSubmitted, setHasSubmitted] = useState(false)
const selection = useSelector(arg.machineActor, selectionSelector) const selection = useSelector(arg.machineActor, selectionSelector)
const [selectionsByType, setSelectionsByType] = useState< const initSelectionsByType = useCallback(() => {
'none' | ResolvedSelectionType[] const selectionRangeEnd = selection.codeBasedSelections[0]?.range[1]
>( return !selectionRangeEnd || selectionRangeEnd === code.length
selection.codeBasedSelections[0]?.range[1] === code.length
? 'none' ? 'none'
: getSelectionType(selection) : getSelectionType(selection)
) }, [selection, code])
const selectionsByType = initSelectionsByType()
const [canSubmitSelection, setCanSubmitSelection] = useState<boolean>( const [canSubmitSelection, setCanSubmitSelection] = useState<boolean>(
canSubmitSelectionArg(selectionsByType, arg) canSubmitSelectionArg(selectionsByType, arg)
) )
@ -51,17 +50,14 @@ function CommandBarSelectionInput({
inputRef.current?.focus() inputRef.current?.focus()
}, [selection, inputRef]) }, [selection, inputRef])
useEffect(() => {
setSelectionsByType(
selection.codeBasedSelections[0]?.range[1] === code.length
? 'none'
: getSelectionType(selection)
)
}, [selection])
// Fast-forward through this arg if it's marked as skippable // Fast-forward through this arg if it's marked as skippable
// and we have a valid selection already // and we have a valid selection already
useEffect(() => { useEffect(() => {
console.log('selection input effect', {
selectionsByType,
canSubmitSelection,
arg,
})
setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg)) setCanSubmitSelection(canSubmitSelectionArg(selectionsByType, arg))
const argValue = commandBarState.context.argumentsToSubmit[arg.name] const argValue = commandBarState.context.argumentsToSubmit[arg.name]
if (canSubmitSelection && arg.skip && argValue === undefined) { if (canSubmitSelection && arg.skip && argValue === undefined) {

View File

@ -77,7 +77,7 @@ export const ModelingMachineProvider = ({
auth, auth,
settings: { settings: {
context: { context: {
app: { theme }, app: { theme, enableSSAO },
modeling: { defaultUnit, highlightEdges }, modeling: { defaultUnit, highlightEdges },
}, },
}, },
@ -87,6 +87,7 @@ export const ModelingMachineProvider = ({
useSetupEngineManager(streamRef, token, { useSetupEngineManager(streamRef, token, {
theme: theme.current, theme: theme.current,
highlightEdges: highlightEdges.current, highlightEdges: highlightEdges.current,
enableSSAO: enableSSAO.current,
}) })
const { htmlRef } = useStore((s) => ({ const { htmlRef } = useStore((s) => ({
htmlRef: s.htmlRef, htmlRef: s.htmlRef,
@ -267,10 +268,12 @@ export const ModelingMachineProvider = ({
'has valid extrude selection': ({ selectionRanges }) => { 'has valid extrude selection': ({ selectionRanges }) => {
// A user can begin extruding if they either have 1+ faces selected or nothing selected // A user can begin extruding if they either have 1+ faces selected or nothing selected
// TODO: I believe this guard only allows for extruding a single face at a time // TODO: I believe this guard only allows for extruding a single face at a time
if (selectionRanges.codeBasedSelections.length < 1) return false
const isPipe = isSketchPipe(selectionRanges) const isPipe = isSketchPipe(selectionRanges)
if (isSelectionLastLine(selectionRanges, codeManager.code)) if (
selectionRanges.codeBasedSelections.length === 0 ||
isSelectionLastLine(selectionRanges, codeManager.code)
)
return true return true
if (!isPipe) return false if (!isPipe) return false

View File

@ -7,7 +7,12 @@ import React, { createContext, useEffect } from 'react'
import useStateMachineCommands from '../hooks/useStateMachineCommands' import useStateMachineCommands from '../hooks/useStateMachineCommands'
import { settingsMachine } from 'machines/settingsMachine' import { settingsMachine } from 'machines/settingsMachine'
import { toast } from 'react-hot-toast' import { toast } from 'react-hot-toast'
import { getThemeColorForEngine, setThemeClass, Themes } from 'lib/theme' import {
getThemeColorForEngine,
getOppositeTheme,
setThemeClass,
Themes,
} from 'lib/theme'
import decamelize from 'decamelize' import decamelize from 'decamelize'
import { import {
AnyStateMachine, AnyStateMachine,
@ -99,6 +104,9 @@ export const SettingsAuthProviderBase = ({
{ {
context: loadedSettings, context: loadedSettings,
actions: { actions: {
//TODO: batch all these and if that's difficult to do from tsx,
// make it easy to do
setClientSideSceneUnits: (context, event) => { setClientSideSceneUnits: (context, event) => {
const newBaseUnit = const newBaseUnit =
event.type === 'set.modeling.defaultUnit' event.type === 'set.modeling.defaultUnit'
@ -115,6 +123,16 @@ export const SettingsAuthProviderBase = ({
color: getThemeColorForEngine(context.app.theme.current), color: getThemeColorForEngine(context.app.theme.current),
}, },
}) })
const opposingTheme = getOppositeTheme(context.app.theme.current)
engineCommandManager.sendSceneCommand({
cmd_id: uuidv4(),
type: 'modeling_cmd_req',
cmd: {
type: 'set_default_system_properties',
color: getThemeColorForEngine(opposingTheme),
},
})
}, },
setEngineEdges: (context) => { setEngineEdges: (context) => {
engineCommandManager.sendSceneCommand({ engineCommandManager.sendSceneCommand({

View File

@ -7,5 +7,7 @@ export const VITE_KC_API_BASE_URL = import.meta.env.VITE_KC_API_BASE_URL
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
.VITE_KC_CONNECTION_TIMEOUT_MS .VITE_KC_CONNECTION_TIMEOUT_MS
export const VITE_KC_WASM_OVERRIDE_URL = import.meta.env
.VITE_KC_WASM_OVERRIDE_URL
export const TEST = import.meta.env.TEST export const TEST = import.meta.env.TEST
export const DEV = import.meta.env.DEV export const DEV = import.meta.env.DEV

View File

@ -11,9 +11,11 @@ export function useSetupEngineManager(
settings = { settings = {
theme: Themes.System, theme: Themes.System,
highlightEdges: true, highlightEdges: true,
enableSSAO: true,
} as { } as {
theme: Themes theme: Themes
highlightEdges: boolean highlightEdges: boolean
enableSSAO: boolean
} }
) { ) {
const { const {

View File

@ -4,7 +4,7 @@ import { Models } from '@kittycad/lib'
import { exportSave } from 'lib/exportSave' import { exportSave } from 'lib/exportSave'
import { uuidv4 } from 'lib/utils' import { uuidv4 } from 'lib/utils'
import { getNodePathFromSourceRange } from 'lang/queryAst' import { getNodePathFromSourceRange } from 'lang/queryAst'
import { Themes, getThemeColorForEngine } from 'lib/theme' import { Themes, getThemeColorForEngine, getOppositeTheme } from 'lib/theme'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
let lastMessage = '' let lastMessage = ''
@ -941,6 +941,7 @@ export class EngineCommandManager {
settings = { settings = {
theme: Themes.Dark, theme: Themes.Dark,
highlightEdges: true, highlightEdges: true,
enableSSAO: true,
}, },
}: { }: {
setMediaStream: (stream: MediaStream) => void setMediaStream: (stream: MediaStream) => void
@ -953,6 +954,7 @@ export class EngineCommandManager {
settings?: { settings?: {
theme: Themes theme: Themes
highlightEdges: boolean highlightEdges: boolean
enableSSAO: boolean
} }
}) { }) {
this.makeDefaultPlanes = makeDefaultPlanes this.makeDefaultPlanes = makeDefaultPlanes
@ -969,7 +971,8 @@ export class EngineCommandManager {
return return
} }
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}` const additionalSettings = settings.enableSSAO ? '&post_effect=ssao' : ''
const url = `${VITE_KC_API_WS_MODELING_URL}?video_res_width=${width}&video_res_height=${height}${additionalSettings}`
this.engineConnection = new EngineConnection({ this.engineConnection = new EngineConnection({
engineCommandManager: this, engineCommandManager: this,
url, url,
@ -989,6 +992,18 @@ export class EngineCommandManager {
color: getThemeColorForEngine(settings.theme), color: getThemeColorForEngine(settings.theme),
}, },
}) })
// Sets the default line colors
const opposingTheme = getOppositeTheme(settings.theme)
this.sendSceneCommand({
cmd_id: uuidv4(),
type: 'modeling_cmd_req',
cmd: {
type: 'set_default_system_properties',
color: getThemeColorForEngine(opposingTheme),
},
})
// Set the edge lines visibility // Set the edge lines visibility
this.sendSceneCommand({ this.sendSceneCommand({
type: 'modeling_cmd_req', type: 'modeling_cmd_req',
@ -1326,6 +1341,17 @@ export class EngineCommandManager {
this.lastArtifactMap = this.artifactMap this.lastArtifactMap = this.artifactMap
this.artifactMap = {} this.artifactMap = {}
await this.initPlanes() await this.initPlanes()
await this.sendSceneCommand({
type: 'modeling_cmd_req',
cmd_id: uuidv4(),
cmd: {
type: 'make_axes_gizmo',
clobber: false,
// If true, axes gizmo will be placed in the corner of the screen.
// If false, it will be placed at the origin of the scene.
gizmo_mode: true,
},
})
} }
subscribeTo<T extends ModelTypes>({ subscribeTo<T extends ModelTypes>({
event, event,

View File

@ -25,7 +25,7 @@ import { AppInfo } from 'wasm-lib/kcl/bindings/AppInfo'
import { CoreDumpManager } from 'lib/coredump' import { CoreDumpManager } from 'lib/coredump'
import openWindow from 'lib/openWindow' import openWindow from 'lib/openWindow'
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
import { TEST } from 'env' import { TEST, VITE_KC_WASM_OVERRIDE_URL } from 'env'
export type { Program } from '../wasm-lib/kcl/bindings/Program' export type { Program } from '../wasm-lib/kcl/bindings/Program'
export type { Value } from '../wasm-lib/kcl/bindings/Value' export type { Value } from '../wasm-lib/kcl/bindings/Value'
@ -76,18 +76,19 @@ export type { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface' export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
export const wasmUrl = () => { export const wasmUrl = () => {
const baseUrl = const baseUrl = VITE_KC_WASM_OVERRIDE_URL
typeof window === 'undefined' ? VITE_KC_WASM_OVERRIDE_URL
? 'http://127.0.0.1:3000' : typeof window === 'undefined'
: window.location.origin.includes('tauri://localhost') ? 'http://127.0.0.1:3000'
? 'tauri://localhost' // custom protocol for macOS : window.location.origin.includes('tauri://localhost')
: window.location.origin.includes('tauri.localhost') ? 'tauri://localhost' // custom protocol for macOS
? 'http://tauri.localhost' // fallback for Windows : window.location.origin.includes('tauri.localhost')
: window.location.origin.includes('localhost') ? 'http://tauri.localhost' // fallback for Windows
? 'http://localhost:3000' : window.location.origin.includes('localhost')
: window.location.origin && window.location.origin !== 'null' ? 'http://localhost:3000'
? window.location.origin : window.location.origin && window.location.origin !== 'null'
: 'http://localhost:3000' ? window.location.origin
: 'http://localhost:3000'
const fullUrl = baseUrl + '/wasm_lib_bg.wasm' const fullUrl = baseUrl + '/wasm_lib_bg.wasm'
console.log(`Full URL for WASM: ${fullUrl}`) console.log(`Full URL for WASM: ${fullUrl}`)

View File

@ -420,7 +420,13 @@ export function getSelectionTypeDisplayText(
const selectionsByType = getSelectionType(selection) const selectionsByType = getSelectionType(selection)
return (selectionsByType as Exclude<typeof selectionsByType, 'none'>) return (selectionsByType as Exclude<typeof selectionsByType, 'none'>)
.map(([type, count]) => `${count} ${type}${count > 1 ? 's' : ''}`) .map(
// Hack for showing "face" instead of "extrude-wall" in command bar text
([type, count]) =>
`${count} ${type.replace('extrude-wall', 'face')}${
count > 1 ? 's' : ''
}`
)
.join(', ') .join(', ')
} }

View File

@ -156,6 +156,13 @@ export function createSettings() {
</div> </div>
), ),
}), }),
enableSSAO: new Setting<boolean>({
defaultValue: true,
description:
'Whether or not Screen Space Ambient Occlusion (SSAO) is enabled',
validate: (v) => typeof v === 'boolean',
hideOnPlatform: 'both', //for now
}),
onboardingStatus: new Setting<string>({ onboardingStatus: new Setting<string>({
defaultValue: '', defaultValue: '',
validate: (v) => typeof v === 'string', validate: (v) => typeof v === 'string',

View File

@ -23,6 +23,17 @@ export function setThemeClass(theme: Themes) {
} }
} }
// Returns the resolved theme in use (Dark || Light)
export function getResolvedTheme(theme: Themes) {
return theme === Themes.System ? getSystemTheme() : theme
}
// Returns the opposing theme
export function getOppositeTheme(theme: Themes) {
const resolvedTheme = getResolvedTheme(theme)
return resolvedTheme === Themes.Dark ? Themes.Light : Themes.Dark
}
/** /**
* The engine takes RGBA values from 0-1 * The engine takes RGBA values from 0-1
* So we convert from the conventional 0-255 found in Figma * So we convert from the conventional 0-255 found in Figma
@ -30,7 +41,7 @@ export function setThemeClass(theme: Themes) {
* @returns { r: number, g: number, b: number, a: number } * @returns { r: number, g: number, b: number, a: number }
*/ */
export function getThemeColorForEngine(theme: Themes) { export function getThemeColorForEngine(theme: Themes) {
const resolvedTheme = theme === Themes.System ? getSystemTheme() : theme const resolvedTheme = getResolvedTheme(theme)
const dark = 28 / 255 const dark = 28 / 255
const light = 249 / 255 const light = 249 / 255
return resolvedTheme === Themes.Dark return resolvedTheme === Themes.Dark

View File

@ -31,9 +31,11 @@ export function useCalculateKclExpression({
newVariableInsertIndex: number newVariableInsertIndex: number
setNewVariableName: (a: string) => void setNewVariableName: (a: string) => void
} { } {
const { programMemory } = useKclContext() const { programMemory, code } = useKclContext()
const { context } = useModelingContext() const { context } = useModelingContext()
const selectionRange = context.selectionRanges.codeBasedSelections[0].range const selectionRange:
| (typeof context.selectionRanges.codeBasedSelections)[number]['range']
| undefined = context.selectionRanges.codeBasedSelections[0]?.range
const inputRef = useRef<HTMLInputElement>(null) const inputRef = useRef<HTMLInputElement>(null)
const [availableVarInfo, setAvailableVarInfo] = useState< const [availableVarInfo, setAvailableVarInfo] = useState<
ReturnType<typeof findAllPreviousVariables> ReturnType<typeof findAllPreviousVariables>
@ -67,7 +69,7 @@ export function useCalculateKclExpression({
} else { } else {
setIsNewVariableNameUnique(true) setIsNewVariableNameUnique(true)
} }
}, [newVariableName]) }, [programMemory, newVariableName])
useEffect(() => { useEffect(() => {
if (!programMemory || !selectionRange) return if (!programMemory || !selectionRange) return
@ -81,8 +83,8 @@ export function useCalculateKclExpression({
useEffect(() => { useEffect(() => {
const execAstAndSetResult = async () => { const execAstAndSetResult = async () => {
const code = `const __result__ = ${value}` const _code = `const __result__ = ${value}`
const ast = parse(code) const ast = parse(_code)
const _programMem: any = { root: {}, return: null } const _programMem: any = { root: {}, return: null }
availableVarInfo.variables.forEach(({ key, value }) => { availableVarInfo.variables.forEach(({ key, value }) => {
_programMem.root[key] = { type: 'userVal', value, __meta: [] } _programMem.root[key] = { type: 'userVal', value, __meta: [] }
@ -111,7 +113,7 @@ export function useCalculateKclExpression({
setCalcResult('NAN') setCalcResult('NAN')
setValueNode(null) setValueNode(null)
}) })
}, [value, availableVarInfo]) }, [value, availableVarInfo, code, kclManager.programMemory])
return { return {
valueNode, valueNode,

View File

@ -6,10 +6,10 @@
serial-integration = { max-threads = 4 } serial-integration = { max-threads = 4 }
[profile.default] [profile.default]
slow-timeout = { period = "10s", terminate-after = 1 } slow-timeout = { period = "30s", terminate-after = 1 }
[profile.ci] [profile.ci]
slow-timeout = { period = "30s", terminate-after = 5 } slow-timeout = { period = "50s", terminate-after = 5 }
[[profile.default.overrides]] [[profile.default.overrides]]
filter = "test(serial_test_)" filter = "test(serial_test_)"

View File

@ -1854,7 +1854,7 @@ dependencies = [
[[package]] [[package]]
name = "kcl-lib" name = "kcl-lib"
version = "0.1.49" version = "0.1.50"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"approx 0.5.1", "approx 0.5.1",
@ -2004,9 +2004,9 @@ dependencies = [
[[package]] [[package]]
name = "kittycad-modeling-cmds" name = "kittycad-modeling-cmds"
version = "0.2.20" version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93f7904109e445ab3dcfbaa4f0f4396d1df22c701075cdce4a7e491701796af" checksum = "e326955e8f315590a1926c17ff6a6082d3013f472c881aba56d73bfa170cf5b3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono", "chrono",

View File

@ -65,7 +65,7 @@ kittycad = { version = "0.3.0", default-features = false, features = ["js", "req
kittycad-execution-plan = "0.1.5" kittycad-execution-plan = "0.1.5"
kittycad-execution-plan-macros = "0.1.9" kittycad-execution-plan-macros = "0.1.9"
kittycad-execution-plan-traits = "0.1.14" kittycad-execution-plan-traits = "0.1.14"
kittycad-modeling-cmds = "0.2.20" kittycad-modeling-cmds = "0.2.21"
kittycad-modeling-session = "0.1.4" kittycad-modeling-session = "0.1.4"
[[test]] [[test]]

View File

@ -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.1.49" version = "0.1.50"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app" repository = "https://github.com/KittyCAD/modeling-app"

View File

@ -62,7 +62,11 @@ impl StdLibFnArg {
} }
pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> { pub fn get_autocomplete_snippet(&self, index: usize) -> Result<Option<(usize, String)>> {
if self.type_ == "SketchGroup" || self.type_ == "ExtrudeGroup" || self.type_ == "SketchSurface" { if self.type_ == "SketchGroup"
|| self.type_ == "ExtrudeGroup"
|| self.type_ == "SketchSurface"
|| self.type_ == "SketchGroupSet"
{
return Ok(Some((index, format!("${{{}:{}}}", index, "%")))); return Ok(Some((index, format!("${{{}:{}}}", index, "%"))));
} }
get_autocomplete_snippet_from_schema(&self.schema.clone(), index) get_autocomplete_snippet_from_schema(&self.schema.clone(), index)
@ -319,7 +323,12 @@ pub fn get_type_string_from_schema(schema: &schemars::schema::Schema) -> Result<
if let Some(format) = &o.format { if let Some(format) = &o.format {
if format == "uuid" { if format == "uuid" {
return Ok((Primitive::Uuid.to_string(), false)); return Ok((Primitive::Uuid.to_string(), false));
} else if format == "double" || format == "uint" || format == "int64" || format == "uint32" { } else if format == "double"
|| format == "uint"
|| format == "int64"
|| format == "uint32"
|| format == "uint64"
{
return Ok((Primitive::Number.to_string(), false)); return Ok((Primitive::Number.to_string(), false));
} else { } else {
anyhow::bail!("unknown format: {}", format); anyhow::bail!("unknown format: {}", format);
@ -456,7 +465,12 @@ pub fn get_autocomplete_snippet_from_schema(
if let Some(format) = &o.format { if let Some(format) = &o.format {
if format == "uuid" { if format == "uuid" {
return Ok(Some((index, format!(r#"${{{}:"tag_or_edge_fn"}}"#, index)))); return Ok(Some((index, format!(r#"${{{}:"tag_or_edge_fn"}}"#, index))));
} else if format == "double" || format == "uint" || format == "int64" || format == "uint32" { } else if format == "double"
|| format == "uint"
|| format == "int64"
|| format == "uint32"
|| format == "uint64"
{
return Ok(Some((index, format!(r#"${{{}:3.14}}"#, index)))); return Ok(Some((index, format!(r#"${{{}:3.14}}"#, index))));
} else { } else {
anyhow::bail!("unknown format: {}", format); anyhow::bail!("unknown format: {}", format);
@ -610,7 +624,12 @@ pub fn get_autocomplete_string_from_schema(schema: &schemars::schema::Schema) ->
if let Some(format) = &o.format { if let Some(format) = &o.format {
if format == "uuid" { if format == "uuid" {
return Ok(Primitive::Uuid.to_string()); return Ok(Primitive::Uuid.to_string());
} else if format == "double" || format == "uint" || format == "int64" || format == "uint32" { } else if format == "double"
|| format == "uint"
|| format == "int64"
|| format == "uint32"
|| format == "uint64"
{
return Ok(Primitive::Number.to_string()); return Ok(Primitive::Number.to_string());
} else { } else {
anyhow::bail!("unknown format: {}", format); anyhow::bail!("unknown format: {}", format);

View File

@ -191,6 +191,15 @@ pub enum SketchGroupSet {
SketchGroups(Vec<Box<SketchGroup>>), SketchGroups(Vec<Box<SketchGroup>>),
} }
/// A extrude group or a group of extrude groups.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum ExtrudeGroupSet {
ExtrudeGroup(Box<ExtrudeGroup>),
ExtrudeGroups(Vec<Box<ExtrudeGroup>>),
}
/// Data for an imported geometry. /// Data for an imported geometry.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)] #[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
#[ts(export)] #[ts(export)]

View File

@ -7,17 +7,23 @@ use uuid::Uuid;
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, ExtrudeSurface, ExtrudeTransform, GeoMeta, MemoryItem, Path, SketchGroup, SketchSurface}, executor::{
ExtrudeGroup, ExtrudeGroupSet, ExtrudeSurface, ExtrudeTransform, GeoMeta, MemoryItem, Path, SketchGroup,
SketchGroupSet, SketchSurface,
},
std::Args, std::Args,
}; };
/// Extrudes by a given amount. /// Extrudes by a given amount.
pub async fn extrude(args: Args) -> Result<MemoryItem, KclError> { pub async fn extrude(args: Args) -> Result<MemoryItem, KclError> {
let (length, sketch_group) = args.get_number_sketch_group()?; let (length, sketch_group_set) = args.get_number_sketch_group_set()?;
let result = inner_extrude(length, sketch_group, args).await?; let result = inner_extrude(length, sketch_group_set, args).await?;
Ok(MemoryItem::ExtrudeGroup(result)) match result {
ExtrudeGroupSet::ExtrudeGroup(extrude_group) => Ok(MemoryItem::ExtrudeGroup(extrude_group)),
ExtrudeGroupSet::ExtrudeGroups(extrude_groups) => Ok(MemoryItem::ExtrudeGroups { value: extrude_groups }),
}
} }
/// Extrudes by a given amount. /// Extrudes by a given amount.
@ -34,21 +40,33 @@ pub async fn extrude(args: Args) -> Result<MemoryItem, KclError> {
#[stdlib { #[stdlib {
name = "extrude" name = "extrude"
}] }]
async fn inner_extrude(length: f64, sketch_group: Box<SketchGroup>, args: Args) -> Result<Box<ExtrudeGroup>, KclError> { async fn inner_extrude(length: f64, sketch_group_set: SketchGroupSet, args: Args) -> Result<ExtrudeGroupSet, KclError> {
let id = uuid::Uuid::new_v4(); let id = uuid::Uuid::new_v4();
// Extrude the element. // Extrude the element(s).
args.send_modeling_cmd( let sketch_groups = match sketch_group_set {
id, SketchGroupSet::SketchGroup(sketch_group) => vec![sketch_group],
kittycad::types::ModelingCmd::Extrude { SketchGroupSet::SketchGroups(sketch_groups) => sketch_groups,
target: sketch_group.id, };
distance: length, let mut extrude_groups = Vec::new();
cap: true, for sketch_group in sketch_groups.iter() {
}, args.send_modeling_cmd(
) id,
.await?; kittycad::types::ModelingCmd::Extrude {
target: sketch_group.id,
distance: length,
cap: true,
},
)
.await?;
extrude_groups.push(do_post_extrude(sketch_group.clone(), length, id, args.clone()).await?);
}
do_post_extrude(sketch_group, length, id, args).await if extrude_groups.len() == 1 {
Ok(ExtrudeGroupSet::ExtrudeGroup(extrude_groups.pop().unwrap()))
} else {
Ok(ExtrudeGroupSet::ExtrudeGroups(extrude_groups))
}
} }
pub(crate) async fn do_post_extrude( pub(crate) async fn do_post_extrude(

View File

@ -11,6 +11,7 @@ pub mod revolve;
pub mod segment; pub mod segment;
pub mod shapes; pub mod shapes;
pub mod sketch; pub mod sketch;
pub mod types;
pub mod utils; pub mod utils;
use std::collections::HashMap; use std::collections::HashMap;
@ -628,6 +629,49 @@ impl Args {
Ok((data, sketch_group)) Ok((data, sketch_group))
} }
fn get_data_and_sketch_group_set<T: serde::de::DeserializeOwned>(&self) -> Result<(T, SketchGroupSet), KclError> {
let first_value = self
.args
.first()
.ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
source_ranges: vec![self.source_range],
})
})?
.get_json_value()?;
let data: T = serde_json::from_value(first_value).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to deserialize struct from JSON: {}", e),
source_ranges: vec![self.source_range],
})
})?;
let second_value = self.args.get(1).ok_or_else(|| {
KclError::Type(KclErrorDetails {
message: format!("Expected a SketchGroup as the second argument, found `{:?}`", self.args),
source_ranges: vec![self.source_range],
})
})?;
let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
SketchGroupSet::SketchGroup(sg.clone())
} else if let MemoryItem::SketchGroups { value } = second_value {
SketchGroupSet::SketchGroups(value.clone())
} else {
return Err(KclError::Type(KclErrorDetails {
message: format!(
"Expected a SketchGroup or Vector of SketchGroups as the second argument, found `{:?}`",
self.args
),
source_ranges: vec![self.source_range],
}));
};
Ok((data, sketch_set))
}
fn get_data_and_sketch_group_and_tag<T: serde::de::DeserializeOwned>( fn get_data_and_sketch_group_and_tag<T: serde::de::DeserializeOwned>(
&self, &self,
) -> Result<(T, Box<SketchGroup>, Option<String>), KclError> { ) -> Result<(T, Box<SketchGroup>, Option<String>), KclError> {
@ -823,7 +867,7 @@ impl Args {
Ok((segment_name, to_number, sketch_group)) Ok((segment_name, to_number, sketch_group))
} }
fn get_number_sketch_group(&self) -> Result<(f64, Box<SketchGroup>), KclError> { fn get_number_sketch_group_set(&self) -> Result<(f64, SketchGroupSet), KclError> {
// Iterate over our args, the first argument should be a number. // Iterate over our args, the first argument should be a number.
// The second argument should be a SketchGroup. // The second argument should be a SketchGroup.
let first_value = self let first_value = self
@ -846,16 +890,21 @@ impl Args {
}) })
})?; })?;
let sketch_group = if let MemoryItem::SketchGroup(sg) = second_value { let sketch_set = if let MemoryItem::SketchGroup(sg) = second_value {
sg.clone() SketchGroupSet::SketchGroup(sg.clone())
} else if let MemoryItem::SketchGroups { value } = second_value {
SketchGroupSet::SketchGroups(value.clone())
} else { } else {
return Err(KclError::Type(KclErrorDetails { return Err(KclError::Type(KclErrorDetails {
message: format!("Expected a SketchGroup as the second argument, found `{:?}`", self.args), message: format!(
"Expected a SketchGroup or Vector of SketchGroups as the second argument, found `{:?}`",
self.args
),
source_ranges: vec![self.source_range], source_ranges: vec![self.source_range],
})); }));
}; };
Ok((number, sketch_group)) Ok((number, sketch_set))
} }
fn get_path_name_extrude_group(&self) -> Result<(String, Box<ExtrudeGroup>), KclError> { fn get_path_name_extrude_group(&self) -> Result<(String, Box<ExtrudeGroup>), KclError> {

View File

@ -8,8 +8,8 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
errors::{KclError, KclErrorDetails}, errors::{KclError, KclErrorDetails},
executor::{ExtrudeGroup, Geometries, Geometry, MemoryItem, SketchGroup}, executor::{ExtrudeGroup, Geometries, Geometry, MemoryItem, SketchGroup, SketchGroupSet},
std::Args, std::{types::Uint, Args},
}; };
/// Data for a linear pattern on a 2D sketch. /// Data for a linear pattern on a 2D sketch.
@ -20,7 +20,7 @@ pub struct LinearPattern2dData {
/// The number of repetitions. Must be greater than 0. /// The number of repetitions. Must be greater than 0.
/// This excludes the original entity. For example, if `repetitions` is 1, /// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once. /// the original entity will be copied once.
pub repetitions: u32, pub repetitions: Uint,
/// The distance between each repetition. This can also be referred to as spacing. /// The distance between each repetition. This can also be referred to as spacing.
pub distance: f64, pub distance: f64,
/// The axis of the pattern. This is a 2D vector. /// The axis of the pattern. This is a 2D vector.
@ -35,7 +35,7 @@ pub struct LinearPattern3dData {
/// The number of repetitions. Must be greater than 0. /// The number of repetitions. Must be greater than 0.
/// This excludes the original entity. For example, if `repetitions` is 1, /// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once. /// the original entity will be copied once.
pub repetitions: u32, pub repetitions: Uint,
/// The distance between each repetition. This can also be referred to as spacing. /// The distance between each repetition. This can also be referred to as spacing.
pub distance: f64, pub distance: f64,
/// The axis of the pattern. /// The axis of the pattern.
@ -57,8 +57,8 @@ impl LinearPattern {
pub fn repetitions(&self) -> u32 { pub fn repetitions(&self) -> u32 {
match self { match self {
LinearPattern::TwoD(lp) => lp.repetitions, LinearPattern::TwoD(lp) => lp.repetitions.u32(),
LinearPattern::ThreeD(lp) => lp.repetitions, LinearPattern::ThreeD(lp) => lp.repetitions.u32(),
} }
} }
@ -72,7 +72,7 @@ impl LinearPattern {
/// A linear pattern on a 2D sketch. /// A linear pattern on a 2D sketch.
pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> { pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
let (data, sketch_group): (LinearPattern2dData, Box<SketchGroup>) = args.get_data_and_sketch_group()?; let (data, sketch_group_set): (LinearPattern2dData, SketchGroupSet) = args.get_data_and_sketch_group_set()?;
if data.axis == [0.0, 0.0] { if data.axis == [0.0, 0.0] {
return Err(KclError::Semantic(KclErrorDetails { return Err(KclError::Semantic(KclErrorDetails {
@ -83,7 +83,7 @@ pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
})); }));
} }
let sketch_groups = inner_pattern_linear_2d(data, sketch_group, args).await?; let sketch_groups = inner_pattern_linear_2d(data, sketch_group_set, args).await?;
Ok(MemoryItem::SketchGroups { value: sketch_groups }) Ok(MemoryItem::SketchGroups { value: sketch_groups })
} }
@ -99,23 +99,33 @@ pub async fn pattern_linear_2d(args: Args) -> Result<MemoryItem, KclError> {
}] }]
async fn inner_pattern_linear_2d( async fn inner_pattern_linear_2d(
data: LinearPattern2dData, data: LinearPattern2dData,
sketch_group: Box<SketchGroup>, sketch_group_set: SketchGroupSet,
args: Args, args: Args,
) -> Result<Vec<Box<SketchGroup>>, KclError> { ) -> Result<Vec<Box<SketchGroup>>, KclError> {
let geometries = pattern_linear( let starting_sketch_groups = match sketch_group_set {
LinearPattern::TwoD(data), SketchGroupSet::SketchGroup(sketch_group) => vec![sketch_group],
Geometry::SketchGroup(sketch_group), SketchGroupSet::SketchGroups(sketch_groups) => sketch_groups,
args.clone(),
)
.await?;
let Geometries::SketchGroups(sketch_groups) = geometries else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a vec of sketch groups".to_string(),
source_ranges: vec![args.source_range],
}));
}; };
let mut sketch_groups = Vec::new();
for sketch_group in starting_sketch_groups.iter() {
let geometries = pattern_linear(
LinearPattern::TwoD(data.clone()),
Geometry::SketchGroup(sketch_group.clone()),
args.clone(),
)
.await?;
let Geometries::SketchGroups(new_sketch_groups) = geometries else {
return Err(KclError::Semantic(KclErrorDetails {
message: "Expected a vec of sketch groups".to_string(),
source_ranges: vec![args.source_range],
}));
};
sketch_groups.extend(new_sketch_groups);
}
Ok(sketch_groups) Ok(sketch_groups)
} }
@ -175,6 +185,19 @@ async fn inner_pattern_linear_3d(
async fn pattern_linear(data: LinearPattern, geometry: Geometry, args: Args) -> Result<Geometries, KclError> { async fn pattern_linear(data: LinearPattern, geometry: Geometry, args: Args) -> Result<Geometries, KclError> {
let id = uuid::Uuid::new_v4(); let id = uuid::Uuid::new_v4();
println!(
"id: {:#?}",
ModelingCmd::EntityLinearPattern {
axis: kittycad::types::Point3D {
x: data.axis()[0],
y: data.axis()[1],
z: data.axis()[2],
},
entity_id: geometry.id(),
num_repetitions: data.repetitions(),
spacing: data.distance(),
}
);
let resp = args let resp = args
.send_modeling_cmd( .send_modeling_cmd(
@ -234,7 +257,7 @@ pub struct CircularPattern2dData {
/// The number of repetitions. Must be greater than 0. /// The number of repetitions. Must be greater than 0.
/// This excludes the original entity. For example, if `repetitions` is 1, /// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once. /// the original entity will be copied once.
pub repetitions: u32, pub repetitions: Uint,
/// The center about which to make the pattern. This is a 2D vector. /// The center about which to make the pattern. This is a 2D vector.
pub center: [f64; 2], pub center: [f64; 2],
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0. /// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
@ -251,7 +274,7 @@ pub struct CircularPattern3dData {
/// The number of repetitions. Must be greater than 0. /// The number of repetitions. Must be greater than 0.
/// This excludes the original entity. For example, if `repetitions` is 1, /// This excludes the original entity. For example, if `repetitions` is 1,
/// the original entity will be copied once. /// the original entity will be copied once.
pub repetitions: u32, pub repetitions: Uint,
/// The axis around which to make the pattern. This is a 3D vector. /// The axis around which to make the pattern. This is a 3D vector.
pub axis: [f64; 3], pub axis: [f64; 3],
/// The center about which to make the pattern. This is a 3D vector. /// The center about which to make the pattern. This is a 3D vector.
@ -284,8 +307,8 @@ impl CircularPattern {
pub fn repetitions(&self) -> u32 { pub fn repetitions(&self) -> u32 {
match self { match self {
CircularPattern::TwoD(lp) => lp.repetitions, CircularPattern::TwoD(lp) => lp.repetitions.u32(),
CircularPattern::ThreeD(lp) => lp.repetitions, CircularPattern::ThreeD(lp) => lp.repetitions.u32(),
} }
} }

View File

@ -0,0 +1,40 @@
//! Custom types for various standard library types.
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// A Uint that allows us to do math but rounds to a uint.
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize, ts_rs::TS)]
#[ts(export)]
pub struct Uint(f64);
impl Uint {
pub fn new(value: f64) -> Self {
if value < 0.0 {
panic!("Uint cannot be negative");
}
Self(value)
}
pub fn value(&self) -> f64 {
self.0
}
pub fn u32(&self) -> u32 {
self.0.round() as u32
}
pub fn u64(&self) -> u64 {
self.0.round() as u64
}
}
impl JsonSchema for Uint {
fn schema_name() -> String {
"Uint".to_string()
}
fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
gen.subschema_for::<u32>()
}
}

View File

@ -0,0 +1,56 @@
// Lego Brick
const lbumps = 10 // number of bumps long
const wbumps = 6 // number of bumps wide
const pitch = 8.0
const clearance = 0.1
const bumpDiam = 4.8
const bumpHeight = 1.8
const height = 3.2
const t = (pitch - (2 * clearance) - bumpDiam) / 2.0
const postDiam = pitch - t // works out to 6.5
const total_length = lbumps * pitch - (2.0 * clearance)
const total_width = wbumps * pitch - (2.0 * clearance)
const lSegments = total_length / (lbumps + 1)
const wSegments = total_width / (wbumps + 1)
// make the base
const s = startSketchOn('XY')
|> startProfileAt([-total_width / 2, -total_length / 2], %)
|> line([total_width, 0], %)
|> line([0, total_length], %)
|> line([-total_width, 0], %)
|> close(%)
|> extrude(height, %)
const shellExtrude = startSketchOn(s, "start")
|> startProfileAt([
-(total_width / 2 - t),
-(total_length / 2 - t)
], %)
|> line([total_width - (2 * t), 0], %)
|> line([0, total_length - (2 * t)], %)
|> line([-(total_width - (2 * t)), 0], %)
|> close(%)
|> extrude(-(height - t), %)
const peg = startSketchOn(s, "end")
|> circle([
-(total_width / 2 - wSegments),
-(total_length / 2 - lSegments)
], bumpDiam / 2, %)
|> patternLinear2d({
axis: [1, 0],
repetitions: 5,
distance: 7
}, %)
|> patternLinear2d({
axis: [0, 1],
repetitions: 9,
distance: 7
}, %)
|> extrude(bumpHeight, %)

View File

@ -115,6 +115,15 @@ async fn serial_test_riddle_small() {
twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, 0.999); twenty_twenty::assert_image("tests/executor/outputs/riddle_small.png", &result, 0.999);
} }
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_lego() {
let code = include_str!("inputs/lego.kcl");
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/lego.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn serial_test_pentagon_fillet_desugar() { async fn serial_test_pentagon_fillet_desugar() {
let code = include_str!("inputs/pentagon_fillet_desugar.kcl"); let code = include_str!("inputs/pentagon_fillet_desugar.kcl");
@ -930,11 +939,32 @@ async fn serial_test_top_level_expression() {
twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, 0.999); twenty_twenty::assert_image("tests/executor/outputs/top_level_expression.png", &result, 0.999);
} }
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_patterns_linear_basic_with_math() {
let code = r#"const num = 12
const distance = 5
const part = startSketchOn('XY')
|> circle([0,0], 2, %)
|> patternLinear2d({axis: [0,1], repetitions: num -1, distance: distance - 1}, %)
|> extrude(1, %)
"#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
.await
.unwrap();
twenty_twenty::assert_image(
"tests/executor/outputs/patterns_linear_basic_with_math.png",
&result,
0.999,
);
}
#[tokio::test(flavor = "multi_thread")] #[tokio::test(flavor = "multi_thread")]
async fn serial_test_patterns_linear_basic() { async fn serial_test_patterns_linear_basic() {
let code = r#"const part = startSketchOn('XY') let code = r#"const part = startSketchOn('XY')
|> circle([0,0], 2, %) |> circle([0,0], 2, %)
|> patternLinear2d({axis: [0,1], repetitions: 12, distance: 2}, %) |> patternLinear2d({axis: [0,1], repetitions: 12, distance: 4}, %)
|> extrude(1, %)
"#; "#;
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm) let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB