Compare commits

...

11 Commits

Author SHA1 Message Date
df8c17ac18 Release derive-docs 0.1.5 (#1236) 2023-12-19 17:06:18 +00:00
9d40f282a8 Remove just one enum (#1096)
# Problem

This is my proposal for fixing #1107 . I've only done it for one stdlib function, `tangentialArcTo` -- if y'all like it, I'll apply this idea to the rest of the stdlib.

Previously, if users want to put a tag on the arc, the function's parameters change type.

```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is object
tangentialArcTo({to: [x, y], tag: "myTag"}, %)
```

# Solution

My proposal in #1006 is that KCL should have optional values. This means we can change the stdlib `tangentialArcTo` function to use them. In this PR, the calls are now like

```
// Tag missing: first param is array
tangentialArcTo([x, y], %)
// Tag present: first param is array still, but we now pass a tag at the end.
tangentialArcTo([x, y], %, "myTag")
```

This adds an "option" type to KCL typesystem, but it's not really revealed to users (no KCL types are revealed to users right now, they write untyped code and only interact with types when they get type errors upon executing programs). Also adds a None type, which is the default case of the Optional enum.
2023-12-18 23:49:32 -06:00
a61d931826 lint: Remove unnecessary parentheses (#1233)
* lint: Remove unnecessary parentheses

* Fix lints
2023-12-18 23:31:19 -06:00
418350ddbc Bump tauri from 1.5.2 to 1.5.3 in /src-tauri (#1157)
Bumps [tauri](https://github.com/tauri-apps/tauri) from 1.5.2 to 1.5.3.
- [Release notes](https://github.com/tauri-apps/tauri/releases)
- [Commits](https://github.com/tauri-apps/tauri/compare/tauri-v1.5.2...tauri-v1.5.3)

---
updated-dependencies:
- dependency-name: tauri
  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>
2023-12-18 11:08:13 -06:00
d43abe20d9 Bump deps (#1230) 2023-12-18 11:02:58 -06:00
84380f3da9 Zoo rebrand (#1228)
* Add new logomarks

* Replace KittyCAD and KCMA with Zoo and ZMA anywhere it's safe

* fmt

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

* Make README logo a PNG instead of an SVG

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-12-18 06:15:26 -05:00
eea55ff2b1 fix gltf snapshots (#1224)
* fix gltf snapshots

* delete old assets
2023-12-18 03:22:15 +00:00
10b6c1cfbc Snapshot exports (#1223)
* delete old exports

* update test

* tweaks and assets

* install kittycad cli

* fix weird typo

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

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2023-12-18 13:17:09 +11:00
d5570e5c62 debug bug (#1211)
* remove camera_drag_move from debug panel

* remove log
2023-12-17 18:10:31 +00:00
0c9589f7ee Revert "Bump actions/upload-artifact from 3 to 4" (#1220)
Revert "Bump actions/upload-artifact from 3 to 4 (#1214)"

This reverts commit 0825cb5a59.
2023-12-15 07:29:58 -05:00
ddf66c1e0f Revert "Bump actions/download-artifact from 3 to 4" (#1219)
Revert "Bump actions/download-artifact from 3 to 4 (#1213)"

This reverts commit 0b5bb5f77d.
2023-12-15 07:29:13 -05:00
75 changed files with 1481 additions and 4154 deletions

View File

@ -107,7 +107,7 @@ jobs:
echo "$(jq --arg url 'https://dl.kittycad.io/releases/modeling-app/nightly/last_update.json' \
'.tauri.updater.endpoints[]=$url' src-tauri/tauri.release.conf.json --indent 2)" > src-tauri/tauri.release.conf.json
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: github.event_name == 'schedule'
with:
path: |
@ -128,7 +128,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
- name: Copy updated .json files
if: github.event_name == 'schedule'
@ -242,7 +242,7 @@ jobs:
with:
args: "${{ matrix.os == 'macos-latest' && '--target universal-apple-darwin' || '' }} ${{ env.TAURI_CONF_ARGS }}"
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
env:
PREFIX: ${{ matrix.os == 'macos-latest' && 'src-tauri/target/universal-apple-darwin' || 'src-tauri/target' }}
MODE: ${{ env.BUILD_RELEASE == 'true' && 'release' || 'debug' }}
@ -272,7 +272,7 @@ jobs:
NOTES: ${{ github.event_name == 'release' && github.event.release.body || format('Nightly build, commit {0}', github.sha) }}
BUCKET_DIR: ${{ github.event_name == 'release' && 'dl.kittycad.io/releases/modeling-app' || 'dl.kittycad.io/releases/modeling-app/nightly' }}
steps:
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v3
- name: Generate the update static endpoint
run: |

View File

@ -14,6 +14,7 @@ jobs:
with:
node-version-file: '.nvmrc'
cache: 'yarn'
- uses: KittyCAD/action-install-cli@v0.2.16
- name: Install dependencies
run: yarn
- name: Install Playwright Browsers
@ -33,7 +34,8 @@ jobs:
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v4
snapshottoken: ${{ secrets.KITTYCAD_API_TOKEN }}
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
@ -68,7 +70,7 @@ jobs:
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
@ -106,7 +108,7 @@ jobs:
env:
CI: true
token: ${{ secrets.KITTYCAD_API_TOKEN_DEV }}
- uses: actions/upload-artifact@v4
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report

9
.gitignore vendored
View File

@ -37,6 +37,15 @@ src/wasm-lib/lcov.info
e2e/playwright/playwright-secrets.env
e2e/playwright/temp1.png
e2e/playwright/temp2.png
# exports from snapshot-tests.spec.ts
e2e/playwright/export-snapshots/*.ply
e2e/playwright/export-snapshots/*.obj
e2e/playwright/export-snapshots/*.step
e2e/playwright/export-snapshots/*.stl
e2e/playwright/export-snapshots/*binary.gltf
e2e/playwright/export-snapshots/*embedded.gltf
/test-results/
/playwright-report/
/blob-report/

View File

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright (c) 2023 The KittyCAD Authors
Copyright (c) 2023 The Zoo Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,17 +1,17 @@
![KittyCAD Modeling App](/public/kcma-logomark-outlined.png)
![Zoo Modeling App](/public/zma-logomark-outlined.png)
## KittyCAD Modeling App
## Zoo Modeling App
live at [app.kittycad.io](https://app.kittycad.io/)
live at [app.zoo.dev](https://app.zoo.dev/)
A CAD application from the future, brought to you by the [KittyCAD team](https://kittycad.io).
A CAD application from the future, brought to you by the [Zoo team](https://zoo.dev).
The KittyCAD modeling app is our take on what a modern modelling experience can be. It is applying several lessons learned in the decades since most major CAD tools came into existence:
Modeling App is our take on what a modern modelling experience can be. It is applying several lessons learned in the decades since most major CAD tools came into existence:
- All artifacts—including parts and assemblies—should be represented as human-readable code. At the end of the day, your CAD project should be "plain text"
- This makes version control—which is a solved problem in software engineering—trivial for CAD
- All GUI (or point-and-click) interactions should be actions performed on this code representation under the hood
- This unlocks a hybrid approach to modeling. Whether you point-and-click as you always have or you write your own KCL code, you are performing the same action in KittyCAD Modeling App
- This unlocks a hybrid approach to modeling. Whether you point-and-click as you always have or you write your own KCL code, you are performing the same action in Modeling App
- Everything graphics _has_ to be built for the GPU
- Most CAD applications have had to retrofit support for GPUs, but our geometry engine is made for GPUs (primarily Nvidia's Vulkan), getting the order of magnitude rendering performance boost with it
- Make the resource-intensive pieces of an application auto-scaling
@ -19,9 +19,9 @@ The KittyCAD modeling app is our take on what a modern modelling experience can
We are excited about what a small team of people could build in a short time with our API. We welcome you to try our API, build your own applications, or contribute to ours!
KittyCAD Modeling App is a _hybrid_ user interface for CAD modeling. You can point-and-click to design parts (and soon assemblies), but everything you make is really just [`kcl` code](https://github.com/KittyCAD/kcl-experiments) under the hood. All of your CAD models can be checked into source control such as GitHub and responsibly versioned, rolled back, and more.
Modeling App is a _hybrid_ user interface for CAD modeling. You can point-and-click to design parts (and soon assemblies), but everything you make is really just [`kcl` code](https://github.com/KittyCAD/kcl-experiments) under the hood. All of your CAD models can be checked into source control such as GitHub and responsibly versioned, rolled back, and more.
The 3D view in KittyCAD Modeling App is just a video stream from our hosted geometry engine. The app sends new modeling commands to the engine via WebSockets, which returns back video frames of the view within the engine.
The 3D view in Modeling App is just a video stream from our hosted geometry engine. The app sends new modeling commands to the engine via WebSockets, which returns back video frames of the view within the engine.
## Tools
@ -183,9 +183,9 @@ For more information on fuzzing you can check out
First time running plawright locally, you'll need to add the secrets file
```bash
touch ./e2e/playwright/playwright-secrets.env
echo 'token="your-token"' > ./e2e/playwright/playwright-secrets.env
echo 'token="your-token"\nsnapshottoken="your-snapshot-token"' > ./e2e/playwright/playwright-secrets2.env
```
then replace "your-token" with a dev token from dev.kittycad.io/account/api-tokens
then replace "your-token" with a dev token from dev.zoo.dev/account/api-tokens
then:
run playwright

View File

@ -19765,46 +19765,16 @@
"tags": [],
"args": [
{
"name": "data",
"type": "TangentialArcToData",
"name": "to",
"type": "[number]",
"schema": {
"description": "Data to draw a tangential arc to a specific point.",
"anyOf": [
{
"description": "A point with a tag.",
"type": "object",
"required": [
"tag",
"to"
],
"properties": {
"tag": {
"description": "The tag.",
"type": "string"
},
"to": {
"description": "Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
}
}
},
{
"description": "A point where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.",
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
}
]
"type": "array",
"items": {
"type": "number",
"format": "double"
},
"maxItems": 2,
"minItems": 2
},
"required": true
},
@ -20246,6 +20216,15 @@
}
},
"required": true
},
{
"name": "tag",
"type": "String",
"schema": {
"type": "string",
"nullable": true
},
"required": true
}
],
"returnValue": {

View File

@ -3905,21 +3905,12 @@ Draw an arc.
```
tangentialArcTo(data: TangentialArcToData, sketch_group: SketchGroup) -> SketchGroup
tangentialArcTo(to: [number], sketch_group: SketchGroup, tag: String) -> SketchGroup
```
#### Arguments
* `data`: `TangentialArcToData` - Data to draw a tangential arc to a specific point.
```
{
// The tag.
tag: string,
// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [number, number],
} |
[number, number]
```
* `to`: `[number]`
* `sketch_group`: `SketchGroup` - A sketch group is a collection of paths.
```
{
@ -3985,6 +3976,7 @@ tangentialArcTo(data: TangentialArcToData, sketch_group: SketchGroup) -> SketchG
}],
}
```
* `tag`: `String`
#### Returns

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

File diff suppressed because it is too large Load Diff

View File

@ -1,189 +0,0 @@
v 0 -4 0
v 0 0 0
v 0 -4 -1
v 0 0 -1
v 3.0950184 -4 -1
v 3.0950184 0 -1
v 5.9513144 -4 -3
v 5.9513144 0 -3
v 9.5 -4 -3
v 9.5 0 -3
v 9.5 -4 -2.5
v 9.5 0 -2.5
v 6.108964 -4 -2.5
v 6.108964 0 -2.5
v 3.4311862 -4 -0.625
v 4.323779 -4 -1.25
v 4.323779 -0 -1.25
v 3.4311862 -0 -0.625
v 2.5385938 0 0
v 2.5385938 -4 0
v 3.342784 -4 0.375
v 4.146974 -4 0.75
v 3.342784 -0 0.375
v 4.146974 -0 0.75
v 5.755354 0 1.5
v 5.755354 -4 1.5
v 9.5 -4 1.5
v 9.5 0 1.5
v 9.5 -4 2
v 9.5 0 2
v 5.644507 -4 2
v 5.644507 0 2
v 3.5 -4 1
v 3.5 0 1
v 0 -4 1
v 0 0 1
vt 0.0127 -0.0508
vt 0.0127 0.0508
vt -0.0127 -0.0508
vt -0.0127 0.0508
vt -0.039306734 0.0508
vt -0.039306734 -0.0508
vt 0.039306734 0.0508
vt 0.039306734 -0.0508
vt -0.04428355 0.0508
vt -0.04428355 -0.0508
vt 0.04428355 0.0508
vt 0.04428355 -0.0508
vt -0.045068305 0.0508
vt -0.045068305 -0.0508
vt 0.045068305 0.0508
vt 0.045068305 -0.0508
vt -0.00635 0.0508
vt -0.00635 -0.0508
vt 0.00635 0.0508
vt 0.00635 -0.0508
vt 0.04306616 -0.0508
vt 0.04306616 0.0508
vt -0.04306616 -0.0508
vt -0.04306616 0.0508
vt -0.027677217 -0.0508
vt 0.000000000000000048572257 -0.0508
vt 0.000000000000000048572257 0.0508
vt 0.055354435 -0.0508
vt 0.055354435 0.0508
vt -0.027677217 0.0508
vt -0.055354435 0.0508
vt -0.055354435 -0.0508
vt -0.02253807 0.0508
vt -0.04507614 0.0508
vt -0.04507614 -0.0508
vt 0.00000000000000005551115 0.0508
vt -0.02253807 -0.0508
vt 0.00000000000000005551115 -0.0508
vt 0.04507614 -0.0508
vt 0.04507614 0.0508
vt -0.047557 0.0508
vt -0.047557 -0.0508
vt 0.047557 0.0508
vt 0.047557 -0.0508
vt 0.04896476 -0.0508
vt 0.04896476 0.0508
vt -0.04896476 -0.0508
vt -0.04896476 0.0508
vt 0.03005076 -0.0508
vt 0.03005076 0.0508
vt -0.03005076 -0.0508
vt -0.03005076 0.0508
vt 0.04445 -0.0508
vt 0.04445 0.0508
vt -0.04445 -0.0508
vt -0.04445 0.0508
vt 0.08490671 0.009525
vt 0.06448028 0
vt 0.0889 0.0254
vt 0.08715213 -0.015875
vt 0.10982399 -0.03175
vt 0.07861347 -0.0254
vt 0.10533314 0.01905
vt 0.15116338 -0.0762
vt 0 -0.0254
vt 0 0
vt 0.2413 -0.0762
vt 0.15516768 -0.0635
vt 0.2413 -0.0635
vt 0.14337048 0.0508
vt 0.146186 0.0381
vt 0.2413 0.0381
vt 0.2413 0.0508
vt 0 0.0254
vn -1 -0 0
vn 0 -0 -1
vn -0.57357645 -0 -0.81915206
vn 1 -0 0
vn 0 -0 1
vn 0.57357645 -0 0.81915206
vn 0.42261827 -0 -0.9063078
vn -0.42261827 -0 0.9063078
vn -0 1 -0
vn 0 -1 0
o Unnamed-0
f 1/1/1 2/2/1 3/3/1
f 3/3/1 2/2/1 4/4/1
f 3/5/2 4/6/2 5/7/2
f 5/7/2 4/6/2 6/8/2
f 5/9/3 6/10/3 7/11/3
f 7/11/3 6/10/3 8/12/3
f 7/13/2 8/14/2 9/15/2
f 9/15/2 8/14/2 10/16/2
f 9/17/4 10/18/4 11/19/4
f 11/19/4 10/18/4 12/20/4
f 11/21/5 12/22/5 13/23/5
f 13/23/5 12/22/5 14/24/5
f 15/25/6 16/26/6 17/27/6
f 16/26/6 13/28/6 14/29/6
f 18/30/6 19/31/6 20/32/6
f 15/25/6 18/30/6 20/32/6
f 16/26/6 14/29/6 17/27/6
f 18/30/6 15/25/6 17/27/6
f 21/33/7 20/34/7 19/35/7
f 22/36/7 21/33/7 23/37/7
f 23/37/7 24/38/7 22/36/7
f 24/38/7 25/39/7 26/40/7
f 21/33/7 19/35/7 23/37/7
f 26/40/7 22/36/7 24/38/7
f 26/41/2 25/42/2 27/43/2
f 27/43/2 25/42/2 28/44/2
f 27/17/4 28/18/4 29/19/4
f 29/19/4 28/18/4 30/20/4
f 29/45/5 30/46/5 31/47/5
f 31/47/5 30/46/5 32/48/5
f 31/49/8 32/50/8 33/51/8
f 33/51/8 32/50/8 34/52/8
f 33/53/5 34/54/5 35/55/5
f 35/55/5 34/54/5 36/56/5
f 35/1/1 36/2/1 1/3/1
f 1/3/1 36/2/1 2/4/1
f 23/57/9 19/58/9 34/59/9
f 18/60/9 17/61/9 6/62/9
f 23/57/9 34/59/9 24/63/9
f 17/61/9 8/64/9 6/62/9
f 4/65/9 19/58/9 6/62/9
f 4/65/9 2/66/9 19/58/9
f 10/67/9 14/68/9 12/69/9
f 10/67/9 8/64/9 14/68/9
f 8/64/9 17/61/9 14/68/9
f 32/70/9 25/71/9 24/63/9
f 6/62/9 19/58/9 18/60/9
f 24/63/9 34/59/9 32/70/9
f 28/72/9 25/71/9 30/73/9
f 25/71/9 32/70/9 30/73/9
f 19/58/9 2/66/9 36/74/9
f 34/59/9 19/58/9 36/74/9
f 21/57/10 33/59/10 20/58/10
f 22/63/10 33/59/10 21/57/10
f 15/60/10 5/62/10 16/61/10
f 22/63/10 26/71/10 31/70/10
f 35/74/10 20/58/10 33/59/10
f 35/74/10 1/66/10 20/58/10
f 31/70/10 26/71/10 29/73/10
f 29/73/10 26/71/10 27/72/10
f 22/63/10 31/70/10 33/59/10
f 20/58/10 5/62/10 15/60/10
f 16/61/10 5/62/10 7/64/10
f 13/68/10 16/61/10 7/64/10
f 11/69/10 13/68/10 9/67/10
f 13/68/10 7/64/10 9/67/10
f 20/58/10 3/65/10 5/62/10
f 3/65/10 20/58/10 1/66/10

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -1,282 +0,0 @@
ply
format ascii 1.0
comment Generated by kittycad.io
element vertex 204
property float x
property float y
property float z
element face 68
property list uchar uint vertex_indices
end_header
0 0 4
0 0 0
0 -1 4
0 -1 4
0 0 0
0 -1 0
0 -1 4
0 -1 0
3.0950184 -1 4
3.0950184 -1 4
0 -1 0
3.0950184 -1 0
3.0950184 -1 4
3.0950184 -1 0
5.9513144 -3 4
5.9513144 -3 4
3.0950184 -1 0
5.9513144 -3 0
5.9513144 -3 4
5.9513144 -3 0
9.5 -3 4
9.5 -3 4
5.9513144 -3 0
9.5 -3 0
9.5 -3 4
9.5 -3 0
9.5 -2.5 4
9.5 -2.5 4
9.5 -3 0
9.5 -2.5 0
9.5 -2.5 4
9.5 -2.5 0
6.108964 -2.5 4
6.108964 -2.5 4
9.5 -2.5 0
6.108964 -2.5 0
3.4311862 -0.625 4
4.323779 -1.25 4
4.323779 -1.25 0
4.323779 -1.25 4
6.108964 -2.5 4
6.108964 -2.5 0
3.4311862 -0.625 0
2.5385938 0 0
2.5385938 0 4
3.4311862 -0.625 4
3.4311862 -0.625 0
2.5385938 0 4
4.323779 -1.25 4
6.108964 -2.5 0
4.323779 -1.25 0
3.4311862 -0.625 0
3.4311862 -0.625 4
4.323779 -1.25 0
3.342784 0.375 4
2.5385938 0 4
2.5385938 0 0
4.146974 0.75 4
3.342784 0.375 4
3.342784 0.375 0
3.342784 0.375 0
4.146974 0.75 0
4.146974 0.75 4
4.146974 0.75 0
5.755354 1.5 0
5.755354 1.5 4
3.342784 0.375 4
2.5385938 0 0
3.342784 0.375 0
5.755354 1.5 4
4.146974 0.75 4
4.146974 0.75 0
5.755354 1.5 4
5.755354 1.5 0
9.5 1.5 4
9.5 1.5 4
5.755354 1.5 0
9.5 1.5 0
9.5 1.5 4
9.5 1.5 0
9.5 2 4
9.5 2 4
9.5 1.5 0
9.5 2 0
9.5 2 4
9.5 2 0
5.644507 2 4
5.644507 2 4
9.5 2 0
5.644507 2 0
5.644507 2 4
5.644507 2 0
3.5 1 4
3.5 1 4
5.644507 2 0
3.5 1 0
3.5 1 4
3.5 1 0
0 1 4
0 1 4
3.5 1 0
0 1 0
0 1 4
0 1 0
0 0 4
0 0 4
0 1 0
0 0 0
3.342784 0.375 0
2.5385938 0 0
3.5 1 0
3.4311862 -0.625 0
4.323779 -1.25 0
3.0950184 -1 0
3.342784 0.375 0
3.5 1 0
4.146974 0.75 0
4.323779 -1.25 0
5.9513144 -3 0
3.0950184 -1 0
0 -1 0
2.5385938 0 0
3.0950184 -1 0
0 -1 0
0 0 0
2.5385938 0 0
9.5 -3 0
6.108964 -2.5 0
9.5 -2.5 0
9.5 -3 0
5.9513144 -3 0
6.108964 -2.5 0
5.9513144 -3 0
4.323779 -1.25 0
6.108964 -2.5 0
5.644507 2 0
5.755354 1.5 0
4.146974 0.75 0
3.0950184 -1 0
2.5385938 0 0
3.4311862 -0.625 0
4.146974 0.75 0
3.5 1 0
5.644507 2 0
9.5 1.5 0
5.755354 1.5 0
9.5 2 0
5.755354 1.5 0
5.644507 2 0
9.5 2 0
2.5385938 0 0
0 0 0
0 1 0
3.5 1 0
2.5385938 0 0
0 1 0
3.342784 0.375 4
3.5 1 4
2.5385938 0 4
4.146974 0.75 4
3.5 1 4
3.342784 0.375 4
3.4311862 -0.625 4
3.0950184 -1 4
4.323779 -1.25 4
4.146974 0.75 4
5.755354 1.5 4
5.644507 2 4
0 1 4
2.5385938 0 4
3.5 1 4
0 1 4
0 0 4
2.5385938 0 4
5.644507 2 4
5.755354 1.5 4
9.5 2 4
9.5 2 4
5.755354 1.5 4
9.5 1.5 4
4.146974 0.75 4
5.644507 2 4
3.5 1 4
2.5385938 0 4
3.0950184 -1 4
3.4311862 -0.625 4
4.323779 -1.25 4
3.0950184 -1 4
5.9513144 -3 4
6.108964 -2.5 4
4.323779 -1.25 4
5.9513144 -3 4
9.5 -2.5 4
6.108964 -2.5 4
9.5 -3 4
6.108964 -2.5 4
5.9513144 -3 4
9.5 -3 4
2.5385938 0 4
0 -1 4
3.0950184 -1 4
0 -1 4
2.5385938 0 4
0 0 4
3 0 1 2
3 3 4 5
3 6 7 8
3 9 10 11
3 12 13 14
3 15 16 17
3 18 19 20
3 21 22 23
3 24 25 26
3 27 28 29
3 30 31 32
3 33 34 35
3 36 37 38
3 39 40 41
3 42 43 44
3 45 46 47
3 48 49 50
3 51 52 53
3 54 55 56
3 57 58 59
3 60 61 62
3 63 64 65
3 66 67 68
3 69 70 71
3 72 73 74
3 75 76 77
3 78 79 80
3 81 82 83
3 84 85 86
3 87 88 89
3 90 91 92
3 93 94 95
3 96 97 98
3 99 100 101
3 102 103 104
3 105 106 107
3 108 109 110
3 111 112 113
3 114 115 116
3 117 118 119
3 120 121 122
3 123 124 125
3 126 127 128
3 129 130 131
3 132 133 134
3 135 136 137
3 138 139 140
3 141 142 143
3 144 145 146
3 147 148 149
3 150 151 152
3 153 154 155
3 156 157 158
3 159 160 161
3 162 163 164
3 165 166 167
3 168 169 170
3 171 172 173
3 174 175 176
3 177 178 179
3 180 181 182
3 183 184 185
3 186 187 188
3 189 190 191
3 192 193 194
3 195 196 197
3 198 199 200
3 201 202 203

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View File

@ -74,171 +74,171 @@ DATA;
#58 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
#59 = VERTEX_POINT('NONE', #58);
#60 = DIRECTION('NONE', (0, -1, 0));
#61 = VECTOR('NONE', #60, 0.0254);
#61 = VECTOR('NONE', #60, 1);
#62 = CARTESIAN_POINT('NONE', (0, 0, -0));
#63 = LINE('NONE', #62, #61);
#64 = DIRECTION('NONE', (0, 0, 1));
#65 = VECTOR('NONE', #64, 0.1016);
#65 = VECTOR('NONE', #64, 1);
#66 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
#67 = LINE('NONE', #66, #65);
#68 = DIRECTION('NONE', (0, -1, 0));
#69 = VECTOR('NONE', #68, 0.0254);
#69 = VECTOR('NONE', #68, 1);
#70 = CARTESIAN_POINT('NONE', (0, 0, 0.1016));
#71 = LINE('NONE', #70, #69);
#72 = DIRECTION('NONE', (0, 0, 1));
#73 = VECTOR('NONE', #72, 0.1016);
#73 = VECTOR('NONE', #72, 1);
#74 = CARTESIAN_POINT('NONE', (0, 0, -0));
#75 = LINE('NONE', #74, #73);
#76 = DIRECTION('NONE', (1, 0, 0));
#77 = VECTOR('NONE', #76, 0.07861346939195568);
#77 = VECTOR('NONE', #76, 1);
#78 = CARTESIAN_POINT('NONE', (0, -0.0254, -0));
#79 = LINE('NONE', #78, #77);
#80 = DIRECTION('NONE', (0, 0, 1));
#81 = VECTOR('NONE', #80, 0.1016);
#81 = VECTOR('NONE', #80, 1);
#82 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
#83 = LINE('NONE', #82, #81);
#84 = DIRECTION('NONE', (1, 0, 0));
#85 = VECTOR('NONE', #84, 0.07861346939195568);
#85 = VECTOR('NONE', #84, 1);
#86 = CARTESIAN_POINT('NONE', (0, -0.0254, 0.1016));
#87 = LINE('NONE', #86, #85);
#88 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
#89 = VECTOR('NONE', #88, 0.08856709721755177);
#89 = VECTOR('NONE', #88, 1);
#90 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, -0));
#91 = LINE('NONE', #90, #89);
#92 = DIRECTION('NONE', (0, 0, 1));
#93 = VECTOR('NONE', #92, 0.1016);
#93 = VECTOR('NONE', #92, 1);
#94 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
#95 = LINE('NONE', #94, #93);
#96 = DIRECTION('NONE', (0.8191520442889919, -0.5735764363510459, 0));
#97 = VECTOR('NONE', #96, 0.08856709721755177);
#97 = VECTOR('NONE', #96, 1);
#98 = CARTESIAN_POINT('NONE', (0.07861346939195568, -0.0254, 0.1016));
#99 = LINE('NONE', #98, #97);
#100 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
#101 = VECTOR('NONE', #100, 0.09013661186554489);
#101 = VECTOR('NONE', #100, 1);
#102 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, -0));
#103 = LINE('NONE', #102, #101);
#104 = DIRECTION('NONE', (0, 0, 1));
#105 = VECTOR('NONE', #104, 0.1016);
#105 = VECTOR('NONE', #104, 1);
#106 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
#107 = LINE('NONE', #106, #105);
#108 = DIRECTION('NONE', (1, -0.0000000000000003079278779307945, 0));
#109 = VECTOR('NONE', #108, 0.09013661186554489);
#109 = VECTOR('NONE', #108, 1);
#110 = CARTESIAN_POINT('NONE', (0.1511633881344551, -0.07619999999999998, 0.1016));
#111 = LINE('NONE', #110, #109);
#112 = DIRECTION('NONE', (0, 1, 0));
#113 = VECTOR('NONE', #112, 0.012700000000000003);
#113 = VECTOR('NONE', #112, 1);
#114 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, -0));
#115 = LINE('NONE', #114, #113);
#116 = DIRECTION('NONE', (0, 0, 1));
#117 = VECTOR('NONE', #116, 0.1016);
#117 = VECTOR('NONE', #116, 1);
#118 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
#119 = LINE('NONE', #118, #117);
#120 = DIRECTION('NONE', (0, 1, 0));
#121 = VECTOR('NONE', #120, 0.012700000000000003);
#121 = VECTOR('NONE', #120, 1);
#122 = CARTESIAN_POINT('NONE', (0.2413, -0.0762, 0.1016));
#123 = LINE('NONE', #122, #121);
#124 = DIRECTION('NONE', (-1, 0, 0));
#125 = VECTOR('NONE', #124, 0.08613231724678178);
#125 = VECTOR('NONE', #124, 1);
#126 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, -0));
#127 = LINE('NONE', #126, #125);
#128 = DIRECTION('NONE', (0, 0, 1));
#129 = VECTOR('NONE', #128, 0.1016);
#129 = VECTOR('NONE', #128, 1);
#130 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
#131 = LINE('NONE', #130, #129);
#132 = DIRECTION('NONE', (-1, 0, 0));
#133 = VECTOR('NONE', #132, 0.08613231724678178);
#133 = VECTOR('NONE', #132, 1);
#134 = CARTESIAN_POINT('NONE', (0.2413, -0.0635, 0.1016));
#135 = LINE('NONE', #134, #133);
#136 = DIRECTION('NONE', (-0.8191520442889918, 0.573576436351046, 0));
#137 = VECTOR('NONE', #136, 0.11070887152193974);
#136 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
#137 = VECTOR('NONE', #136, 1);
#138 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, -0));
#139 = LINE('NONE', #138, #137);
#140 = DIRECTION('NONE', (0, 0, 1));
#141 = VECTOR('NONE', #140, 0.1016);
#141 = VECTOR('NONE', #140, 1);
#142 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
#143 = LINE('NONE', #142, #141);
#144 = DIRECTION('NONE', (-0.8191520442889918, 0.573576436351046, 0));
#145 = VECTOR('NONE', #144, 0.11070887152193974);
#144 = DIRECTION('NONE', (-0.8191520442889919, 0.573576436351046, 0));
#145 = VECTOR('NONE', #144, 1);
#146 = CARTESIAN_POINT('NONE', (0.1551676827532182, -0.0635, 0.1016));
#147 = LINE('NONE', #146, #145);
#148 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
#149 = VECTOR('NONE', #148, 0.09015228031811025);
#149 = VECTOR('NONE', #148, 1);
#150 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, -0));
#151 = LINE('NONE', #150, #149);
#152 = DIRECTION('NONE', (0, 0, 1));
#153 = VECTOR('NONE', #152, 0.1016);
#153 = VECTOR('NONE', #152, 1);
#154 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
#155 = LINE('NONE', #154, #153);
#156 = DIRECTION('NONE', (0.90630778703665, 0.4226182617406993, 0));
#157 = VECTOR('NONE', #156, 0.09015228031811025);
#157 = VECTOR('NONE', #156, 1);
#158 = CARTESIAN_POINT('NONE', (0.06448028432509392, 0, 0.1016));
#159 = LINE('NONE', #158, #157);
#160 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
#161 = VECTOR('NONE', #160, 0.09511400200349182);
#161 = VECTOR('NONE', #160, 1);
#162 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, -0));
#163 = LINE('NONE', #162, #161);
#164 = DIRECTION('NONE', (0, 0, 1));
#165 = VECTOR('NONE', #164, 0.1016);
#165 = VECTOR('NONE', #164, 1);
#166 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
#167 = LINE('NONE', #166, #165);
#168 = DIRECTION('NONE', (1, -0.00000000000000007295344279228718, 0));
#169 = VECTOR('NONE', #168, 0.09511400200349182);
#169 = VECTOR('NONE', #168, 1);
#170 = CARTESIAN_POINT('NONE', (0.14618599799650817, 0.03810000000000001, 0.1016));
#171 = LINE('NONE', #170, #169);
#172 = DIRECTION('NONE', (0, 1, 0));
#173 = VECTOR('NONE', #172, 0.012699999999999996);
#173 = VECTOR('NONE', #172, 1);
#174 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, -0));
#175 = LINE('NONE', #174, #173);
#176 = DIRECTION('NONE', (0, 0, 1));
#177 = VECTOR('NONE', #176, 0.1016);
#177 = VECTOR('NONE', #176, 1);
#178 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
#179 = LINE('NONE', #178, #177);
#180 = DIRECTION('NONE', (0, 1, 0));
#181 = VECTOR('NONE', #180, 0.012699999999999996);
#181 = VECTOR('NONE', #180, 1);
#182 = CARTESIAN_POINT('NONE', (0.2413, 0.0381, 0.1016));
#183 = LINE('NONE', #182, #181);
#184 = DIRECTION('NONE', (-1, 0, 0));
#185 = VECTOR('NONE', #184, 0.0979295242190572);
#185 = VECTOR('NONE', #184, 1);
#186 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, -0));
#187 = LINE('NONE', #186, #185);
#188 = DIRECTION('NONE', (0, 0, 1));
#189 = VECTOR('NONE', #188, 0.1016);
#189 = VECTOR('NONE', #188, 1);
#190 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
#191 = LINE('NONE', #190, #189);
#192 = DIRECTION('NONE', (-1, 0, 0));
#193 = VECTOR('NONE', #192, 0.0979295242190572);
#193 = VECTOR('NONE', #192, 1);
#194 = CARTESIAN_POINT('NONE', (0.2413, 0.0508, 0.1016));
#195 = LINE('NONE', #194, #193);
#196 = DIRECTION('NONE', (-0.9063077870366499, -0.42261826174069944, 0));
#197 = VECTOR('NONE', #196, 0.06010152021207346);
#196 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
#197 = VECTOR('NONE', #196, 1);
#198 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, -0));
#199 = LINE('NONE', #198, #197);
#200 = DIRECTION('NONE', (0, 0, 1));
#201 = VECTOR('NONE', #200, 0.1016);
#201 = VECTOR('NONE', #200, 1);
#202 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
#203 = LINE('NONE', #202, #201);
#204 = DIRECTION('NONE', (-0.9063077870366499, -0.42261826174069944, 0));
#205 = VECTOR('NONE', #204, 0.06010152021207346);
#204 = DIRECTION('NONE', (-0.90630778703665, -0.42261826174069944, 0));
#205 = VECTOR('NONE', #204, 1);
#206 = CARTESIAN_POINT('NONE', (0.14337047578094278, 0.0508, 0.1016));
#207 = LINE('NONE', #206, #205);
#208 = DIRECTION('NONE', (-1, 0, 0));
#209 = VECTOR('NONE', #208, 0.08889999999999999);
#209 = VECTOR('NONE', #208, 1);
#210 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, -0));
#211 = LINE('NONE', #210, #209);
#212 = DIRECTION('NONE', (0, 0, 1));
#213 = VECTOR('NONE', #212, 0.1016);
#213 = VECTOR('NONE', #212, 1);
#214 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
#215 = LINE('NONE', #214, #213);
#216 = DIRECTION('NONE', (-1, 0, 0));
#217 = VECTOR('NONE', #216, 0.08889999999999999);
#217 = VECTOR('NONE', #216, 1);
#218 = CARTESIAN_POINT('NONE', (0.08889999999999999, 0.0254, 0.1016));
#219 = LINE('NONE', #218, #217);
#220 = DIRECTION('NONE', (0, -1, 0));
#221 = VECTOR('NONE', #220, 0.0254);
#221 = VECTOR('NONE', #220, 1);
#222 = CARTESIAN_POINT('NONE', (0, 0.0254, -0));
#223 = LINE('NONE', #222, #221);
#224 = DIRECTION('NONE', (0, -1, 0));
#225 = VECTOR('NONE', #224, 0.0254);
#225 = VECTOR('NONE', #224, 1);
#226 = CARTESIAN_POINT('NONE', (0, 0.0254, 0.1016));
#227 = LINE('NONE', #226, #225);
#228 = EDGE_CURVE('NONE', #5, #7, #63, .T.);

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -14,6 +14,7 @@ try {
} catch (err) {
// probably running in CI
secrets.token = process.env.token || ''
secrets.snapshottoken = process.env.snapshottoken || ''
// add more env vars here to make them available in CI
}

View File

@ -5,6 +5,8 @@ import { v4 as uuidv4 } from 'uuid'
import { getUtils } from './test-utils'
import { Models } from '@kittycad/lib'
import fsp from 'fs/promises'
import { spawn } from 'child_process'
import { APP_NAME } from 'lib/constants'
test.beforeEach(async ({ context, page }) => {
await context.addInitScript(async (token) => {
@ -137,6 +139,7 @@ test('change camera, show planes', async ({ page, context }) => {
test('exports of each format should work', async ({ page, context }) => {
// FYI this test doesn't work with only engine running locally
// And you will need to have the KittyCAD CLI installed
const u = getUtils(page)
await context.addInitScript(async () => {
;(window as any).playwrightSkipFilePicker = true
@ -194,9 +197,16 @@ const part001 = startSketchOn('-XZ')
await page.waitForTimeout(1000)
await u.clearAndCloseDebugPanel()
await page.getByRole('button', { name: 'KittyCAD Modeling App' }).click()
await page.getByRole('button', { name: APP_NAME }).click()
const doExport = async (output: Models['OutputFormat_type']) => {
interface Paths {
modelPath: string
imagePath: string
outputType: string
}
const doExport = async (
output: Models['OutputFormat_type']
): Promise<Paths> => {
await page.getByRole('button', { name: 'Export Model' }).click()
const exportSelect = page.getByTestId('export-type')
@ -210,10 +220,10 @@ const part001 = startSketchOn('-XZ')
const downloadPromise = page.waitForEvent('download')
await page.getByRole('button', { name: 'Export', exact: true }).click()
const download = await downloadPromise
const downloadLocationer = (extra = '') =>
const downloadLocationer = (extra = '', isImage = false) =>
`./e2e/playwright/export-snapshots/${output.type}-${
'storage' in output ? output.storage : ''
}${extra}.${output.type}`
}${extra}.${isImage ? 'png' : output.type}`
const downloadLocation = downloadLocationer()
const downloadLocation2 = downloadLocationer('-2')
@ -249,6 +259,11 @@ const part001 = startSketchOn('-XZ')
)
await fsp.writeFile(downloadLocation, newFileContents)
}
return {
modelPath: downloadLocation,
imagePath: downloadLocationer('', true),
outputType: output.type,
}
}
const axisDirectionPair: Models['AxisDirectionPair_type'] = {
axis: 'z',
@ -258,67 +273,116 @@ const part001 = startSketchOn('-XZ')
forward: axisDirectionPair,
up: axisDirectionPair,
}
const exportLocations: Paths[] = []
// NOTE it was easiest to leverage existing types and have doExport take Models['OutputFormat_type'] as in input
// just note that only `type` and `storage` are used for selecting the drop downs is the app
// the rest are only there to make typescript happy
await doExport({
type: 'step',
coords: sysType,
})
await doExport({
type: 'gltf',
storage: 'embedded',
presentation: 'pretty',
})
await doExport({
type: 'gltf',
storage: 'binary',
presentation: 'pretty',
})
exportLocations.push(
await doExport({
type: 'step',
coords: sysType,
})
)
exportLocations.push(
await doExport({
type: 'ply',
coords: sysType,
selection: { type: 'default_scene' },
storage: 'ascii',
units: 'in',
})
)
exportLocations.push(
await doExport({
type: 'ply',
storage: 'binary_little_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
)
exportLocations.push(
await doExport({
type: 'ply',
storage: 'binary_big_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
)
exportLocations.push(
await doExport({
type: 'stl',
storage: 'ascii',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
)
exportLocations.push(
await doExport({
type: 'stl',
storage: 'binary',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
)
exportLocations.push(
await doExport({
// obj seems to be a little flaky, times out tests sometimes
type: 'obj',
coords: sysType,
units: 'in',
})
)
exportLocations.push(
await doExport({
type: 'gltf',
storage: 'embedded',
presentation: 'pretty',
})
)
exportLocations.push(
await doExport({
type: 'gltf',
storage: 'binary',
presentation: 'pretty',
})
)
// TODO: gltfs don't seem to work with snap shots. push onto exportLocations once it's figured out
await doExport({
type: 'gltf',
storage: 'standard',
presentation: 'pretty',
})
await doExport({
type: 'ply',
coords: sysType,
selection: { type: 'default_scene' },
storage: 'ascii',
units: 'in',
})
await doExport({
type: 'ply',
storage: 'binary_little_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
await doExport({
type: 'ply',
storage: 'binary_big_endian',
coords: sysType,
selection: { type: 'default_scene' },
units: 'in',
})
await doExport({
type: 'stl',
storage: 'ascii',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
await doExport({
type: 'stl',
storage: 'binary',
coords: sysType,
units: 'in',
selection: { type: 'default_scene' },
})
await doExport({
// obj seems to be a little flaky, times out tests sometimes
type: 'obj',
coords: sysType,
units: 'in',
})
// close page to disconnect websocket since we can only have one open atm
await page.close()
// snapshot exports, good compromise to capture that exports are healthy without getting bogged down in "did the formatting change" changes
// context: https://github.com/KittyCAD/modeling-app/issues/1222
for (const { modelPath, imagePath, outputType } of exportLocations) {
const cliCommand = `export KITTYCAD_TOKEN=${secrets.snapshottoken} && kittycad file snapshot --output-format=png --src-format=${outputType} ${modelPath} ${imagePath}`
const child = spawn(cliCommand, { shell: true })
await new Promise((resolve, reject) => {
child.on('error', (code: any, msg: any) => {
console.log('error', code, msg)
reject()
})
child.on('exit', (code, msg) => {
console.log('exit', code, msg)
if (code !== 0) {
reject(`exit code ${code} for model ${modelPath}`)
} else {
resolve(true)
}
})
child.stderr.on('data', (data) => console.log(`stderr: ${data}`))
child.stdout.on('data', (data) => console.log(`stdout: ${data}`))
})
}
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

@ -7,12 +7,12 @@
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="An open-source CAD modeling tool from the future by KittyCAD."
content="An open-source CAD modeling tool from the future by Zoo."
/>
<link rel="apple-touch-icon" href="/logo192.png" />
<link rel="manifest" href="/manifest.json" />
<script defer data-domain="app.kittycad.io" src="https://plausible.corp.kittycad.io/js/script.js"></script>
<title>KittyCAD Modeling App</title>
<title>Modeling App</title>
</head>
<body class="body-bg">
<noscript>You need to enable JavaScript to run this app.</noscript>

View File

@ -1,4 +1,4 @@
## KittyCAD Modeling App Roadmap
## Zoo Modeling App Roadmap
This document ties into our [GH Discussions Feature List](https://github.com/KittyCAD/modeling-app/discussions). Please upvote any features that you want to see next, or add ones that are not listed and we will review.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

13
public/zma-logomark.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 13 KiB

7
public/zoo-logo.svg Normal file
View File

@ -0,0 +1,7 @@
<svg width="438" height="145" viewBox="0 0 438 145" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M88.2136 25.3021V3.12744H0.595269V34.3994H79.827L0.609484 120.312H0.595269V120.326L0.581055 120.34L0.595269 120.355V141.364H20.8936L41.3341 119.189V141.364H128.952V110.092H49.7349L128.952 24.1649V3.12744L108.64 3.15587L88.2136 25.3021Z" fill="white"/>
<path d="M167.36 72.4372C167.36 49.7366 185.824 31.2719 208.525 31.2719C216.514 31.2719 223.976 33.5605 230.288 37.5121L251.78 14.3709C239.698 5.34466 224.73 0 208.525 0C168.582 0 136.088 32.4944 136.088 72.4372C136.088 90.5465 142.769 107.135 153.828 119.857L175.32 96.7156C170.316 89.9069 167.36 81.5061 167.36 72.4372Z" fill="white"/>
<path d="M241.745 48.1442C246.734 54.9671 249.691 63.3679 249.691 72.4368C249.691 95.1232 231.226 113.588 208.525 113.588C200.537 113.588 193.088 111.299 186.777 107.348L165.271 130.503C177.353 139.515 192.321 144.86 208.525 144.86C248.468 144.86 280.963 112.365 280.963 72.4368C280.963 54.3133 274.282 37.7249 263.223 25.0029L241.745 48.1442Z" fill="white"/>
<path d="M419.312 25.0029L397.834 48.1442C402.823 54.9671 405.779 63.3679 405.779 72.4368C405.779 95.1232 387.315 113.588 364.614 113.588C356.626 113.588 349.177 111.299 342.866 107.348L321.359 130.503C333.442 139.515 348.41 144.86 364.614 144.86C404.557 144.86 437.051 112.365 437.051 72.4368C437.051 54.3133 430.371 37.7249 419.312 25.0029Z" fill="white"/>
<path d="M323.449 72.4372C323.449 49.7366 341.913 31.2719 364.614 31.2719C372.603 31.2719 380.065 33.5605 386.376 37.5121L407.869 14.3709C395.786 5.34466 380.819 0 364.614 0C324.671 0 292.177 32.4944 292.177 72.4372C292.177 90.5465 298.858 107.135 309.916 119.857L331.409 96.7156C326.405 89.9069 323.449 81.5061 323.449 72.4372Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

156
src-tauri/Cargo.lock generated
View File

@ -547,12 +547,12 @@ dependencies = [
[[package]]
name = "ctor"
version = "0.1.26"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d2301688392eb071b0bf1a37be05c469d3cc4dbbd95df672fe28ab021e6a096"
checksum = "30d2b3721e861707777e3195b0158f950ae6dc4a27e4d02ff9f67e3eb3de199e"
dependencies = [
"quote",
"syn 1.0.109",
"syn 2.0.33",
]
[[package]]
@ -1530,9 +1530,9 @@ dependencies = [
[[package]]
name = "infer"
version = "0.12.0"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a898e4b7951673fce96614ce5751d13c40fc5674bc2d759288e46c3ab62598b3"
checksum = "f551f8c3a39f68f986517db0d1759de85881894fdc7db798bd2a9df9cb04b7fc"
dependencies = [
"cfb",
]
@ -1652,9 +1652,9 @@ dependencies = [
[[package]]
name = "json-patch"
version = "1.0.0"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f54898088ccb91df1b492cc80029a6fdf1c48ca0db7c6822a8babad69c94658"
checksum = "55ff1e1486799e3f64129f8ccad108b38290df9cd7015cd31bed17239f0789d6"
dependencies = [
"serde",
"serde_json",
@ -2194,11 +2194,11 @@ dependencies = [
[[package]]
name = "openssl"
version = "0.10.55"
version = "0.10.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d"
checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
dependencies = [
"bitflags 1.3.2",
"bitflags 2.4.0",
"cfg-if",
"foreign-types",
"libc",
@ -2226,9 +2226,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.90"
version = "0.9.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b"
dependencies = [
"cc",
"libc",
@ -2400,9 +2400,17 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_macros 0.10.0",
"phf_shared 0.10.0",
"proc-macro-hack",
]
[[package]]
name = "phf"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
dependencies = [
"phf_macros 0.11.2",
"phf_shared 0.11.2",
]
[[package]]
@ -2445,6 +2453,16 @@ dependencies = [
"rand 0.8.5",
]
[[package]]
name = "phf_generator"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0"
dependencies = [
"phf_shared 0.11.2",
"rand 0.8.5",
]
[[package]]
name = "phf_macros"
version = "0.8.0"
@ -2461,16 +2479,15 @@ dependencies = [
[[package]]
name = "phf_macros"
version = "0.10.0"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58fdf3184dd560f160dd73922bea2d5cd6e8f064bf4b13110abd81b03697b4e0"
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
dependencies = [
"phf_generator 0.10.0",
"phf_shared 0.10.0",
"proc-macro-hack",
"phf_generator 0.11.2",
"phf_shared 0.11.2",
"proc-macro2",
"quote",
"syn 1.0.109",
"syn 2.0.33",
]
[[package]]
@ -2491,6 +2508,15 @@ dependencies = [
"siphasher",
]
[[package]]
name = "phf_shared"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b"
dependencies = [
"siphasher",
]
[[package]]
name = "phonenumber"
version = "0.3.3+8.13.9"
@ -3734,9 +3760,9 @@ dependencies = [
[[package]]
name = "tauri"
version = "1.5.2"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bfe673cf125ef364d6f56b15e8ce7537d9ca7e4dae1cf6fbbdeed2e024db3d9"
checksum = "32d563b672acde8d0cc4c1b1f5b855976923f67e8d6fe1eba51df0211e197be2"
dependencies = [
"anyhow",
"bytes",
@ -3831,9 +3857,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "1.4.1"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "613740228de92d9196b795ac455091d3a5fbdac2654abb8bb07d010b62ab43af"
checksum = "acea6445eececebd72ed7720cfcca46eee3b5bad8eb408be8f7ef2e3f7411500"
dependencies = [
"heck 0.4.1",
"proc-macro2",
@ -3878,9 +3904,9 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "0.14.1"
version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8141d72b6b65f2008911e9ef5b98a68d1e3413b7a1464e8f85eb3673bb19a895"
checksum = "803a01101bc611ba03e13329951a1bde44287a54234189b9024b78619c1bc206"
dependencies = [
"cocoa",
"gtk",
@ -3898,9 +3924,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "1.5.0"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34d55e185904a84a419308d523c2c6891d5e2dbcee740c4997eb42e75a7b0f46"
checksum = "a52165bb340e6f6a75f1f5eeeab1bb49f861c12abe3a176067d53642b5454986"
dependencies = [
"brotli",
"ctor",
@ -3913,7 +3939,7 @@ dependencies = [
"kuchikiki",
"log",
"memchr",
"phf 0.10.1",
"phf 0.11.2",
"proc-macro2",
"quote",
"semver",
@ -3923,7 +3949,7 @@ dependencies = [
"thiserror",
"url",
"walkdir",
"windows 0.39.0",
"windows-version",
]
[[package]]
@ -4747,12 +4773,36 @@ dependencies = [
"windows_x86_64_msvc 0.48.0",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm 0.52.0",
"windows_aarch64_msvc 0.52.0",
"windows_i686_gnu 0.52.0",
"windows_i686_msvc 0.52.0",
"windows_x86_64_gnu 0.52.0",
"windows_x86_64_gnullvm 0.52.0",
"windows_x86_64_msvc 0.52.0",
]
[[package]]
name = "windows-tokens"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f838de2fe15fe6bac988e74b798f26499a8b21a9d97edec321e79b28d1d7f597"
[[package]]
name = "windows-version"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75aa004c988e080ad34aff5739c39d0312f4684699d6d71fc8a198d057b8b9b4"
dependencies = [
"windows-targets 0.52.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
@ -4765,6 +4815,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.37.0"
@ -4789,6 +4845,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.37.0"
@ -4813,6 +4875,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.37.0"
@ -4837,6 +4905,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.37.0"
@ -4861,6 +4935,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
@ -4873,6 +4953,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.37.0"
@ -4897,6 +4983,12 @@ version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.5.15"
@ -4928,9 +5020,9 @@ dependencies = [
[[package]]
name = "wry"
version = "0.24.4"
version = "0.24.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88ef04bdad49eba2e01f06e53688c8413bd6a87b0bc14b72284465cf96e3578e"
checksum = "64a70547e8f9d85da0f5af609143f7bde3ac7457a6e1073104d9b73d6c5ac744"
dependencies = [
"base64 0.13.1",
"block",

View File

@ -20,7 +20,7 @@ kittycad = "0.2.42"
oauth2 = "4.4.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tauri = { version = "1.5.2", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri = { version = "1.5.3", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "devtools"] }
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tokio = { version = "1.34.0", features = ["time"] }
toml = "0.8.2"

View File

@ -76,16 +76,13 @@ async fn login(app: tauri::AppHandle, host: &str) -> Result<String, InvokeError>
// Here we're using an env var to enable the /tmp file (windows not supported for now)
// and bypass the shell::open call as it fails on GitHub Actions.
let e2e_tauri_enabled = env::var("E2E_TAURI_ENABLED").is_ok();
if (e2e_tauri_enabled) {
if e2e_tauri_enabled {
println!(
"E2E_TAURI_ENABLED is set, won't open {} externally",
auth_uri.secret()
);
fs::write(
"/tmp/kittycad_user_code",
details.user_code().secret().to_string(),
)
.expect("Unable to write /tmp/kittycad_user_code file");
fs::write("/tmp/kittycad_user_code", details.user_code().secret())
.expect("Unable to write /tmp/kittycad_user_code file");
} else {
tauri::api::shell::open(&app.shell_scope(), auth_uri.secret(), None)
.map_err(|e| InvokeError::from_anyhow(e.into()))?;

View File

@ -8,7 +8,7 @@ export const Auth = ({ children }: React.PropsWithChildren) => {
return isLoggingIn ? (
<Loading>
<span data-testid="initial-load">Loading KittyCAD Modeling App...</span>
<span data-testid="initial-load">Loading Modeling App...</span>
</Loading>
) : (
<>{children}</>

View File

@ -33,7 +33,7 @@ export const AppHeader = ({
className={
'w-full grid ' +
styles.header +
' overlaid-panes sticky top-0 z-20 py-1 px-5 bg-chalkboard-10/70 dark:bg-chalkboard-100/50 border-b dark:border-b-2 border-chalkboard-30 dark:border-chalkboard-90 items-center ' +
' overlaid-panes sticky top-0 z-20 py-1 px-2 bg-chalkboard-10/70 dark:bg-chalkboard-100/50 border-b dark:border-b-2 border-chalkboard-30 dark:border-chalkboard-90 items-center ' +
className
}
>

View File

@ -19,7 +19,7 @@ const DownloadAppBanner = () => {
<div className="max-w-3xl mx-auto">
<div className="flex gap-2 justify-between items-start">
<h2 className="text-xl font-bold mb-4">
KittyCAD Modeling App is better as a desktop app!
Zoo Modeling App is better as a desktop app!
</h2>
<ActionButton
Element="button"
@ -42,7 +42,7 @@ const DownloadAppBanner = () => {
</code>
, and isn't backed up anywhere! Visit{' '}
<a
href="https://kittycad.io/modeling-app/download"
href="https://zoo.dev/modeling-app/download"
rel="noopener noreferrer"
target="_blank"
className="!text-warn-80 dark:!text-warn-80 dark:hover:!text-warn-70 underline"

33
src/components/Logo.tsx Normal file
View File

@ -0,0 +1,33 @@
export const Logo = ({
className = 'w-auto h-5 text-chalkboard-120 dark:text-chalkboard-10',
...props
}: React.SVGProps<SVGSVGElement>) => (
<svg
{...props}
className={className}
viewBox="0 0 438 145"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M88.2136 25.3021V3.12744H0.595269V34.3994H79.827L0.609484 120.312H0.595269V120.326L0.581055 120.34L0.595269 120.355V141.364H20.8936L41.3341 119.189V141.364H128.952V110.092H49.7349L128.952 24.1649V3.12744L108.64 3.15587L88.2136 25.3021Z"
fill="currentColor"
/>
<path
d="M167.36 72.4372C167.36 49.7366 185.824 31.2719 208.525 31.2719C216.514 31.2719 223.976 33.5605 230.288 37.5121L251.78 14.3709C239.698 5.34466 224.73 0 208.525 0C168.582 0 136.088 32.4944 136.088 72.4372C136.088 90.5465 142.769 107.135 153.828 119.857L175.32 96.7156C170.316 89.9069 167.36 81.5061 167.36 72.4372Z"
fill="currentColor"
/>
<path
d="M241.745 48.1442C246.734 54.9671 249.691 63.3679 249.691 72.4368C249.691 95.1232 231.226 113.588 208.525 113.588C200.537 113.588 193.088 111.299 186.777 107.348L165.271 130.503C177.353 139.515 192.321 144.86 208.525 144.86C248.468 144.86 280.963 112.365 280.963 72.4368C280.963 54.3133 274.282 37.7249 263.223 25.0029L241.745 48.1442Z"
fill="currentColor"
/>
<path
d="M419.312 25.0029L397.834 48.1442C402.823 54.9671 405.779 63.3679 405.779 72.4368C405.779 95.1232 387.315 113.588 364.614 113.588C356.626 113.588 349.177 111.299 342.866 107.348L321.359 130.503C333.442 139.515 348.41 144.86 364.614 144.86C404.557 144.86 437.051 112.365 437.051 72.4368C437.051 54.3133 430.371 37.7249 419.312 25.0029Z"
fill="currentColor"
/>
<path
d="M323.449 72.4372C323.449 49.7366 341.913 31.2719 364.614 31.2719C372.603 31.2719 380.065 33.5605 386.376 37.5121L407.869 14.3709C395.786 5.34466 380.819 0 364.614 0C324.671 0 292.177 32.4944 292.177 72.4372C292.177 90.5465 298.858 107.135 309.916 119.857L331.409 96.7156C326.405 89.9069 323.449 81.5061 323.449 72.4372Z"
fill="currentColor"
/>
</svg>
)

View File

@ -4,6 +4,7 @@ import ProjectSidebarMenu from './ProjectSidebarMenu'
import { ProjectWithEntryPointMetadata } from '../Router'
import { GlobalStateProvider } from './GlobalStateProvider'
import CommandBarProvider from './CommandBar/CommandBar'
import { APP_NAME } from 'lib/constants'
const now = new Date()
const projectWellFormed = {
@ -71,9 +72,7 @@ describe('ProjectSidebarMenu tests', () => {
fireEvent.click(screen.getByTestId('project-sidebar-toggle'))
expect(screen.getByTestId('projectName')).toHaveTextContent(
'KittyCAD Modeling App'
)
expect(screen.getByTestId('projectName')).toHaveTextContent(APP_NAME)
})
test('Renders as a link if set to do so', () => {

View File

@ -8,6 +8,8 @@ import { ExportButton } from './ExportButton'
import { Fragment } from 'react'
import { FileTree } from './FileTree'
import { sep } from '@tauri-apps/api/path'
import { Logo } from './Logo'
import { APP_NAME } from 'lib/constants'
const ProjectSidebarMenu = ({
project,
@ -21,37 +23,29 @@ const ProjectSidebarMenu = ({
return renderAsLink ? (
<Link
to={paths.HOME}
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 p-0.5 pr-2 flex items-center gap-4 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center gap-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"
data-testid="project-sidebar-link"
>
<img
src="/kitt-8bit-winking.svg"
alt="KittyCAD App"
className="w-auto h-9"
/>
<Logo />
<span
className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block"
data-testid="project-sidebar-link-name"
>
{project?.name ? project.name : 'KittyCAD Modeling App'}
{project?.name ? project.name : APP_NAME}
</span>
</Link>
) : (
<Popover className="relative">
<Popover.Button
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 p-0.5 pr-2 flex items-center gap-4 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"
className="rounded-sm h-9 mr-auto max-h-min min-w-max border-0 py-1 px-2 flex items-center gap-3 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-energy-50 dark:hover:bg-chalkboard-90"
data-testid="project-sidebar-toggle"
>
<img
src="/kitt-8bit-winking.svg"
alt="KittyCAD App"
className="w-auto h-full"
/>
<Logo />
<div className="flex flex-col items-start py-0.5">
<span className="hidden text-sm text-chalkboard-110 dark:text-chalkboard-20 whitespace-nowrap lg:block">
{isTauri() && file?.name
? file.name.slice(file.name.lastIndexOf(sep) + 1)
: 'KittyCAD Modeling App'}
: APP_NAME}
</span>
{isTauri() && project?.name && (
<span className="hidden text-xs text-chalkboard-70 dark:text-chalkboard-40 whitespace-nowrap lg:block">
@ -88,18 +82,13 @@ const ProjectSidebarMenu = ({
{({ close }) => (
<>
<div className="flex items-center gap-4 px-4 py-3">
<img
src="/kitt-8bit-winking.svg"
alt="KittyCAD App"
className="w-auto h-9"
/>
<Logo />
<div>
<p
className="m-0 text-chalkboard-100 dark:text-energy-10 text-mono"
data-testid="projectName"
>
{project?.name ? project.name : 'KittyCAD Modeling App'}
{project?.name ? project.name : APP_NAME}
</p>
{project?.entrypointMetadata && (
<p

View File

@ -981,10 +981,11 @@ export class EngineCommandManager {
!(
command.type === 'modeling_cmd_req' &&
(command.cmd.type === 'highlight_set_entity' ||
command.cmd.type === 'mouse_move')
command.cmd.type === 'mouse_move' ||
command.cmd.type === 'camera_drag_move')
)
) {
// highlight_set_entity and mouse_move are sent over the unreliable channel and are too noisy
// highlight_set_entity, mouse_move and camera_drag_move are sent over the unreliable channel and are too noisy
this.addCommandLog({
type: 'send-scene',
data: command,

View File

@ -1537,7 +1537,8 @@ export function isLiteralArrayOrStatic(
if (!val) return false
if (Array.isArray(val)) {
const [a, b] = val
const a = val[0]
const b = val[1]
return isLiteralArrayOrStatic(a) && isLiteralArrayOrStatic(b)
}
return (
@ -1550,7 +1551,8 @@ export function isNotLiteralArrayOrStatic(
val: Value | [Value, Value] | [Value, Value, Value]
): boolean {
if (Array.isArray(val)) {
const [a, b] = val
const a = val[0]
const b = val[1]
return isNotLiteralArrayOrStatic(a) && isNotLiteralArrayOrStatic(b)
}
return (

1
src/lib/constants.ts Normal file
View File

@ -0,0 +1 @@
export const APP_NAME = 'Modeling App'

View File

@ -27,7 +27,7 @@ export default function Units() {
<div className="fixed inset-0 z-50 grid items-end justify-start px-4 pointer-events-none">
<div
className={
'max-w-2xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -14,7 +14,7 @@ export default function CmdK() {
<div className="fixed inset-0 z-50 grid items-end justify-center pointer-events-none">
<div
className={
'max-w-full xl:max-w-4xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-full xl:max-w-4xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>
@ -34,8 +34,8 @@ export default function CmdK() {
</p>
<p className="my-4">
We are working on a command bar that will allow you to quickly see and
search for any available commands. We are building KittyCAD Modeling
App's state management system on top of{' '}
search for any available commands. We are building Zoo Modeling App's
state management system on top of{' '}
<a
href="https://xstate.js.org/"
rel="noreferrer noopener"

View File

@ -17,7 +17,7 @@ export default function CodeEditor() {
></div>
<div
className={
'z-10 max-w-xl h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -1,3 +1,4 @@
import { APP_NAME } from 'lib/constants'
import { OnboardingButtons, onboardingPaths, useDismiss, useNextClick } from '.'
import { useStore } from '../../useStore'
@ -12,7 +13,7 @@ export default function Export() {
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
<div
className={
'max-w-full xl:max-w-2xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>
@ -22,9 +23,9 @@ export default function Export() {
Try opening the project menu and clicking "Export Model".
</p>
<p className="my-4">
KittyCAD Modeling App uses{' '}
{APP_NAME} uses{' '}
<a
href="https://kittycad.io/gltf-format-extension"
href="https://zoo.dev/gltf-format-extension"
rel="noopener noreferrer"
target="_blank"
>
@ -32,7 +33,7 @@ export default function Export() {
</a>{' '}
for the GLTF file format.{' '}
<a
href="https://kittycad.io/docs/api/convert-cad-file"
href="https://zoo.dev/docs/api/convert-cad-file"
rel="noopener noreferrer"
target="_blank"
>

View File

@ -3,6 +3,7 @@ import { useEffect } from 'react'
import { bracket } from 'lib/exampleKcl'
import { kclManager } from 'lang/KclSinglton'
import { useModelingContext } from 'hooks/useModelingContext'
import { APP_NAME } from 'lib/constants'
export default function FutureWork() {
const { send } = useModelingContext()
@ -22,7 +23,7 @@ export default function FutureWork() {
return (
<div className="fixed grid justify-center items-center inset-0 bg-chalkboard-100/50 z-50">
<div className="max-w-full xl:max-w-2xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
<div className="max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded">
<h1 className="text-2xl font-bold">Future Work</h1>
<p className="my-4">
We have curves, cuts, and many more CAD features coming soon. We want
@ -32,10 +33,10 @@ export default function FutureWork() {
</p>
<p className="my-4">
If you make anything with the app we'd love to see it! Thank you for
taking time to try out KittyCAD Modeling App, and build the future of
hardware design with us 💚.
taking time to try out {APP_NAME}, and build the future of hardware
design with us.
</p>
<p className="my-4"> The KittyCAD Team</p>
<p className="my-4">💚 The Zoo Team</p>
<OnboardingButtons
className="mt-6"
dismiss={dismiss}

View File

@ -17,7 +17,7 @@ export default function InteractiveNumbers() {
></div>
<div
className={
'z-10 max-w-xl h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -21,6 +21,7 @@ import { paths } from 'Router'
import { useEffect } from 'react'
import { kclManager } from 'lang/KclSinglton'
import { sep } from '@tauri-apps/api/path'
import { APP_NAME } from 'lib/constants'
function OnboardingWithNewFile() {
const navigate = useNavigate()
@ -129,8 +130,8 @@ export default function Introduction() {
<div className="max-w-3xl p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
<h1 className="flex flex-wrap items-center gap-4 text-2xl font-bold">
<img
src={`/kcma-logomark${getLogoTheme()}.svg`}
alt="KittyCAD Modeling App"
src={`/zma-logomark${getLogoTheme()}.svg`}
alt={APP_NAME}
className="h-20 max-w-full"
/>
<span className="px-3 py-1 text-base rounded-full bg-energy-10 text-energy-80">
@ -139,11 +140,11 @@ export default function Introduction() {
</h1>
<section className="my-12">
<p className="my-4">
Welcome to KittyCAD Modeling App! This is a hardware design tool
that lets you edit visually, with code, or both. It's powered by the
first API created for anyone to build hardware design tools. The 3D
view is not running on your computer, but is instead being streamed
to you from a remote GPU as video.
Welcome to {APP_NAME}! This is a hardware design tool that lets you
edit visually, with code, or both. It's powered by the first API
created for anyone to build hardware design tools. The 3D view is
not running on your computer, but is instead being streamed to you
from a remote GPU as video.
</p>
<p className="my-4">
This is an alpha release, so you will encounter bugs and missing

View File

@ -29,7 +29,7 @@ export default function ParametricModeling() {
></div>
<div
className={
'z-10 max-w-xl h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'z-10 max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -13,7 +13,7 @@ export default function ProjectMenu() {
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
<div
className={
'max-w-xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -22,7 +22,7 @@ export default function Sketching() {
<div className="fixed grid justify-center items-end inset-0 z-50 pointer-events-none">
<div
className={
'max-w-full xl:max-w-2xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-full xl:max-w-2xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>
@ -35,7 +35,7 @@ export default function Sketching() {
</p>
<p className="my-4">
Watch the code pane as you click. Point-and-click interactions are
always just modifying and generating code in KittyCAD Modeling App.
always just modifying and generating code in Zoo Modeling App.
</p>
<OnboardingButtons
className="mt-6"

View File

@ -12,7 +12,7 @@ export default function Streaming() {
<div className="fixed grid justify-start items-center inset-0 z-50 pointer-events-none">
<div
className={
'max-w-xl h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-xl border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg h-3/4 flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>
@ -20,18 +20,18 @@ export default function Streaming() {
<h2 className="text-2xl">Streaming Video</h2>
<p className="my-4">
The 3D view is not running on your computer. Instead, our
infrastructure spins up the KittyCAD Geometry Engine on a remote
GPU, KittyCAD Modeling App sends it a series of commands via
Websockets and WebRTC, and the Geometry Engine sends back a video
stream of the 3D view.
infrastructure spins up the Zoo Geometry Engine on a remote GPU, Zoo
Modeling App sends it a series of commands via Websockets and
WebRTC, and the Geometry Engine sends back a video stream of the 3D
view.
</p>
<p className="my-4">
This means that you could run KittyCAD Modeling App on a Chromebook,
a tablet, or even a phone, as long as you have a good internet
This means that you could run Zoo Modeling App on a Chromebook, a
tablet, or even a phone, as long as you have a good internet
connection.
</p>
<p className="my-4">
It also means that whatever tools you build on top of the KittyCAD
It also means that whatever tools you build on top of the Zoo
Geometry Engine will be able to run on any device with a browser,
and you won't have to worry about the performance of the device.
</p>

View File

@ -12,7 +12,7 @@ export default function UserMenu() {
<div className="fixed grid justify-center items-start inset-0 z-50 pointer-events-none">
<div
className={
'max-w-xl flex flex-col justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
'max-w-xl flex flex-col border border-chalkboard-50 dark:border-chalkboard-80 shadow-lg justify-center bg-chalkboard-10 dark:bg-chalkboard-90 p-8 rounded' +
(buttonDownInStream ? '' : ' pointer-events-auto')
}
>

View File

@ -5,6 +5,7 @@ import { VITE_KC_SITE_BASE_URL, VITE_KC_API_BASE_URL } from '../env'
import { Themes, getSystemTheme } from '../lib/theme'
import { paths } from '../Router'
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
import { APP_NAME } from 'lib/constants'
const SignIn = () => {
const {
@ -45,20 +46,20 @@ const SignIn = () => {
</span>
</div>
<h1 className="font-bold text-2xl mt-12 mb-6">
Sign in to get started with the KittyCAD Modeling App
Sign in to get started with the {APP_NAME}
</h1>
<p className="py-4">
KCMA is an open-source CAD application for creating accurate 3D models
for use in manufacturing. It is built on top of the KittyCAD API.
KittyCAD is the first software infrastructure company built
specifically for the needs of the manufacturing industry. With KCMA we
are showing how the KittyCAD API can be used to build entirely new
kinds of software for manufacturing.
ZMA is an open-source CAD application for creating accurate 3D models
for use in manufacturing. It is built on top of KittyCAD, the design
API from Zoo. Zoo is the first software infrastructure company built
specifically for the needs of the manufacturing industry. With ZMA we
are showing how the KittyCAD API from Zoo can be used to build
entirely new kinds of software for manufacturing.
</p>
<p className="py-4">
KCMA is currently in development. If you would like to be notified
when KCMA is ready for production, please sign up for our mailing list
at <a href="https://kittycad.io">kittycad.io</a>.
ZMA is currently in development. If you would like to be notified when
ZMA is ready for production, please sign up for our mailing list at{' '}
<a href="https://zoo.dev">zoo.dev</a>.
</p>
{isTauri() ? (
<ActionButton

View File

@ -715,28 +715,16 @@ dependencies = [
[[package]]
name = "derive-docs"
version = "0.1.4"
version = "0.1.5"
dependencies = [
"convert_case",
"expectorate",
"once_cell",
"openapitor",
"pretty_assertions",
"proc-macro2",
"quote",
"serde",
"serde_tokenstream",
"syn 2.0.39",
]
[[package]]
name = "derive-docs"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c357dec14992ba88803535217ed83d6f6cd80efcb8fa8e3f8a30a9b84fadc1c7"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"regex",
"serde",
"serde_tokenstream",
"syn 2.0.39",
@ -1436,7 +1424,7 @@ dependencies = [
"criterion",
"dashmap",
"databake",
"derive-docs 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"derive-docs",
"expectorate",
"futures",
"insta",
@ -3959,18 +3947,18 @@ checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zerocopy"
version = "0.7.12"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8db0ac2df3d060f81ec0380ccc5b71c2a7c092cfced671feeee1320e95559c87"
checksum = "1c4061bedbb353041c12f413700357bec76df2c7e2ca8e4df8bac24c6bf68e3d"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.12"
version = "0.7.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b6093bc6d5265ff40b479c834cdd25d8e20784781a2a29a8106327393d0a9ff"
checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
dependencies = [
"proc-macro2",
"quote",

View File

@ -1,7 +1,7 @@
[package]
name = "derive-docs"
description = "A tool for generating documentation from Rust derive macros"
version = "0.1.4"
version = "0.1.5"
edition = "2021"
license = "MIT"
repository = "https://github.com/KittyCAD/modeling-app"
@ -14,8 +14,10 @@ proc-macro = true
[dependencies]
convert_case = "0.6.0"
once_cell = "1.18.0"
proc-macro2 = "1"
quote = "1"
regex = "1.10"
serde = { version = "1.0.193", features = ["derive"] }
serde_tokenstream = "0.2"
syn = { version = "2.0.39", features = ["full"] }

View File

@ -1,15 +1,11 @@
// Copyright 2023 Oxide Computer Company
//! This package defines macro attributes associated with HTTP handlers. These
//! attributes are used both to define an HTTP API and to generate an OpenAPI
//! Spec (OAS) v3 document that describes the API.
// Clippy's style advice is definitely valuable, but not worth the trouble for
// automated enforcement.
#![allow(clippy::style)]
use convert_case::Casing;
use once_cell::sync::Lazy;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use regex::Regex;
use serde::Deserialize;
use serde_tokenstream::{from_tokenstream, Error};
use syn::{
@ -209,6 +205,18 @@ fn do_stdlib_inner(
quote! {
Vec<#ty_ident>
}
} else if ty_string.starts_with("Option<") {
let ty_string = ty_string.trim_start_matches("Option<").trim_end_matches('>');
let ty_ident = format_ident!("{}", ty_string);
quote! {
Option<#ty_ident>
}
} else if let Some((inner_array_type, num)) = parse_array_type(&ty_string) {
let ty_string = inner_array_type.to_owned();
let ty_ident = format_ident!("{}", ty_string);
quote! {
[#ty_ident; #num]
}
} else if ty_string.starts_with("Box<") {
let ty_string = ty_string.trim_start_matches("Box<").trim_end_matches('>');
let ty_ident = format_ident!("{}", ty_string);
@ -222,10 +230,13 @@ fn do_stdlib_inner(
}
};
let ty_string = clean_type(&ty_string);
let ty_string = rust_type_to_openapi_type(&ty_string);
if ty_string != "Args" {
let schema = if ty_ident.to_string().starts_with("Vec < ") {
let schema = if ty_ident.to_string().starts_with("Vec < ")
|| ty_ident.to_string().starts_with("Option <")
|| ty_ident.to_string().starts_with('[')
{
quote! {
<#ty_ident>::json_schema(&mut generator)
}
@ -263,7 +274,7 @@ fn do_stdlib_inner(
ret_ty_string.trim().to_string()
};
let ret_ty_ident = format_ident!("{}", ret_ty_string);
let ret_ty_string = clean_type(&ret_ty_string);
let ret_ty_string = rust_type_to_openapi_type(&ret_ty_string);
quote! {
Some(#docs_crate::StdLibFnArg {
name: "".to_string(),
@ -488,15 +499,22 @@ impl Parse for ItemFnForSignature {
}
}
fn clean_type(t: &str) -> String {
fn rust_type_to_openapi_type(t: &str) -> String {
let mut t = t.to_string();
// Turn vecs into arrays.
// TODO: handle nested types
if t.starts_with("Vec<") {
t = t.replace("Vec<", "[").replace('>', "]");
}
if t.starts_with("Box<") {
t = t.replace("Box<", "").replace('>', "");
}
if t.starts_with("Option<") {
t = t.replace("Option<", "").replace('>', "");
}
if let Some((inner_type, _length)) = parse_array_type(&t) {
t = format!("[{inner_type}]")
}
if t == "f64" {
return "number".to_string();
@ -507,6 +525,14 @@ fn clean_type(t: &str) -> String {
}
}
fn parse_array_type(type_name: &str) -> Option<(&str, usize)> {
static RE: Lazy<Regex> = Lazy::new(|| Regex::new(r"\[([a-zA-Z0-9<>]+); ?(\d+)\]").unwrap());
let cap = RE.captures(type_name)?;
let inner_type = cap.get(1)?;
let length = cap.get(2)?.as_str().parse().ok()?;
Some((inner_type.as_str(), length))
}
#[cfg(test)]
mod tests {
@ -514,6 +540,19 @@ mod tests {
use super::*;
#[test]
fn test_get_inner_array_type() {
for (expected, input) in [
(Some(("f64", 2)), "[f64;2]"),
(Some(("String", 2)), "[String; 2]"),
(Some(("Option<String>", 12)), "[Option<String>;12]"),
(Some(("Option<String>", 12)), "[Option<String>; 12]"),
] {
let actual = parse_array_type(input);
assert_eq!(actual, expected);
}
}
#[test]
fn test_stdlib_line_to() {
let (item, errors) = do_stdlib(
@ -608,4 +647,46 @@ mod tests {
assert!(errors.is_empty());
expectorate::assert_contents("tests/box.gen", &openapitor::types::get_text_fmt(&item).unwrap());
}
#[test]
fn test_stdlib_option() {
let (item, errors) = do_stdlib(
quote! {
name = "show",
},
quote! {
fn inner_show(
/// The args to do shit to.
args: Option<f64>
) -> Box<f64> {
args
}
},
)
.unwrap();
assert!(errors.is_empty());
expectorate::assert_contents("tests/option.gen", &openapitor::types::get_text_fmt(&item).unwrap());
}
#[test]
fn test_stdlib_array() {
let (item, errors) = do_stdlib(
quote! {
name = "show",
},
quote! {
fn inner_show(
/// The args to do shit to.
args: [f64; 2]
) -> Box<f64> {
args
}
},
)
.unwrap();
assert!(errors.is_empty());
expectorate::assert_contents("tests/array.gen", &openapitor::types::get_text_fmt(&item).unwrap());
}
}

View File

@ -0,0 +1,82 @@
#[allow(non_camel_case_types, missing_docs)]
#[doc = "Std lib function: show"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)]
#[ts(export)]
pub(crate) struct Show {}
#[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: show"]
pub(crate) const Show: Show = Show {};
fn boxed_show(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(show(args))
}
impl crate::docs::StdLibFn for Show {
fn name(&self) -> String {
"show".to_string()
}
fn summary(&self) -> String {
"".to_string()
}
fn description(&self) -> String {
"".to_string()
}
fn tags(&self) -> Vec<String> {
vec![]
}
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "[number]".to_string(),
schema: <[f64; 2usize]>::json_schema(&mut generator),
required: true,
}]
}
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),
schema: f64::json_schema(&mut generator),
required: true,
})
}
fn unpublished(&self) -> bool {
false
}
fn deprecated(&self) -> bool {
false
}
fn std_lib_fn(&self) -> crate::std::StdFn {
boxed_show
}
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {
Box::new(self.clone())
}
}
fn inner_show(#[doc = r" The args to do shit to."] args: [f64; 2]) -> Box<f64> {
args
}

View File

@ -0,0 +1,82 @@
#[allow(non_camel_case_types, missing_docs)]
#[doc = "Std lib function: show"]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, schemars :: JsonSchema, ts_rs :: TS)]
#[ts(export)]
pub(crate) struct Show {}
#[allow(non_upper_case_globals, missing_docs)]
#[doc = "Std lib function: show"]
pub(crate) const Show: Show = Show {};
fn boxed_show(
args: crate::std::Args,
) -> std::pin::Pin<
Box<
dyn std::future::Future<
Output = anyhow::Result<crate::executor::MemoryItem, crate::errors::KclError>,
>,
>,
> {
Box::pin(show(args))
}
impl crate::docs::StdLibFn for Show {
fn name(&self) -> String {
"show".to_string()
}
fn summary(&self) -> String {
"".to_string()
}
fn description(&self) -> String {
"".to_string()
}
fn tags(&self) -> Vec<String> {
vec![]
}
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
vec![crate::docs::StdLibFnArg {
name: "args".to_string(),
type_: "number".to_string(),
schema: <Option<f64>>::json_schema(&mut generator),
required: true,
}]
}
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
let mut settings = schemars::gen::SchemaSettings::openapi3();
settings.inline_subschemas = true;
let mut generator = schemars::gen::SchemaGenerator::new(settings);
Some(crate::docs::StdLibFnArg {
name: "".to_string(),
type_: "number".to_string(),
schema: f64::json_schema(&mut generator),
required: true,
})
}
fn unpublished(&self) -> bool {
false
}
fn deprecated(&self) -> bool {
false
}
fn std_lib_fn(&self) -> crate::std::StdFn {
boxed_show
}
fn clone_box(&self) -> Box<dyn crate::docs::StdLibFn> {
Box::new(self.clone())
}
}
fn inner_show(#[doc = r" The args to do shit to."] args: Option<f64>) -> Box<f64> {
args
}

View File

@ -18,8 +18,8 @@ async-trait = "0.1.73"
clap = { version = "4.4.8", features = ["cargo", "derive", "env", "unicode"], optional = true }
dashmap = "5.5.3"
databake = { version = "0.1.7", features = ["derive"] }
derive-docs = { version = "0.1.4" }
#derive-docs = { path = "../derive-docs" }
# derive-docs = { version = "0.1.4" }
derive-docs = { path = "../derive-docs" }
kittycad = { workspace = true }
lazy_static = "1.4.0"
parse-display = "0.8.2"

View File

@ -1,6 +1,6 @@
//! Data types for the AST.
use std::{collections::HashMap, fmt::Write};
use std::{collections::HashMap, fmt::Write, ops::RangeInclusive};
use anyhow::Result;
use databake::*;
@ -11,6 +11,7 @@ use serde_json::{Map, Value as JValue};
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
pub use self::literal_value::LiteralValue;
pub use self::none::KclNone;
use crate::{
docs::StdLibFn,
errors::{KclError, KclErrorDetails},
@ -20,6 +21,7 @@ use crate::{
};
mod literal_value;
mod none;
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
#[databake(path = kcl_lib::ast::types)]
@ -407,6 +409,7 @@ pub enum Value {
ObjectExpression(Box<ObjectExpression>),
MemberExpression(Box<MemberExpression>),
UnaryExpression(Box<UnaryExpression>),
None(KclNone),
}
impl Value {
@ -423,6 +426,9 @@ impl Value {
Value::PipeExpression(pipe_exp) => pipe_exp.recast(options, indentation_level),
Value::UnaryExpression(unary_exp) => unary_exp.recast(options),
Value::PipeSubstitution(_) => crate::parser::PIPE_SUBSTITUTION_OPERATOR.to_string(),
Value::None(_) => {
unimplemented!("there is no literal None, see https://github.com/KittyCAD/modeling-app/issues/1115")
}
}
}
@ -444,6 +450,7 @@ impl Value {
Value::PipeExpression(ref mut pipe_exp) => pipe_exp.replace_value(source_range, new_value),
Value::UnaryExpression(ref mut unary_exp) => unary_exp.replace_value(source_range, new_value),
Value::PipeSubstitution(_) => {}
Value::None(_) => {}
}
}
@ -460,6 +467,7 @@ impl Value {
Value::ObjectExpression(object_expression) => object_expression.start(),
Value::MemberExpression(member_expression) => member_expression.start(),
Value::UnaryExpression(unary_expression) => unary_expression.start(),
Value::None(none) => none.start,
}
}
@ -476,6 +484,7 @@ impl Value {
Value::ObjectExpression(object_expression) => object_expression.end(),
Value::MemberExpression(member_expression) => member_expression.end(),
Value::UnaryExpression(unary_expression) => unary_expression.end(),
Value::None(none) => none.end,
}
}
@ -483,19 +492,22 @@ impl Value {
/// This is really recursive so keep that in mind.
pub fn get_hover_value_for_position(&self, pos: usize, code: &str) -> Option<Hover> {
match self {
Value::Literal(_literal) => None,
Value::Identifier(_identifier) => None,
Value::BinaryExpression(binary_expression) => binary_expression.get_hover_value_for_position(pos, code),
Value::FunctionExpression(function_expression) => {
function_expression.get_hover_value_for_position(pos, code)
}
Value::CallExpression(call_expression) => call_expression.get_hover_value_for_position(pos, code),
Value::PipeExpression(pipe_expression) => pipe_expression.get_hover_value_for_position(pos, code),
Value::PipeSubstitution(_) => None,
Value::ArrayExpression(array_expression) => array_expression.get_hover_value_for_position(pos, code),
Value::ObjectExpression(object_expression) => object_expression.get_hover_value_for_position(pos, code),
Value::MemberExpression(member_expression) => member_expression.get_hover_value_for_position(pos, code),
Value::UnaryExpression(unary_expression) => unary_expression.get_hover_value_for_position(pos, code),
// TODO: LSP hover information for values/types. https://github.com/KittyCAD/modeling-app/issues/1126
Value::None(_) => None,
Value::Literal(_) => None,
Value::Identifier(_) => None,
// TODO: LSP hover information for symbols. https://github.com/KittyCAD/modeling-app/issues/1127
Value::PipeSubstitution(_) => None,
}
}
@ -519,6 +531,7 @@ impl Value {
member_expression.rename_identifiers(old_name, new_name)
}
Value::UnaryExpression(ref mut unary_expression) => unary_expression.rename_identifiers(old_name, new_name),
Value::None(_) => {}
}
}
@ -539,6 +552,7 @@ impl Value {
Value::ObjectExpression(object_expression) => object_expression.get_constraint_level(),
Value::MemberExpression(member_expression) => member_expression.get_constraint_level(),
Value::UnaryExpression(unary_expression) => unary_expression.get_constraint_level(),
Value::None(none) => none.get_constraint_level(),
}
}
}
@ -926,6 +940,7 @@ impl CallExpression {
for arg in &self.arguments {
let result: MemoryItem = match arg {
Value::None(none) => none.into(),
Value::Literal(literal) => literal.into(),
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
@ -991,7 +1006,7 @@ impl CallExpression {
if fn_args.len() != function_expression.params.len() {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"Expected {} arguments, got {}",
"this function expected {} arguments, got {}",
function_expression.params.len(),
fn_args.len(),
),
@ -1607,6 +1622,7 @@ impl ArrayExpression {
for element in &self.elements {
let result = match element {
Value::Literal(literal) => literal.into(),
Value::None(none) => none.into(),
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
value.clone()
@ -1759,6 +1775,7 @@ impl ObjectExpression {
for property in &self.properties {
let result = match &property.value {
Value::Literal(literal) => literal.into(),
Value::None(none) => none.into(),
Value::Identifier(identifier) => {
let value = memory.get(&identifier.name, identifier.into())?;
value.clone()
@ -2646,6 +2663,23 @@ impl FunctionExpression {
}
}
/// Required parameters must be declared before optional parameters.
/// This gets all the required parameters.
pub fn required_params(&self) -> &[Parameter] {
let end_of_required_params = self
.params
.iter()
.position(|param| param.optional)
// If there's no optional params, then all the params are required params.
.unwrap_or(self.params.len());
&self.params[..end_of_required_params]
}
/// Minimum and maximum number of arguments this function can take.
pub fn number_of_args(&self) -> RangeInclusive<usize> {
self.required_params().len()..=self.params.len()
}
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
self.body.replace_value(source_range, new_value);
}
@ -3400,4 +3434,107 @@ const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
);
}
}
#[test]
fn required_params() {
for (i, (test_name, expected, function_expr)) in [
(
"no params",
(0..=0),
FunctionExpression {
start: 0,
end: 0,
params: vec![],
body: Program {
start: 0,
end: 0,
body: Vec::new(),
non_code_meta: Default::default(),
},
},
),
(
"all required params",
(1..=1),
FunctionExpression {
start: 0,
end: 0,
params: vec![Parameter {
identifier: Identifier {
start: 0,
end: 0,
name: "foo".to_owned(),
},
optional: false,
}],
body: Program {
start: 0,
end: 0,
body: Vec::new(),
non_code_meta: Default::default(),
},
},
),
(
"all optional params",
(0..=1),
FunctionExpression {
start: 0,
end: 0,
params: vec![Parameter {
identifier: Identifier {
start: 0,
end: 0,
name: "foo".to_owned(),
},
optional: true,
}],
body: Program {
start: 0,
end: 0,
body: Vec::new(),
non_code_meta: Default::default(),
},
},
),
(
"mixed params",
(1..=2),
FunctionExpression {
start: 0,
end: 0,
params: vec![
Parameter {
identifier: Identifier {
start: 0,
end: 0,
name: "foo".to_owned(),
},
optional: false,
},
Parameter {
identifier: Identifier {
start: 0,
end: 0,
name: "bar".to_owned(),
},
optional: true,
},
],
body: Program {
start: 0,
end: 0,
body: Vec::new(),
non_code_meta: Default::default(),
},
},
),
]
.into_iter()
.enumerate()
{
let actual = function_expr.number_of_args();
assert_eq!(expected, actual, "failed test #{i} '{test_name}'");
}
}
}

View File

@ -0,0 +1,58 @@
//! KCL has optional parameters. Their type is [`KclOption`].
//! If an optional parameter is not given, it will have a value of type [`KclNone`].
use databake::*;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::executor::{MemoryItem, SourceRange, UserVal};
use super::ConstraintLevel;
/// KCL value for an optional parameter which was not given an argument.
/// (remember, parameters are in the function declaration,
/// arguments are in the function call/application).
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake, Default)]
#[databake(path = kcl_lib::ast::types)]
#[ts(export)]
#[serde(tag = "type")]
pub struct KclNone {
// TODO: Convert this to be an Option<SourceRange>.
pub start: usize,
pub end: usize,
}
impl From<&KclNone> for SourceRange {
fn from(v: &KclNone) -> Self {
Self([v.start, v.end])
}
}
impl From<&KclNone> for UserVal {
fn from(none: &KclNone) -> Self {
UserVal {
value: serde_json::to_value(none).expect("can always serialize a None"),
meta: Default::default(),
}
}
}
impl From<&KclNone> for MemoryItem {
fn from(none: &KclNone) -> Self {
let val = UserVal::from(none);
MemoryItem::UserVal(val)
}
}
impl KclNone {
pub fn source_range(&self) -> SourceRange {
SourceRange([self.start, self.end])
}
/// Get the constraint level.
/// KCL None is never constrained.
pub fn get_constraint_level(&self) -> ConstraintLevel {
ConstraintLevel::None {
source_ranges: vec![self.source_range()],
}
}
}

View File

@ -4,7 +4,7 @@ use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
use crate::executor::SourceRange;
#[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone)]
#[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq, Eq)]
#[ts(export)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum KclError {
@ -32,7 +32,7 @@ pub enum KclError {
Internal(KclErrorDetails),
}
#[derive(Debug, Serialize, Deserialize, ts_rs::TS, Clone)]
#[derive(Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq, Eq)]
#[ts(export)]
pub struct KclErrorDetails {
#[serde(rename = "sourceRanges")]

View File

@ -8,10 +8,11 @@ use kittycad::types::{Color, ModelingCmd, Point3D};
use parse_display::{Display, FromStr};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value as JValue;
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
use crate::{
ast::types::{BodyItem, FunctionExpression, Value},
ast::types::{BodyItem, FunctionExpression, KclNone, Value},
engine::{EngineConnection, EngineManager},
errors::{KclError, KclErrorDetails},
std::{FunctionKind, StdLib},
@ -49,6 +50,7 @@ impl ProgramMemory {
}
/// Get a value from the program memory.
/// Return Err if not found.
pub fn get(&self, key: &str, source_range: SourceRange) -> Result<&MemoryItem, KclError> {
self.root.get(key).ok_or_else(|| {
KclError::UndefinedValue(KclErrorDetails {
@ -349,6 +351,40 @@ impl MemoryItem {
}
}
/// Get a JSON value and deserialize it into some concrete type.
pub fn get_json<T: serde::de::DeserializeOwned>(&self) -> Result<T, KclError> {
let json = self.get_json_value()?;
serde_json::from_value(json).map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to deserialize struct from JSON: {}", e),
source_ranges: self.clone().into(),
})
})
}
/// Get a JSON value and deserialize it into some concrete type.
/// If it's a KCL None, return None. Otherwise return Some.
pub fn get_json_opt<T: serde::de::DeserializeOwned>(&self) -> Result<Option<T>, KclError> {
let json = self.get_json_value()?;
if let JValue::Object(ref o) = json {
if let Some(JValue::String(s)) = o.get("type") {
if s == "KclNone" {
return Ok(None);
}
}
}
serde_json::from_value(json)
.map_err(|e| {
KclError::Type(KclErrorDetails {
message: format!("Failed to deserialize struct from JSON: {}", e),
source_ranges: self.clone().into(),
})
})
.map(Some)
}
/// If this memory item is a function, call it with the given arguments, return its val as Ok.
/// If it's not a function, return Err.
pub async fn call_fn(
@ -873,6 +909,9 @@ pub async fn execute(
let metadata = Metadata { source_range };
match &declaration.init {
Value::None(none) => {
memory.add(&var_name, none.into(), source_range)?;
}
Value::Literal(literal) => {
memory.add(&var_name, literal.into(), source_range)?;
}
@ -892,27 +931,8 @@ pub async fn execute(
_metadata: Vec<Metadata>,
ctx: ExecutorContext| {
Box::pin(async move {
let mut fn_memory = memory.clone();
if args.len() != function_expression.params.len() {
return Err(KclError::Semantic(KclErrorDetails {
message: format!(
"Expected {} arguments, got {}",
function_expression.params.len(),
args.len(),
),
source_ranges: vec![(&function_expression).into()],
}));
}
// Add the arguments to the memory.
for (index, param) in function_expression.params.iter().enumerate() {
fn_memory.add(
&param.identifier.name,
args.get(index).unwrap().clone(),
(&param.identifier).into(),
)?;
}
let mut fn_memory =
assign_args_to_params(&function_expression, args, memory.clone())?;
let result = execute(
function_expression.body.clone(),
@ -1010,6 +1030,9 @@ pub async fn execute(
}
Value::PipeSubstitution(_) => {}
Value::FunctionExpression(_) => {}
Value::None(none) => {
memory.return_ = Some(ProgramReturn::Value(MemoryItem::from(none)));
}
},
}
}
@ -1017,10 +1040,67 @@ pub async fn execute(
Ok(memory.clone())
}
/// For each argument given,
/// assign it to a parameter of the function, in the given block of function memory.
/// Returns Err if too few/too many arguments were given for the function.
fn assign_args_to_params(
function_expression: &FunctionExpression,
args: Vec<MemoryItem>,
mut fn_memory: ProgramMemory,
) -> Result<ProgramMemory, KclError> {
let num_args = function_expression.number_of_args();
let (min_params, max_params) = num_args.into_inner();
let n = args.len();
// Check if the user supplied too many arguments
// (we'll check for too few arguments below).
let err_wrong_number_args = KclError::Semantic(KclErrorDetails {
message: if min_params == max_params {
format!("Expected {min_params} arguments, got {n}")
} else {
format!("Expected {min_params}-{max_params} arguments, got {n}")
},
source_ranges: vec![function_expression.into()],
});
if n > max_params {
return Err(err_wrong_number_args);
}
// Add the arguments to the memory.
for (index, param) in function_expression.params.iter().enumerate() {
if let Some(arg) = args.get(index) {
// Argument was provided.
fn_memory.add(&param.identifier.name, arg.clone(), (&param.identifier).into())?;
} else {
// Argument was not provided.
if param.optional {
// If the corresponding parameter is optional,
// then it's fine, the user doesn't need to supply it.
let none = KclNone {
start: param.identifier.start,
end: param.identifier.end,
};
fn_memory.add(
&param.identifier.name,
MemoryItem::from(&none),
(&param.identifier).into(),
)?;
} else {
// But if the corresponding parameter was required,
// then the user has called with too few arguments.
return Err(err_wrong_number_args);
}
}
}
Ok(fn_memory)
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use crate::ast::types::{Identifier, Parameter};
use super::*;
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
@ -1566,4 +1646,122 @@ show(bracket)
"#;
parse_execute(ast).await.unwrap();
}
#[test]
fn test_assign_args_to_params() {
// Set up a little framework for this test.
fn mem(number: usize) -> MemoryItem {
MemoryItem::UserVal(UserVal {
value: number.into(),
meta: Default::default(),
})
}
fn ident(s: &'static str) -> Identifier {
Identifier {
start: 0,
end: 0,
name: s.to_owned(),
}
}
fn opt_param(s: &'static str) -> Parameter {
Parameter {
identifier: ident(s),
optional: true,
}
}
fn req_param(s: &'static str) -> Parameter {
Parameter {
identifier: ident(s),
optional: false,
}
}
// Declare the test cases.
for (test_name, params, args, expected) in [
("empty", Vec::new(), Vec::new(), Ok(ProgramMemory::new())),
(
"all params required, and all given, should be OK",
vec![req_param("x")],
vec![mem(1)],
Ok(ProgramMemory {
return_: None,
root: HashMap::from([("x".to_owned(), mem(1))]),
}),
),
(
"all params required, none given, should error",
vec![req_param("x")],
vec![],
Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![SourceRange([0, 0])],
message: "Expected 1 arguments, got 0".to_owned(),
})),
),
(
"all params optional, none given, should be OK",
vec![opt_param("x")],
vec![],
Ok(ProgramMemory {
return_: None,
root: HashMap::from([("x".to_owned(), MemoryItem::from(&KclNone::default()))]),
}),
),
(
"mixed params, too few given",
vec![req_param("x"), opt_param("y")],
vec![],
Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![SourceRange([0, 0])],
message: "Expected 1-2 arguments, got 0".to_owned(),
})),
),
(
"mixed params, minimum given, should be OK",
vec![req_param("x"), opt_param("y")],
vec![mem(1)],
Ok(ProgramMemory {
return_: None,
root: HashMap::from([
("x".to_owned(), mem(1)),
("y".to_owned(), MemoryItem::from(&KclNone::default())),
]),
}),
),
(
"mixed params, maximum given, should be OK",
vec![req_param("x"), opt_param("y")],
vec![mem(1), mem(2)],
Ok(ProgramMemory {
return_: None,
root: HashMap::from([("x".to_owned(), mem(1)), ("y".to_owned(), mem(2))]),
}),
),
(
"mixed params, too many given",
vec![req_param("x"), opt_param("y")],
vec![mem(1), mem(2), mem(3)],
Err(KclError::Semantic(KclErrorDetails {
source_ranges: vec![SourceRange([0, 0])],
message: "Expected 1-2 arguments, got 3".to_owned(),
})),
),
] {
// Run each test.
let func_expr = &FunctionExpression {
start: 0,
end: 0,
params,
body: crate::ast::types::Program {
start: 0,
end: 0,
body: Vec::new(),
non_code_meta: Default::default(),
},
};
let actual = assign_args_to_params(func_expr, args, ProgramMemory::new());
assert_eq!(
actual, expected,
"failed test '{test_name}':\ngot {actual:?}\nbut expected\n{expected:?}"
);
}
}
}

View File

@ -299,6 +299,15 @@ fn operand(i: TokenSlice) -> PResult<BinaryPart> {
message: TODO_783.to_owned(),
}))
}
Value::None(_) => {
return Err(KclError::Semantic(KclErrorDetails {
source_ranges,
// TODO: Better error message here.
// Once we have ways to use None values (e.g. by replacing with a default value)
// we should suggest one of them here.
message: "cannot use a KCL None value as an operand".to_owned(),
}));
}
Value::UnaryExpression(x) => BinaryPart::UnaryExpression(x),
Value::Literal(x) => BinaryPart::Literal(x),
Value::Identifier(x) => BinaryPart::Identifier(x),

View File

@ -154,6 +154,7 @@ impl Default for StdLib {
}
}
#[derive(Debug)]
pub enum FunctionKind {
Core(Box<dyn StdLibFn>),
Std(Box<dyn KclStdLibFn>),

View File

@ -6,6 +6,7 @@ use kittycad::types::{Angle, ModelingCmd, Point3D};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::executor::SourceRange;
use crate::{
errors::{KclError, KclErrorDetails},
executor::{
@ -1122,7 +1123,7 @@ async fn inner_tangential_arc(
path: sketch_group.id,
segment: kittycad::types::PathSegment::TangentialArc {
radius: *radius,
offset: kittycad::types::Angle {
offset: Angle {
unit: kittycad::types::UnitAngle::Degrees,
value: *offset,
},
@ -1178,27 +1179,32 @@ fn tan_arc_to(sketch_group: &SketchGroup, to: &[f64; 2]) -> ModelingCmd {
}
}
/// Data to draw a tangential arc to a specific point.
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, JsonSchema, ts_rs::TS)]
#[ts(export)]
#[serde(rename_all = "camelCase", untagged)]
pub enum TangentialArcToData {
/// A point with a tag.
PointWithTag {
/// Where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
to: [f64; 2],
/// The tag.
tag: String,
},
/// A point where the arc should end. Must lie in the same plane as the current path pen position. Must not be colinear with current path pen position.
Point([f64; 2]),
fn too_few_args(source_range: SourceRange) -> KclError {
KclError::Syntax(KclErrorDetails {
source_ranges: vec![source_range],
message: "too few arguments".to_owned(),
})
}
fn get_arg<I: Iterator>(it: &mut I, src: SourceRange) -> Result<I::Item, KclError> {
it.next().ok_or_else(|| too_few_args(src))
}
/// Draw a tangential arc to a specific point.
pub async fn tangential_arc_to(args: Args) -> Result<MemoryItem, KclError> {
let (data, sketch_group): (TangentialArcToData, Box<SketchGroup>) = args.get_data_and_sketch_group()?;
let src = args.source_range;
let new_sketch_group = inner_tangential_arc_to(data, sketch_group, args).await?;
// Get arguments to function call
let mut it = args.args.iter();
let to: [f64; 2] = get_arg(&mut it, src)?.get_json()?;
let sketch_group: Box<SketchGroup> = get_arg(&mut it, src)?.get_json()?;
let tag = if let Ok(memory_item) = get_arg(&mut it, src) {
memory_item.get_json_opt()?
} else {
None
};
let new_sketch_group = inner_tangential_arc_to(to, sketch_group, tag, args).await?;
Ok(MemoryItem::SketchGroup(new_sketch_group))
}
@ -1207,29 +1213,23 @@ pub async fn tangential_arc_to(args: Args) -> Result<MemoryItem, KclError> {
name = "tangentialArcTo",
}]
async fn inner_tangential_arc_to(
data: TangentialArcToData,
to: [f64; 2],
sketch_group: Box<SketchGroup>,
tag: Option<String>,
args: Args,
) -> Result<Box<SketchGroup>, KclError> {
let from: Point2d = sketch_group.get_coords_from_paths()?;
let to = match &data {
TangentialArcToData::PointWithTag { to, .. } => to,
TangentialArcToData::Point(to) => to,
};
let [to_x, to_y] = to;
let delta = [to[0] - from.x, to[1] - from.y];
let delta = [to_x - from.x, to_y - from.y];
let id = uuid::Uuid::new_v4();
args.send_modeling_cmd(id, tan_arc_to(&sketch_group, &delta)).await?;
let current_path = Path::ToPoint {
base: BasePath {
from: from.into(),
to: *to,
name: if let TangentialArcToData::PointWithTag { tag, .. } = data {
tag.to_string()
} else {
"".to_string()
},
to,
name: tag.unwrap_or_default(),
geo_meta: GeoMeta {
id,
metadata: args.source_range.into(),

View File

@ -474,6 +474,24 @@ show(square)
twenty_twenty::assert_image("tests/executor/outputs/holes.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn optional_params() {
let code = r#"
fn circle = (pos, radius, tag?) => {
const sg = startSketchOn('XY')
|> startProfileAt(pos, %)
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|> close(%)
return sg
}
show(circle([2, 2], 20))
"#;
let result = execute_and_snapshot(code).await.unwrap();
twenty_twenty::assert_image("tests/executor/outputs/optional_params.png", &result, 0.999);
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_rounded_with_holes() {
let code = r#"fn circle = (pos, radius) => {
@ -488,17 +506,21 @@ async fn serial_test_rounded_with_holes() {
return sg
}
fn tarc = (to, sketchGroup, tag?) => {
return tangentialArcTo(to, sketchGroup, tag)
}
fn roundedRectangle = (pos, w, l, cornerRadius) => {
const rr = startSketchOn('XY')
|> startProfileAt([pos[0] - w/2, 0], %)
|> lineTo([pos[0] - w/2, pos[1] - l/2 + cornerRadius], %)
|> tangentialArcTo([pos[0] - w/2 + cornerRadius, pos[1] - l/2], %)
|> tarc([pos[0] - w/2 + cornerRadius, pos[1] - l/2], %, "arc0")
|> lineTo([pos[0] + w/2 - cornerRadius, pos[1] - l/2], %)
|> tangentialArcTo([pos[0] + w/2, pos[1] - l/2 + cornerRadius], %)
|> tarc([pos[0] + w/2, pos[1] - l/2 + cornerRadius], %)
|> lineTo([pos[0] + w/2, pos[1] + l/2 - cornerRadius], %)
|> tangentialArcTo([pos[0] + w/2 - cornerRadius, pos[1] + l/2], %)
|> tarc([pos[0] + w/2 - cornerRadius, pos[1] + l/2], %, "arc2")
|> lineTo([pos[0] - w/2 + cornerRadius, pos[1] + l/2], %)
|> tangentialArcTo([pos[0] - w/2, pos[1] + l/2 - cornerRadius], %)
|> tarc([pos[0] - w/2, pos[1] + l/2 - cornerRadius], %)
|> close(%)
return rr
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB