Compare commits
36 Commits
kurt-add-s
...
achalmers/
Author | SHA1 | Date | |
---|---|---|---|
bf5287faab | |||
73bca2dcfc | |||
c6a50a3cdf | |||
b81c9d04cc | |||
9d8a7064da | |||
b0e6140e9f | |||
f9df7ff885 | |||
aec9637d7a | |||
e4c5fad8c7 | |||
cc0d601294 | |||
69cefafc19 | |||
b187ca3422 | |||
1edadcaa0f | |||
95c0ded8cf | |||
0ebb4e2cad | |||
f3e0939057 | |||
f5e233d8a0 | |||
1cab3e628f | |||
2ca6ba52b6 | |||
f741ea2e09 | |||
9f2a7781fc | |||
990f2b4154 | |||
0af0f15281 | |||
b558548b94 | |||
29e0f9a270 | |||
9385c32cfb | |||
ce3fb5c353 | |||
f920490518 | |||
d681e667ee | |||
5c6515a60e | |||
eb8a33312d | |||
d351b3bbe4 | |||
47d40eb801 | |||
adc4b6148d | |||
27d0d4a28b | |||
fb609c19ef |
@ -3,4 +3,3 @@ VITE_KC_API_BASE_URL=https://api.dev.zoo.dev
|
|||||||
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
VITE_KC_SITE_BASE_URL=https://dev.zoo.dev
|
||||||
VITE_KC_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
VITE_KC_CONNECTION_TIMEOUT_MS=5000
|
||||||
VITE_KC_SENTRY_DSN=
|
|
||||||
|
@ -3,4 +3,3 @@ VITE_KC_API_BASE_URL=https://api.zoo.dev
|
|||||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||||
VITE_KC_SKIP_AUTH=false
|
VITE_KC_SKIP_AUTH=false
|
||||||
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
VITE_KC_CONNECTION_TIMEOUT_MS=15000
|
||||||
VITE_KC_SENTRY_DSN=
|
|
||||||
|
16
.github/workflows/cargo-test.yml
vendored
@ -40,6 +40,20 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
|
||||||
|
- name: Install vector
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSfL https://sh.vector.dev > /tmp/vector.sh
|
||||||
|
chmod +x /tmp/vector.sh
|
||||||
|
/tmp/vector.sh -y -no-modify-path
|
||||||
|
mkdir -p /tmp/vector
|
||||||
|
cp .github/workflows/vector.toml /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_WORKFLOW#${GITHUB_WORKFLOW}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REPOSITORY#${GITHUB_REPOSITORY}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_SHA#${GITHUB_SHA}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GITHUB_REF_NAME#${GITHUB_REF_NAME}#g" /tmp/vector.toml
|
||||||
|
sed -i "s#GH_ACTIONS_AXIOM_TOKEN#${{secrets.GH_ACTIONS_AXIOM_TOKEN}}#g" /tmp/vector.toml
|
||||||
|
cat /tmp/vector.toml
|
||||||
|
${HOME}/.vector/bin/vector --config /tmp/vector.toml &
|
||||||
- uses: taiki-e/install-action@cargo-llvm-cov
|
- uses: taiki-e/install-action@cargo-llvm-cov
|
||||||
- uses: taiki-e/install-action@nextest
|
- uses: taiki-e/install-action@nextest
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
@ -48,7 +62,7 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |-
|
run: |-
|
||||||
cd "${{ matrix.dir }}"
|
cd "${{ matrix.dir }}"
|
||||||
cargo nextest run --workspace --no-fail-fast -P ci
|
cargo nextest run --workspace --no-fail-fast -P ci 2>&1 | tee /tmp/github-actions.log
|
||||||
env:
|
env:
|
||||||
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
KITTYCAD_API_TOKEN: ${{secrets.KITTYCAD_API_TOKEN}}
|
||||||
RUST_MIN_STACK: 10485760000
|
RUST_MIN_STACK: 10485760000
|
||||||
|
2
.github/workflows/ci.yml
vendored
@ -336,7 +336,7 @@ jobs:
|
|||||||
cat last_download.json
|
cat last_download.json
|
||||||
|
|
||||||
- name: Authenticate to Google Cloud
|
- name: Authenticate to Google Cloud
|
||||||
uses: 'google-github-actions/auth@v2.1.1'
|
uses: 'google-github-actions/auth@v2.1.2'
|
||||||
with:
|
with:
|
||||||
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
credentials_json: '${{ secrets.GOOGLE_CLOUD_DL_SA }}'
|
||||||
|
|
||||||
|
5
.github/workflows/playwright.yml
vendored
@ -4,6 +4,11 @@ on:
|
|||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
playwright-ubuntu:
|
playwright-ubuntu:
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
|
21
.github/workflows/vector.toml
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[sources.github-actions-file]
|
||||||
|
type = "file"
|
||||||
|
data_dir = "/tmp/vector"
|
||||||
|
include = ["/tmp/github-actions.log"]
|
||||||
|
|
||||||
|
# Modify the logs to include the action name.
|
||||||
|
[transforms.add-action-name]
|
||||||
|
type = "remap"
|
||||||
|
inputs = [ "github-actions-file" ]
|
||||||
|
source = '''
|
||||||
|
.action = "GITHUB_WORKFLOW"
|
||||||
|
.repo = "GITHUB_REPOSITORY"
|
||||||
|
.sha = "GITHUB_SHA"
|
||||||
|
.ref = "GITHUB_REF_NAME"
|
||||||
|
'''
|
||||||
|
|
||||||
|
[sinks.axiom]
|
||||||
|
type = "axiom"
|
||||||
|
inputs = ["add-action-name"]
|
||||||
|
token = "GH_ACTIONS_AXIOM_TOKEN"
|
||||||
|
dataset = "github-actions"
|
2
.gitignore
vendored
@ -54,3 +54,5 @@ e2e/playwright/export-snapshots/*embedded.gltf
|
|||||||
|
|
||||||
## generated files
|
## generated files
|
||||||
src/**/*.typegen.ts
|
src/**/*.typegen.ts
|
||||||
|
|
||||||
|
src/wasm-lib/grackle/stdlib_cube_partial.json
|
||||||
|
@ -8,10 +8,6 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
model for that sketch and its underlying 3D object.
|
model for that sketch and its underlying 3D object.
|
||||||
If you see a red line around your model, it means this is happening.
|
If you see a red line around your model, it means this is happening.
|
||||||
|
|
||||||
- **Patterns**: If you try and pass a pattern to `hole` currently only the first
|
|
||||||
item in the pattern is being subtracted. This is an engine bug that is being
|
|
||||||
worked on.
|
|
||||||
|
|
||||||
- **Import**: Right now you can import a file, even if that file has brep data
|
- **Import**: Right now you can import a file, even if that file has brep data
|
||||||
you cannot edit it, after v1, the engine will account for this. You also cannot
|
you cannot edit it, after v1, the engine will account for this. You also cannot
|
||||||
currently move or transform the imported objects at all, once we have assemblies
|
currently move or transform the imported objects at all, once we have assemblies
|
||||||
|
2443
docs/kcl/std.json
474
docs/kcl/std.md
@ -20,6 +20,7 @@
|
|||||||
* [`atan`](#atan)
|
* [`atan`](#atan)
|
||||||
* [`bezierCurve`](#bezierCurve)
|
* [`bezierCurve`](#bezierCurve)
|
||||||
* [`ceil`](#ceil)
|
* [`ceil`](#ceil)
|
||||||
|
* [`circle`](#circle)
|
||||||
* [`close`](#close)
|
* [`close`](#close)
|
||||||
* [`cos`](#cos)
|
* [`cos`](#cos)
|
||||||
* [`e`](#e)
|
* [`e`](#e)
|
||||||
@ -49,7 +50,6 @@
|
|||||||
* [`segEndX`](#segEndX)
|
* [`segEndX`](#segEndX)
|
||||||
* [`segEndY`](#segEndY)
|
* [`segEndY`](#segEndY)
|
||||||
* [`segLen`](#segLen)
|
* [`segLen`](#segLen)
|
||||||
* [`show`](#show)
|
|
||||||
* [`sin`](#sin)
|
* [`sin`](#sin)
|
||||||
* [`sqrt`](#sqrt)
|
* [`sqrt`](#sqrt)
|
||||||
* [`startProfileAt`](#startProfileAt)
|
* [`startProfileAt`](#startProfileAt)
|
||||||
@ -3438,6 +3438,290 @@ ceil(num: number) -> number
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### circle
|
||||||
|
|
||||||
|
Sketch a circle on the given plane
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
circle(plane: SketchData, center: [number, number], radius: number) -> SketchGroup
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
* `plane`: `SketchData` - Data for start sketch on. You can start a sketch on a plane or an extrude group.
|
||||||
|
```
|
||||||
|
"XY" |
|
||||||
|
"-XY" |
|
||||||
|
"XZ" |
|
||||||
|
"-XZ" |
|
||||||
|
"YZ" |
|
||||||
|
"-YZ" |
|
||||||
|
{
|
||||||
|
plane: {
|
||||||
|
// Origin of the plane.
|
||||||
|
origin: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// What should the plane’s X axis be?
|
||||||
|
x_axis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// What should the plane’s Y axis be?
|
||||||
|
y_axis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The z-axis (normal).
|
||||||
|
z_axis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The id of the extrusion end cap
|
||||||
|
endCapId: uuid,
|
||||||
|
// The height of the extrude group.
|
||||||
|
height: number,
|
||||||
|
// The id of the extrude group.
|
||||||
|
id: uuid,
|
||||||
|
// The position of the extrude group.
|
||||||
|
position: [number, number, number],
|
||||||
|
// The rotation of the extrude group.
|
||||||
|
rotation: [number, number, number, number],
|
||||||
|
// The id of the extrusion start cap
|
||||||
|
startCapId: uuid,
|
||||||
|
// The extrude surfaces.
|
||||||
|
value: [{
|
||||||
|
// The face id for the extrude plane.
|
||||||
|
faceId: uuid,
|
||||||
|
// The id of the geometry.
|
||||||
|
id: uuid,
|
||||||
|
// The name.
|
||||||
|
name: string,
|
||||||
|
// The position.
|
||||||
|
position: [number, number, number],
|
||||||
|
// The rotation.
|
||||||
|
rotation: [number, number, number, number],
|
||||||
|
// The source range.
|
||||||
|
sourceRange: [number, number],
|
||||||
|
type: "extrudePlane",
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The face id for the extrude plane.
|
||||||
|
faceId: uuid,
|
||||||
|
// The id of the geometry.
|
||||||
|
id: uuid,
|
||||||
|
// The name.
|
||||||
|
name: string,
|
||||||
|
// The position.
|
||||||
|
position: [number, number, number],
|
||||||
|
// The rotation.
|
||||||
|
rotation: [number, number, number, number],
|
||||||
|
// The source range.
|
||||||
|
sourceRange: [number, number],
|
||||||
|
type: "extrudeArc",
|
||||||
|
}],
|
||||||
|
// The x-axis of the extrude group base plane in the 3D space
|
||||||
|
xAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The y-axis of the extrude group base plane in the 3D space
|
||||||
|
yAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The z-axis of the extrude group base plane in the 3D space
|
||||||
|
zAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* `center`: `[number, number]`
|
||||||
|
* `radius`: `number`
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
* `SketchGroup` - A sketch group is a collection of paths.
|
||||||
|
```
|
||||||
|
{
|
||||||
|
// The plane id or face id of the sketch group.
|
||||||
|
entityId: uuid,
|
||||||
|
// The id of the sketch group.
|
||||||
|
id: uuid,
|
||||||
|
// What the sketch is on (can be a plane or a face).
|
||||||
|
on: {
|
||||||
|
// The id of the plane.
|
||||||
|
id: uuid,
|
||||||
|
// Origin of the plane.
|
||||||
|
origin: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
type: "plane",
|
||||||
|
// Type for a plane.
|
||||||
|
value: "XY" | "XZ" | "YZ" | "Custom",
|
||||||
|
// What should the plane’s X axis be?
|
||||||
|
xAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// What should the plane’s Y axis be?
|
||||||
|
yAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The z-axis (normal).
|
||||||
|
zAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The id of the face.
|
||||||
|
id: uuid,
|
||||||
|
// The original sketch group id of the object we are sketching on.
|
||||||
|
sketchGroupId: uuid,
|
||||||
|
type: "face",
|
||||||
|
// The tag of the face.
|
||||||
|
value: string,
|
||||||
|
// What should the face’s X axis be?
|
||||||
|
xAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// What should the face’s Y axis be?
|
||||||
|
yAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The z-axis (normal).
|
||||||
|
zAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// The position of the sketch group.
|
||||||
|
position: [number, number, number],
|
||||||
|
// The rotation of the sketch group base plane.
|
||||||
|
rotation: [number, number, number, number],
|
||||||
|
// The starting path.
|
||||||
|
start: {
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
},
|
||||||
|
// The paths in the sketch group.
|
||||||
|
value: [{
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "ToPoint",
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// arc's direction
|
||||||
|
ccw: string,
|
||||||
|
// the arc's center
|
||||||
|
center: [number, number],
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "TangentialArcTo",
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "TangentialArc",
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "Horizontal",
|
||||||
|
// The x coordinate.
|
||||||
|
x: number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "AngledLineTo",
|
||||||
|
// The x coordinate.
|
||||||
|
x: number,
|
||||||
|
// The y coordinate.
|
||||||
|
y: number,
|
||||||
|
} |
|
||||||
|
{
|
||||||
|
// The from point.
|
||||||
|
from: [number, number],
|
||||||
|
// The name of the path.
|
||||||
|
name: string,
|
||||||
|
// The to point.
|
||||||
|
to: [number, number],
|
||||||
|
type: "Base",
|
||||||
|
}],
|
||||||
|
// The x-axis of the sketch group base plane in the 3D space
|
||||||
|
xAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The y-axis of the sketch group base plane in the 3D space
|
||||||
|
yAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
// The z-axis of the sketch group base plane in the 3D space
|
||||||
|
zAxis: {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
z: number,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### close
|
### close
|
||||||
|
|
||||||
Close the current sketch.
|
Close the current sketch.
|
||||||
@ -4703,6 +4987,7 @@ hole(hole_sketch_group: SketchGroupSet, sketch_group: SketchGroup) -> SketchGrou
|
|||||||
Import a CAD file.
|
Import a CAD file.
|
||||||
|
|
||||||
For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters. Otherwise you can specify the unit by passing in the options parameter. If you import a gltf file, we will try to find the bin file and import it as well.
|
For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters. Otherwise you can specify the unit by passing in the options parameter. If you import a gltf file, we will try to find the bin file and import it as well.
|
||||||
|
Import paths are relative to the current project directory. This only works in the desktop app not in browser.
|
||||||
|
|
||||||
```
|
```
|
||||||
import(file_path: String, options: ImportFormat) -> ImportedGeometry
|
import(file_path: String, options: ImportFormat) -> ImportedGeometry
|
||||||
@ -6086,8 +6371,8 @@ patternCircular(data: CircularPatternData, geometry: Geometry) -> Geometries
|
|||||||
{
|
{
|
||||||
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
||||||
arcDegrees: number,
|
arcDegrees: number,
|
||||||
// The axis around which to make the pattern. This is a 3D vector.
|
// The axis around which to make the pattern. This is a 2D vector.
|
||||||
axis: [number, number, number],
|
axis: [number, number],
|
||||||
// The center about which to make th pattern. This is a 3D vector.
|
// The center about which to make th pattern. This is a 3D vector.
|
||||||
center: [number, number, number],
|
center: [number, number, number],
|
||||||
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
||||||
@ -6355,8 +6640,8 @@ patternLinear(data: LinearPatternData, geometry: Geometry) -> Geometries
|
|||||||
* `data`: `LinearPatternData` - Data for a linear pattern.
|
* `data`: `LinearPatternData` - Data for a linear pattern.
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
// The axis of the pattern. This is a 3D vector.
|
// The axis of the pattern. This is a 2D vector.
|
||||||
axis: [number, number, number],
|
axis: [number, number],
|
||||||
// The distance between each repetition. This can also be referred to as spacing.
|
// The distance between each repetition. This can also be referred to as spacing.
|
||||||
distance: number,
|
distance: number,
|
||||||
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
// The number of repetitions. Must be greater than 0. This excludes the original entity. For example, if `repetitions` is 1, the original entity will be copied once.
|
||||||
@ -7383,185 +7668,6 @@ segLen(segment_name: string, sketch_group: SketchGroup) -> number
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
### show
|
|
||||||
|
|
||||||
Render a model.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
show(sketch: SketchGroup)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Arguments
|
|
||||||
|
|
||||||
* `sketch`: `SketchGroup` - A sketch group is a collection of paths.
|
|
||||||
```
|
|
||||||
{
|
|
||||||
// The plane id or face id of the sketch group.
|
|
||||||
entityId: uuid,
|
|
||||||
// The id of the sketch group.
|
|
||||||
id: uuid,
|
|
||||||
// What the sketch is on (can be a plane or a face).
|
|
||||||
on: {
|
|
||||||
// The id of the plane.
|
|
||||||
id: uuid,
|
|
||||||
// Origin of the plane.
|
|
||||||
origin: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
type: "plane",
|
|
||||||
// Type for a plane.
|
|
||||||
value: "XY" | "XZ" | "YZ" | "Custom",
|
|
||||||
// What should the plane’s X axis be?
|
|
||||||
xAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// What should the plane’s Y axis be?
|
|
||||||
yAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// The z-axis (normal).
|
|
||||||
zAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// The id of the face.
|
|
||||||
id: uuid,
|
|
||||||
// The original sketch group id of the object we are sketching on.
|
|
||||||
sketchGroupId: uuid,
|
|
||||||
type: "face",
|
|
||||||
// The tag of the face.
|
|
||||||
value: string,
|
|
||||||
// What should the face’s X axis be?
|
|
||||||
xAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// What should the face’s Y axis be?
|
|
||||||
yAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// The z-axis (normal).
|
|
||||||
zAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// The position of the sketch group.
|
|
||||||
position: [number, number, number],
|
|
||||||
// The rotation of the sketch group base plane.
|
|
||||||
rotation: [number, number, number, number],
|
|
||||||
// The starting path.
|
|
||||||
start: {
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
},
|
|
||||||
// The paths in the sketch group.
|
|
||||||
value: [{
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "ToPoint",
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// arc's direction
|
|
||||||
ccw: string,
|
|
||||||
// the arc's center
|
|
||||||
center: [number, number],
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "TangentialArcTo",
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "TangentialArc",
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "Horizontal",
|
|
||||||
// The x coordinate.
|
|
||||||
x: number,
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "AngledLineTo",
|
|
||||||
// The x coordinate.
|
|
||||||
x: number,
|
|
||||||
// The y coordinate.
|
|
||||||
y: number,
|
|
||||||
} |
|
|
||||||
{
|
|
||||||
// The from point.
|
|
||||||
from: [number, number],
|
|
||||||
// The name of the path.
|
|
||||||
name: string,
|
|
||||||
// The to point.
|
|
||||||
to: [number, number],
|
|
||||||
type: "Base",
|
|
||||||
}],
|
|
||||||
// The x-axis of the sketch group base plane in the 3D space
|
|
||||||
xAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// The y-axis of the sketch group base plane in the 3D space
|
|
||||||
yAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
// The z-axis of the sketch group base plane in the 3D space
|
|
||||||
zAxis: {
|
|
||||||
x: number,
|
|
||||||
y: number,
|
|
||||||
z: number,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### sin
|
### sin
|
||||||
|
|
||||||
Computes the sine of a number (in radians).
|
Computes the sine of a number (in radians).
|
||||||
|
@ -3,6 +3,7 @@ import { secrets } from './secrets'
|
|||||||
import { getUtils } from './test-utils'
|
import { getUtils } from './test-utils'
|
||||||
import waitOn from 'wait-on'
|
import waitOn from 'wait-on'
|
||||||
import { Themes } from '../../src/lib/theme'
|
import { Themes } from '../../src/lib/theme'
|
||||||
|
import { roundOff } from 'lib/utils'
|
||||||
|
|
||||||
/*
|
/*
|
||||||
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
|
debug helper: unfortunately we do rely on exact coord mouse clicks in a few places
|
||||||
@ -15,9 +16,9 @@ document.addEventListener('mousemove', (e) =>
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const commonPoints = {
|
const commonPoints = {
|
||||||
startAt: '[26.38, -35.59]',
|
startAt: '[9.06, -12.22]',
|
||||||
num1: 26.63,
|
num1: 9.14,
|
||||||
num2: 53.01,
|
num2: 18.2,
|
||||||
}
|
}
|
||||||
|
|
||||||
test.beforeEach(async ({ context, page }) => {
|
test.beforeEach(async ({ context, page }) => {
|
||||||
@ -65,10 +66,8 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
|
|
||||||
// click on "Start Sketch" button
|
// click on "Start Sketch" button
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await u.doAndWaitForImageDiff(
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
await page.waitForTimeout(100)
|
||||||
200
|
|
||||||
)
|
|
||||||
|
|
||||||
// select a plane
|
// select a plane
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
@ -90,7 +89,6 @@ test('Basic sketch', async ({ page }) => {
|
|||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const num = 26.63
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
@ -284,10 +282,9 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
}) => {
|
}) => {
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
await u.updateCamPosition(viewCmd)
|
|
||||||
|
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
||||||
|
await u.updateCamPosition(viewCmd)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(clickCoords.x, clickCoords.y)
|
await page.mouse.click(clickCoords.x, clickCoords.y)
|
||||||
@ -315,7 +312,7 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
const codeTemplate = (
|
const codeTemplate = (
|
||||||
plane = 'XY'
|
plane = 'XY'
|
||||||
) => `const part001 = startSketchOn('${plane}')
|
) => `const part001 = startSketchOn('${plane}')
|
||||||
|> startProfileAt([32.13, -43.34], %)`
|
|> startProfileAt([1.14, -1.54], %)`
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camPos,
|
viewCmd: camPos,
|
||||||
expectedCode: codeTemplate('XY'),
|
expectedCode: codeTemplate('XY'),
|
||||||
@ -325,7 +322,7 @@ test('Can create sketches on all planes and their back sides', async ({
|
|||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camPos,
|
viewCmd: camPos,
|
||||||
expectedCode: codeTemplate('YZ'),
|
expectedCode: codeTemplate('YZ'),
|
||||||
clickCoords: { x: 700, y: 300 }, // green plane
|
clickCoords: { x: 700, y: 250 }, // green plane
|
||||||
})
|
})
|
||||||
await TestSinglePlane({
|
await TestSinglePlane({
|
||||||
viewCmd: camPos,
|
viewCmd: camPos,
|
||||||
@ -386,12 +383,16 @@ test('Auto complete works', async ({ page }) => {
|
|||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('ArrowDown')
|
await page.keyboard.press('ArrowDown')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.keyboard.type('(5, %)')
|
// finish line with comment
|
||||||
|
await page.keyboard.type('(5, %) // lin')
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
// there shouldn't be any auto complete options for 'lin' in the comment
|
||||||
|
await expect(page.locator('.cm-completionLabel')).not.toBeVisible()
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('XY')
|
.toHaveText(`const part001 = startSketchOn('XY')
|
||||||
|> startProfileAt([0,0], %)
|
|> startProfileAt([0,0], %)
|
||||||
|> xLine(5, %)`)
|
|> xLine(5, %) // lin`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Onboarding tests
|
// Onboarding tests
|
||||||
@ -699,6 +700,8 @@ test('Can extrude from the command bar', async ({ page, context }) => {
|
|||||||
).toBeDisabled()
|
).toBeDisabled()
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
|
|
||||||
// Check that the code was updated
|
// Check that the code was updated
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
// Unfortunately this indentation seems to matter for the test
|
// Unfortunately this indentation seems to matter for the test
|
||||||
@ -793,7 +796,7 @@ test('Can add multiple sketches', async ({ page }) => {
|
|||||||
await u.clearAndCloseDebugPanel()
|
await u.clearAndCloseDebugPanel()
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
const startAt2 = '[26.23, -35.39]'
|
const startAt2 = '[0.93,-1.25]'
|
||||||
await expect(
|
await expect(
|
||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
@ -807,7 +810,7 @@ const part002 = startSketchOn('XY')
|
|||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const num2 = 26.48
|
const num2 = 0.94
|
||||||
await expect(
|
await expect(
|
||||||
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
(await page.locator('.cm-content').innerText()).replace(/\s/g, '')
|
||||||
).toBe(
|
).toBe(
|
||||||
@ -825,7 +828,7 @@ const part002 = startSketchOn('XY')
|
|||||||
const part002 = startSketchOn('XY')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)
|
|> startProfileAt(${startAt2}, %)
|
||||||
|> line([${num2}, 0], %)
|
|> line([${num2}, 0], %)
|
||||||
|> line([0, ${num2}], %)`.replace(/\s/g, '')
|
|> line([0, ${roundOff(num2 - 0.01)}], %)`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(
|
await expect(
|
||||||
@ -835,8 +838,8 @@ const part002 = startSketchOn('XY')
|
|||||||
const part002 = startSketchOn('XY')
|
const part002 = startSketchOn('XY')
|
||||||
|> startProfileAt(${startAt2}, %)
|
|> startProfileAt(${startAt2}, %)
|
||||||
|> line([${num2}, 0], %)
|
|> line([${num2}, 0], %)
|
||||||
|> line([0, ${num2}], %)
|
|> line([0, ${roundOff(num2 - 0.01)}], %)
|
||||||
|> line([-52.71, 0], %)`.replace(/\s/g, '')
|
|> line([-1.87, 0], %)`.replace(/\s/g, '')
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -384,13 +384,13 @@ test('extrude on each default plane should be stable', async ({
|
|||||||
}) => {
|
}) => {
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|
const makeCode = (plane = 'XY') => `const part001 = startSketchOn('${plane}')
|
||||||
|> startProfileAt([0.70, 0.44], %)
|
|> startProfileAt([7.00, 4.40], %)
|
||||||
|> line([0.66, -0.02], %)
|
|> line([6.60, -0.20], %)
|
||||||
|> line([0.28, 0.50], %)
|
|> line([2.80, 5.00], %)
|
||||||
|> line([-0.56, 0.44], %)
|
|> line([-5.60, 4.40], %)
|
||||||
|> line([-0.54, -0.38], %)
|
|> line([-5.40, -3.80], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1.00, %)
|
|> extrude(10.00, %)
|
||||||
`
|
`
|
||||||
await context.addInitScript(async (code) => {
|
await context.addInitScript(async (code) => {
|
||||||
localStorage.setItem('persistCode', code)
|
localStorage.setItem('persistCode', code)
|
||||||
@ -435,7 +435,23 @@ test('extrude on each default plane should be stable', async ({
|
|||||||
await runSnapshotsForOtherPlanes('-YZ')
|
await runSnapshotsForOtherPlanes('-YZ')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Draft segments should look right', async ({ page }) => {
|
test('Draft segments should look right', async ({ page, context }) => {
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'SETTINGS_PERSIST_KEY',
|
||||||
|
JSON.stringify({
|
||||||
|
baseUnit: 'in',
|
||||||
|
cameraControls: 'KittyCAD',
|
||||||
|
defaultDirectory: '',
|
||||||
|
defaultProjectName: 'project-$nnn',
|
||||||
|
onboardingStatus: 'dismissed',
|
||||||
|
showDebugPanel: true,
|
||||||
|
textWrapping: 'On',
|
||||||
|
theme: 'system',
|
||||||
|
unitSystem: 'imperial',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
const u = getUtils(page)
|
const u = getUtils(page)
|
||||||
await page.setViewportSize({ width: 1200, height: 500 })
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
@ -468,7 +484,7 @@ test('Draft segments should look right', async ({ page }) => {
|
|||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
|> startProfileAt([9.06, -12.22], %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
@ -482,8 +498,8 @@ test('Draft segments should look right', async ({ page }) => {
|
|||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`const part001 = startSketchOn('-XZ')
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> startProfileAt([9.06, -12.22], %)
|
||||||
|> line([${commonPoints.num1}, 0], %)`)
|
|> line([9.14, 0], %)`)
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
|
||||||
@ -493,3 +509,202 @@ test('Draft segments should look right', async ({ page }) => {
|
|||||||
maxDiffPixels: 100,
|
maxDiffPixels: 100,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Client side scene scale should match engine scale inch', async ({
|
||||||
|
page,
|
||||||
|
context,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'SETTINGS_PERSIST_KEY',
|
||||||
|
JSON.stringify({
|
||||||
|
baseUnit: 'in',
|
||||||
|
cameraControls: 'KittyCAD',
|
||||||
|
defaultDirectory: '',
|
||||||
|
defaultProjectName: 'project-$nnn',
|
||||||
|
onboardingStatus: 'dismissed',
|
||||||
|
showDebugPanel: true,
|
||||||
|
textWrapping: 'On',
|
||||||
|
theme: 'system',
|
||||||
|
unitSystem: 'imperial',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.doAndWaitForImageDiff(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
200
|
||||||
|
)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const part001 = startSketchOn('-XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([9.06, -12.22], %)`)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([9.06, -12.22], %)
|
||||||
|
|> line([9.14, 0], %)`)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([9.06, -12.22], %)
|
||||||
|
|> line([9.14, 0], %)
|
||||||
|
|> tangentialArcTo([27.34, -3.08], %)`)
|
||||||
|
|
||||||
|
// click tangential arc tool again to unequip it
|
||||||
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// screen shot should show the sketch
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
// exit sketch
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// second screen shot should look almost identical, i.e. scale should be the same.
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Client side scene scale should match engine scale mm', async ({
|
||||||
|
page,
|
||||||
|
context,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript(async () => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'SETTINGS_PERSIST_KEY',
|
||||||
|
JSON.stringify({
|
||||||
|
baseUnit: 'mm',
|
||||||
|
cameraControls: 'KittyCAD',
|
||||||
|
defaultDirectory: '',
|
||||||
|
defaultProjectName: 'project-$nnn',
|
||||||
|
onboardingStatus: 'dismissed',
|
||||||
|
showDebugPanel: true,
|
||||||
|
textWrapping: 'On',
|
||||||
|
theme: 'system',
|
||||||
|
unitSystem: 'metric',
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
const u = getUtils(page)
|
||||||
|
await page.setViewportSize({ width: 1200, height: 500 })
|
||||||
|
const PUR = 400 / 37.5 //pixeltoUnitRatio
|
||||||
|
await page.goto('/')
|
||||||
|
await u.waitForAuthSkipAppStart()
|
||||||
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).not.toBeDisabled()
|
||||||
|
await expect(page.getByRole('button', { name: 'Start Sketch' })).toBeVisible()
|
||||||
|
|
||||||
|
// click on "Start Sketch" button
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await u.doAndWaitForImageDiff(
|
||||||
|
() => page.getByRole('button', { name: 'Start Sketch' }).click(),
|
||||||
|
200
|
||||||
|
)
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
|
`const part001 = startSketchOn('-XZ')`
|
||||||
|
)
|
||||||
|
|
||||||
|
await page.waitForTimeout(300) // TODO detect animation ending, or disable animation
|
||||||
|
|
||||||
|
const startXPx = 600
|
||||||
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([230.03, -310.33], %)`)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([230.03, -310.33], %)
|
||||||
|
|> line([232.2, 0], %)`)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
|
await expect(page.locator('.cm-content'))
|
||||||
|
.toHaveText(`const part001 = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([230.03, -310.33], %)
|
||||||
|
|> line([232.2, 0], %)
|
||||||
|
|> tangentialArcTo([694.43, -78.12], %)`)
|
||||||
|
|
||||||
|
await page.getByRole('button', { name: 'Tangential Arc' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// screen shot should show the sketch
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
|
||||||
|
// exit sketch
|
||||||
|
await u.openAndClearDebugPanel()
|
||||||
|
await page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||||
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.clearAndCloseDebugPanel()
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
|
||||||
|
// second screen shot should look almost identical, i.e. scale should be the same.
|
||||||
|
await expect(page).toHaveScreenshot({
|
||||||
|
maxDiffPixels: 100,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 36 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 126 KiB |
Before Width: | Height: | Size: 94 KiB After Width: | Height: | Size: 115 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 50 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 53 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "untitled-app",
|
"name": "untitled-app",
|
||||||
"version": "0.15.2",
|
"version": "0.15.4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/autocomplete": "^6.10.2",
|
"@codemirror/autocomplete": "^6.10.2",
|
||||||
@ -10,12 +10,11 @@
|
|||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.17",
|
"@headlessui/react": "^1.7.17",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "^0.0.53",
|
"@kittycad/lib": "^0.0.54",
|
||||||
"@lezer/javascript": "^1.4.9",
|
"@lezer/javascript": "^1.4.9",
|
||||||
"@open-rpc/client-js": "^1.8.1",
|
"@open-rpc/client-js": "^1.8.1",
|
||||||
"@react-hook/resize-observer": "^1.2.6",
|
"@react-hook/resize-observer": "^1.2.6",
|
||||||
"@replit/codemirror-interact": "^6.3.0",
|
"@replit/codemirror-interact": "^6.3.0",
|
||||||
"@sentry/react": "^7.77.0",
|
|
||||||
"@tauri-apps/api": "^1.5.1",
|
"@tauri-apps/api": "^1.5.1",
|
||||||
"@testing-library/jest-dom": "^5.14.1",
|
"@testing-library/jest-dom": "^5.14.1",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
|
@ -18,7 +18,7 @@ export default defineConfig({
|
|||||||
/* Retry on CI only */
|
/* Retry on CI only */
|
||||||
retries: process.env.CI ? 3 : 0,
|
retries: process.env.CI ? 3 : 0,
|
||||||
/* Opt out of parallel tests on CI. */
|
/* Opt out of parallel tests on CI. */
|
||||||
workers: process.env.CI ? 1 : 1,
|
workers: process.env.CI ? 2 : 1,
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
||||||
reporter: 'html',
|
reporter: 'html',
|
||||||
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
|
||||||
|
22
src-tauri/Cargo.lock
generated
@ -67,9 +67,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "anyhow"
|
name = "anyhow"
|
||||||
version = "1.0.79"
|
version = "1.0.80"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
|
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "app"
|
name = "app"
|
||||||
@ -1664,9 +1664,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.53"
|
version = "0.2.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a086e1a1bbddb3b38959c0f0ce6de6b3a3b7566e38e0b7d5fb101e91911beed4"
|
checksum = "049c3881ffbe77bf1c3a968372a246ce906eceb79f61cd0bc5fa229bec3504cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -3235,9 +3235,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.196"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "870026e60fa08c69f064aa766c10f10b1d62db9ccd4d0abb206472bee0ce3b32"
|
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
@ -3253,9 +3253,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.196"
|
version = "1.0.197"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -3275,9 +3275,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.113"
|
version = "1.0.114"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -3872,7 +3872,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-fs-extra"
|
name = "tauri-plugin-fs-extra"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#01211ff0759d578e0e9ac8c98c31fdf09077eb34"
|
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#ed682dd96eb765e7cd3cdbc3cc64f794a0d6f9df"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -16,7 +16,7 @@ tauri-build = { version = "1.5.1", features = [] }
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
kittycad = "0.2.53"
|
kittycad = "0.2.58"
|
||||||
oauth2 = "4.4.2"
|
oauth2 = "4.4.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "zoo-modeling-app",
|
"productName": "zoo-modeling-app",
|
||||||
"version": "0.15.2"
|
"version": "0.15.4"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
@ -38,8 +38,6 @@ import { ContextFrom } from 'xstate'
|
|||||||
import CommandBarProvider, {
|
import CommandBarProvider, {
|
||||||
CommandBar,
|
CommandBar,
|
||||||
} from 'components/CommandBar/CommandBar'
|
} from 'components/CommandBar/CommandBar'
|
||||||
import { TEST, VITE_KC_SENTRY_DSN } from './env'
|
|
||||||
import * as Sentry from '@sentry/react'
|
|
||||||
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
import ModelingMachineProvider from 'components/ModelingMachineProvider'
|
||||||
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
|
import { KclContextProvider, kclManager } from 'lang/KclSingleton'
|
||||||
import FileMachineProvider from 'components/FileMachineProvider'
|
import FileMachineProvider from 'components/FileMachineProvider'
|
||||||
@ -48,38 +46,6 @@ import { paths } from 'lib/paths'
|
|||||||
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
|
import { IndexLoaderData, HomeLoaderData } from 'lib/types'
|
||||||
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
||||||
|
|
||||||
if (VITE_KC_SENTRY_DSN && !TEST) {
|
|
||||||
Sentry.init({
|
|
||||||
dsn: VITE_KC_SENTRY_DSN,
|
|
||||||
// TODO(paultag): pass in the right env here.
|
|
||||||
// environment: "production",
|
|
||||||
integrations: [
|
|
||||||
new Sentry.BrowserTracing({
|
|
||||||
routingInstrumentation: Sentry.reactRouterV6Instrumentation(
|
|
||||||
useEffect,
|
|
||||||
useLocation,
|
|
||||||
useNavigationType,
|
|
||||||
createRoutesFromChildren,
|
|
||||||
matchRoutes
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
new Sentry.Replay(),
|
|
||||||
],
|
|
||||||
|
|
||||||
// Set tracesSampleRate to 1.0 to capture 100%
|
|
||||||
// of transactions for performance monitoring.
|
|
||||||
tracesSampleRate: 1.0,
|
|
||||||
|
|
||||||
// TODO: Add in kittycad.io endpoints
|
|
||||||
tracePropagationTargets: ['localhost'],
|
|
||||||
|
|
||||||
// Capture Replay for 10% of all sessions,
|
|
||||||
// plus for 100% of sessions with an error
|
|
||||||
replaysSessionSampleRate: 0.1,
|
|
||||||
replaysOnErrorSampleRate: 1.0,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BROWSER_FILE_NAME = 'new'
|
export const BROWSER_FILE_NAME = 'new'
|
||||||
|
|
||||||
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
type CreateBrowserRouterArg = Parameters<typeof createBrowserRouter>[0]
|
||||||
|
@ -16,7 +16,11 @@ import {
|
|||||||
SKETCH_LAYER,
|
SKETCH_LAYER,
|
||||||
ZOOM_MAGIC_NUMBER,
|
ZOOM_MAGIC_NUMBER,
|
||||||
} from './sceneInfra'
|
} from './sceneInfra'
|
||||||
import { EngineCommand, engineCommandManager } from 'lang/std/engineConnection'
|
import {
|
||||||
|
EngineCommand,
|
||||||
|
Subscription,
|
||||||
|
engineCommandManager,
|
||||||
|
} from 'lang/std/engineConnection'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { deg2Rad } from 'lib/utils2d'
|
import { deg2Rad } from 'lib/utils2d'
|
||||||
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
import { isReducedMotion, roundOff, throttle } from 'lib/utils'
|
||||||
@ -28,6 +32,12 @@ const FRAMES_TO_ANIMATE_IN = 30
|
|||||||
|
|
||||||
const tempQuaternion = new Quaternion() // just used for maths
|
const tempQuaternion = new Quaternion() // just used for maths
|
||||||
|
|
||||||
|
type interactionType = 'pan' | 'rotate' | 'zoom'
|
||||||
|
|
||||||
|
const throttledEngCmd = throttle((cmd: EngineCommand) => {
|
||||||
|
engineCommandManager.sendSceneCommand(cmd)
|
||||||
|
}, 1000 / 15)
|
||||||
|
|
||||||
interface ThreeCamValues {
|
interface ThreeCamValues {
|
||||||
position: Vector3
|
position: Vector3
|
||||||
quaternion: Quaternion
|
quaternion: Quaternion
|
||||||
@ -110,10 +120,11 @@ const throttledUpdateEngineFov = throttle(
|
|||||||
lastCmdDelay
|
lastCmdDelay
|
||||||
) as any as number
|
) as any as number
|
||||||
},
|
},
|
||||||
1000 / 15
|
1000 / 30
|
||||||
)
|
)
|
||||||
|
|
||||||
export class CameraControls {
|
export class CameraControls {
|
||||||
|
syncDirection: 'clientToEngine' | 'engineToClient' = 'engineToClient'
|
||||||
camera: PerspectiveCamera | OrthographicCamera
|
camera: PerspectiveCamera | OrthographicCamera
|
||||||
target: Vector3
|
target: Vector3
|
||||||
domElement: HTMLCanvasElement
|
domElement: HTMLCanvasElement
|
||||||
@ -151,6 +162,17 @@ export class CameraControls {
|
|||||||
get isPerspective() {
|
get isPerspective() {
|
||||||
return this.camera instanceof PerspectiveCamera
|
return this.camera instanceof PerspectiveCamera
|
||||||
}
|
}
|
||||||
|
private debounceTimer = 0
|
||||||
|
|
||||||
|
handleStart = () => {
|
||||||
|
if (this.debounceTimer) clearTimeout(this.debounceTimer)
|
||||||
|
this._isCamMovingCallback(true, false)
|
||||||
|
}
|
||||||
|
handleEnd = () => {
|
||||||
|
this.debounceTimer = setTimeout(() => {
|
||||||
|
this._isCamMovingCallback(false, false)
|
||||||
|
}, 400) as any as number
|
||||||
|
}
|
||||||
|
|
||||||
// reacts hooks into some of this singleton's properties
|
// reacts hooks into some of this singleton's properties
|
||||||
reactCameraProperties: ReactCameraProperties = {
|
reactCameraProperties: ReactCameraProperties = {
|
||||||
@ -209,6 +231,46 @@ export class CameraControls {
|
|||||||
this.onWindowResize()
|
this.onWindowResize()
|
||||||
|
|
||||||
this.update()
|
this.update()
|
||||||
|
this._usePerspectiveCamera()
|
||||||
|
|
||||||
|
const cb: Subscription<
|
||||||
|
'default_camera_zoom' | 'camera_drag_end' | 'default_camera_get_settings'
|
||||||
|
>['callback'] = ({ data, type }) => {
|
||||||
|
const camSettings = data.settings
|
||||||
|
this.camera.position.set(
|
||||||
|
camSettings.pos.x,
|
||||||
|
camSettings.pos.y,
|
||||||
|
camSettings.pos.z
|
||||||
|
)
|
||||||
|
this.target.set(
|
||||||
|
camSettings.center.x,
|
||||||
|
camSettings.center.y,
|
||||||
|
camSettings.center.z
|
||||||
|
)
|
||||||
|
if (this.camera instanceof PerspectiveCamera && camSettings.fov_y) {
|
||||||
|
this.camera.fov = camSettings.fov_y
|
||||||
|
} else if (
|
||||||
|
this.camera instanceof OrthographicCamera &&
|
||||||
|
camSettings.ortho_scale
|
||||||
|
) {
|
||||||
|
this.camera.zoom = camSettings.ortho_scale
|
||||||
|
}
|
||||||
|
this.onCameraChange()
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
engineCommandManager.subscribeTo({
|
||||||
|
event: 'camera_drag_end',
|
||||||
|
callback: cb,
|
||||||
|
})
|
||||||
|
engineCommandManager.subscribeTo({
|
||||||
|
event: 'default_camera_zoom',
|
||||||
|
callback: cb,
|
||||||
|
})
|
||||||
|
engineCommandManager.subscribeTo({
|
||||||
|
event: 'default_camera_get_settings',
|
||||||
|
callback: cb,
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
|
private _isCamMovingCallback: (isMoving: boolean, isTween: boolean) => void =
|
||||||
@ -242,6 +304,21 @@ export class CameraControls {
|
|||||||
onMouseDown = (event: MouseEvent) => {
|
onMouseDown = (event: MouseEvent) => {
|
||||||
this.isDragging = true
|
this.isDragging = true
|
||||||
this.mouseDownPosition.set(event.clientX, event.clientY)
|
this.mouseDownPosition.set(event.clientX, event.clientY)
|
||||||
|
let interaction = this.getInteractionType(event)
|
||||||
|
if (interaction === 'none') return
|
||||||
|
this.handleStart()
|
||||||
|
|
||||||
|
if (this.syncDirection === 'engineToClient') {
|
||||||
|
void engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'camera_drag_start',
|
||||||
|
interaction,
|
||||||
|
window: { x: event.clientX, y: event.clientY },
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseMove = (event: MouseEvent) => {
|
onMouseMove = (event: MouseEvent) => {
|
||||||
@ -252,36 +329,34 @@ export class CameraControls {
|
|||||||
.sub(this.mouseDownPosition)
|
.sub(this.mouseDownPosition)
|
||||||
this.mouseDownPosition.copy(this.mouseNewPosition)
|
this.mouseDownPosition.copy(this.mouseNewPosition)
|
||||||
|
|
||||||
let state: 'pan' | 'rotate' | 'zoom' = 'pan'
|
const interaction = this.getInteractionType(event)
|
||||||
|
if (interaction === 'none') return
|
||||||
|
|
||||||
if (this.interactionGuards.pan.callback(event as any)) {
|
if (this.syncDirection === 'engineToClient') {
|
||||||
if (this.enablePan === false) return
|
throttledEngCmd({
|
||||||
// handleMouseDownPan(event)
|
type: 'modeling_cmd_req',
|
||||||
state = 'pan'
|
cmd: {
|
||||||
} else if (this.interactionGuards.rotate.callback(event as any)) {
|
type: 'camera_drag_move',
|
||||||
if (this.enableRotate === false) return
|
interaction,
|
||||||
// handleMouseDownRotate(event)
|
window: { x: event.clientX, y: event.clientY },
|
||||||
state = 'rotate'
|
},
|
||||||
} else if (this.interactionGuards.zoom.dragCallback(event as any)) {
|
cmd_id: uuidv4(),
|
||||||
if (this.enableZoom === false) return
|
})
|
||||||
// handleMouseDownDolly(event)
|
|
||||||
state = 'zoom'
|
|
||||||
} else {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement camera movement logic here based on deltaMove
|
// Implement camera movement logic here based on deltaMove
|
||||||
// For example, for rotating the camera around the target:
|
// For example, for rotating the camera around the target:
|
||||||
if (state === 'rotate') {
|
if (interaction === 'rotate') {
|
||||||
this.pendingRotation = this.pendingRotation
|
this.pendingRotation = this.pendingRotation
|
||||||
? this.pendingRotation
|
? this.pendingRotation
|
||||||
: new Vector2()
|
: new Vector2()
|
||||||
this.pendingRotation.x += deltaMove.x
|
this.pendingRotation.x += deltaMove.x
|
||||||
this.pendingRotation.y += deltaMove.y
|
this.pendingRotation.y += deltaMove.y
|
||||||
} else if (state === 'zoom') {
|
} else if (interaction === 'zoom') {
|
||||||
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
||||||
this.pendingZoom *= 1 + deltaMove.y * 0.01
|
this.pendingZoom *= 1 + deltaMove.y * 0.01
|
||||||
} else if (state === 'pan') {
|
} else if (interaction === 'pan') {
|
||||||
this.pendingPan = this.pendingPan ? this.pendingPan : new Vector2()
|
this.pendingPan = this.pendingPan ? this.pendingPan : new Vector2()
|
||||||
let distance = this.camera.position.distanceTo(this.target)
|
let distance = this.camera.position.distanceTo(this.target)
|
||||||
if (this.camera instanceof OrthographicCamera) {
|
if (this.camera instanceof OrthographicCamera) {
|
||||||
@ -297,15 +372,52 @@ export class CameraControls {
|
|||||||
|
|
||||||
onMouseUp = (event: MouseEvent) => {
|
onMouseUp = (event: MouseEvent) => {
|
||||||
this.isDragging = false
|
this.isDragging = false
|
||||||
|
this.handleEnd()
|
||||||
|
if (this.syncDirection === 'engineToClient') {
|
||||||
|
const interaction = this.getInteractionType(event)
|
||||||
|
if (interaction === 'none') return
|
||||||
|
void engineCommandManager.sendSceneCommand({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'camera_drag_end',
|
||||||
|
interaction,
|
||||||
|
window: { x: event.clientX, y: event.clientY },
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseWheel = (event: WheelEvent) => {
|
onMouseWheel = (event: WheelEvent) => {
|
||||||
// Assume trackpad if the deltas are small and integers
|
// Assume trackpad if the deltas are small and integers
|
||||||
|
this.handleStart()
|
||||||
|
|
||||||
|
if (this.syncDirection === 'engineToClient') {
|
||||||
|
const interactions = this.interactionGuards.zoom.scrollCallback(
|
||||||
|
event as any
|
||||||
|
)
|
||||||
|
if (!interactions) {
|
||||||
|
this.handleEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
throttledEngCmd({
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_zoom',
|
||||||
|
magnitude: -event.deltaY * 0.4,
|
||||||
|
},
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
})
|
||||||
|
this.handleEnd()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const isTrackpad = Math.abs(event.deltaY) <= 1 || event.deltaY % 1 === 0
|
const isTrackpad = Math.abs(event.deltaY) <= 1 || event.deltaY % 1 === 0
|
||||||
|
|
||||||
const zoomSpeed = isTrackpad ? 0.02 : 0.1 // Reduced zoom speed for trackpad
|
const zoomSpeed = isTrackpad ? 0.02 : 0.1 // Reduced zoom speed for trackpad
|
||||||
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
this.pendingZoom = this.pendingZoom ? this.pendingZoom : 1
|
||||||
this.pendingZoom *= 1 + (event.deltaY > 0 ? zoomSpeed : -zoomSpeed)
|
this.pendingZoom *= 1 + (event.deltaY > 0 ? zoomSpeed : -zoomSpeed)
|
||||||
|
this.handleEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
useOrthographicCamera = () => {
|
useOrthographicCamera = () => {
|
||||||
@ -358,7 +470,7 @@ export class CameraControls {
|
|||||||
|
|
||||||
return this.camera
|
return this.camera
|
||||||
}
|
}
|
||||||
usePerspectiveCamera = () => {
|
_usePerspectiveCamera = () => {
|
||||||
const { x: px, y: py, z: pz } = this.camera.position
|
const { x: px, y: py, z: pz } = this.camera.position
|
||||||
const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion
|
const { x: qx, y: qy, z: qz, w: qw } = this.camera.quaternion
|
||||||
const zoom = this.camera.zoom
|
const zoom = this.camera.zoom
|
||||||
@ -374,14 +486,17 @@ export class CameraControls {
|
|||||||
)
|
)
|
||||||
direction.normalize()
|
direction.normalize()
|
||||||
this.camera.position.copy(this.target).addScaledVector(direction, distance)
|
this.camera.position.copy(this.target).addScaledVector(direction, distance)
|
||||||
|
}
|
||||||
|
usePerspectiveCamera = () => {
|
||||||
|
this._usePerspectiveCamera()
|
||||||
engineCommandManager.sendSceneCommand({
|
engineCommandManager.sendSceneCommand({
|
||||||
type: 'modeling_cmd_req',
|
type: 'modeling_cmd_req',
|
||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_set_perspective',
|
type: 'default_camera_set_perspective',
|
||||||
parameters: {
|
parameters: {
|
||||||
fov_y: this.camera.fov,
|
fov_y:
|
||||||
|
this.camera instanceof PerspectiveCamera ? this.camera.fov : 45,
|
||||||
...calculateNearFarFromFOV(this.lastPerspectiveFov),
|
...calculateNearFarFromFOV(this.lastPerspectiveFov),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -618,6 +733,10 @@ export class CameraControls {
|
|||||||
duration = 500,
|
duration = 500,
|
||||||
toOrthographic = true
|
toOrthographic = true
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
if (this.syncDirection === 'engineToClient')
|
||||||
|
console.warn(
|
||||||
|
'tweenCameraToQuaternion not design to work with engineToClient syncDirection.'
|
||||||
|
)
|
||||||
const isVertical = isQuaternionVertical(targetQuaternion)
|
const isVertical = isQuaternionVertical(targetQuaternion)
|
||||||
let remainingDuration = duration
|
let remainingDuration = duration
|
||||||
if (isVertical) {
|
if (isVertical) {
|
||||||
@ -700,6 +819,10 @@ export class CameraControls {
|
|||||||
|
|
||||||
animateToOrthographic = () =>
|
animateToOrthographic = () =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
|
if (this.syncDirection === 'engineToClient')
|
||||||
|
console.warn(
|
||||||
|
'animate To Orthographic not design to work with engineToClient syncDirection.'
|
||||||
|
)
|
||||||
this.isFovAnimationInProgress = true
|
this.isFovAnimationInProgress = true
|
||||||
let currentFov = this.lastPerspectiveFov
|
let currentFov = this.lastPerspectiveFov
|
||||||
this.fovBeforeOrtho = currentFov
|
this.fovBeforeOrtho = currentFov
|
||||||
@ -733,6 +856,10 @@ export class CameraControls {
|
|||||||
})
|
})
|
||||||
animateToPerspective = () =>
|
animateToPerspective = () =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
|
if (this.syncDirection === 'engineToClient')
|
||||||
|
console.warn(
|
||||||
|
'animate To Perspective not design to work with engineToClient syncDirection.'
|
||||||
|
)
|
||||||
this.isFovAnimationInProgress = true
|
this.isFovAnimationInProgress = true
|
||||||
// Immediately set the camera to perspective with a very low FOV
|
// Immediately set the camera to perspective with a very low FOV
|
||||||
const targetFov = this.fovBeforeOrtho // Target FOV for perspective
|
const targetFov = this.fovBeforeOrtho // Target FOV for perspective
|
||||||
@ -779,13 +906,14 @@ export class CameraControls {
|
|||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
throttledUpdateEngineCamera({
|
if (this.syncDirection === 'clientToEngine')
|
||||||
quaternion: this.camera.quaternion,
|
throttledUpdateEngineCamera({
|
||||||
position: this.camera.position,
|
quaternion: this.camera.quaternion,
|
||||||
zoom: this.camera.zoom,
|
position: this.camera.position,
|
||||||
isPerspective: this.isPerspective,
|
zoom: this.camera.zoom,
|
||||||
target: this.target,
|
isPerspective: this.isPerspective,
|
||||||
})
|
target: this.target,
|
||||||
|
})
|
||||||
this.deferReactUpdate({
|
this.deferReactUpdate({
|
||||||
type: this.isPerspective ? 'perspective' : 'orthographic',
|
type: this.isPerspective ? 'perspective' : 'orthographic',
|
||||||
[this.isPerspective ? 'fov' : 'zoom']:
|
[this.isPerspective ? 'fov' : 'zoom']:
|
||||||
@ -806,9 +934,18 @@ export class CameraControls {
|
|||||||
})
|
})
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
|
getInteractionType = (event: any) =>
|
||||||
|
_getInteractionType(
|
||||||
|
this.interactionGuards,
|
||||||
|
event,
|
||||||
|
this.enablePan,
|
||||||
|
this.enableRotate,
|
||||||
|
this.enableZoom
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// currently duplicated, delete one
|
// Pure function helpers
|
||||||
|
|
||||||
function calculateNearFarFromFOV(fov: number) {
|
function calculateNearFarFromFOV(fov: number) {
|
||||||
const nearFarRatio = (fov - 3) / (45 - 3)
|
const nearFarRatio = (fov - 3) / (45 - 3)
|
||||||
// const z_near = 0.1 + nearFarRatio * (5 - 0.1)
|
// const z_near = 0.1 + nearFarRatio * (5 - 0.1)
|
||||||
@ -816,7 +953,6 @@ function calculateNearFarFromFOV(fov: number) {
|
|||||||
return { z_near: 0.1, z_far }
|
return { z_near: 0.1, z_far }
|
||||||
}
|
}
|
||||||
|
|
||||||
// currently duplicated, delete one
|
|
||||||
function convertThreeCamValuesToEngineCam({
|
function convertThreeCamValuesToEngineCam({
|
||||||
target,
|
target,
|
||||||
position,
|
position,
|
||||||
@ -857,8 +993,6 @@ function convertThreeCamValuesToEngineCam({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pure function helpers
|
|
||||||
|
|
||||||
function _lookAt(position: Vector3, target: Vector3, up: Vector3): Quaternion {
|
function _lookAt(position: Vector3, target: Vector3, up: Vector3): Quaternion {
|
||||||
// Direction from position to target, normalized.
|
// Direction from position to target, normalized.
|
||||||
let direction = new Vector3().subVectors(target, position).normalize()
|
let direction = new Vector3().subVectors(target, position).normalize()
|
||||||
@ -877,3 +1011,17 @@ function _lookAt(position: Vector3, target: Vector3, up: Vector3): Quaternion {
|
|||||||
|
|
||||||
return quaternion
|
return quaternion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getInteractionType(
|
||||||
|
interactionGuards: MouseGuard,
|
||||||
|
event: any,
|
||||||
|
enablePan: boolean,
|
||||||
|
enableRotate: boolean,
|
||||||
|
enableZoom: boolean
|
||||||
|
): interactionType | 'none' {
|
||||||
|
let state: interactionType | 'none' = 'none'
|
||||||
|
if (enablePan && interactionGuards.pan.callback(event)) return 'pan'
|
||||||
|
if (enableRotate && interactionGuards.rotate.callback(event)) return 'rotate'
|
||||||
|
if (enableZoom && interactionGuards.zoom.dragCallback(event)) return 'zoom'
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
@ -56,6 +56,7 @@ import { engineCommandManager } from 'lang/std/engineConnection'
|
|||||||
import {
|
import {
|
||||||
createArcGeometry,
|
createArcGeometry,
|
||||||
dashedStraight,
|
dashedStraight,
|
||||||
|
profileStart,
|
||||||
straightSegment,
|
straightSegment,
|
||||||
tangentialArcToSegment,
|
tangentialArcToSegment,
|
||||||
} from './segments'
|
} from './segments'
|
||||||
@ -64,6 +65,7 @@ import {
|
|||||||
addNewSketchLn,
|
addNewSketchLn,
|
||||||
changeSketchArguments,
|
changeSketchArguments,
|
||||||
compareVec2Epsilon2,
|
compareVec2Epsilon2,
|
||||||
|
updateStartProfileAtArgs,
|
||||||
} from 'lang/std/sketch'
|
} from 'lang/std/sketch'
|
||||||
import { isReducedMotion, throttle } from 'lib/utils'
|
import { isReducedMotion, throttle } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
@ -85,6 +87,7 @@ export const TANGENTIAL_ARC_TO_SEGMENT = 'tangential-arc-to-segment'
|
|||||||
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
|
export const TANGENTIAL_ARC_TO_SEGMENT_BODY = 'tangential-arc-to-segment-body'
|
||||||
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
|
export const TANGENTIAL_ARC_TO__SEGMENT_DASH =
|
||||||
'tangential-arc-to-segment-body-dashed'
|
'tangential-arc-to-segment-body-dashed'
|
||||||
|
export const PROFILE_START = 'profile-start'
|
||||||
|
|
||||||
// This singleton Class is responsible for all of the things the user sees and interacts with.
|
// This singleton Class is responsible for all of the things the user sees and interacts with.
|
||||||
// That mostly mean sketch elements.
|
// That mostly mean sketch elements.
|
||||||
@ -106,9 +109,10 @@ class SceneEntities {
|
|||||||
|
|
||||||
Object.values(this.activeSegments).forEach((segment) => {
|
Object.values(this.activeSegments).forEach((segment) => {
|
||||||
const factor =
|
const factor =
|
||||||
sceneInfra.camControls.camera instanceof OrthographicCamera
|
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, segment)
|
: perspScale(sceneInfra.camControls.camera, segment)) /
|
||||||
|
sceneInfra._baseUnitMultiplier
|
||||||
if (
|
if (
|
||||||
segment.userData.from &&
|
segment.userData.from &&
|
||||||
segment.userData.to &&
|
segment.userData.to &&
|
||||||
@ -136,6 +140,9 @@ class SceneEntities {
|
|||||||
scale: factor,
|
scale: factor,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (segment.name === PROFILE_START) {
|
||||||
|
segment.scale.set(factor, factor, factor)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
if (this.axisGroup) {
|
if (this.axisGroup) {
|
||||||
const factor =
|
const factor =
|
||||||
@ -143,9 +150,9 @@ class SceneEntities {
|
|||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
|
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
|
||||||
const x = this.axisGroup.getObjectByName(X_AXIS)
|
const x = this.axisGroup.getObjectByName(X_AXIS)
|
||||||
x?.scale.set(1, factor, 1)
|
x?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1)
|
||||||
const y = this.axisGroup.getObjectByName(Y_AXIS)
|
const y = this.axisGroup.getObjectByName(Y_AXIS)
|
||||||
y?.scale.set(factor, 1, 1)
|
y?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,6 +176,7 @@ class SceneEntities {
|
|||||||
this.scene.add(this.intersectionPlane)
|
this.scene.add(this.intersectionPlane)
|
||||||
}
|
}
|
||||||
createSketchAxis(sketchPathToNode: PathToNode) {
|
createSketchAxis(sketchPathToNode: PathToNode) {
|
||||||
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
const baseXColor = 0x000055
|
const baseXColor = 0x000055
|
||||||
const baseYColor = 0x550000
|
const baseYColor = 0x550000
|
||||||
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
|
const xAxisGeometry = new BoxGeometry(100000, 0.3, 0.01)
|
||||||
@ -208,6 +216,14 @@ class SceneEntities {
|
|||||||
sceneInfra.camControls.target
|
sceneInfra.camControls.target
|
||||||
)
|
)
|
||||||
gridHelper.scale.set(sceneScale, sceneScale, sceneScale)
|
gridHelper.scale.set(sceneScale, sceneScale, sceneScale)
|
||||||
|
|
||||||
|
const factor =
|
||||||
|
sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
|
? orthoFactor
|
||||||
|
: perspScale(sceneInfra.camControls.camera, this.axisGroup)
|
||||||
|
xAxisMesh?.scale.set(1, factor / sceneInfra._baseUnitMultiplier, 1)
|
||||||
|
yAxisMesh?.scale.set(factor / sceneInfra._baseUnitMultiplier, 1, 1)
|
||||||
|
|
||||||
this.axisGroup.add(xAxisMesh, yAxisMesh, gridHelper)
|
this.axisGroup.add(xAxisMesh, yAxisMesh, gridHelper)
|
||||||
this.currentSketchQuaternion &&
|
this.currentSketchQuaternion &&
|
||||||
this.axisGroup.setRotationFromQuaternion(this.currentSketchQuaternion)
|
this.axisGroup.setRotationFromQuaternion(this.currentSketchQuaternion)
|
||||||
@ -279,9 +295,28 @@ class SceneEntities {
|
|||||||
)
|
)
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
const factor =
|
const factor =
|
||||||
sceneInfra.camControls.camera instanceof OrthographicCamera
|
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, dummy)
|
: perspScale(sceneInfra.camControls.camera, dummy)) /
|
||||||
|
sceneInfra._baseUnitMultiplier
|
||||||
|
|
||||||
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
|
draftSegment ? truncatedAst : kclManager.ast,
|
||||||
|
sketchGroup.start.__geoMeta.sourceRange
|
||||||
|
)
|
||||||
|
const _profileStart = profileStart({
|
||||||
|
from: sketchGroup.start.from,
|
||||||
|
id: sketchGroup.start.__geoMeta.id,
|
||||||
|
pathToNode: segPathToNode,
|
||||||
|
scale: factor,
|
||||||
|
})
|
||||||
|
_profileStart.layers.set(SKETCH_LAYER)
|
||||||
|
_profileStart.traverse((child) => {
|
||||||
|
child.layers.set(SKETCH_LAYER)
|
||||||
|
})
|
||||||
|
group.add(_profileStart)
|
||||||
|
this.activeSegments[JSON.stringify(segPathToNode)] = _profileStart
|
||||||
|
|
||||||
sketchGroup.value.forEach((segment, index) => {
|
sketchGroup.value.forEach((segment, index) => {
|
||||||
let segPathToNode = getNodePathFromSourceRange(
|
let segPathToNode = getNodePathFromSourceRange(
|
||||||
draftSegment ? truncatedAst : kclManager.ast,
|
draftSegment ? truncatedAst : kclManager.ast,
|
||||||
@ -363,7 +398,11 @@ class SceneEntities {
|
|||||||
mat.color.set(obj.userData.baseColor)
|
mat.color.set(obj.userData.baseColor)
|
||||||
mat.color.offsetHSL(0, 0, 0.5)
|
mat.color.offsetHSL(0, 0, 0.5)
|
||||||
}
|
}
|
||||||
const parent = getParentGroup(object)
|
const parent = getParentGroup(object, [
|
||||||
|
STRAIGHT_SEGMENT,
|
||||||
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
PROFILE_START,
|
||||||
|
])
|
||||||
if (parent?.userData?.pathToNode) {
|
if (parent?.userData?.pathToNode) {
|
||||||
const updatedAst = parse(recast(kclManager.ast))
|
const updatedAst = parse(recast(kclManager.ast))
|
||||||
const node = getNodeFromPath<CallExpression>(
|
const node = getNodeFromPath<CallExpression>(
|
||||||
@ -404,7 +443,7 @@ class SceneEntities {
|
|||||||
const isClosingSketch = compareVec2Epsilon2(
|
const isClosingSketch = compareVec2Epsilon2(
|
||||||
firstSeg.from,
|
firstSeg.from,
|
||||||
[intersection2d.x, intersection2d.y],
|
[intersection2d.x, intersection2d.y],
|
||||||
1
|
0.5
|
||||||
)
|
)
|
||||||
let modifiedAst
|
let modifiedAst
|
||||||
if (isClosingSketch) {
|
if (isClosingSketch) {
|
||||||
@ -500,7 +539,11 @@ class SceneEntities {
|
|||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
const group = getParentGroup(object)
|
const group = getParentGroup(object, [
|
||||||
|
STRAIGHT_SEGMENT,
|
||||||
|
TANGENTIAL_ARC_TO_SEGMENT,
|
||||||
|
PROFILE_START,
|
||||||
|
])
|
||||||
if (!group) return
|
if (!group) return
|
||||||
const pathToNode: PathToNode = JSON.parse(
|
const pathToNode: PathToNode = JSON.parse(
|
||||||
JSON.stringify(group.userData.pathToNode)
|
JSON.stringify(group.userData.pathToNode)
|
||||||
@ -524,13 +567,28 @@ class SceneEntities {
|
|||||||
).node
|
).node
|
||||||
if (node.type !== 'CallExpression') return
|
if (node.type !== 'CallExpression') return
|
||||||
|
|
||||||
const modded = changeSketchArguments(
|
let modded: {
|
||||||
modifiedAst,
|
modifiedAst: Program
|
||||||
kclManager.programMemory,
|
pathToNode: PathToNode
|
||||||
[node.start, node.end],
|
}
|
||||||
to,
|
if (group.name === PROFILE_START) {
|
||||||
from
|
modded = updateStartProfileAtArgs({
|
||||||
)
|
node: modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
previousProgramMemory: kclManager.programMemory,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
modded = changeSketchArguments(
|
||||||
|
modifiedAst,
|
||||||
|
kclManager.programMemory,
|
||||||
|
[node.start, node.end],
|
||||||
|
to,
|
||||||
|
from
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
modifiedAst = modded.modifiedAst
|
modifiedAst = modded.modifiedAst
|
||||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||||
draftInfo
|
draftInfo
|
||||||
@ -549,10 +607,16 @@ class SceneEntities {
|
|||||||
programMemoryOverride,
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
this.sceneProgramMemory = programMemory
|
this.sceneProgramMemory = programMemory
|
||||||
const sketchGroup = programMemory.root[variableDeclarationName]
|
const sketchGroup = programMemory.root[
|
||||||
.value as Path[]
|
variableDeclarationName
|
||||||
|
] as SketchGroup
|
||||||
|
const sgPaths = sketchGroup.value
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
sketchGroup.forEach((segment, index) => {
|
|
||||||
|
const updateSegment = (
|
||||||
|
segment: Path | SketchGroup['start'],
|
||||||
|
index: number
|
||||||
|
) => {
|
||||||
const segPathToNode = getNodePathFromSourceRange(
|
const segPathToNode = getNodePathFromSourceRange(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
segment.__geoMeta.sourceRange
|
segment.__geoMeta.sourceRange
|
||||||
@ -567,12 +631,13 @@ class SceneEntities {
|
|||||||
// const prevSegment = sketchGroup.slice(index - 1)[0]
|
// const prevSegment = sketchGroup.slice(index - 1)[0]
|
||||||
const type = group?.userData?.type
|
const type = group?.userData?.type
|
||||||
const factor =
|
const factor =
|
||||||
sceneInfra.camControls.camera instanceof OrthographicCamera
|
(sceneInfra.camControls.camera instanceof OrthographicCamera
|
||||||
? orthoFactor
|
? orthoFactor
|
||||||
: perspScale(sceneInfra.camControls.camera, group)
|
: perspScale(sceneInfra.camControls.camera, group)) /
|
||||||
|
sceneInfra._baseUnitMultiplier
|
||||||
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
if (type === TANGENTIAL_ARC_TO_SEGMENT) {
|
||||||
this.updateTangentialArcToSegment({
|
this.updateTangentialArcToSegment({
|
||||||
prevSegment: sketchGroup[index - 1],
|
prevSegment: sgPaths[index - 1],
|
||||||
from: segment.from,
|
from: segment.from,
|
||||||
to: segment.to,
|
to: segment.to,
|
||||||
group: group,
|
group: group,
|
||||||
@ -585,8 +650,13 @@ class SceneEntities {
|
|||||||
group: group,
|
group: group,
|
||||||
scale: factor,
|
scale: factor,
|
||||||
})
|
})
|
||||||
|
} else if (type === PROFILE_START) {
|
||||||
|
group.position.set(segment.from[0], segment.from[1], 0)
|
||||||
|
group.scale.set(factor, factor, factor)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
updateSegment(sketchGroup.start, 0)
|
||||||
|
sgPaths.forEach(updateSegment)
|
||||||
})()
|
})()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,9 +676,7 @@ class SceneEntities {
|
|||||||
group.userData.from = from
|
group.userData.from = from
|
||||||
group.userData.to = to
|
group.userData.to = to
|
||||||
group.userData.prevSegment = prevSegment
|
group.userData.prevSegment = prevSegment
|
||||||
const arrowGroup = group.children.find(
|
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||||
(child) => child.userData.type === ARROWHEAD
|
|
||||||
) as Group
|
|
||||||
|
|
||||||
arrowGroup.position.set(to[0], to[1], 0)
|
arrowGroup.position.set(to[0], to[1], 0)
|
||||||
|
|
||||||
@ -683,9 +751,7 @@ class SceneEntities {
|
|||||||
const shape = new Shape()
|
const shape = new Shape()
|
||||||
shape.moveTo(0, -0.08 * scale)
|
shape.moveTo(0, -0.08 * scale)
|
||||||
shape.lineTo(0, 0.08 * scale) // The width of the line
|
shape.lineTo(0, 0.08 * scale) // The width of the line
|
||||||
const arrowGroup = group.children.find(
|
const arrowGroup = group.getObjectByName(ARROWHEAD) as Group
|
||||||
(child) => child.userData.type === ARROWHEAD
|
|
||||||
) as Group
|
|
||||||
|
|
||||||
arrowGroup.position.set(to[0], to[1], 0)
|
arrowGroup.position.set(to[0], to[1], 0)
|
||||||
|
|
||||||
@ -968,9 +1034,9 @@ export function quaternionFromSketchGroup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function colorSegment(object: any, color: number) {
|
function colorSegment(object: any, color: number) {
|
||||||
const arrowHead = getParentGroup(object, [ARROWHEAD])
|
const segmentHead = getParentGroup(object, [ARROWHEAD, PROFILE_START])
|
||||||
if (arrowHead) {
|
if (segmentHead) {
|
||||||
arrowHead.traverse((child) => {
|
segmentHead.traverse((child) => {
|
||||||
if (child instanceof Mesh) {
|
if (child instanceof Mesh) {
|
||||||
child.material.color.set(color)
|
child.material.color.set(color)
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,8 @@ import {
|
|||||||
Intersection,
|
Intersection,
|
||||||
Object3D,
|
Object3D,
|
||||||
Object3DEventMap,
|
Object3DEventMap,
|
||||||
BoxGeometry,
|
|
||||||
} from 'three'
|
} from 'three'
|
||||||
import { compareVec2Epsilon2 } from 'lang/std/sketch'
|
import { Coords2d, compareVec2Epsilon2 } from 'lang/std/sketch'
|
||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
import * as TWEEN from '@tweenjs/tween.js'
|
import * as TWEEN from '@tweenjs/tween.js'
|
||||||
import { SourceRange } from 'lang/wasm'
|
import { SourceRange } from 'lang/wasm'
|
||||||
@ -88,6 +87,8 @@ class SceneInfra {
|
|||||||
fov = 45
|
fov = 45
|
||||||
fovBeforeAnimate = 45
|
fovBeforeAnimate = 45
|
||||||
isFovAnimationInProgress = false
|
isFovAnimationInProgress = false
|
||||||
|
_baseUnit: BaseUnit = 'mm'
|
||||||
|
_baseUnitMultiplier = 1
|
||||||
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
onDragCallback: (arg: OnDragCallbackArgs) => void = () => {}
|
||||||
onMoveCallback: (arg: onMoveCallbackArgs) => void = () => {}
|
onMoveCallback: (arg: onMoveCallbackArgs) => void = () => {}
|
||||||
onClickCallback: (arg?: OnClickCallbackArgs) => void = () => {}
|
onClickCallback: (arg?: OnClickCallbackArgs) => void = () => {}
|
||||||
@ -107,6 +108,15 @@ class SceneInfra {
|
|||||||
this.onMouseLeave = callbacks.onMouseLeave || this.onMouseLeave
|
this.onMouseLeave = callbacks.onMouseLeave || this.onMouseLeave
|
||||||
this.selected = null // following selections between callbacks being set is too tricky
|
this.selected = null // following selections between callbacks being set is too tricky
|
||||||
}
|
}
|
||||||
|
set baseUnit(unit: BaseUnit) {
|
||||||
|
this._baseUnit = unit
|
||||||
|
this._baseUnitMultiplier = baseUnitTomm(unit)
|
||||||
|
this.scene.scale.set(
|
||||||
|
this._baseUnitMultiplier,
|
||||||
|
this._baseUnitMultiplier,
|
||||||
|
this._baseUnitMultiplier
|
||||||
|
)
|
||||||
|
}
|
||||||
resetMouseListeners = () => {
|
resetMouseListeners = () => {
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onDrag: () => {},
|
onDrag: () => {},
|
||||||
@ -202,7 +212,12 @@ class SceneInfra {
|
|||||||
const axisGroup = this.scene
|
const axisGroup = this.scene
|
||||||
.getObjectByName(AXIS_GROUP)
|
.getObjectByName(AXIS_GROUP)
|
||||||
?.getObjectByName('gridHelper')
|
?.getObjectByName('gridHelper')
|
||||||
planesGroup && planesGroup.scale.set(scale, scale, scale)
|
planesGroup &&
|
||||||
|
planesGroup.scale.set(
|
||||||
|
scale / sceneInfra._baseUnitMultiplier,
|
||||||
|
scale / sceneInfra._baseUnitMultiplier,
|
||||||
|
scale / sceneInfra._baseUnitMultiplier
|
||||||
|
)
|
||||||
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
axisGroup?.name === 'gridHelper' && axisGroup.scale.set(scale, scale, scale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,8 +285,11 @@ class SceneInfra {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
intersection2d: new Vector2(transformedPoint.x, transformedPoint.y), // z should be 0
|
intersection2d: new Vector2(
|
||||||
intersectPoint,
|
transformedPoint.x / this._baseUnitMultiplier,
|
||||||
|
transformedPoint.y / this._baseUnitMultiplier
|
||||||
|
), // z should be 0
|
||||||
|
intersectPoint: intersectPoint.divideScalar(this._baseUnitMultiplier),
|
||||||
intersection: planeIntersects[0],
|
intersection: planeIntersects[0],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,7 +501,11 @@ class SceneInfra {
|
|||||||
this.camControls.camera,
|
this.camControls.camera,
|
||||||
this.camControls.target
|
this.camControls.target
|
||||||
)
|
)
|
||||||
planesGroup.scale.set(sceneScale, sceneScale, sceneScale)
|
planesGroup.scale.set(
|
||||||
|
sceneScale / sceneInfra._baseUnitMultiplier,
|
||||||
|
sceneScale / sceneInfra._baseUnitMultiplier,
|
||||||
|
sceneScale / sceneInfra._baseUnitMultiplier
|
||||||
|
)
|
||||||
this.scene.add(planesGroup)
|
this.scene.add(planesGroup)
|
||||||
}
|
}
|
||||||
removeDefaultPlanes() {
|
removeDefaultPlanes() {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Coords2d } from 'lang/std/sketch'
|
import { Coords2d } from 'lang/std/sketch'
|
||||||
import {
|
import {
|
||||||
|
BoxGeometry,
|
||||||
BufferGeometry,
|
BufferGeometry,
|
||||||
CatmullRomCurve3,
|
CatmullRomCurve3,
|
||||||
ConeGeometry,
|
ConeGeometry,
|
||||||
@ -19,6 +20,7 @@ import {
|
|||||||
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
import { mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js'
|
||||||
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
import { PathToNode, SketchGroup, getTangentialArcToInfo } from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
|
PROFILE_START,
|
||||||
STRAIGHT_SEGMENT,
|
STRAIGHT_SEGMENT,
|
||||||
STRAIGHT_SEGMENT_BODY,
|
STRAIGHT_SEGMENT_BODY,
|
||||||
STRAIGHT_SEGMENT_DASH,
|
STRAIGHT_SEGMENT_DASH,
|
||||||
@ -29,6 +31,38 @@ import {
|
|||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import { ARROWHEAD } from './sceneInfra'
|
import { ARROWHEAD } from './sceneInfra'
|
||||||
|
|
||||||
|
export function profileStart({
|
||||||
|
from,
|
||||||
|
id,
|
||||||
|
pathToNode,
|
||||||
|
scale = 1,
|
||||||
|
}: {
|
||||||
|
from: Coords2d
|
||||||
|
id: string
|
||||||
|
pathToNode: PathToNode
|
||||||
|
scale?: number
|
||||||
|
}) {
|
||||||
|
const group = new Group()
|
||||||
|
|
||||||
|
const geometry = new BoxGeometry(0.8, 0.8, 0.8)
|
||||||
|
const body = new MeshBasicMaterial({ color: 0xffffff })
|
||||||
|
const mesh = new Mesh(geometry, body)
|
||||||
|
|
||||||
|
group.add(mesh)
|
||||||
|
|
||||||
|
group.userData = {
|
||||||
|
type: PROFILE_START,
|
||||||
|
id,
|
||||||
|
from,
|
||||||
|
pathToNode,
|
||||||
|
isSelected: false,
|
||||||
|
}
|
||||||
|
group.name = PROFILE_START
|
||||||
|
group.position.set(from[0], from[1], 0)
|
||||||
|
group.scale.set(scale, scale, scale)
|
||||||
|
return group
|
||||||
|
}
|
||||||
|
|
||||||
export function straightSegment({
|
export function straightSegment({
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
@ -18,6 +18,7 @@ import {
|
|||||||
import { isTauri } from 'lib/isTauri'
|
import { isTauri } from 'lib/isTauri'
|
||||||
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
|
import { settingsCommandBarConfig } from 'lib/commandBarConfigs/settingsCommandConfig'
|
||||||
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
|
import { authCommandBarConfig } from 'lib/commandBarConfigs/authCommandConfig'
|
||||||
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -97,6 +98,7 @@ export const GlobalStateProvider = ({
|
|||||||
if (settingsState.context.theme !== 'system') return
|
if (settingsState.context.theme !== 'system') return
|
||||||
setThemeClass(e.matches ? Themes.Dark : Themes.Light)
|
setThemeClass(e.matches ? Themes.Dark : Themes.Light)
|
||||||
}
|
}
|
||||||
|
sceneInfra.baseUnit = settingsState?.context?.baseUnit || 'mm'
|
||||||
|
|
||||||
matcher.addEventListener('change', listener)
|
matcher.addEventListener('change', listener)
|
||||||
return () => matcher.removeEventListener('change', listener)
|
return () => matcher.removeEventListener('change', listener)
|
||||||
|
@ -25,8 +25,7 @@ describe('processMemory', () => {
|
|||||||
|> lineTo([-3.35, 0.17], %)
|
|> lineTo([-3.35, 0.17], %)
|
||||||
|> lineTo([0.98, 5.16], %)
|
|> lineTo([0.98, 5.16], %)
|
||||||
|> lineTo([2.15, 4.32], %)
|
|> lineTo([2.15, 4.32], %)
|
||||||
// |> rx(90, %)
|
// |> rx(90, %)`
|
||||||
show(theExtrude, theSketch)`
|
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast, {
|
const programMemory = await enginelessExecutor(ast, {
|
||||||
root: {},
|
root: {},
|
||||||
|
@ -1,15 +1,8 @@
|
|||||||
import {
|
import { MouseEventHandler, useEffect, useRef, useState } from 'react'
|
||||||
MouseEventHandler,
|
|
||||||
WheelEventHandler,
|
|
||||||
useEffect,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import { useStore } from '../useStore'
|
import { useStore } from '../useStore'
|
||||||
import { getNormalisedCoordinates, throttle } from '../lib/utils'
|
import { getNormalisedCoordinates } from '../lib/utils'
|
||||||
import Loading from './Loading'
|
import Loading from './Loading'
|
||||||
import { cameraMouseDragGuards } from 'lib/cameraControls'
|
|
||||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||||
@ -36,7 +29,6 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
streamDimensions: s.streamDimensions,
|
streamDimensions: s.streamDimensions,
|
||||||
}))
|
}))
|
||||||
const { settings } = useGlobalStateContext()
|
const { settings } = useGlobalStateContext()
|
||||||
const cameraControls = settings?.context?.cameraControls
|
|
||||||
const { state } = useModelingContext()
|
const { state } = useModelingContext()
|
||||||
const { isExecuting } = useKclContext()
|
const { isExecuting } = useKclContext()
|
||||||
const { overallState } = useNetworkStatus()
|
const { overallState } = useNetworkStatus()
|
||||||
@ -68,19 +60,6 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
setClickCoords({ x, y })
|
setClickCoords({ x, y })
|
||||||
}
|
}
|
||||||
|
|
||||||
const fps = 60
|
|
||||||
const handleScroll: WheelEventHandler<HTMLVideoElement> = throttle((e) => {
|
|
||||||
if (!cameraMouseDragGuards[cameraControls].zoom.scrollCallback(e)) return
|
|
||||||
engineCommandManager.sendSceneCommand({
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_zoom',
|
|
||||||
magnitude: e.deltaY * 0.4,
|
|
||||||
},
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
})
|
|
||||||
}, Math.round(1000 / fps))
|
|
||||||
|
|
||||||
const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
|
const handleMouseUp: MouseEventHandler<HTMLDivElement> = ({
|
||||||
clientX,
|
clientX,
|
||||||
clientY,
|
clientY,
|
||||||
@ -159,7 +138,6 @@ export const Stream = ({ className = '' }: { className?: string }) => {
|
|||||||
muted
|
muted
|
||||||
autoPlay
|
autoPlay
|
||||||
controls={false}
|
controls={false}
|
||||||
onWheel={handleScroll}
|
|
||||||
onPlay={() => setIsLoading(false)}
|
onPlay={() => setIsLoading(false)}
|
||||||
onMouseMoveCapture={handleMouseMove}
|
onMouseMoveCapture={handleMouseMove}
|
||||||
className={`w-full cursor-pointer h-full ${isExecuting && 'blur-md'}`}
|
className={`w-full cursor-pointer h-full ${isExecuting && 'blur-md'}`}
|
||||||
|
@ -27,6 +27,8 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
phone: '555-555-5555',
|
phone: '555-555-5555',
|
||||||
first_name: 'Test',
|
first_name: 'Test',
|
||||||
last_name: 'User',
|
last_name: 'User',
|
||||||
|
can_train_on_data: false,
|
||||||
|
is_service_account: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
render(
|
||||||
@ -57,6 +59,8 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
first_name: '',
|
first_name: '',
|
||||||
last_name: '',
|
last_name: '',
|
||||||
name: '',
|
name: '',
|
||||||
|
can_train_on_data: false,
|
||||||
|
is_service_account: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
render(
|
||||||
@ -84,6 +88,8 @@ describe('UserSidebarMenu tests', () => {
|
|||||||
first_name: 'Test',
|
first_name: 'Test',
|
||||||
last_name: 'User',
|
last_name: 'User',
|
||||||
image: '',
|
image: '',
|
||||||
|
can_train_on_data: false,
|
||||||
|
is_service_account: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
render(
|
||||||
|
@ -4,6 +4,7 @@ import { ViewPlugin, hoverTooltip, tooltips } from '@codemirror/view'
|
|||||||
import { CompletionTriggerKind } from 'vscode-languageserver-protocol'
|
import { CompletionTriggerKind } from 'vscode-languageserver-protocol'
|
||||||
import { offsetToPos } from 'editor/plugins/lsp/util'
|
import { offsetToPos } from 'editor/plugins/lsp/util'
|
||||||
import { LanguageServerOptions } from 'editor/plugins/lsp'
|
import { LanguageServerOptions } from 'editor/plugins/lsp'
|
||||||
|
import { syntaxTree } from '@codemirror/language'
|
||||||
import {
|
import {
|
||||||
LanguageServerPlugin,
|
LanguageServerPlugin,
|
||||||
documentUri,
|
documentUri,
|
||||||
@ -40,6 +41,14 @@ export function kclPlugin(options: LanguageServerOptions): Extension {
|
|||||||
if (plugin == null) return null
|
if (plugin == null) return null
|
||||||
|
|
||||||
const { state, pos, explicit } = context
|
const { state, pos, explicit } = context
|
||||||
|
|
||||||
|
let nodeBefore = syntaxTree(state).resolveInner(pos, -1)
|
||||||
|
if (
|
||||||
|
nodeBefore.name === 'BlockComment' ||
|
||||||
|
nodeBefore.name === 'LineComment'
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
|
||||||
const line = state.doc.lineAt(pos)
|
const line = state.doc.lineAt(pos)
|
||||||
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
|
let trigKind: CompletionTriggerKind = CompletionTriggerKind.Invoked
|
||||||
let trigChar: string | undefined
|
let trigChar: string | undefined
|
||||||
@ -60,6 +69,7 @@ export function kclPlugin(options: LanguageServerOptions): Extension {
|
|||||||
) {
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return await plugin.requestCompletion(
|
return await plugin.requestCompletion(
|
||||||
context,
|
context,
|
||||||
offsetToPos(state.doc, pos),
|
offsetToPos(state.doc, pos),
|
||||||
|
@ -7,6 +7,5 @@ export const VITE_KC_API_BASE_URL = import.meta.env.VITE_KC_API_BASE_URL
|
|||||||
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
|
export const VITE_KC_SITE_BASE_URL = import.meta.env.VITE_KC_SITE_BASE_URL
|
||||||
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
|
export const VITE_KC_CONNECTION_TIMEOUT_MS = import.meta.env
|
||||||
.VITE_KC_CONNECTION_TIMEOUT_MS
|
.VITE_KC_CONNECTION_TIMEOUT_MS
|
||||||
export const VITE_KC_SENTRY_DSN = import.meta.env.VITE_KC_SENTRY_DSN
|
|
||||||
export const TEST = import.meta.env.TEST
|
export const TEST = import.meta.env.TEST
|
||||||
export const DEV = import.meta.env.DEV
|
export const DEV = import.meta.env.DEV
|
||||||
|
@ -93,7 +93,7 @@ class KclManager {
|
|||||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||||
this._params.id &&
|
this._params.id &&
|
||||||
writeTextFile(this._params.id, code).catch((err) => {
|
writeTextFile(this._params.id, code).catch((err) => {
|
||||||
// TODO: add Sentry per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
// TODO: add tracing per GH issue #254 (https://github.com/KittyCAD/modeling-app/issues/254)
|
||||||
console.error('error saving file', err)
|
console.error('error saving file', err)
|
||||||
toast.error('Error saving file, please check file permissions')
|
toast.error('Error saving file, please check file permissions')
|
||||||
})
|
})
|
||||||
|
@ -11,59 +11,53 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)`
|
||||||
show(mySketch001)`
|
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const shown = programMemory?.return?.map(
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
// @ts-ignore
|
expect(sketch001).toEqual({
|
||||||
(a) => programMemory?.root?.[a.name]
|
type: 'SketchGroup',
|
||||||
)
|
on: expect.any(Object),
|
||||||
expect(shown).toEqual([
|
start: {
|
||||||
{
|
to: [0, 0],
|
||||||
type: 'SketchGroup',
|
from: [0, 0],
|
||||||
on: expect.any(Object),
|
name: '',
|
||||||
start: {
|
__geoMeta: {
|
||||||
to: [0, 0],
|
id: expect.any(String),
|
||||||
from: [0, 0],
|
sourceRange: [46, 71],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
value: [
|
||||||
|
{
|
||||||
|
type: 'ToPoint',
|
||||||
name: '',
|
name: '',
|
||||||
|
to: [-1.59, -1.54],
|
||||||
|
from: [0, 0],
|
||||||
__geoMeta: {
|
__geoMeta: {
|
||||||
|
sourceRange: [77, 102],
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
sourceRange: [46, 71],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
value: [
|
{
|
||||||
{
|
type: 'ToPoint',
|
||||||
type: 'ToPoint',
|
to: [0.46, -5.82],
|
||||||
name: '',
|
from: [-1.59, -1.54],
|
||||||
to: [-1.59, -1.54],
|
name: '',
|
||||||
from: [0, 0],
|
__geoMeta: {
|
||||||
__geoMeta: {
|
sourceRange: [108, 132],
|
||||||
sourceRange: [77, 102],
|
id: expect.any(String),
|
||||||
id: expect.any(String),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
type: 'ToPoint',
|
],
|
||||||
to: [0.46, -5.82],
|
position: [0, 0, 0],
|
||||||
from: [-1.59, -1.54],
|
rotation: [0, 0, 0, 1],
|
||||||
name: '',
|
xAxis: { x: 1, y: 0, z: 0 },
|
||||||
__geoMeta: {
|
yAxis: { x: 0, y: 1, z: 0 },
|
||||||
sourceRange: [108, 132],
|
zAxis: { x: 0, y: 0, z: 1 },
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
},
|
entityId: expect.any(String),
|
||||||
},
|
__meta: [{ sourceRange: [46, 71] }],
|
||||||
],
|
})
|
||||||
position: [0, 0, 0],
|
|
||||||
rotation: [0, 0, 0, 1],
|
|
||||||
xAxis: { x: 1, y: 0, z: 0 },
|
|
||||||
yAxis: { x: 0, y: 1, z: 0 },
|
|
||||||
zAxis: { x: 0, y: 0, z: 1 },
|
|
||||||
id: expect.any(String),
|
|
||||||
entityId: expect.any(String),
|
|
||||||
__meta: [{ sourceRange: [46, 71] }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
test('extrude artifacts', async () => {
|
test('extrude artifacts', async () => {
|
||||||
// Enable rotations #152
|
// Enable rotations #152
|
||||||
@ -73,30 +67,24 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)`
|
||||||
show(mySketch001)`
|
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const shown = programMemory?.return?.map(
|
const sketch001 = programMemory?.root?.mySketch001
|
||||||
// @ts-ignore
|
expect(sketch001).toEqual({
|
||||||
(a) => programMemory?.root?.[a.name]
|
type: 'ExtrudeGroup',
|
||||||
)
|
id: expect.any(String),
|
||||||
expect(shown).toEqual([
|
value: [],
|
||||||
{
|
height: 2,
|
||||||
type: 'ExtrudeGroup',
|
position: [0, 0, 0],
|
||||||
id: expect.any(String),
|
rotation: [0, 0, 0, 1],
|
||||||
value: [],
|
endCapId: null,
|
||||||
height: 2,
|
startCapId: null,
|
||||||
position: [0, 0, 0],
|
xAxis: { x: 1, y: 0, z: 0 },
|
||||||
rotation: [0, 0, 0, 1],
|
yAxis: { x: 0, y: 1, z: 0 },
|
||||||
endCapId: null,
|
zAxis: { x: 0, y: 0, z: 1 },
|
||||||
startCapId: null,
|
__meta: [{ sourceRange: [46, 71] }],
|
||||||
xAxis: { x: 1, y: 0, z: 0 },
|
})
|
||||||
yAxis: { x: 0, y: 1, z: 0 },
|
|
||||||
zAxis: { x: 0, y: 0, z: 1 },
|
|
||||||
__meta: [{ sourceRange: [46, 71] }],
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
test('sketch extrude and sketch on one of the faces', async () => {
|
test('sketch extrude and sketch on one of the faces', async () => {
|
||||||
// Enable rotations #152
|
// Enable rotations #152
|
||||||
@ -120,14 +108,10 @@ const sk2 = startSketchOn('XY')
|
|||||||
// |> transform(theTransf, %)
|
// |> transform(theTransf, %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
|
`
|
||||||
show(theExtrude, sk2)`
|
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = programMemory?.return?.map(
|
const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2]
|
||||||
// @ts-ignore
|
|
||||||
({ name }) => programMemory?.root?.[name]
|
|
||||||
)
|
|
||||||
expect(geos).toEqual([
|
expect(geos).toEqual([
|
||||||
{
|
{
|
||||||
type: 'ExtrudeGroup',
|
type: 'ExtrudeGroup',
|
||||||
|
@ -47,9 +47,8 @@ const newVar = myVar + 1`
|
|||||||
|> lineTo([2,3], %)
|
|> lineTo([2,3], %)
|
||||||
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
|
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
|
||||||
// |> close(%)
|
// |> close(%)
|
||||||
show(mySketch)
|
|
||||||
`
|
`
|
||||||
const { root, return: _return } = await exe(code)
|
const { root } = await exe(code)
|
||||||
// geo is three js buffer geometry and is very bloated to have in tests
|
// geo is three js buffer geometry and is very bloated to have in tests
|
||||||
const minusGeo = root.mySketch.value
|
const minusGeo = root.mySketch.value
|
||||||
expect(minusGeo).toEqual([
|
expect(minusGeo).toEqual([
|
||||||
@ -84,15 +83,6 @@ show(mySketch)
|
|||||||
name: 'rightPath',
|
name: 'rightPath',
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
|
||||||
expect(_return).toEqual([
|
|
||||||
{
|
|
||||||
type: 'Identifier',
|
|
||||||
start: 203,
|
|
||||||
end: 211,
|
|
||||||
name: 'mySketch',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('pipe binary expression into call expression', async () => {
|
it('pipe binary expression into call expression', async () => {
|
||||||
@ -357,7 +347,6 @@ describe('testing math operators', () => {
|
|||||||
` -legLen(segLen('seg01', %), myVar)`,
|
` -legLen(segLen('seg01', %), myVar)`,
|
||||||
`], %)`,
|
`], %)`,
|
||||||
``,
|
``,
|
||||||
`show(part001)`,
|
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const { root } = await exe(code)
|
const { root } = await exe(code)
|
||||||
const sketch = root.part001
|
const sketch = root.part001
|
||||||
@ -392,8 +381,7 @@ const theExtrude = startSketchOn('XY')
|
|||||||
|> line([-0.76], myVarZ, %)
|
|> line([-0.76], myVarZ, %)
|
||||||
|> line([5,5], %)
|
|> line([5,5], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(4, %)
|
|> extrude(4, %)`
|
||||||
show(theExtrude)`
|
|
||||||
await expect(exe(code)).rejects.toEqual(
|
await expect(exe(code)).rejects.toEqual(
|
||||||
new KCLError(
|
new KCLError(
|
||||||
'undefined_value',
|
'undefined_value',
|
||||||
|
@ -122,7 +122,6 @@ describe('Testing addSketchTo', () => {
|
|||||||
expect(str).toBe(`const part001 = startSketchOn('YZ')
|
expect(str).toBe(`const part001 = startSketchOn('YZ')
|
||||||
|> startProfileAt('default', %)
|
|> startProfileAt('default', %)
|
||||||
|> line('default', %)
|
|> line('default', %)
|
||||||
show(part001)
|
|
||||||
`)
|
`)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -147,8 +146,7 @@ describe('Testing giveSketchFnCallTag', () => {
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([-2.57, -0.13], %)
|
|> line([-2.57, -0.13], %)
|
||||||
|> line([0, 0.83], %)
|
|> line([0, 0.83], %)
|
||||||
|> line([0.82, 0.34], %)
|
|> line([0.82, 0.34], %)`
|
||||||
show(part001)`
|
|
||||||
it('Should add tag to a sketch function call', () => {
|
it('Should add tag to a sketch function call', () => {
|
||||||
const { newCode, tag, isTagExisting } = giveSketchFnCallTagTestHelper(
|
const { newCode, tag, isTagExisting } = giveSketchFnCallTagTestHelper(
|
||||||
code,
|
code,
|
||||||
@ -204,8 +202,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLine([def(yo), 3.09], %)
|
|> angledLine([def(yo), 3.09], %)
|
||||||
|> angledLine([ghi(%), 3.09], %)
|
|> angledLine([ghi(%), 3.09], %)
|
||||||
|> angledLine([jkl(yo) + 2, 3.09], %)
|
|> angledLine([jkl(yo) + 2, 3.09], %)
|
||||||
const yo2 = hmm([identifierGuy + 5])
|
const yo2 = hmm([identifierGuy + 5])`
|
||||||
show(part001)`
|
|
||||||
it('should move a binary expression into a new variable', async () => {
|
it('should move a binary expression into a new variable', async () => {
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
|
@ -128,16 +128,8 @@ export function addSketchTo(
|
|||||||
createPipeExpression(pipeBody)
|
createPipeExpression(pipeBody)
|
||||||
)
|
)
|
||||||
|
|
||||||
const showCallIndex = getShowIndex(_node)
|
_node.body = [...node.body, variableDeclaration]
|
||||||
let sketchIndex = showCallIndex
|
let sketchIndex = _node.body.length - 1
|
||||||
if (showCallIndex === -1) {
|
|
||||||
_node.body = [...node.body, variableDeclaration]
|
|
||||||
sketchIndex = _node.body.length - 1
|
|
||||||
} else {
|
|
||||||
const newBody = [...node.body]
|
|
||||||
newBody.splice(showCallIndex, 0, variableDeclaration)
|
|
||||||
_node.body = newBody
|
|
||||||
}
|
|
||||||
let pathToNode: PathToNode = [
|
let pathToNode: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[sketchIndex, 'index'],
|
[sketchIndex, 'index'],
|
||||||
@ -150,7 +142,7 @@ export function addSketchTo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: addToShow(_node, _name),
|
modifiedAst: _node,
|
||||||
id: _name,
|
id: _name,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
@ -191,44 +183,6 @@ export function findUniqueName(
|
|||||||
return findUniqueName(searchStr, name, pad, index + 1)
|
return findUniqueName(searchStr, name, pad, index + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToShow(node: Program, name: string): Program {
|
|
||||||
const _node = { ...node }
|
|
||||||
const dumbyStartend = { start: 0, end: 0 }
|
|
||||||
const showCallIndex = getShowIndex(_node)
|
|
||||||
if (showCallIndex === -1) {
|
|
||||||
const showCall = createCallExpressionStdLib('show', [
|
|
||||||
createIdentifier(name),
|
|
||||||
])
|
|
||||||
const showExpressionStatement: ExpressionStatement = {
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
...dumbyStartend,
|
|
||||||
expression: showCall,
|
|
||||||
}
|
|
||||||
_node.body = [..._node.body, showExpressionStatement]
|
|
||||||
return _node
|
|
||||||
}
|
|
||||||
const showCall = { ..._node.body[showCallIndex] } as ExpressionStatement
|
|
||||||
const showCallArgs = (showCall.expression as CallExpression).arguments
|
|
||||||
const newShowCallArgs: Value[] = [...showCallArgs, createIdentifier(name)]
|
|
||||||
const newShowExpression = createCallExpressionStdLib('show', newShowCallArgs)
|
|
||||||
|
|
||||||
_node.body[showCallIndex] = {
|
|
||||||
...showCall,
|
|
||||||
expression: newShowExpression,
|
|
||||||
}
|
|
||||||
return _node
|
|
||||||
}
|
|
||||||
|
|
||||||
function getShowIndex(node: Program): number {
|
|
||||||
return node.body.findIndex(
|
|
||||||
(statement) =>
|
|
||||||
statement.type === 'ExpressionStatement' &&
|
|
||||||
statement.expression.type === 'CallExpression' &&
|
|
||||||
statement.expression.callee.type === 'Identifier' &&
|
|
||||||
statement.expression.callee.name === 'show'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mutateArrExp(
|
export function mutateArrExp(
|
||||||
node: Value,
|
node: Value,
|
||||||
updateWith: ArrayExpression
|
updateWith: ArrayExpression
|
||||||
@ -348,15 +302,10 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
const name = findUniqueName(node, 'part')
|
const name = findUniqueName(node, 'part')
|
||||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
||||||
let showCallIndex = getShowIndex(_node)
|
_node.body.splice(_node.body.length, 0, VariableDeclaration)
|
||||||
if (showCallIndex === -1) {
|
|
||||||
// We didn't find a show, so let's just append everything
|
|
||||||
showCallIndex = _node.body.length
|
|
||||||
}
|
|
||||||
_node.body.splice(showCallIndex, 0, VariableDeclaration)
|
|
||||||
const pathToExtrudeArg: PathToNode = [
|
const pathToExtrudeArg: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[showCallIndex, 'index'],
|
[_node.body.length, 'index'],
|
||||||
['declarations', 'VariableDeclaration'],
|
['declarations', 'VariableDeclaration'],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
@ -365,7 +314,7 @@ export function extrudeSketch(
|
|||||||
]
|
]
|
||||||
return {
|
return {
|
||||||
modifiedAst: node,
|
modifiedAst: node,
|
||||||
pathToNode: [...pathToNode.slice(0, -1), [showCallIndex, 'index']],
|
pathToNode: [...pathToNode.slice(0, -1), [-1, 'index']],
|
||||||
pathToExtrudeArg,
|
pathToExtrudeArg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -425,7 +374,7 @@ export function sketchOnExtrudedFace(
|
|||||||
_node.body.splice(expressionIndex + 1, 0, newSketch)
|
_node.body.splice(expressionIndex + 1, 0, newSketch)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: addToShow(_node, newSketchName),
|
modifiedAst: _node,
|
||||||
pathToNode: [...pathToNode.slice(0, -1), [expressionIndex, 'index']],
|
pathToNode: [...pathToNode.slice(0, -1), [expressionIndex, 'index']],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,8 +34,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||||
|
|
||||||
const variableBelowShouldNotBeIncluded = 3
|
const variableBelowShouldNotBeIncluded = 3
|
||||||
|
`
|
||||||
show(part001)`
|
|
||||||
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
@ -69,8 +68,7 @@ describe('testing argIsNotIdentifier', () => {
|
|||||||
|> angledLine([ghi(%), 3.09], %)
|
|> angledLine([ghi(%), 3.09], %)
|
||||||
|> angledLine([jkl('yo') + 2, 3.09], %)
|
|> angledLine([jkl('yo') + 2, 3.09], %)
|
||||||
const yo = 5 + 6
|
const yo = 5 + 6
|
||||||
const yo2 = hmm([identifierGuy + 5])
|
const yo2 = hmm([identifierGuy + 5])`
|
||||||
show(part001)`
|
|
||||||
it('find a safe binaryExpression', () => {
|
it('find a safe binaryExpression', () => {
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const rangeStart = code.indexOf('100 + 100') + 2
|
const rangeStart = code.indexOf('100 + 100') + 2
|
||||||
@ -201,8 +199,7 @@ describe('testing getNodePathFromSourceRange', () => {
|
|||||||
const code = `const part001 = startSketchOn('XY')
|
const code = `const part001 = startSketchOn('XY')
|
||||||
|> startProfileAt([0.39, -0.05], %)
|
|> startProfileAt([0.39, -0.05], %)
|
||||||
|> line([0.94, 2.61], %)
|
|> line([0.94, 2.61], %)
|
||||||
|> line([-0.21, -1.4], %)
|
|> line([-0.21, -1.4], %)`
|
||||||
show(part001)`
|
|
||||||
it('finds the second line when cursor is put at the end', () => {
|
it('finds the second line when cursor is put at the end', () => {
|
||||||
const searchLn = `line([0.94, 2.61], %)`
|
const searchLn = `line([0.94, 2.61], %)`
|
||||||
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
const sourceIndex = code.indexOf(searchLn) + searchLn.length
|
||||||
|
@ -68,8 +68,6 @@ log(5, myVar)
|
|||||||
|> lineTo([1, 1], %)
|
|> lineTo([1, 1], %)
|
||||||
|> lineTo({ to: [1, 0], tag: "rightPath" }, %)
|
|> lineTo({ to: [1, 0], tag: "rightPath" }, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|
|
||||||
show(mySketch)
|
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
@ -331,7 +329,6 @@ describe('it recasts wrapped object expressions in pipe bodies with correct inde
|
|||||||
intersectTag: 'seg01'
|
intersectTag: 'seg01'
|
||||||
}, %)
|
}, %)
|
||||||
|> line([-0.42, -1.72], %)
|
|> line([-0.42, -1.72], %)
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
const { ast } = code2ast(code)
|
const { ast } = code2ast(code)
|
||||||
const recasted = recast(ast)
|
const recasted = recast(ast)
|
||||||
|
@ -3,7 +3,6 @@ import { VITE_KC_API_WS_MODELING_URL, VITE_KC_CONNECTION_TIMEOUT_MS } from 'env'
|
|||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { exportSave } from 'lib/exportSave'
|
import { exportSave } from 'lib/exportSave'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
import * as Sentry from '@sentry/react'
|
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
import { sceneInfra } from 'clientSideScene/sceneInfra'
|
||||||
|
|
||||||
@ -290,12 +289,6 @@ class EngineConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shouldTrace will return true when Sentry should be used to instrument
|
|
||||||
// the Engine.
|
|
||||||
shouldTrace() {
|
|
||||||
return Sentry.getCurrentHub()?.getClient()?.getOptions()?.sendClientReports
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect will attempt to connect to the Engine over a WebSocket, and
|
// connect will attempt to connect to the Engine over a WebSocket, and
|
||||||
// establish the WebRTC connections.
|
// establish the WebRTC connections.
|
||||||
//
|
//
|
||||||
@ -308,41 +301,6 @@ class EngineConnection {
|
|||||||
|
|
||||||
// Information on the connect transaction
|
// Information on the connect transaction
|
||||||
|
|
||||||
class SpanPromise {
|
|
||||||
span: Sentry.Span
|
|
||||||
promise: Promise<void>
|
|
||||||
resolve?: (v: void) => void
|
|
||||||
|
|
||||||
constructor(span: Sentry.Span) {
|
|
||||||
this.span = span
|
|
||||||
this.promise = new Promise((resolve) => {
|
|
||||||
this.resolve = (v: void) => {
|
|
||||||
// here we're going to invoke finish before resolving the
|
|
||||||
// promise so that a `.then()` will order strictly after
|
|
||||||
// all spans have -- for sure -- been resolved, rather than
|
|
||||||
// doing a `then` on this promise.
|
|
||||||
this.span.finish()
|
|
||||||
resolve(v)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let webrtcMediaTransaction: Sentry.Transaction
|
|
||||||
let websocketSpan: SpanPromise
|
|
||||||
let mediaTrackSpan: SpanPromise
|
|
||||||
let dataChannelSpan: SpanPromise
|
|
||||||
let handshakeSpan: SpanPromise
|
|
||||||
let iceSpan: SpanPromise
|
|
||||||
|
|
||||||
const spanStart = (op: string) =>
|
|
||||||
new SpanPromise(webrtcMediaTransaction.startChild({ op }))
|
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
webrtcMediaTransaction = Sentry.startTransaction({ name: 'webrtc-media' })
|
|
||||||
websocketSpan = spanStart('websocket')
|
|
||||||
}
|
|
||||||
|
|
||||||
const createPeerConnection = () => {
|
const createPeerConnection = () => {
|
||||||
this.pc = new RTCPeerConnection()
|
this.pc = new RTCPeerConnection()
|
||||||
|
|
||||||
@ -393,10 +351,6 @@ class EngineConnection {
|
|||||||
// From what I understand, only after have we done the ICE song and
|
// From what I understand, only after have we done the ICE song and
|
||||||
// dance is it safest to connect the video tracks / stream
|
// dance is it safest to connect the video tracks / stream
|
||||||
case 'connected':
|
case 'connected':
|
||||||
if (this.shouldTrace()) {
|
|
||||||
iceSpan.resolve?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the browser attach to the video stream now
|
// Let the browser attach to the video stream now
|
||||||
this.onNewTrack({ conn: this, mediaStream: this.mediaStream! })
|
this.onNewTrack({ conn: this, mediaStream: this.mediaStream! })
|
||||||
break
|
break
|
||||||
@ -429,17 +383,6 @@ class EngineConnection {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
let mediaStreamTrack = mediaStream.getVideoTracks()[0]
|
|
||||||
mediaStreamTrack.addEventListener('unmute', () => {
|
|
||||||
// let settings = mediaStreamTrack.getSettings()
|
|
||||||
// mediaTrackSpan.span.setTag("fps", settings.frameRate)
|
|
||||||
// mediaTrackSpan.span.setTag("width", settings.width)
|
|
||||||
// mediaTrackSpan.span.setTag("height", settings.height)
|
|
||||||
mediaTrackSpan.resolve?.()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
this.webrtcStatsCollector = (): Promise<ClientMetrics> => {
|
this.webrtcStatsCollector = (): Promise<ClientMetrics> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if (mediaStream.getVideoTracks().length !== 1) {
|
if (mediaStream.getVideoTracks().length !== 1) {
|
||||||
@ -522,10 +465,6 @@ class EngineConnection {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
dataChannelSpan.resolve?.()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Everything is now connected.
|
// Everything is now connected.
|
||||||
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
|
this.state = { type: EngineConnectionStateType.ConnectionEstablished }
|
||||||
|
|
||||||
@ -577,27 +516,6 @@ class EngineConnection {
|
|||||||
if (this.token) {
|
if (this.token) {
|
||||||
this.send({ headers: { Authorization: `Bearer ${this.token}` } })
|
this.send({ headers: { Authorization: `Bearer ${this.token}` } })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
websocketSpan.resolve?.()
|
|
||||||
|
|
||||||
handshakeSpan = spanStart('handshake')
|
|
||||||
iceSpan = spanStart('ice')
|
|
||||||
dataChannelSpan = spanStart('data-channel')
|
|
||||||
mediaTrackSpan = spanStart('media-track')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
void Promise.all([
|
|
||||||
handshakeSpan.promise,
|
|
||||||
iceSpan.promise,
|
|
||||||
dataChannelSpan.promise,
|
|
||||||
mediaTrackSpan.promise,
|
|
||||||
]).then(() => {
|
|
||||||
console.log('All spans finished, reporting')
|
|
||||||
webrtcMediaTransaction?.finish()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.websocket.addEventListener('close', (event) => {
|
this.websocket.addEventListener('close', (event) => {
|
||||||
@ -786,13 +704,6 @@ failed cmd type was ${artifactThatFailed?.commandType}`
|
|||||||
type: ConnectingType.WebRTCConnecting,
|
type: ConnectingType.WebRTCConnecting,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.shouldTrace()) {
|
|
||||||
// When both ends have a local and remote SDP, we've been able to
|
|
||||||
// set up successfully. We'll still need to find the right ICE
|
|
||||||
// servers, but this is hand-shook.
|
|
||||||
handshakeSpan.resolve?.()
|
|
||||||
}
|
|
||||||
break
|
break
|
||||||
|
|
||||||
case 'trickle_ice':
|
case 'trickle_ice':
|
||||||
@ -885,7 +796,7 @@ interface UnreliableSubscription<T extends UnreliableResponses['type']> {
|
|||||||
callback: (data: Extract<UnreliableResponses, { type: T }>) => void
|
callback: (data: Extract<UnreliableResponses, { type: T }>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Subscription<T extends ModelTypes> {
|
export interface Subscription<T extends ModelTypes> {
|
||||||
event: T
|
event: T
|
||||||
callback: (
|
callback: (
|
||||||
data: Extract<Models['OkModelingCmdResponse_type'], { type: T }>
|
data: Extract<Models['OkModelingCmdResponse_type'], { type: T }>
|
||||||
@ -1015,6 +926,15 @@ export class EngineCommandManager {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
sceneInfra.camControls.onCameraChange()
|
sceneInfra.camControls.onCameraChange()
|
||||||
|
this.sendSceneCommand({
|
||||||
|
// CameraControls subscribes to default_camera_get_settings response events
|
||||||
|
// firing this at connection ensure the camera's are synced initially
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
this.initPlanes().then(() => {
|
this.initPlanes().then(() => {
|
||||||
this.resolveReady()
|
this.resolveReady()
|
||||||
|
@ -101,7 +101,6 @@ describe('testing changeSketchArguments', () => {
|
|||||||
|> ${line}
|
|> ${line}
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
show(mySketch001)
|
|
||||||
`
|
`
|
||||||
const code = genCode(lineToChange)
|
const code = genCode(lineToChange)
|
||||||
const expectedCode = genCode(lineAfterChange)
|
const expectedCode = genCode(lineAfterChange)
|
||||||
@ -128,8 +127,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)`
|
||||||
show(mySketch001)`
|
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const programMemory = await enginelessExecutor(ast)
|
const programMemory = await enginelessExecutor(ast)
|
||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
@ -155,7 +153,6 @@ show(mySketch001)`
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
|> lineTo([2, 3], %)
|
|> lineTo([2, 3], %)
|
||||||
show(mySketch001)
|
|
||||||
`
|
`
|
||||||
expect(recast(modifiedAst)).toBe(expectedCode)
|
expect(recast(modifiedAst)).toBe(expectedCode)
|
||||||
|
|
||||||
@ -177,7 +174,6 @@ show(mySketch001)
|
|||||||
|> lineTo([-1.59, -1.54], %)
|
|> lineTo([-1.59, -1.54], %)
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
show(mySketch001)
|
|
||||||
`
|
`
|
||||||
expect(recast(modifiedAst)).toBe(expectedCode)
|
expect(recast(modifiedAst)).toBe(expectedCode)
|
||||||
})
|
})
|
||||||
@ -192,7 +188,6 @@ describe('testing addTagForSketchOnFace', () => {
|
|||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> ${line}
|
|> ${line}
|
||||||
|> lineTo([0.46, -5.82], %)
|
|> lineTo([0.46, -5.82], %)
|
||||||
show(mySketch001)
|
|
||||||
`
|
`
|
||||||
const code = genCode(originalLine)
|
const code = genCode(originalLine)
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
|
@ -91,12 +91,6 @@ export function createFirstArg(
|
|||||||
throw new Error('all sketch line types should have been covered')
|
throw new Error('all sketch line types should have been covered')
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
type LineData = {
|
|
||||||
from: [number, number, number]
|
|
||||||
to: [number, number, number]
|
|
||||||
}
|
|
||||||
|
|
||||||
export const lineTo: SketchLineHelper = {
|
export const lineTo: SketchLineHelper = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
@ -966,6 +960,30 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
addTag: addTagWithTo('angleTo'), // TODO might be wrong
|
addTag: addTagWithTo('angleTo'), // TODO might be wrong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
||||||
|
node,
|
||||||
|
pathToNode,
|
||||||
|
to,
|
||||||
|
}) => {
|
||||||
|
const _node = { ...node }
|
||||||
|
const { node: callExpression } = getNodeFromPath<CallExpression>(
|
||||||
|
_node,
|
||||||
|
pathToNode
|
||||||
|
)
|
||||||
|
|
||||||
|
const toArrExp = createArrayExpression([
|
||||||
|
createLiteral(roundOff(to[0])),
|
||||||
|
createLiteral(roundOff(to[1])),
|
||||||
|
])
|
||||||
|
|
||||||
|
mutateArrExp(callExpression.arguments?.[0], toArrExp) ||
|
||||||
|
mutateObjExpProp(callExpression.arguments?.[0], toArrExp, 'to')
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
||||||
line,
|
line,
|
||||||
lineTo,
|
lineTo,
|
||||||
|
@ -88,7 +88,6 @@ describe('testing swapping out sketch calls with xLine/xLineTo', () => {
|
|||||||
` |> yLine(-1.07, %)`,
|
` |> yLine(-1.07, %)`,
|
||||||
` |> xLineTo(3.27, %)`,
|
` |> xLineTo(3.27, %)`,
|
||||||
` |> yLineTo(2.14, %)`,
|
` |> yLineTo(2.14, %)`,
|
||||||
`show(part001)`,
|
|
||||||
]
|
]
|
||||||
const bigExample = bigExampleArr.join('\n')
|
const bigExample = bigExampleArr.join('\n')
|
||||||
it('line with tag converts to xLine', async () => {
|
it('line with tag converts to xLine', async () => {
|
||||||
@ -290,7 +289,6 @@ describe('testing swapping out sketch calls with xLine/xLineTo while keeping var
|
|||||||
` |> angledLineToX([330, angledLineToXx], %)`,
|
` |> angledLineToX([330, angledLineToXx], %)`,
|
||||||
` |> angledLineToY([217, angledLineToYy], %)`,
|
` |> angledLineToY([217, angledLineToYy], %)`,
|
||||||
` |> line([0.89, -0.1], %)`,
|
` |> line([0.89, -0.1], %)`,
|
||||||
`show(part001)`,
|
|
||||||
]
|
]
|
||||||
const varExample = variablesExampleArr.join('\n')
|
const varExample = variablesExampleArr.join('\n')
|
||||||
it('line keeps variable when converted to xLine', async () => {
|
it('line keeps variable when converted to xLine', async () => {
|
||||||
@ -378,8 +376,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([0, 0.4], %)
|
|> line([0, 0.4], %)
|
||||||
|> xLine(3.48, %)
|
|> xLine(3.48, %)
|
||||||
|> line([2.14, 1.35], %) // normal-segment
|
|> line([2.14, 1.35], %) // normal-segment
|
||||||
|> xLine(3.54, %)
|
|> xLine(3.54, %)`
|
||||||
show(part001)`
|
|
||||||
it('normal case works', async () => {
|
it('normal case works', async () => {
|
||||||
const programMemory = await enginelessExecutor(parse(code))
|
const programMemory = await enginelessExecutor(parse(code))
|
||||||
const index = code.indexOf('// normal-segment') - 7
|
const index = code.indexOf('// normal-segment') - 7
|
||||||
|
@ -123,7 +123,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> yLine(1.04, %) // ln-yLine-free should sub in segLen
|
|> yLine(1.04, %) // ln-yLine-free should sub in segLen
|
||||||
|> xLineTo(30, %) // ln-xLineTo-free should convert to xLine
|
|> xLineTo(30, %) // ln-xLineTo-free should convert to xLine
|
||||||
|> yLineTo(20, %) // ln-yLineTo-free should convert to yLine
|
|> yLineTo(20, %) // ln-yLineTo-free should convert to yLine
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
const expectModifiedScript = `const myVar = 3
|
const expectModifiedScript = `const myVar = 3
|
||||||
const myVar2 = 5
|
const myVar2 = 5
|
||||||
@ -196,7 +195,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> yLine(segLen('seg01', %), %) // ln-yLine-free should sub in segLen
|
|> yLine(segLen('seg01', %), %) // ln-yLine-free should sub in segLen
|
||||||
|> xLine(segLen('seg01', %), %) // ln-xLineTo-free should convert to xLine
|
|> xLine(segLen('seg01', %), %) // ln-xLineTo-free should convert to xLine
|
||||||
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
|> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
it('should transform the ast', async () => {
|
it('should transform the ast', async () => {
|
||||||
const ast = parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
@ -257,7 +255,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
||||||
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
it('should transform horizontal lines the ast', async () => {
|
it('should transform horizontal lines the ast', async () => {
|
||||||
const expectModifiedScript = `const myVar = 2
|
const expectModifiedScript = `const myVar = 2
|
||||||
@ -286,7 +283,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
|> angledLineToY([223, 7.68], %) // select for vertical constraint 9
|
||||||
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
|> xLineTo(myVar3, %) // select for horizontal constraint 10
|
||||||
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
|> angledLineToY([301, myVar], %) // select for vertical constraint 10
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
@ -345,7 +341,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> yLineTo(7.68, %) // select for vertical constraint 9
|
|> yLineTo(7.68, %) // select for vertical constraint 9
|
||||||
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
|> angledLineToX([333, myVar3], %) // select for horizontal constraint 10
|
||||||
|> yLineTo(myVar, %) // select for vertical constraint 10
|
|> yLineTo(myVar, %) // select for vertical constraint 10
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
const ast = parse(inputScript)
|
const ast = parse(inputScript)
|
||||||
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
const selectionRanges: Selections['codeBasedSelections'] = inputScript
|
||||||
@ -389,7 +384,6 @@ const part001 = startSketchOn('XY')
|
|||||||
|> line([0.45, 1.46], %) // free
|
|> line([0.45, 1.46], %) // free
|
||||||
|> line([myVar, 0.01], %) // xRelative
|
|> line([myVar, 0.01], %) // xRelative
|
||||||
|> line([0.7, myVar], %) // yRelative
|
|> line([0.7, myVar], %) // yRelative
|
||||||
show(part001)
|
|
||||||
`
|
`
|
||||||
it('testing for free to horizontal and vertical distance', async () => {
|
it('testing for free to horizontal and vertical distance', async () => {
|
||||||
const expectedHorizontalCode = await helperThing(
|
const expectedHorizontalCode = await helperThing(
|
||||||
@ -501,8 +495,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> xLine(3.36, %) // partial
|
|> xLine(3.36, %) // partial
|
||||||
|> line([-1.49, 1.06], %) // free
|
|> line([-1.49, 1.06], %) // free
|
||||||
|> xLine(-3.43 + 0, %) // full
|
|> xLine(-3.43 + 0, %) // full
|
||||||
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full
|
|> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full`
|
||||||
show(part001)`
|
|
||||||
const ast = parse(code)
|
const ast = parse(code)
|
||||||
const constraintLevels: ReturnType<
|
const constraintLevels: ReturnType<
|
||||||
typeof getConstraintLevelFromSourceRange
|
typeof getConstraintLevelFromSourceRange
|
||||||
|
@ -15,8 +15,7 @@ describe('testing angledLineThatIntersects', () => {
|
|||||||
offset: ${offset},
|
offset: ${offset},
|
||||||
tag: "yo2"
|
tag: "yo2"
|
||||||
}, %)
|
}, %)
|
||||||
const intersect = segEndX('yo2', part001)
|
const intersect = segEndX('yo2', part001)`
|
||||||
show(part001)`
|
|
||||||
const { root } = await enginelessExecutor(parse(code('-1')))
|
const { root } = await enginelessExecutor(parse(code('-1')))
|
||||||
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
|
expect(root.intersect.value).toBe(1 + Math.sqrt(2))
|
||||||
const { root: noOffset } = await enginelessExecutor(parse(code('0')))
|
const { root: noOffset } = await enginelessExecutor(parse(code('0')))
|
||||||
|
@ -40,9 +40,9 @@ export interface MouseGuard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const butName = (e: React.MouseEvent) => ({
|
const butName = (e: React.MouseEvent) => ({
|
||||||
middle: !!(e.buttons & 4),
|
middle: !!(e.buttons & 4) || e.button === 1,
|
||||||
right: !!(e.buttons & 2),
|
right: !!(e.buttons & 2) || e.button === 2,
|
||||||
left: !!(e.buttons & 1),
|
left: !!(e.buttons & 1) || e.button === 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
export const cameraMouseDragGuards: Record<CameraSystem, MouseGuard> = {
|
||||||
|
@ -20,6 +20,8 @@ const LOCAL_USER: Models['User_type'] = {
|
|||||||
phone: '555-555-5555',
|
phone: '555-555-5555',
|
||||||
first_name: 'Test',
|
first_name: 'Test',
|
||||||
last_name: 'User',
|
last_name: 'User',
|
||||||
|
can_train_on_data: false,
|
||||||
|
is_service_account: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UserContext {
|
export interface UserContext {
|
||||||
|
@ -119,7 +119,7 @@ export type MoveDesc = { line: number; snippet: string }
|
|||||||
|
|
||||||
export const modelingMachine = createMachine(
|
export const modelingMachine = createMachine(
|
||||||
{
|
{
|
||||||
/** @xstate-layout N4IgpgJg5mDOIC5QFkD2EwBsCWA7KAxAMICGuAxlgNoAMAuoqAA6qzYAu2qujIAHogC0AdgCsAZgB04gEyjhADnEA2GgoUAWJQBoQAT0QBGGuICckmoZkbTM42YWKAvk91oMOfAQDKYdgAJYLDByTm5aBiQQFjYwniiBBFtpGhlxDRphGg1ZURlhXQMEcRLDSVF5UwV84XEK8Rc3dCw8KElsCEwwHz9A4NCuXAjeGI5B3kTBLWFJDWV5hVSZZTrlBUKjS1FZiUrDeWVDasaQdxb8ds7ugFFcdjAAJ0CAaz9yAAthqNG4iaF07bCNLiRYlGiZUyZDbFYQzfIaLSQ5Sw0TGZQnM6eNodLoEW73J6wV7sD5UQyRZisMbcP4IQRrGiSdRmfIqUxrCrQlbKcoQ4RHDTWUx5DHNLGXXHXPjsB4AVwwX0psXGCSEohWO3Uh2UOSsmmhhn2jPyNGUolMhjWwvZoo8rUk3mJH2IZEomEdb0+9BGVN+qoQhoUPOMwgRi1BplkBqOZUDCkN4ksClEClt5zaHpJ7wdTveAEkrj0AkEugNwt7vr6VaBEoZQ2UKsIOcmrPMDRCLIKaOa6oKU6I0+LMx8c56C7jkCRXn0oABbMB3fwAN0enHIJEwiuiVZp-qsFskRxkmibmlSBX0Rg0hikifBcgk+SUGkH9uH2ff4+6k+nQTnC4Cd5UAebAAC9uHYDctx+at+CMeFJFUINu0NfJkRkA1xENcoNHkSwVmMCoZFfC531HLMv2IbhYBlEg8H8ICQPAu4N38CBsBo10wGgnd4hreDRA0Ts0hoKp0gtdRoTSdkLAqURwVwi0bxIjNc3Ij5KKIajaPolcHjXVj2M4ihuIrJVqT4uCA2WBRJGFY9UR1YxNAwy8EBkVlymsAVYSBM0X1cU4xTfNTP0LLTcBoh46NwfwAEEACFvH8AANHjlV3fjrOTWZxDEQwbCwmRbHEKThSE4VkUWVJzVqBpAsxELPXU-Nwu06L6MS5KAE10os2k9W2aw7GUOR9jNUboUTdRJCBOTLTUUMAqaO1SNC3NNPamL-DIKAuj6v0spvHJpCUapk0UOtTCk+S4X7Xz1WEUxTGEFSWpazbIp02KunwdgvQpbcMss2sShmFR1VEm80n2KTDlsptTGvJ6wxUV6GuCtbmrC3EIqi7amEeQncHY8hZUwEgniMyCTIO2Daw82ybEjKwMiOVRlCk+MhP5KoXqBBENDEN6yJx7o8e+hjgLAiCN0wPQdpwKAhjMoH+r3ZYZCZFMtAkXCMhWA1HMkYqddhfJ1RKEX1rHNqvo62K9IMzB5cV7BlbpzKrKsJ7cuPJ7HLMVEDVG7YUxeqo1mKxx6pW9N3rFqj7e22BcBIJh-HYVBUs9kGjCwqRlhBNQXuqMwpOyLWPPVeZVBTIFiIx1bVOxja7fx+jU-TzPs961WYK90GbwsJssNhPLjA0KSCpmQTHDkC1Qx1WOgubhO29xrb6LAABHWVWN+qB-tzgbiqkVEhaerI8gkC8ijrRNcpMcFuwm0xrdb23N+T+imEpuXqD914gNWQMwlgER7MjKeblDCRjAaUQ4z1MhqAHE3eOosN7iy3rFB4YBZyoBXP4cg2D2CwBPhrLQiFWbqmPNeFMpUYFKDKPhWw2QwRBkbnHIcNsKKFgAEpgEEGAPgIRZT3HIUdOoUgbywMTGkIWFRDDQnUFrdIqRkS1yUIcD+WYPqFmuHvbAGcAAyeAwA91QJuIBwMBoKBkhIFQzM9aSTckoMOmRhpyDmKiQwOiRyJwMbKIxmddoAWwKxSm5Ae4SKsiNWyIJLaWnkG4hh99jBlFPHXRQ6Q5jLVXugtScUADudFALS2YpBTAbEOI00oP4PAAAzVABAIDcDAO0XAS5UCvEkDAdgghGIyxYpgQQjTUAxMSCJISCIdSiVqM9ceUlrzmGTMeGGyhbCPT8dmYppSpZMVllU6mXF6m4CaQQR4DxgKSCYBTdgTSHizl6X4AZ5TDmjLOeM6x6ssoiW2IaBECZcLHhelJEEWtsimnBHUNCvi0HcOarsjgy5VzYHXEcmpJyxktLaR0rpPS+mCCdmijcHymkTMQDDLWN5LomGRLA-IUljxSEcDfOwpQgTbMkEigIxL0XVOMnU7Flzrm3JIPc4CTzCV8tJWMil7kipMm7GIReOsmWCkPBNVIIJzpKC5Ty+KSVUqnPOa03A7S8D4vaYSkgAAjWAgg+Bkq+YDAeecFWWlmqNe8cg6jrDcsVUah4MjXmXprWo+qSnIq6sa4VDwrkPBuXch5UqXl2odU6uV3zDqxLHrlHVdRIRzzKp6v1ZosLZFDO-eFTVdEGpjd1E1zSzUWs6d061ab7WCD0M6+V6zpCNgqLYOBuEpqmhmDYOx8l6HdjyY1LGdao0BAbU2i58bRXJslc8-p6bu29uzfTSlebzwrD8isUNU1LRCVNOyUaY08qcPyQixdezdpdFXS2vF7bt2CDfYIrNrrgH+hhuDSwN4b7PWDgGioPJIzdjAxUIMsJI2vvwO+uNCak3ipTT+v9+7AM2OA1hBG2Q9RWBLnWG6dZDzgayHIbsyx0ZcNrSOA1h9-oftxZa79hL2PvHwz6QjvysLbByNJKBMhzxwxyqaDlKg6xWBQ8ivjq6RWJrFRKx5P6+MCcrEJ3NnrjAmBTJaE8rkijLA1fMawdiFEonRDWhdrGl3+EJg8YmpNyaUwFbU8x2LP3cYJS8tzHm0VeYeIIY5JldPmRzZMzWdl6HWDvVHUQXM5DSH5HMM0kHfVKYCCFhcnmKZU0xSZVT671Obq04SwrJMwslci2VygMW1ZxcpQl4UShkseVS1JXymWCrzHNPZCQXLTHmosZgIsfRSxxD7ZJ8w15JNHEyEcPIqTKUvUPF10SNmkKPvnS3XRE3zFZ0sXiQxGcaYwDuOEqpkTokHsHpS2EQl9hmHBOoPmnM3L83zYoWEokHPMac9mU7U3JB5lwBwAgC2VBMgSatpGC8uTxhNiCSMkI8pPTSONsxkPoew7JARn5sT8ha3kseK95GNnQlhEtlYEhNkbPBKg0Hx2RwQ-O5gSQuBJUbhmyWEI83nvuusLUaQx54wcIyDXLkQtZrXgUdza8WF8eTZ55IAActnAACqgPApCCBxQgBAQIkF9KucN3cPt8wKqv3NIKRYOouQbNyhHLIigzoa7O6gSxOv9c2+N6QEyVjSftYDJocwJg0jVFErrFxRQshlBsIg0ox4VhcqJ+wOHYuBr4VmBdOlDGE8GmBLMDRywoWWbhRz96Oe4fkkE2T2sKEmSQkhHMWQ4ICrl4PIoPWhxLORhB0+lj2YAAqoS7sRIeFErOQv+ii4j4e9yo1zAbKUMKRMthirl+vEyK9knLC4RTFy6f+Awlz4X80wJwTuf+-Dy3yPlnpmJkH8KU05nEBIx5ObNIPCU0OYJjcfMHSQWUEmbOTifSd8AAeVwBxXNS-R6Tim8En0EEgNaUEBgPYHgJVlXxewDFkCkDmBMFwlEkyEWDvkQHNFshbEQxpScW2X8H538AaRIEoB6DmzYjAA4PJgKwpnNXlUEAKnMASWM0NFDAkFgS5AyyRnZCG0sAfFAKO0kDIGwFnHFVaB7lcyEO6ACzbR6Q0K0PuEECzkEA4MoHlUUUPGoMWAyCWChDclGniSsDBBBVDHUDehMO0PwF0LFXNSXzm0GD7SBBozkAXiQlDGhDnkPFWDsHBBWBqB8Jh1MJ0Kzn8GERqR0KJE9CQNbStXULSPFUEQsLyKzBsLNCoVZGeg8jSFhGhDoQHWPGALsDmFoRcECn5wwHgCiCOxfzX0EEWDKFyGySQk0B0DckEEEiVSgX2DyFGktDehxDAEGKIPpEoUehehBFwhPGUXRwkDUA+3cMsGrXr3fHWPdXZCZmLxUFLyRimi1g2UqEDmcnZzAM5w-EwSuJAWehNh1HjDymqF2GjGSBvDylSGqAtBhPy32SGUqR8yxU+V+L3CDCZgyFLn5BqFSDBWSABDvHmFsH5DhJlQxUFT8xRL01byMBUU1V7ykQ3yUQDXowsG8m5mG1RDhJjRSibVRKOjsSkDUCKlH0SNEjKiwh2CqlUAWhTEO0xi+O5RcxXTGX5O9jsUp1oWI0rjHWqOrkHSqGhLhL-T5OpMjyOGFEQjvByAcDyEjBujUCZDjxg0yFGjqDhJU1VLNLXwtMZAyFWBM1HzbADW5FmhhMcGRCRktjhLq2K28yiyFSpNix9OTH-3ZHkjPDTxKH63R15mTAtEbBtEc0VMf0sTVMmXsBHhKGTEnXRNiNUBNmVxWBl1wmRF90Jxh3YHLMpX2GYTqhrMNLWC5HBPmByRWC7w2XbK1350eQ3G7PchsAnXHmKjUFjx-wQBT1mEUMjCKiDFkCnKf0D38ANyNz6OTKIOsAkBNiYS0HsDSENjcibCrnjATCOCbFvOz07PnMIk331msBMG3z7xgRjBo3UATCTHP2LPekv1u04Bvx7nnLkE0Fmj8hUAciemuhgQYwBKDA5GvisA+LULIiwOgMtzwNzAQO-KtHKF7FTL9VkEwqKEEh5DmAZTPyqnVBYLYKsLWO9I2L7BouWVUDqCekmhcIy2vHsGnS0FkFTCgt8M4H8MyMCN4vPPdUtCkBelZxsHkFpzkJ5AAojLnmmlUIVOKM0L8KgF0OyJolyNzG-McBNlhGQjynZHZSmibF5HsGoKFmKgKi6KcCAA */
|
/** @xstate-layout N4IgpgJg5mDOIC5QFkD2EwBsCWA7KAxAMICGuAxlgNoAMAuoqAA6qzYAu2qujIAHogC0AdgCsAZgB04gEyjhADnEA2GgoUAWJQBoQAT0QBGGuICckmoZkbTM42YWKAvk91oMOfAQDKYdgAJYLDByTm5aBiQQFjYwniiBBFtpGhlxDRphGg1ZURlhXQMEcRLDSVF5UwV84XEK8Rc3dCw8KElsCEwwHz9A4NCuXAjeGI5B3kTBLWFJDWV5hVSZZTrlBUKjS1FZiUrDeWVDasaQdxb8ds7ugFFcdjAAJ0CAaz9yAAthqNG4iaF07bCNLiRYlGiZUyZDbFYQzfIaLSQ5Sw0TGZQnM6eNodLoEW73J6wV7sD5UQyRZisMbcP4IQRrGiSdRmfIqUxrCrQlbKcoQ4RHDTWUx5DHNLGXXHXPjsB4AVwwX0psXGCSEohWO3Uh2UOSsmmhhn2jPyNGUolMhjWwvZoo8rUk3mJH2IZEomEdb0+9BGVN+qoQhoUPOMwgRi1BplkBqOZUDCkN4ksClEClt5zaHpJ7wdTveAEkrj0AkEugNwt7vr6VaBEoZQ2UKsIOcmrPMDRCLIKaOa6oKU6I0+LMx8c56C7jkCRXn0oABbMB3fwAN0enHIJEwiuiVZp-qsFskRxkmibmlSBX0Rg0hikifBcgk+SUGkH9uH2ff4+6k+nQTnC4Cd5UAebAAC9uHYDctx+at+CMeFJFUINu0NfJkRkA1xENcoNHkSwVmMCoZFfC531HLMv2IbhYBlEg8H8ICQPAu4N38CBsBo10wGgnd4hreDRA0Ts0hoKp0gtdRoTSdkLAqURwVwi0bxIjNc3Ij5KKIajaPolcHjXVj2M4ihuIrJVqT4uCA2WBRJGFY9UR1YxNAwy8EBkVlymsAVYSBM0X1cU4xTfNTP0LLTcBoh46NwfwAEEACFvH8AANHjlV3fjrOTWZxDEQwbCwmRbHEKThSE4VkUWVJzVqBpAsxELPXU-Nwu06L6MS5KAE10os2k9W2aw7GUOR9jNUboUTdRJCBOTLTUUMAqaO1SNC3NNPamL-DIKAuj6v0spvHJpCUapk0UOtTCk+S4X7Xz1WEUxTGEFSWpazbIp02KunwdgvQpbcMss2sShmFR1VEm80n2KTDlsptTGvJ6wxUV6GuCtbmrC3EIqi7amEeQncHY8hZUwEgniMyCTIO2Daw82ybEjKwMiOVRlCk+MhP5KoXqBBENDEN6yJx7o8e+hjgLAiCN0wPQdpwKAhjMoH+r3ZYZCZFMtAkXCMhWA1HMkYqddhfJ1RKEX1rHNqvo62K9IMzB5cV7BlbpzKrKsJ7cuPJ7HLMVEDVG7YUxeqo1mKxx6pW9N3rFqj7e22BcBIJh-HYVBUs9kGjCwqRlhBNQXuqMwpOyLWPPVeZVBTIFiIx1bVOxja7fx+jU-TzPs961WYK90GbwsJssNhPLjA0KSCpmQTHDkC1Qx1WOgubhO29xrb6LAABHWVWN+qB-tzgbiqkVEhaerI8gkC8ijrRNcpMcFuwm0xrdb23N+T+imEpuXqD914gNWQMwlgER7MjKeblDCRjAaUQ4z1MhqAHE3eOosN7iy3rFB4YBZyoBXP4cg2D2CwBPhrLQiFWbqmPNeFMpUYFKDKPhWw2QwRBkbnHIcNsKKFgAEpgEEGAPgIRZT3HIUdOoUgbywMTGkIWFRDDQnUFrdIqRkS1yUIcD+WYPqFmuHvbAGcAAyeAwA91QJuIBwMBoKBkhIFQzM9aSTckoMOmRhpyDmKiQwOiRyJwMbKIxmddoAWwKxSm5Ae4SKsiNWyIJLaWnkG4hh99jBlFPHXRQ6Q5jLVXugtScUADudFALS2YpBTAbEOI00oP4PAAAzVABAIDcDAO0XAS5UCvEkDAdgghGIyxYpgQQjTUAxMSCJISCIdSiVqM9ceUlrzmGTMeGGyhbCPT8dmYppSpZMVllU6mXF6m4CaQQR4DxgKSCYBTdgTSHizl6X4AZ5TDmjLOeM6x6ssoiW2IaBECZcLHhelJEEWtsimnBHUNCvi0HcOarsjgy5VzYHXEcmpJyxktLaR0rpPS+mCCdmijcHymkTMQDDLWN5LomGRLA-IUljxSEcDfOwpQgTbMkEigIxL0XVOMnU7Flzrm3JIPc4CTzCV8tJWMil7kipMm7GIReOsmWCkPBNVIIJzpKC5Ty+KSVUqnPOa03A7S8D4vaYSkgAAjWAgg+Bkq+YDAeecFWWlmqNe8cg6jrDcsVUah4MjXmXprWo+qSnIq6sa4VDwrkPBuXch5UqXl2odU6uV3zDqxLHrlHVdRIRzzKp6v1ZosLZFDO-eFTVdEGpjd1E1zSzUWs6d061ab7WCD0M6+V6zpCNgqLYOBuEpqmhmDYOx8l6HdjyY1LGdao0BAbU2i58bRXJslc8-p6bu29uzfTSlebzwrD8isUNU1LRCVNOyUaY08qcPyQixdezdpdFXS2vF7bt2CDfYIrNrrgH+hhuDSwN4b7PWDgGioPJIzdjAxUIMsJI2vvwO+uNCak3ipTT+v9+7AM2OA1hBG2Q9RWBLnWG6dZDzgayHIbsyx0ZcNrSOA1h9-oftxZa79hL2PvHwz6QjvysLbByNJKBMhzxwxyqaDlKg6xWBQ8ivjq6RWJrFRKx5P6+MCcrEJ3NnrjAmBTJaE8rkijLA1fMawdiFEonRDWhdrGl3+EJg8YmpNyaUwFbU8x2LP3cYJS8tzHm0VeYeIIY5JldPmRzZMzWdl6HWDvVHUQXM5DSH5HMM0kHfVKYCCFhcnmKZU0xSZVT671Obq04SwrJMwslci2VygMW1ZxcpQl4UShkseVS1JXymWCrzHNPZCQXLTHmosZgIsfRSxxD7ZJ8w15JNHEyEcPIqTKUvUPF10SNmkKPvnS3XRE3zFZ0sXiQxGcaYwDuOEqpkTokHsHpS2EQl9hmHBOoPmnM3L83zYoWEokHPMac9mU7U3JB5lwBwAgC2VBMgSatpGC8uTxhNiCSMkI8pPTSONsxkPoew7JARn5sT8ha3kseK95GNnQlhEtlYEhNkbPBKg0Hx2RwQ-O5gSQuBJUbhmyWEI83nvuusLUaQx54wcIyDXLkQtZrXgUdza8WF8eTZ55IAActnAACqgPApCCBxQgBAQIkF9KucN3cPt8wKqv3NIKRYOouQbNyhHLIigzoa7O6gSxOv9c2+N6QEyVjSftYDJocwJg0jVFErrFxRQshlBsIg0ox4VhcqJ+wOHYuBr4VmBdOlDGE8GmBLMDRywoWWbhRz96Oe4fkkE2T2sKEmSQkhHMWQ4ICrl4PIoPWhxLORhB0+lj2YAAqoS7sRIeFErOQv+ii4j4e9yo1zAbKUMKRMthirl+vEyK9knLC4RTFy6f+Awlz4X80wJwTuf+-Dy3yPlnpmJkH8KU05nEBIx5ObNIPCU0OYJjcfMHSQWUEmbOTifSd8AAeVwBxXNS-R6Tim8En0EEgNaUEBgPYHgJVlXxewDFkCkDmBMFwlEkyEWDvkQHNFshbEQxpScW2X8H538AaRIEoB6DmzYjAA4PJgKwpnNXlUEAKnMASWM0NFDAkFgS5AyyRnZCG0sAfFAKO0kDIGwFnHFVaB7lcyEO6ACzbR6Q0K0PuEECzkEA4MoHlUUUPGoMWAyCWChDclGniSsDBBBVDHUDehMO0PwF0LFXNSXzm0GD7SBBozkAXiQlDGhDnkPFWDsHBBWBqB8Jh1MJ0Kzn8GERqR0KJE9CQNbStXULSPFUEQsLyKzBsLNCoVZGeg8jSFhGhDoQHWPGALsDmFoRcECn5wwHgCiCOxfzX0EEWDKFyGySQk0B0DcmGMZAcMk3kmfnrDHzUJxDAEGKIPpEoUehehBFwhPGUXRwkDUA+3cMsGrXr3fHWPdXZCZmLxUFLyRi5EQi62sETHSGqC2Uc05w-EwSuJAWehNh1HjDyg+LqGjGSBvDylSGqAtFhPy32SGUqR8yxU+T+L3CDCZgyFLn5BqFSDBWSABDvHmFsH5HhJlQxUFT81RL01byMBUU1V7ykQ3yUQDXowsG8m5mG1RHhJjRSibTRKOjsSkDUCKlH0SNEjKiwh2CqlUAWhTEO0xm+O5RcxXTGQFO9jsUp1oWI0rjHWqOrkHSqBhPhL-X5JpMjyOGFEQjvByAcDyEjBujUCZDjxg0yFGjqHhJUzVPNLX0tMZAyFWBM1HzbADW5FmlhMcGRCRktnhLq2K28yiyFWpNi19OTH-3ZHkjPDTxKH63R15mTAtEbBtC+Pekf0sXVMmXsBHhKGTEnQxNiNUBNmVxhJsHozrzAKVLLN5xzwrMpX2GYTqlrKNLWC5AhPmByRWC7w2V90h350eQ3F7PchsAnXHmKjUFjx-wQBT1mEUMjCKiDFkBnK11138ANyNz6JTKIOsAkBNiYS0HsDSENjcibCrnjATCOCbHvOzxh3YEXMIk331msBMG3z7xgRjBo3UATCTHPxLLIkv1u04Bvx7kXLkE0Fmj8hUAciemuhgQY0BKDA5GvisHZw7PeiwOgMtzwNzAQL-KtHKF7DTL9VkBwqKEEh5DmAZTPyqnVBYLYKsLWJ9I2L7HouWVUDqCekmhcIy2vHsGnS0FkFTBLN8M4H8MyMCIEsvPdUtCkBelZxsHkFpzkJ5GAsjLnmmlUMVOKM0L8KgF0OyJolyNzD-McBNlhGQjynZHZSmibF5HsGoKFmKgKi6KcCAA */
|
||||||
id: 'Modeling',
|
id: 'Modeling',
|
||||||
|
|
||||||
tsTypes: {} as import('./modelingMachine.typegen').Typegen0,
|
tsTypes: {} as import('./modelingMachine.typegen').Typegen0,
|
||||||
@ -481,6 +481,7 @@ export const modelingMachine = createMachine(
|
|||||||
'animate after sketch',
|
'animate after sketch',
|
||||||
'tear down client sketch',
|
'tear down client sketch',
|
||||||
'remove sketch grid',
|
'remove sketch grid',
|
||||||
|
'engineToClient cam sync direction',
|
||||||
],
|
],
|
||||||
|
|
||||||
entry: ['add axis n grid', 'conditionally equip line tool'],
|
entry: ['add axis n grid', 'conditionally equip line tool'],
|
||||||
@ -514,6 +515,8 @@ export const modelingMachine = createMachine(
|
|||||||
internal: true,
|
internal: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
entry: 'clientToEngine cam sync direction',
|
||||||
},
|
},
|
||||||
|
|
||||||
'animating to existing sketch': {
|
'animating to existing sketch': {
|
||||||
@ -524,6 +527,8 @@ export const modelingMachine = createMachine(
|
|||||||
onDone: 'Sketch',
|
onDone: 'Sketch',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
|
entry: 'clientToEngine cam sync direction',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -845,6 +850,12 @@ export const modelingMachine = createMachine(
|
|||||||
// (note the orbit controls are always active though)
|
// (note the orbit controls are always active though)
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
},
|
},
|
||||||
|
'clientToEngine cam sync direction': () => {
|
||||||
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
|
},
|
||||||
|
'engineToClient cam sync direction': () => {
|
||||||
|
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||||
|
},
|
||||||
},
|
},
|
||||||
// end actions
|
// end actions
|
||||||
}
|
}
|
||||||
|
147
src/wasm-lib/Cargo.lock
generated
@ -246,7 +246,7 @@ checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -257,7 +257,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -574,9 +574,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.0"
|
version = "4.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
|
checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@ -584,9 +584,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.0"
|
version = "4.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
|
checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@ -606,7 +606,7 @@ dependencies = [
|
|||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -856,7 +856,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -897,7 +897,7 @@ checksum = "377af281d8f23663862a7c84623bc5dcf7f8c44b13c7496a590bdc157f941a43"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
"synstructure 0.13.0",
|
"synstructure 0.13.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -949,7 +949,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_tokenstream",
|
"serde_tokenstream",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -965,7 +965,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_tokenstream",
|
"serde_tokenstream",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -977,7 +977,7 @@ dependencies = [
|
|||||||
"diesel_table_macro_syntax",
|
"diesel_table_macro_syntax",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -986,7 +986,7 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1036,7 +1036,7 @@ checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1118,7 +1118,7 @@ checksum = "eecf8589574ce9b895052fa12d69af7a233f99e6107f5cb8dd1044f2a17bfdcb"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1335,7 +1335,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1404,9 +1404,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gif"
|
name = "gif"
|
||||||
version = "0.12.0"
|
version = "0.13.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"color_quant",
|
"color_quant",
|
||||||
"weezl",
|
"weezl",
|
||||||
@ -1440,7 +1440,7 @@ dependencies = [
|
|||||||
"inflections",
|
"inflections",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1460,13 +1460,17 @@ name = "grackle"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"kcl-lib",
|
"kcl-lib",
|
||||||
|
"kittycad",
|
||||||
"kittycad-execution-plan",
|
"kittycad-execution-plan",
|
||||||
|
"kittycad-execution-plan-macros",
|
||||||
"kittycad-execution-plan-traits",
|
"kittycad-execution-plan-traits",
|
||||||
|
"kittycad-modeling-cmds",
|
||||||
"kittycad-modeling-session",
|
"kittycad-modeling-session",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1711,9 +1715,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "image"
|
name = "image"
|
||||||
version = "0.24.8"
|
version = "0.24.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "034bbe799d1909622a74d1193aa50147769440040ff36cb2baa947609b0a4e23"
|
checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
"bytemuck",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
@ -1912,7 +1916,7 @@ dependencies = [
|
|||||||
"itertools 0.12.1",
|
"itertools 0.12.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
"kittycad-execution-plan-macros 0.1.4 (git+https://github.com/KittyCAD/modeling-api?branch=main)",
|
"kittycad-execution-plan-macros",
|
||||||
"kittycad-execution-plan-traits",
|
"kittycad-execution-plan-traits",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"parse-display 0.9.0",
|
"parse-display 0.9.0",
|
||||||
@ -1943,14 +1947,14 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.2.54"
|
version = "0.2.58"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "13958174d876353f429ea8230dc92fe86f164819cea2e51bbf22e01a4c2a496e"
|
checksum = "049c3881ffbe77bf1c3a968372a246ce906eceb79f61cd0bc5fa229bec3504cb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1986,7 +1990,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan"
|
name = "kittycad-execution-plan"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"insta",
|
"insta",
|
||||||
@ -2004,30 +2008,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan-macros"
|
name = "kittycad-execution-plan-macros"
|
||||||
version = "0.1.4"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
checksum = "71d31b689c944d00aadda2ef83d8422a6efff97e1be5654a61f9d95496f0c19e"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "kittycad-execution-plan-macros"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#632b75a0242400fa34373d7973b9149b0e08aa3f"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.49",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-execution-plan-traits"
|
name = "kittycad-execution-plan-traits"
|
||||||
version = "0.1.10"
|
version = "0.1.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
checksum = "a3ec8efd57b59697eb140b63c0ffe7db44fdfe5a55f14e45513411eba2280ba5"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
@ -2036,8 +2028,8 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds"
|
name = "kittycad-modeling-cmds"
|
||||||
version = "0.1.18"
|
version = "0.1.28"
|
||||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -2047,8 +2039,9 @@ dependencies = [
|
|||||||
"enum-iterator-derive",
|
"enum-iterator-derive",
|
||||||
"euler",
|
"euler",
|
||||||
"http 0.2.9",
|
"http 0.2.9",
|
||||||
"kittycad-execution-plan-macros 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"kittycad-execution-plan-macros",
|
||||||
"kittycad-execution-plan-traits",
|
"kittycad-execution-plan-traits",
|
||||||
|
"kittycad-modeling-cmds-macros",
|
||||||
"kittycad-unit-conversion-derive",
|
"kittycad-unit-conversion-derive",
|
||||||
"measurements",
|
"measurements",
|
||||||
"parse-display 0.8.2",
|
"parse-display 0.8.2",
|
||||||
@ -2061,10 +2054,20 @@ dependencies = [
|
|||||||
"webrtc",
|
"webrtc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kittycad-modeling-cmds-macros"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.52",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-session"
|
name = "kittycad-modeling-session"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#08f05d91062380fe3a69f4baa1f1301532d31977"
|
source = "git+https://github.com/KittyCAD/modeling-api?branch=main#ada70b2e6c89438385963eda90d04baa2f9a9cca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
"futures",
|
||||||
"kittycad",
|
"kittycad",
|
||||||
@ -2496,7 +2499,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2653,7 +2656,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.7.5",
|
"regex-syntax 0.7.5",
|
||||||
"structmeta 0.2.0",
|
"structmeta 0.2.0",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2667,7 +2670,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"regex-syntax 0.8.2",
|
"regex-syntax 0.8.2",
|
||||||
"structmeta 0.3.0",
|
"structmeta 0.3.0",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2733,7 +2736,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3601,7 +3604,7 @@ checksum = "33c85360c95e7d137454dc81d9a4ed2b8efd8fbe19cee57357b32b9771fccb67"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3635,7 +3638,7 @@ checksum = "8725e1dfadb3a50f7e5ce0b1a540466f6ed3fe7a0fca2ac2b8b831d31316bd00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3656,7 +3659,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"serde",
|
"serde",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3913,7 +3916,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"structmeta-derive 0.2.0",
|
"structmeta-derive 0.2.0",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3925,7 +3928,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"structmeta-derive 0.3.0",
|
"structmeta-derive 0.3.0",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3936,7 +3939,7 @@ checksum = "a60bcaff7397072dca0017d1db428e30d5002e00b6847703e2e42005c95fbe00"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -3947,7 +3950,7 @@ checksum = "152a0b65a590ff6c3da95cabe2353ee04e6167c896b28e3b14478c2636c922fc"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4019,9 +4022,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.49"
|
version = "2.0.52"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "915aea9e586f80826ee59f8453c1101f9d1c4b3964cd2460185ee8e299ada496"
|
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -4054,7 +4057,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
"unicode-xid",
|
"unicode-xid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4184,7 +4187,7 @@ checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4291,7 +4294,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4454,7 +4457,7 @@ checksum = "84fd902d4e0b9a4b27f2f440108dc034e1758628a9b702f8ec61ad66355422fa"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4482,7 +4485,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -4566,7 +4569,7 @@ dependencies = [
|
|||||||
"Inflector",
|
"Inflector",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
"termcolor",
|
"termcolor",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4813,7 +4816,7 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4848,7 +4851,7 @@ checksum = "642f325be6301eb8107a83d12a8ac6c1e1c54345a7ef1a9261962dfefda09e66"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
@ -5134,9 +5137,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "weezl"
|
name = "weezl"
|
||||||
version = "0.1.7"
|
version = "0.1.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi"
|
name = "winapi"
|
||||||
@ -5466,7 +5469,7 @@ checksum = "b3c129550b3e6de3fd0ba67ba5c81818f9805e58b8d7fee80a3a59d2c9fc601a"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -5486,7 +5489,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.49",
|
"syn 2.0.52",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -21,7 +21,7 @@ wasm-bindgen-futures = "0.4.41"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
image = "0.24.8"
|
image = "0.24.9"
|
||||||
kittycad = { workspace = true, default-features = true }
|
kittycad = { workspace = true, default-features = true }
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
reqwest = { version = "0.11.24", default-features = false }
|
reqwest = { version = "0.11.24", default-features = false }
|
||||||
@ -58,11 +58,12 @@ members = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
kittycad = { version = "0.2.54", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.2.58", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
kittycad-execution-plan = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
kittycad-execution-plan-traits = "0.1.10"
|
|
||||||
kittycad-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
|
||||||
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
kittycad-execution-plan-macros = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
|
kittycad-execution-plan-traits = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
|
kittycad-modeling-cmds = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
|
kittycad-modeling-session = { git = "https://github.com/KittyCAD/modeling-api", branch = "main" }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "executor"
|
name = "executor"
|
||||||
@ -73,6 +74,9 @@ name = "modify"
|
|||||||
path = "tests/modify/main.rs"
|
path = "tests/modify/main.rs"
|
||||||
|
|
||||||
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
|
# Example: how to point modeling-api at a different repo (e.g. a branch or a local clone)
|
||||||
# [patch."https://github.com/KittyCAD/modeling-api"]
|
#[patch."https://github.com/KittyCAD/modeling-api"]
|
||||||
# kittycad-execution-plan = { path = "../../../modeling-api/execution-plan" }
|
#kittycad-execution-plan = { path = "../../../modeling-api/execution-plan" }
|
||||||
# kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
#kittycad-execution-plan-macros = { path = "../../../modeling-api/execution-plan-macros" }
|
||||||
|
#kittycad-execution-plan-traits = { path = "../../../modeling-api/execution-plan-traits" }
|
||||||
|
#kittycad-modeling-cmds = { path = "../../../modeling-api/modeling-cmds" }
|
||||||
|
#kittycad-modeling-session = { path = "../../../modeling-api/modeling-session" }
|
||||||
|
@ -19,7 +19,7 @@ quote = "1"
|
|||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
serde = { version = "1.0.193", features = ["derive"] }
|
serde = { version = "1.0.193", features = ["derive"] }
|
||||||
serde_tokenstream = "0.2"
|
serde_tokenstream = "0.2"
|
||||||
syn = { version = "2.0.49", features = ["full"] }
|
syn = { version = "2.0.52", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
expectorate = "1.1.0"
|
expectorate = "1.1.0"
|
||||||
|
@ -7,11 +7,15 @@ description = "A new executor for KCL which compiles to Execution Plans"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
|
kittycad = { workspace = true }
|
||||||
kittycad-execution-plan = { workspace = true }
|
kittycad-execution-plan = { workspace = true }
|
||||||
kittycad-execution-plan-traits = { workspace = true }
|
kittycad-execution-plan-traits = { workspace = true }
|
||||||
|
kittycad-execution-plan-macros = { workspace = true }
|
||||||
|
kittycad-modeling-cmds = { workspace = true }
|
||||||
kittycad-modeling-session = { workspace = true }
|
kittycad-modeling-session = { workspace = true }
|
||||||
thiserror = "1.0.57"
|
thiserror = "1.0.57"
|
||||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||||
|
uuid = "1.7"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1"
|
pretty_assertions = "1"
|
||||||
|
@ -103,7 +103,7 @@ impl BindingScope {
|
|||||||
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
("add".into(), EpBinding::from(KclFunction::Add(native_functions::Add))),
|
||||||
(
|
(
|
||||||
"startSketchAt".into(),
|
"startSketchAt".into(),
|
||||||
EpBinding::from(KclFunction::StartSketchAt(native_functions::StartSketchAt)),
|
EpBinding::from(KclFunction::StartSketchAt(native_functions::sketch::StartSketchAt)),
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
parent: None,
|
parent: None,
|
||||||
|
@ -45,6 +45,13 @@ pub enum CompileError {
|
|||||||
NoReturnStmt,
|
NoReturnStmt,
|
||||||
#[error("You used the %, which means \"substitute this argument for the value to the left in this |> pipeline\". But there is no such value, because you're not calling a pipeline.")]
|
#[error("You used the %, which means \"substitute this argument for the value to the left in this |> pipeline\". But there is no such value, because you're not calling a pipeline.")]
|
||||||
NotInPipeline,
|
NotInPipeline,
|
||||||
|
#[error("The function '{fn_name}' expects a parameter of type {expected} as argument number {arg_number} but you supplied {actual}")]
|
||||||
|
ArgWrongType {
|
||||||
|
fn_name: &'static str,
|
||||||
|
expected: &'static str,
|
||||||
|
actual: String,
|
||||||
|
arg_number: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
|
@ -618,7 +618,7 @@ impl Eq for UserDefinedFunction {}
|
|||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
enum KclFunction {
|
enum KclFunction {
|
||||||
Id(native_functions::Id),
|
Id(native_functions::Id),
|
||||||
StartSketchAt(native_functions::StartSketchAt),
|
StartSketchAt(native_functions::sketch::StartSketchAt),
|
||||||
Add(native_functions::Add),
|
Add(native_functions::Add),
|
||||||
UserDefined(UserDefinedFunction),
|
UserDefined(UserDefinedFunction),
|
||||||
}
|
}
|
||||||
|
@ -2,12 +2,13 @@
|
|||||||
//! This includes some of the stdlib, e.g. `startSketchAt`.
|
//! This includes some of the stdlib, e.g. `startSketchAt`.
|
||||||
//! But some other stdlib functions will be written in KCL.
|
//! But some other stdlib functions will be written in KCL.
|
||||||
|
|
||||||
use kcl_lib::std::sketch::PlaneData;
|
|
||||||
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
|
use kittycad_execution_plan::{BinaryArithmetic, Destination, Instruction};
|
||||||
use kittycad_execution_plan_traits::{Address, Value};
|
use kittycad_execution_plan_traits::Address;
|
||||||
|
|
||||||
use crate::{CompileError, EpBinding, EvalPlan};
|
use crate::{CompileError, EpBinding, EvalPlan};
|
||||||
|
|
||||||
|
pub mod sketch;
|
||||||
|
|
||||||
/// The identity function. Always returns its first input.
|
/// The identity function. Always returns its first input.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
@ -41,34 +42,6 @@ impl Callable for Id {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
|
||||||
pub struct StartSketchAt;
|
|
||||||
|
|
||||||
impl Callable for StartSketchAt {
|
|
||||||
fn call(&self, next_addr: &mut Address, _args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
|
||||||
let mut instructions = Vec::new();
|
|
||||||
// Store the plane.
|
|
||||||
let plane = PlaneData::XY.into_parts();
|
|
||||||
instructions.push(Instruction::SetValue {
|
|
||||||
address: next_addr.offset_by(plane.len()),
|
|
||||||
value_parts: plane,
|
|
||||||
});
|
|
||||||
// TODO: Get the plane ID from global context.
|
|
||||||
// TODO: Send this command:
|
|
||||||
// ModelingCmd::SketchModeEnable {
|
|
||||||
// animated: false,
|
|
||||||
// ortho: false,
|
|
||||||
// plane_id: plane.id,
|
|
||||||
// // We pass in the normal for the plane here.
|
|
||||||
// disable_camera_with_plane: Some(plane.z_axis.clone().into()),
|
|
||||||
// },
|
|
||||||
// TODO: Send ModelingCmd::StartPath at the given point.
|
|
||||||
// TODO (maybe): Store the SketchGroup in KCEP memory.
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A test function that adds two numbers.
|
/// A test function that adds two numbers.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[cfg_attr(test, derive(Eq, PartialEq))]
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
|
7
src/wasm-lib/grackle/src/native_functions/sketch.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
//! Native functions for sketching on the plane.
|
||||||
|
|
||||||
|
pub mod helpers;
|
||||||
|
pub mod stdlib_functions;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
|
pub use stdlib_functions::StartSketchAt;
|
135
src/wasm-lib/grackle/src/native_functions/sketch/helpers.rs
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
use kittycad_execution_plan::{api_request::ApiRequest, Instruction};
|
||||||
|
use kittycad_execution_plan_traits::{Address, InMemory};
|
||||||
|
use kittycad_modeling_cmds::{id::ModelingCmdId, ModelingCmdEndpoint};
|
||||||
|
|
||||||
|
use crate::{binding_scope::EpBinding, error::CompileError};
|
||||||
|
|
||||||
|
/// Emit instructions for an API call with no parameters.
|
||||||
|
pub fn no_arg_api_call(instrs: &mut Vec<Instruction>, endpoint: ModelingCmdEndpoint, cmd_id: ModelingCmdId) {
|
||||||
|
instrs.push(Instruction::ApiRequest(ApiRequest {
|
||||||
|
endpoint,
|
||||||
|
store_response: None,
|
||||||
|
arguments: vec![],
|
||||||
|
cmd_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Emit instructions for an API call with the given parameters.
|
||||||
|
/// The API parameters are stored in the EP memory stack.
|
||||||
|
/// So, they have to be pushed onto the stack in the right order,
|
||||||
|
/// i.e. the reverse order in which the API call's Rust struct defines the fields.
|
||||||
|
pub fn stack_api_call<const N: usize>(
|
||||||
|
instrs: &mut Vec<Instruction>,
|
||||||
|
endpoint: ModelingCmdEndpoint,
|
||||||
|
store_response: Option<Address>,
|
||||||
|
cmd_id: ModelingCmdId,
|
||||||
|
data: [Vec<kittycad_execution_plan_traits::Primitive>; N],
|
||||||
|
) {
|
||||||
|
let arguments = vec![InMemory::StackPop; data.len()];
|
||||||
|
instrs.extend(data.map(|data| Instruction::StackPush { data }));
|
||||||
|
instrs.push(Instruction::ApiRequest(ApiRequest {
|
||||||
|
endpoint,
|
||||||
|
store_response,
|
||||||
|
arguments,
|
||||||
|
cmd_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn single_binding(
|
||||||
|
b: EpBinding,
|
||||||
|
fn_name: &'static str,
|
||||||
|
expected: &'static str,
|
||||||
|
arg_number: usize,
|
||||||
|
) -> Result<Address, CompileError> {
|
||||||
|
match b {
|
||||||
|
EpBinding::Single(a) => Ok(a),
|
||||||
|
EpBinding::Sequence { .. } => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "array".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "object".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "function".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sequence_binding(
|
||||||
|
b: EpBinding,
|
||||||
|
fn_name: &'static str,
|
||||||
|
expected: &'static str,
|
||||||
|
arg_number: usize,
|
||||||
|
) -> Result<Vec<EpBinding>, CompileError> {
|
||||||
|
match b {
|
||||||
|
EpBinding::Sequence { elements, .. } => Ok(elements),
|
||||||
|
EpBinding::Single(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "single".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
EpBinding::Map { .. } => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "object".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
EpBinding::Function(_) => Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: "function".to_owned(),
|
||||||
|
arg_number,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract a 2D point from an argument to a Cabble.
|
||||||
|
pub fn arg_point2d(
|
||||||
|
arg: EpBinding,
|
||||||
|
fn_name: &'static str,
|
||||||
|
instructions: &mut Vec<Instruction>,
|
||||||
|
next_addr: &mut Address,
|
||||||
|
arg_number: usize,
|
||||||
|
) -> Result<Address, CompileError> {
|
||||||
|
let expected = "2D point (array with length 2)";
|
||||||
|
let elements = sequence_binding(arg, "startSketchAt", "an array of length 2", arg_number)?;
|
||||||
|
if elements.len() != 2 {
|
||||||
|
return Err(CompileError::ArgWrongType {
|
||||||
|
fn_name,
|
||||||
|
expected,
|
||||||
|
actual: format!("array of length {}", elements.len()),
|
||||||
|
arg_number: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// KCL stores points as an array.
|
||||||
|
// KC API stores them as Rust objects laid flat out in memory.
|
||||||
|
let start = next_addr.offset_by(2);
|
||||||
|
let start_x = start;
|
||||||
|
let start_y = start + 1;
|
||||||
|
let start_z = start + 2;
|
||||||
|
instructions.extend([
|
||||||
|
Instruction::Copy {
|
||||||
|
source: single_binding(elements[0].clone(), "startSketchAt", "number", arg_number)?,
|
||||||
|
destination: start_x,
|
||||||
|
},
|
||||||
|
Instruction::Copy {
|
||||||
|
source: single_binding(elements[1].clone(), "startSketchAt", "number", arg_number)?,
|
||||||
|
destination: start_y,
|
||||||
|
},
|
||||||
|
Instruction::SetPrimitive {
|
||||||
|
address: start_z,
|
||||||
|
value: 0.0.into(),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
Ok(start)
|
||||||
|
}
|
@ -0,0 +1,134 @@
|
|||||||
|
use kittycad_execution_plan::{api_request::ApiRequest, Instruction};
|
||||||
|
use kittycad_execution_plan_traits::{Address, InMemory, Value};
|
||||||
|
use kittycad_modeling_cmds::{
|
||||||
|
shared::{Point3d, Point4d},
|
||||||
|
ModelingCmdEndpoint,
|
||||||
|
};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
helpers::{arg_point2d, no_arg_api_call, single_binding, stack_api_call},
|
||||||
|
types::{Axes, BasePath, Plane, SketchGroup},
|
||||||
|
};
|
||||||
|
use crate::{binding_scope::EpBinding, error::CompileError, native_functions::Callable, EvalPlan};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
#[cfg_attr(test, derive(Eq, PartialEq))]
|
||||||
|
pub struct StartSketchAt;
|
||||||
|
|
||||||
|
impl Callable for StartSketchAt {
|
||||||
|
fn call(&self, next_addr: &mut Address, args: Vec<EpBinding>) -> Result<EvalPlan, CompileError> {
|
||||||
|
let mut instructions = Vec::new();
|
||||||
|
// First, before we send any API calls, let's validate the arguments to this function.
|
||||||
|
let mut args_iter = args.into_iter();
|
||||||
|
let Some(start) = args_iter.next() else {
|
||||||
|
return Err(CompileError::NotEnoughArgs {
|
||||||
|
fn_name: "startSketchAt".into(),
|
||||||
|
required: 1,
|
||||||
|
actual: 0,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let start_point = arg_point2d(start, "startSketchAt", &mut instructions, next_addr, 0)?;
|
||||||
|
let tag = match args_iter.next() {
|
||||||
|
None => None,
|
||||||
|
Some(b) => Some(single_binding(b, "startSketchAt", "a single string", 1)?),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Define some constants:
|
||||||
|
let axes = Axes {
|
||||||
|
x: Point3d { x: 1.0, y: 0.0, z: 0.0 },
|
||||||
|
y: Point3d { x: 0.0, y: 1.0, z: 0.0 },
|
||||||
|
z: Point3d { x: 0.0, y: 0.0, z: 1.0 },
|
||||||
|
};
|
||||||
|
let origin = Point3d::default();
|
||||||
|
|
||||||
|
// Now the function can start.
|
||||||
|
// First API call: make the plane.
|
||||||
|
let plane_id = Uuid::new_v4();
|
||||||
|
stack_api_call(
|
||||||
|
&mut instructions,
|
||||||
|
ModelingCmdEndpoint::MakePlane,
|
||||||
|
None,
|
||||||
|
plane_id.into(),
|
||||||
|
[
|
||||||
|
Some(true).into_parts(), // hide
|
||||||
|
vec![false.into()], // clobber
|
||||||
|
vec![60.0.into()], // size
|
||||||
|
axes.y.into_parts(),
|
||||||
|
axes.x.into_parts(),
|
||||||
|
origin.into_parts(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Next, enter sketch mode.
|
||||||
|
stack_api_call(
|
||||||
|
&mut instructions,
|
||||||
|
ModelingCmdEndpoint::SketchModeEnable,
|
||||||
|
None,
|
||||||
|
Uuid::new_v4().into(),
|
||||||
|
[
|
||||||
|
Some(axes.z).into_parts(),
|
||||||
|
vec![false.into()], // animated
|
||||||
|
vec![false.into()], // ortho mode
|
||||||
|
vec![plane_id.into()],
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Then start a path
|
||||||
|
let path_id = Uuid::new_v4();
|
||||||
|
no_arg_api_call(&mut instructions, ModelingCmdEndpoint::StartPath, path_id.into());
|
||||||
|
|
||||||
|
// Move the path pen to the given point.
|
||||||
|
instructions.push(Instruction::StackPush {
|
||||||
|
data: vec![path_id.into()],
|
||||||
|
});
|
||||||
|
instructions.push(Instruction::ApiRequest(ApiRequest {
|
||||||
|
endpoint: ModelingCmdEndpoint::MovePathPen,
|
||||||
|
store_response: None,
|
||||||
|
arguments: vec![InMemory::StackPop, InMemory::Address(start_point)],
|
||||||
|
cmd_id: Uuid::new_v4().into(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Starting a sketch creates a sketch group.
|
||||||
|
// Updating the sketch will update this sketch group later.
|
||||||
|
let sketch_group = SketchGroup {
|
||||||
|
id: path_id,
|
||||||
|
position: origin,
|
||||||
|
rotation: Point4d {
|
||||||
|
x: 0.0,
|
||||||
|
y: 0.0,
|
||||||
|
z: 0.0,
|
||||||
|
w: 1.0,
|
||||||
|
},
|
||||||
|
// TODO: Must copy the existing data (from the arguments to this KCL function)
|
||||||
|
// over these values after writing to memory.
|
||||||
|
path_first: BasePath {
|
||||||
|
from: Default::default(),
|
||||||
|
to: Default::default(),
|
||||||
|
name: Default::default(),
|
||||||
|
},
|
||||||
|
path_rest: Vec::new(),
|
||||||
|
on: super::types::SketchSurface::Plane(Plane {
|
||||||
|
id: plane_id,
|
||||||
|
value: super::types::PlaneType::XY,
|
||||||
|
origin,
|
||||||
|
axes,
|
||||||
|
}),
|
||||||
|
axes,
|
||||||
|
entity_id: Some(plane_id),
|
||||||
|
};
|
||||||
|
let sketch_group_primitives = sketch_group.clone().into_parts();
|
||||||
|
|
||||||
|
let sketch_group_addr = next_addr.offset_by(sketch_group_primitives.len());
|
||||||
|
instructions.push(Instruction::SetValue {
|
||||||
|
address: sketch_group_addr,
|
||||||
|
value_parts: sketch_group_primitives,
|
||||||
|
});
|
||||||
|
instructions.extend(sketch_group.set_base_path(sketch_group_addr, start_point, tag));
|
||||||
|
|
||||||
|
Ok(EvalPlan {
|
||||||
|
instructions,
|
||||||
|
binding: EpBinding::Single(sketch_group_addr),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
133
src/wasm-lib/grackle/src/native_functions/sketch/types.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
use kittycad_execution_plan::Instruction;
|
||||||
|
use kittycad_execution_plan_macros::ExecutionPlanValue;
|
||||||
|
use kittycad_execution_plan_traits::{Address, Value};
|
||||||
|
use kittycad_modeling_cmds::shared::{Point2d, Point3d, Point4d};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
/// A sketch group is a collection of paths.
|
||||||
|
#[derive(Clone, ExecutionPlanValue)]
|
||||||
|
pub struct SketchGroup {
|
||||||
|
/// The id of the sketch group.
|
||||||
|
pub id: Uuid,
|
||||||
|
/// What the sketch is on (can be a plane or a face).
|
||||||
|
pub on: SketchSurface,
|
||||||
|
/// The position of the sketch group.
|
||||||
|
pub position: Point3d,
|
||||||
|
/// The rotation of the sketch group base plane.
|
||||||
|
pub rotation: Point4d,
|
||||||
|
/// The X, Y and Z axes of this sketch's base plane, in 3D space.
|
||||||
|
pub axes: Axes,
|
||||||
|
/// The plane id or face id of the sketch group.
|
||||||
|
pub entity_id: Option<Uuid>,
|
||||||
|
/// The base path.
|
||||||
|
pub path_first: BasePath,
|
||||||
|
/// Paths after the first path, if any.
|
||||||
|
pub path_rest: Vec<Path>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SketchGroup {
|
||||||
|
pub fn set_base_path(&self, sketch_group: Address, start_point: Address, tag: Option<Address>) -> Vec<Instruction> {
|
||||||
|
let base_path_addr = sketch_group
|
||||||
|
+ self.id.into_parts().len()
|
||||||
|
+ self.on.into_parts().len()
|
||||||
|
+ self.position.into_parts().len()
|
||||||
|
+ self.rotation.into_parts().len()
|
||||||
|
+ self.axes.into_parts().len()
|
||||||
|
+ self.entity_id.into_parts().len()
|
||||||
|
+ self.entity_id.into_parts().len();
|
||||||
|
let mut out = vec![
|
||||||
|
// Copy over the `from` field.
|
||||||
|
Instruction::Copy {
|
||||||
|
source: start_point,
|
||||||
|
destination: base_path_addr,
|
||||||
|
},
|
||||||
|
// Copy over the `to` field.
|
||||||
|
Instruction::Copy {
|
||||||
|
source: start_point,
|
||||||
|
destination: base_path_addr + self.path_first.from.into_parts().len(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if let Some(tag) = tag {
|
||||||
|
// Copy over the `name` field.
|
||||||
|
out.push(Instruction::Copy {
|
||||||
|
source: tag,
|
||||||
|
destination: base_path_addr
|
||||||
|
+ self.path_first.from.into_parts().len()
|
||||||
|
+ self.path_first.to.into_parts().len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
out
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The X, Y and Z axes.
|
||||||
|
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||||
|
pub struct Axes {
|
||||||
|
pub x: Point3d,
|
||||||
|
pub y: Point3d,
|
||||||
|
pub z: Point3d,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, ExecutionPlanValue)]
|
||||||
|
pub struct BasePath {
|
||||||
|
pub from: Point2d<f64>,
|
||||||
|
pub to: Point2d<f64>,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A path.
|
||||||
|
#[derive(Clone, ExecutionPlanValue)]
|
||||||
|
pub enum Path {
|
||||||
|
/// A path that goes to a point.
|
||||||
|
ToPoint { base: BasePath },
|
||||||
|
/// A arc that is tangential to the last path segment that goes to a point
|
||||||
|
TangentialArcTo {
|
||||||
|
base: BasePath,
|
||||||
|
/// the arc's center
|
||||||
|
center: Point2d,
|
||||||
|
/// arc's direction
|
||||||
|
ccw: bool,
|
||||||
|
},
|
||||||
|
/// A path that is horizontal.
|
||||||
|
Horizontal {
|
||||||
|
base: BasePath,
|
||||||
|
/// The x coordinate.
|
||||||
|
x: f64,
|
||||||
|
},
|
||||||
|
/// An angled line to.
|
||||||
|
AngledLineTo {
|
||||||
|
base: BasePath,
|
||||||
|
/// The x coordinate.
|
||||||
|
x: Option<f64>,
|
||||||
|
/// The y coordinate.
|
||||||
|
y: Option<f64>,
|
||||||
|
},
|
||||||
|
/// A base path.
|
||||||
|
Base { base: BasePath },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||||
|
pub enum SketchSurface {
|
||||||
|
Plane(Plane),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A plane.
|
||||||
|
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||||
|
pub struct Plane {
|
||||||
|
/// The id of the plane.
|
||||||
|
pub id: Uuid,
|
||||||
|
// The code for the plane either a string or custom.
|
||||||
|
pub value: PlaneType,
|
||||||
|
/// Origin of the plane.
|
||||||
|
pub origin: Point3d,
|
||||||
|
pub axes: Axes,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type for a plane.
|
||||||
|
#[derive(Clone, Copy, ExecutionPlanValue)]
|
||||||
|
pub enum PlaneType {
|
||||||
|
XY,
|
||||||
|
XZ,
|
||||||
|
YZ,
|
||||||
|
Custom,
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
use std::collections::HashMap;
|
use std::{collections::HashMap, env};
|
||||||
|
|
||||||
use ep::{Destination, UnaryArithmetic};
|
use ep::{Destination, UnaryArithmetic};
|
||||||
use ept::{ListHeader, ObjectHeader};
|
use ept::{ListHeader, ObjectHeader};
|
||||||
|
use kittycad_modeling_session::SessionBuilder;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -1044,6 +1045,71 @@ fn store_object_with_array_property() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn stdlib_cube_partial() {
|
||||||
|
let program = r#"
|
||||||
|
let cube = startSketchAt([22.0, 33.0])
|
||||||
|
"#;
|
||||||
|
let (plan, _scope) = must_plan(program);
|
||||||
|
std::fs::write("stdlib_cube_partial.json", serde_json::to_string_pretty(&plan).unwrap()).unwrap();
|
||||||
|
let ast = kcl_lib::parser::Parser::new(kcl_lib::token::lexer(program))
|
||||||
|
.ast()
|
||||||
|
.unwrap();
|
||||||
|
let mem = crate::execute(ast, Some(test_client().await)).await.unwrap();
|
||||||
|
dbg!(mem);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn test_client() -> Session {
|
||||||
|
let kittycad_api_token = env::var("KITTYCAD_API_TOKEN").expect("You must set $KITTYCAD_API_TOKEN");
|
||||||
|
let kittycad_api_client = kittycad::Client::new(kittycad_api_token);
|
||||||
|
let session_builder = SessionBuilder {
|
||||||
|
client: kittycad_api_client,
|
||||||
|
fps: Some(10),
|
||||||
|
unlocked_framerate: Some(false),
|
||||||
|
video_res_height: Some(720),
|
||||||
|
video_res_width: Some(1280),
|
||||||
|
buffer_reqs: None,
|
||||||
|
await_response_timeout: None,
|
||||||
|
};
|
||||||
|
match Session::start(session_builder).await {
|
||||||
|
Err(e) => match e {
|
||||||
|
kittycad::types::error::Error::InvalidRequest(s) => panic!("Request did not meet requirements {s}"),
|
||||||
|
kittycad::types::error::Error::CommunicationError(e) => {
|
||||||
|
panic!(" A server error either due to the data, or with the connection: {e}")
|
||||||
|
}
|
||||||
|
kittycad::types::error::Error::RequestError(e) => panic!("Could not build request: {e}"),
|
||||||
|
kittycad::types::error::Error::SerdeError { error, status } => {
|
||||||
|
panic!("Serde error (HTTP {status}): {error}")
|
||||||
|
}
|
||||||
|
kittycad::types::error::Error::InvalidResponsePayload { error, response } => {
|
||||||
|
panic!("Invalid response payload. Error {error}, response {response:?}")
|
||||||
|
}
|
||||||
|
kittycad::types::error::Error::Server { body, status } => panic!("Server error (HTTP {status}): {body}"),
|
||||||
|
kittycad::types::error::Error::UnexpectedResponse(resp) => {
|
||||||
|
let status = resp.status();
|
||||||
|
let url = resp.url().to_owned();
|
||||||
|
match resp.text().await {
|
||||||
|
Ok(body) => panic!(
|
||||||
|
"Unexpected response from KittyCAD API.
|
||||||
|
URL:{url}
|
||||||
|
HTTP {status}
|
||||||
|
---Body----
|
||||||
|
{body}"
|
||||||
|
),
|
||||||
|
Err(e) => panic!(
|
||||||
|
"Unexpected response from KittyCAD API.
|
||||||
|
URL:{url}
|
||||||
|
HTTP {status}
|
||||||
|
---Body could not be read, the error is----
|
||||||
|
{e}"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Ok(x) => x,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[ignore = "haven't done API calls or stdlib yet"]
|
#[ignore = "haven't done API calls or stdlib yet"]
|
||||||
#[test]
|
#[test]
|
||||||
fn stdlib_api_calls() {
|
fn stdlib_api_calls() {
|
||||||
|
@ -15,7 +15,7 @@ databake = "0.1.7"
|
|||||||
kcl-lib = { path = "../kcl" }
|
kcl-lib = { path = "../kcl" }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
syn = { version = "2.0.49", features = ["full"] }
|
syn = { version = "2.0.52", features = ["full"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
pretty_assertions = "1.4.0"
|
pretty_assertions = "1.4.0"
|
||||||
|
@ -14,7 +14,7 @@ keywords = ["kcl", "KittyCAD", "CAD"]
|
|||||||
anyhow = { version = "1.0.79", features = ["backtrace"] }
|
anyhow = { version = "1.0.79", features = ["backtrace"] }
|
||||||
async-recursion = "1.0.5"
|
async-recursion = "1.0.5"
|
||||||
async-trait = "0.1.77"
|
async-trait = "0.1.77"
|
||||||
clap = { version = "4.5.0", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
clap = { version = "4.5.1", features = ["cargo", "derive", "env", "unicode"], optional = true }
|
||||||
dashmap = "5.5.3"
|
dashmap = "5.5.3"
|
||||||
databake = { version = "0.1.7", features = ["derive"] }
|
databake = { version = "0.1.7", features = ["derive"] }
|
||||||
derive-docs = { version = "0.1.8" }
|
derive-docs = { version = "0.1.8" }
|
||||||
|
@ -96,7 +96,19 @@ impl Program {
|
|||||||
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
|
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
|
||||||
Some(noncodes) => noncodes
|
Some(noncodes) => noncodes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|custom_white_space_or_comment| custom_white_space_or_comment.format(&indentation))
|
.enumerate()
|
||||||
|
.map(|(i, custom_white_space_or_comment)| {
|
||||||
|
let formatted = custom_white_space_or_comment.format(&indentation);
|
||||||
|
if i == 0 && !formatted.trim().is_empty() {
|
||||||
|
if let NonCodeValue::BlockComment { .. } = custom_white_space_or_comment.value {
|
||||||
|
format!("\n{}", formatted)
|
||||||
|
} else {
|
||||||
|
formatted
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formatted
|
||||||
|
}
|
||||||
|
})
|
||||||
.collect::<String>(),
|
.collect::<String>(),
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
};
|
};
|
||||||
@ -159,6 +171,35 @@ impl Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a non code meta that includes the given character position.
|
||||||
|
pub fn get_non_code_meta_for_position(&self, pos: usize) -> Option<&NonCodeMeta> {
|
||||||
|
// Check if its in the body.
|
||||||
|
if self.non_code_meta.contains(pos) {
|
||||||
|
return Some(&self.non_code_meta);
|
||||||
|
}
|
||||||
|
let Some(item) = self.get_body_item_for_position(pos) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Recurse over the item.
|
||||||
|
let value = match item {
|
||||||
|
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
||||||
|
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_value_for_position(pos),
|
||||||
|
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if the value's non code meta contains the position.
|
||||||
|
if let Some(value) = value {
|
||||||
|
if let Some(non_code_meta) = value.get_non_code_meta() {
|
||||||
|
if non_code_meta.contains(pos) {
|
||||||
|
return Some(non_code_meta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns all the lsp symbols in the program.
|
/// Returns all the lsp symbols in the program.
|
||||||
pub fn get_lsp_symbols(&self, code: &str) -> Vec<DocumentSymbol> {
|
pub fn get_lsp_symbols(&self, code: &str) -> Vec<DocumentSymbol> {
|
||||||
let mut symbols = vec![];
|
let mut symbols = vec![];
|
||||||
@ -431,6 +472,24 @@ impl Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the non code meta for the value.
|
||||||
|
pub fn get_non_code_meta(&self) -> Option<&NonCodeMeta> {
|
||||||
|
match self {
|
||||||
|
Value::BinaryExpression(_bin_exp) => None,
|
||||||
|
Value::ArrayExpression(_array_exp) => None,
|
||||||
|
Value::ObjectExpression(_obj_exp) => None,
|
||||||
|
Value::MemberExpression(_mem_exp) => None,
|
||||||
|
Value::Literal(_literal) => None,
|
||||||
|
Value::FunctionExpression(_func_exp) => None,
|
||||||
|
Value::CallExpression(_call_exp) => None,
|
||||||
|
Value::Identifier(_ident) => None,
|
||||||
|
Value::PipeExpression(pipe_exp) => Some(&pipe_exp.non_code_meta),
|
||||||
|
Value::UnaryExpression(_unary_exp) => None,
|
||||||
|
Value::PipeSubstitution(_pipe_substitution) => None,
|
||||||
|
Value::None(_none) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Value) {
|
||||||
if source_range == self.clone().into() {
|
if source_range == self.clone().into() {
|
||||||
*self = new_value;
|
*self = new_value;
|
||||||
@ -736,6 +795,10 @@ pub struct NonCodeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl NonCodeNode {
|
impl NonCodeNode {
|
||||||
|
pub fn contains(&self, pos: usize) -> bool {
|
||||||
|
self.start <= pos && pos <= self.end
|
||||||
|
}
|
||||||
|
|
||||||
pub fn value(&self) -> String {
|
pub fn value(&self) -> String {
|
||||||
match &self.value {
|
match &self.value {
|
||||||
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
|
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
|
||||||
@ -755,18 +818,27 @@ impl NonCodeNode {
|
|||||||
value,
|
value,
|
||||||
style: CommentStyle::Block,
|
style: CommentStyle::Block,
|
||||||
} => format!(" /* {} */", value),
|
} => format!(" /* {} */", value),
|
||||||
NonCodeValue::BlockComment { value, style } => {
|
NonCodeValue::BlockComment { value, style } => match style {
|
||||||
let add_start_new_line = if self.start == 0 { "" } else { "\n" };
|
CommentStyle::Block => format!("{}/* {} */", indentation, value),
|
||||||
match style {
|
CommentStyle::Line => {
|
||||||
CommentStyle::Block => format!("{}{}/* {} */", add_start_new_line, indentation, value),
|
if value.trim().is_empty() {
|
||||||
CommentStyle::Line => format!("{}{}// {}\n", add_start_new_line, indentation, value),
|
format!("{}//\n", indentation)
|
||||||
|
} else {
|
||||||
|
format!("{}// {}\n", indentation, value.trim())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
NonCodeValue::NewLineBlockComment { value, style } => {
|
NonCodeValue::NewLineBlockComment { value, style } => {
|
||||||
let add_start_new_line = if self.start == 0 { "" } else { "\n\n" };
|
let add_start_new_line = if self.start == 0 { "" } else { "\n\n" };
|
||||||
match style {
|
match style {
|
||||||
CommentStyle::Block => format!("{}{}/* {} */\n", add_start_new_line, indentation, value),
|
CommentStyle::Block => format!("{}{}/* {} */\n", add_start_new_line, indentation, value),
|
||||||
CommentStyle::Line => format!("{}{}// {}\n", add_start_new_line, indentation, value),
|
CommentStyle::Line => {
|
||||||
|
if value.trim().is_empty() {
|
||||||
|
format!("{}{}//\n", add_start_new_line, indentation)
|
||||||
|
} else {
|
||||||
|
format!("{}{}// {}\n", add_start_new_line, indentation, value.trim())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||||
@ -863,6 +935,16 @@ impl NonCodeMeta {
|
|||||||
pub fn insert(&mut self, i: usize, new: NonCodeNode) {
|
pub fn insert(&mut self, i: usize, new: NonCodeNode) {
|
||||||
self.non_code_nodes.entry(i).or_default().push(new);
|
self.non_code_nodes.entry(i).or_default().push(new);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn contains(&self, pos: usize) -> bool {
|
||||||
|
if self.start.iter().any(|node| node.contains(pos)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.non_code_nodes
|
||||||
|
.iter()
|
||||||
|
.any(|(_, nodes)| nodes.iter().any(|node| node.contains(pos)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
@ -2533,7 +2615,13 @@ impl PipeExpression {
|
|||||||
let non_code_meta = self.non_code_meta.clone();
|
let non_code_meta = self.non_code_meta.clone();
|
||||||
if let Some(non_code_meta_value) = non_code_meta.non_code_nodes.get(&index) {
|
if let Some(non_code_meta_value) = non_code_meta.non_code_nodes.get(&index) {
|
||||||
for val in non_code_meta_value {
|
for val in non_code_meta_value {
|
||||||
s += val.format(&indentation).trim_end_matches('\n')
|
let formatted = val.format(&indentation).trim_end_matches('\n').to_string();
|
||||||
|
if let NonCodeValue::BlockComment { .. } = val.value {
|
||||||
|
s += "\n";
|
||||||
|
s += &formatted;
|
||||||
|
} else {
|
||||||
|
s += &formatted;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3003,8 +3091,7 @@ let baz = {a: 1, b: "thing"}
|
|||||||
fn ghi = (x) => {
|
fn ghi = (x) => {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
|
"#;
|
||||||
show(part001)"#;
|
|
||||||
let tokens = crate::token::lexer(code);
|
let tokens = crate::token::lexer(code);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
let program = parser.ast().unwrap();
|
let program = parser.ast().unwrap();
|
||||||
@ -3106,6 +3193,109 @@ show(part001)"#;
|
|||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_comment_under_variable() {
|
||||||
|
let some_program_string = r#"const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
const thing = 'foo'
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
const thing = 'foo'
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_multiline_comment_start_file() {
|
||||||
|
let some_program_string = r#"// hello world
|
||||||
|
// I am a comment
|
||||||
|
const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
// hello
|
||||||
|
const thing = 'foo'
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"// hello world
|
||||||
|
// I am a comment
|
||||||
|
const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
// hello
|
||||||
|
const thing = 'foo'
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_empty_comment() {
|
||||||
|
let some_program_string = r#"// hello world
|
||||||
|
//
|
||||||
|
// I am a comment
|
||||||
|
const key = 'c'
|
||||||
|
|
||||||
|
//
|
||||||
|
// I am a comment
|
||||||
|
const thing = 'c'
|
||||||
|
|
||||||
|
const foo = 'bar' //
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"// hello world
|
||||||
|
//
|
||||||
|
// I am a comment
|
||||||
|
const key = 'c'
|
||||||
|
|
||||||
|
//
|
||||||
|
// I am a comment
|
||||||
|
const thing = 'c'
|
||||||
|
|
||||||
|
const foo = 'bar' //
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_multiline_comment_under_variable() {
|
||||||
|
let some_program_string = r#"const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
// hello
|
||||||
|
const thing = 'foo'
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"const key = 'c'
|
||||||
|
// this is also a comment
|
||||||
|
// hello
|
||||||
|
const thing = 'foo'
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recast_comment_at_start() {
|
fn test_recast_comment_at_start() {
|
||||||
let test_program = r#"
|
let test_program = r#"
|
||||||
@ -3181,9 +3371,7 @@ const mySk1 = startSketchOn('XY')
|
|||||||
offset: -1.35,
|
offset: -1.35,
|
||||||
intersectTag: 'seg01'
|
intersectTag: 'seg01'
|
||||||
}, %)
|
}, %)
|
||||||
|> line([-0.42, -1.72], %)
|
|> line([-0.42, -1.72], %)"#;
|
||||||
|
|
||||||
show(part001)"#;
|
|
||||||
let tokens = crate::token::lexer(some_program_string);
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
let program = parser.ast().unwrap();
|
let program = parser.ast().unwrap();
|
||||||
@ -3332,8 +3520,7 @@ let baz = {a: 1, part001: "thing"}
|
|||||||
fn ghi = (part001) => {
|
fn ghi = (part001) => {
|
||||||
return part001
|
return part001
|
||||||
}
|
}
|
||||||
|
"#;
|
||||||
show(part001)"#;
|
|
||||||
let tokens = crate::token::lexer(some_program_string);
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
let mut program = parser.ast().unwrap();
|
let mut program = parser.ast().unwrap();
|
||||||
@ -3355,8 +3542,6 @@ let baz = { a: 1, part001: "thing" }
|
|||||||
fn ghi = (part001) => {
|
fn ghi = (part001) => {
|
||||||
return part001
|
return part001
|
||||||
}
|
}
|
||||||
|
|
||||||
show(mySuperCoolPart)
|
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -3381,6 +3566,97 @@ show(mySuperCoolPart)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_trailing_comma() {
|
||||||
|
let some_program_string = r#"startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> arc({
|
||||||
|
radius: 1,
|
||||||
|
angle_start: 0,
|
||||||
|
angle_end: 180,
|
||||||
|
}, %)"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> arc({
|
||||||
|
radius: 1,
|
||||||
|
angle_start: 0,
|
||||||
|
angle_end: 180
|
||||||
|
}, %)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ast_get_non_code_node() {
|
||||||
|
let some_program_string = r#"const r = 20 / pow(pi(), 1 / 3)
|
||||||
|
const h = 30
|
||||||
|
|
||||||
|
// st
|
||||||
|
const cylinder = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([50, 0], %)
|
||||||
|
|> arc({
|
||||||
|
angle_end: 360,
|
||||||
|
angle_start: 0,
|
||||||
|
radius: r
|
||||||
|
}, %)
|
||||||
|
|> extrude(h, %)
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let value = program.get_non_code_meta_for_position(50);
|
||||||
|
|
||||||
|
assert!(value.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ast_get_non_code_node_pipe() {
|
||||||
|
let some_program_string = r#"const r = 20 / pow(pi(), 1 / 3)
|
||||||
|
const h = 30
|
||||||
|
|
||||||
|
// st
|
||||||
|
const cylinder = startSketchOn('-XZ')
|
||||||
|
|> startProfileAt([50, 0], %)
|
||||||
|
// comment
|
||||||
|
|> arc({
|
||||||
|
angle_end: 360,
|
||||||
|
angle_start: 0,
|
||||||
|
radius: r
|
||||||
|
}, %)
|
||||||
|
|> extrude(h, %)
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let value = program.get_non_code_meta_for_position(124);
|
||||||
|
|
||||||
|
assert!(value.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ast_get_non_code_node_inline_comment() {
|
||||||
|
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> xLine(5, %) // lin
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let value = program.get_non_code_meta_for_position(86);
|
||||||
|
|
||||||
|
assert!(value.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recast_negative_var() {
|
fn test_recast_negative_var() {
|
||||||
let some_program_string = r#"const w = 20
|
let some_program_string = r#"const w = 20
|
||||||
@ -3394,8 +3670,7 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([0, -l], %)
|
|> line([0, -l], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)
|
||||||
|
"#;
|
||||||
show(firstExtrude)"#;
|
|
||||||
let tokens = crate::token::lexer(some_program_string);
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
let program = parser.ast().unwrap();
|
let program = parser.ast().unwrap();
|
||||||
@ -3414,8 +3689,48 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([0, -l], %)
|
|> line([0, -l], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
show(firstExtrude)
|
#[test]
|
||||||
|
fn test_recast_multiline_comment() {
|
||||||
|
let some_program_string = r#"const w = 20
|
||||||
|
const l = 8
|
||||||
|
const h = 10
|
||||||
|
|
||||||
|
// This is my comment
|
||||||
|
// It has multiple lines
|
||||||
|
// And it's really long
|
||||||
|
const firstExtrude = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> line([0, l], %)
|
||||||
|
|> line([w, 0], %)
|
||||||
|
|> line([0, -l], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(h, %)
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
|
||||||
|
let recasted = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
recasted,
|
||||||
|
r#"const w = 20
|
||||||
|
const l = 8
|
||||||
|
const h = 10
|
||||||
|
|
||||||
|
// This is my comment
|
||||||
|
// It has multiple lines
|
||||||
|
// And it's really long
|
||||||
|
const firstExtrude = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([0, l], %)
|
||||||
|
|> line([w, 0], %)
|
||||||
|
|> line([0, -l], %)
|
||||||
|
|> close(%)
|
||||||
|
|> extrude(h, %)
|
||||||
"#
|
"#
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -566,17 +566,4 @@ mod tests {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_deserialize_function_show() {
|
|
||||||
let some_function_string = r#"{"type":"StdLib","func":{"name":"show","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}}"#;
|
|
||||||
let some_function: crate::ast::types::Function = serde_json::from_str(some_function_string).unwrap();
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
some_function,
|
|
||||||
crate::ast::types::Function::StdLib {
|
|
||||||
func: Box::new(crate::std::Show),
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
//! Functions for setting up our WebSocket and WebRTC connections for communications with the
|
||||||
//! engine.
|
//! engine.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
@ -15,6 +15,12 @@ use crate::{
|
|||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum SocketHealth {
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
}
|
||||||
|
|
||||||
type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>;
|
type WebSocketTcpWrite = futures::stream::SplitSink<tokio_tungstenite::WebSocketStream<reqwest::Upgraded>, WsMsg>;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[allow(dead_code)] // for the TcpReadHandle
|
#[allow(dead_code)] // for the TcpReadHandle
|
||||||
@ -22,6 +28,7 @@ pub struct EngineConnection {
|
|||||||
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
engine_req_tx: mpsc::Sender<ToEngineReq>,
|
||||||
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
|
responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>>,
|
||||||
tcp_read_handle: Arc<TcpReadHandle>,
|
tcp_read_handle: Arc<TcpReadHandle>,
|
||||||
|
socket_health: Arc<Mutex<SocketHealth>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TcpRead {
|
pub struct TcpRead {
|
||||||
@ -119,7 +126,9 @@ impl EngineConnection {
|
|||||||
|
|
||||||
let responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>> = Arc::new(DashMap::new());
|
let responses: Arc<DashMap<uuid::Uuid, WebSocketResponse>> = Arc::new(DashMap::new());
|
||||||
let responses_clone = responses.clone();
|
let responses_clone = responses.clone();
|
||||||
|
let socket_health = Arc::new(Mutex::new(SocketHealth::Active));
|
||||||
|
|
||||||
|
let socket_health_tcp_read = socket_health.clone();
|
||||||
let tcp_read_handle = tokio::spawn(async move {
|
let tcp_read_handle = tokio::spawn(async move {
|
||||||
// Get Websocket messages from API server
|
// Get Websocket messages from API server
|
||||||
loop {
|
loop {
|
||||||
@ -131,6 +140,7 @@ impl EngineConnection {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("got ws error: {:?}", e);
|
println!("got ws error: {:?}", e);
|
||||||
|
*socket_health_tcp_read.lock().unwrap() = SocketHealth::Inactive;
|
||||||
return Err(e);
|
return Err(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,6 +153,7 @@ impl EngineConnection {
|
|||||||
handle: Arc::new(tcp_read_handle),
|
handle: Arc::new(tcp_read_handle),
|
||||||
}),
|
}),
|
||||||
responses,
|
responses,
|
||||||
|
socket_health,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -192,6 +203,14 @@ impl EngineManager for EngineConnection {
|
|||||||
// Wait for the response.
|
// Wait for the response.
|
||||||
let current_time = std::time::Instant::now();
|
let current_time = std::time::Instant::now();
|
||||||
while current_time.elapsed().as_secs() < 60 {
|
while current_time.elapsed().as_secs() < 60 {
|
||||||
|
if let Ok(guard) = self.socket_health.lock() {
|
||||||
|
if *guard == SocketHealth::Inactive {
|
||||||
|
return Err(KclError::Engine(KclErrorDetails {
|
||||||
|
message: "Modeling command failed: websocket closed early".to_string(),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
// We pop off the responses to cleanup our mappings.
|
// We pop off the responses to cleanup our mappings.
|
||||||
if let Some((_, resp)) = self.responses.remove(&id) {
|
if let Some((_, resp)) = self.responses.remove(&id) {
|
||||||
return if let Some(data) = &resp.resp {
|
return if let Some(data) = &resp.resp {
|
||||||
|
@ -1017,7 +1017,7 @@ impl ExecutorContext {
|
|||||||
pub async fn execute(
|
pub async fn execute(
|
||||||
program: crate::ast::types::Program,
|
program: crate::ast::types::Program,
|
||||||
memory: &mut ProgramMemory,
|
memory: &mut ProgramMemory,
|
||||||
options: BodyType,
|
_options: BodyType,
|
||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
) -> Result<ProgramMemory, KclError> {
|
) -> Result<ProgramMemory, KclError> {
|
||||||
// Before we even start executing the program, set the units.
|
// Before we even start executing the program, set the units.
|
||||||
@ -1073,24 +1073,11 @@ pub async fn execute(
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _show_fn = Box::new(crate::std::Show);
|
|
||||||
match ctx.stdlib.get_either(&call_expr.callee.name) {
|
match ctx.stdlib.get_either(&call_expr.callee.name) {
|
||||||
FunctionKind::Core(func) => {
|
FunctionKind::Core(func) => {
|
||||||
use crate::docs::StdLibFn;
|
let args = crate::std::Args::new(args, call_expr.into(), ctx.clone());
|
||||||
if func.name() == _show_fn.name() {
|
let result = func.std_lib_fn()(args).await?;
|
||||||
if options != BodyType::Root {
|
memory.return_ = Some(ProgramReturn::Value(result));
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: "Cannot call show outside of a root".to_string(),
|
|
||||||
source_ranges: vec![call_expr.into()],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
|
|
||||||
} else {
|
|
||||||
let args = crate::std::Args::new(args, call_expr.into(), ctx.clone());
|
|
||||||
let result = func.std_lib_fn()(args).await?;
|
|
||||||
memory.return_ = Some(ProgramReturn::Value(result));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
FunctionKind::Std(func) => {
|
FunctionKind::Std(func) => {
|
||||||
let mut newmem = memory.clone();
|
let mut newmem = memory.clone();
|
||||||
@ -1352,8 +1339,7 @@ const newVar = myVar + 1"#;
|
|||||||
offset: {},
|
offset: {},
|
||||||
tag: "yo2"
|
tag: "yo2"
|
||||||
}}, %)
|
}}, %)
|
||||||
const intersect = segEndX('yo2', part001)
|
const intersect = segEndX('yo2', part001)"#,
|
||||||
show(part001)"#,
|
|
||||||
offset
|
offset
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
@ -1399,8 +1385,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> angledLine([ghi(2), 3.04], %)
|
|> angledLine([ghi(2), 3.04], %)
|
||||||
|> angledLine([jkl(yo) + 2, 3.05], %)
|
|> angledLine([jkl(yo) + 2, 3.05], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
const yo2 = hmm([identifierGuy + 5])
|
const yo2 = hmm([identifierGuy + 5])"#;
|
||||||
show(part001)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1415,8 +1400,7 @@ const part001 = startSketchOn('XY')
|
|||||||
min(segLen('seg01', %), myVar),
|
min(segLen('seg01', %), myVar),
|
||||||
-legLen(segLen('seg01', %), myVar)
|
-legLen(segLen('seg01', %), myVar)
|
||||||
], %)
|
], %)
|
||||||
|
"#;
|
||||||
show(part001)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1431,8 +1415,7 @@ const part001 = startSketchOn('XY')
|
|||||||
min(segLen('seg01', %), myVar),
|
min(segLen('seg01', %), myVar),
|
||||||
legLen(segLen('seg01', %), myVar)
|
legLen(segLen('seg01', %), myVar)
|
||||||
], %)
|
], %)
|
||||||
|
"#;
|
||||||
show(part001)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1454,8 +1437,7 @@ const part001 = startSketchOn('XY')
|
|||||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||||
|
|
||||||
const variableBelowShouldNotBeIncluded = 3
|
const variableBelowShouldNotBeIncluded = 3
|
||||||
|
"#;
|
||||||
show(part001)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1476,9 +1458,7 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([w, 0], %)
|
|> line([w, 0], %)
|
||||||
|> line([0, thing()], %)
|
|> line([0, thing()], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)"#;
|
||||||
|
|
||||||
show(firstExtrude)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1499,9 +1479,7 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([w, 0], %)
|
|> line([w, 0], %)
|
||||||
|> line([0, thing(8)], %)
|
|> line([0, thing(8)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)"#;
|
||||||
|
|
||||||
show(firstExtrude)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1522,9 +1500,7 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([w, 0], %)
|
|> line([w, 0], %)
|
||||||
|> line(thing(8), %)
|
|> line(thing(8), %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)"#;
|
||||||
|
|
||||||
show(firstExtrude)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1549,9 +1525,7 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> line([w, 0], %)
|
|> line([w, 0], %)
|
||||||
|> line([0, thing(8)], %)
|
|> line([0, thing(8)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)"#;
|
||||||
|
|
||||||
show(firstExtrude)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1570,9 +1544,7 @@ show(firstExtrude)"#;
|
|||||||
return myBox
|
return myBox
|
||||||
}
|
}
|
||||||
|
|
||||||
const fnBox = box(3, 6, 10)
|
const fnBox = box(3, 6, 10)"#;
|
||||||
|
|
||||||
show(fnBox)"#;
|
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1592,8 +1564,6 @@ show(fnBox)"#;
|
|||||||
}
|
}
|
||||||
|
|
||||||
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
||||||
|
|
||||||
show(thisBox)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1613,8 +1583,6 @@ show(thisBox)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
||||||
|
|
||||||
show(thisBox)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1634,8 +1602,6 @@ show(thisBox)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
const thisBox = box({start: [0,0], l: 6, w: 10, h: 3})
|
||||||
|
|
||||||
show(thisBox)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1657,7 +1623,6 @@ let myBox = startSketchOn('XY')
|
|||||||
|
|
||||||
for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h: 1.5}] {
|
for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h: 1.5}] {
|
||||||
const thisBox = box(var)
|
const thisBox = box(var)
|
||||||
show(thisBox)
|
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
@ -1681,7 +1646,6 @@ for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h:
|
|||||||
|
|
||||||
for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
||||||
const thisBox = box(var[0], var[1], var[2], var[3])
|
const thisBox = box(var[0], var[1], var[2], var[3])
|
||||||
show(thisBox)
|
|
||||||
}"#;
|
}"#;
|
||||||
|
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
@ -1703,7 +1667,6 @@ for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
|||||||
|
|
||||||
const thisBox = box([[0,0], 6, 10, 3])
|
const thisBox = box([[0,0], 6, 10, 3])
|
||||||
|
|
||||||
show(thisBox)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1820,7 +1783,6 @@ const bracket = startSketchOn('XY')
|
|||||||
|> line([0, -1 * leg1 + thickness], %)
|
|> line([0, -1 * leg1 + thickness], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(width, %)
|
|> extrude(width, %)
|
||||||
show(bracket)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
@ -1845,7 +1807,6 @@ const bracket = startSketchOn('XY')
|
|||||||
|> line([0, -1 * leg1 + thickness], %)
|
|> line([0, -1 * leg1 + thickness], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(width, %)
|
|> extrude(width, %)
|
||||||
show(bracket)
|
|
||||||
"#;
|
"#;
|
||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -17,14 +17,13 @@ use tower_lsp::{
|
|||||||
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Documentation, FullDocumentDiagnosticReport,
|
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Documentation, FullDocumentDiagnosticReport,
|
||||||
Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
|
Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
|
||||||
InitializedParams, InlayHint, InlayHintParams, InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf,
|
InitializedParams, InlayHint, InlayHintParams, InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf,
|
||||||
ParameterInformation, ParameterLabel, Position, RelatedFullDocumentDiagnosticReport, RenameFilesParams,
|
Position, RelatedFullDocumentDiagnosticReport, RenameFilesParams, RenameParams, SemanticToken,
|
||||||
RenameParams, SemanticToken, SemanticTokenType, SemanticTokens, SemanticTokensFullOptions,
|
SemanticTokenType, SemanticTokens, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
|
||||||
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensRegistrationOptions,
|
SemanticTokensParams, SemanticTokensRegistrationOptions, SemanticTokensResult,
|
||||||
SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp,
|
SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp, SignatureHelpOptions, SignatureHelpParams,
|
||||||
SignatureHelpOptions, SignatureHelpParams, SignatureInformation, StaticRegistrationOptions, TextDocumentItem,
|
StaticRegistrationOptions, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
|
||||||
TextDocumentRegistrationOptions, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
TextDocumentSyncKind, TextDocumentSyncOptions, TextEdit, WorkDoneProgressOptions, WorkspaceEdit,
|
||||||
TextEdit, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFoldersServerCapabilities,
|
WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
|
||||||
WorkspaceServerCapabilities,
|
|
||||||
},
|
},
|
||||||
Client, LanguageServer,
|
Client, LanguageServer,
|
||||||
};
|
};
|
||||||
@ -636,8 +635,9 @@ impl LanguageServer for Backend {
|
|||||||
/// Get completions from our stdlib.
|
/// Get completions from our stdlib.
|
||||||
pub fn get_completions_from_stdlib(stdlib: &crate::std::StdLib) -> Result<HashMap<String, CompletionItem>> {
|
pub fn get_completions_from_stdlib(stdlib: &crate::std::StdLib) -> Result<HashMap<String, CompletionItem>> {
|
||||||
let mut completions = HashMap::new();
|
let mut completions = HashMap::new();
|
||||||
|
let combined = stdlib.combined();
|
||||||
|
|
||||||
for internal_fn in stdlib.fns.values() {
|
for internal_fn in combined.values() {
|
||||||
completions.insert(internal_fn.name(), internal_fn.to_completion_item());
|
completions.insert(internal_fn.name(), internal_fn.to_completion_item());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,32 +652,12 @@ pub fn get_completions_from_stdlib(stdlib: &crate::std::StdLib) -> Result<HashMa
|
|||||||
/// Get signatures from our stdlib.
|
/// Get signatures from our stdlib.
|
||||||
pub fn get_signatures_from_stdlib(stdlib: &crate::std::StdLib) -> Result<HashMap<String, SignatureHelp>> {
|
pub fn get_signatures_from_stdlib(stdlib: &crate::std::StdLib) -> Result<HashMap<String, SignatureHelp>> {
|
||||||
let mut signatures = HashMap::new();
|
let mut signatures = HashMap::new();
|
||||||
|
let combined = stdlib.combined();
|
||||||
|
|
||||||
for internal_fn in stdlib.fns.values() {
|
for internal_fn in combined.values() {
|
||||||
signatures.insert(internal_fn.name(), internal_fn.to_signature_help());
|
signatures.insert(internal_fn.name(), internal_fn.to_signature_help());
|
||||||
}
|
}
|
||||||
|
|
||||||
let show = SignatureHelp {
|
|
||||||
signatures: vec![SignatureInformation {
|
|
||||||
label: "show".to_string(),
|
|
||||||
documentation: Some(Documentation::MarkupContent(MarkupContent {
|
|
||||||
kind: MarkupKind::PlainText,
|
|
||||||
value: "Show a model.".to_string(),
|
|
||||||
})),
|
|
||||||
parameters: Some(vec![ParameterInformation {
|
|
||||||
label: ParameterLabel::Simple("sg: SketchGroup".to_string()),
|
|
||||||
documentation: Some(Documentation::MarkupContent(MarkupContent {
|
|
||||||
kind: MarkupKind::PlainText,
|
|
||||||
value: "A sketch group.".to_string(),
|
|
||||||
})),
|
|
||||||
}]),
|
|
||||||
active_parameter: None,
|
|
||||||
}],
|
|
||||||
active_signature: Some(0),
|
|
||||||
active_parameter: None,
|
|
||||||
};
|
|
||||||
signatures.insert("show".to_string(), show);
|
|
||||||
|
|
||||||
Ok(signatures)
|
Ok(signatures)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -440,6 +440,7 @@ fn object(i: TokenSlice) -> PResult<ObjectExpression> {
|
|||||||
"a comma-separated list of key-value pairs, e.g. 'height: 4, width: 3'",
|
"a comma-separated list of key-value pairs, e.g. 'height: 4, width: 3'",
|
||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
|
ignore_trailing_comma(i);
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let end = close_brace(i)?.end;
|
let end = close_brace(i)?.end;
|
||||||
Ok(ObjectExpression { start, end, properties })
|
Ok(ObjectExpression { start, end, properties })
|
||||||
@ -977,6 +978,11 @@ fn ignore_whitespace(i: TokenSlice) {
|
|||||||
let _: PResult<()> = repeat(0.., whitespace).parse_next(i);
|
let _: PResult<()> = repeat(0.., whitespace).parse_next(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A helper function to ignore a trailing comma.
|
||||||
|
fn ignore_trailing_comma(i: TokenSlice) {
|
||||||
|
let _ = opt(comma).parse_next(i);
|
||||||
|
}
|
||||||
|
|
||||||
/// Matches at least 1 whitespace.
|
/// Matches at least 1 whitespace.
|
||||||
fn require_whitespace(i: TokenSlice) -> PResult<()> {
|
fn require_whitespace(i: TokenSlice) -> PResult<()> {
|
||||||
repeat(1.., whitespace).parse_next(i)
|
repeat(1.., whitespace).parse_next(i)
|
||||||
@ -1903,7 +1909,6 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
let test_program = r#"startSketchAt([0, 0])
|
let test_program = r#"startSketchAt([0, 0])
|
||||||
|> lineTo([0, -0], %) // MoveRelative
|
|> lineTo([0, -0], %) // MoveRelative
|
||||||
|
|
||||||
show(svg)
|
|
||||||
"#;
|
"#;
|
||||||
let tokens = crate::token::lexer(test_program);
|
let tokens = crate::token::lexer(test_program);
|
||||||
let mut slice = &tokens[..];
|
let mut slice = &tokens[..];
|
||||||
@ -2233,8 +2238,6 @@ const firstExtrude = startSketchOn('XY')
|
|||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
|
|
||||||
show(firstExtrude)
|
|
||||||
|
|
||||||
const secondExtrude = startSketchOn('XY')
|
const secondExtrude = startSketchOn('XY')
|
||||||
|> startProfileAt([0,0], %)
|
|> startProfileAt([0,0], %)
|
||||||
|",
|
|",
|
||||||
@ -2718,9 +2721,7 @@ const b2 = cube([3,3], 4)
|
|||||||
|
|
||||||
const pt1 = b1[0]
|
const pt1 = b1[0]
|
||||||
const pt2 = b2[0]
|
const pt2 = b2[0]
|
||||||
|
"#;
|
||||||
show(b1)
|
|
||||||
show(b2)"#;
|
|
||||||
let tokens = crate::token::lexer(some_program_string);
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
parser.ast().unwrap();
|
parser.ast().unwrap();
|
||||||
@ -2749,7 +2750,7 @@ let other_thing = 2 * cos(3)"#;
|
|||||||
return myBox
|
return myBox
|
||||||
}
|
}
|
||||||
let myBox = box([0,0], -3, -16, -10)
|
let myBox = box([0,0], -3, -16, -10)
|
||||||
show(myBox)"#;
|
"#;
|
||||||
let tokens = crate::token::lexer(some_program_string);
|
let tokens = crate::token::lexer(some_program_string);
|
||||||
let parser = crate::parser::Parser::new(tokens);
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
parser.ast().unwrap();
|
parser.ast().unwrap();
|
||||||
|
@ -4,18 +4,18 @@ expression: actual
|
|||||||
---
|
---
|
||||||
{
|
{
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"end": 90,
|
"end": 59,
|
||||||
"body": [
|
"body": [
|
||||||
{
|
{
|
||||||
"type": "VariableDeclaration",
|
"type": "VariableDeclaration",
|
||||||
"type": "VariableDeclaration",
|
"type": "VariableDeclaration",
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"end": 74,
|
"end": 58,
|
||||||
"declarations": [
|
"declarations": [
|
||||||
{
|
{
|
||||||
"type": "VariableDeclarator",
|
"type": "VariableDeclarator",
|
||||||
"start": 6,
|
"start": 6,
|
||||||
"end": 74,
|
"end": 58,
|
||||||
"id": {
|
"id": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"start": 6,
|
"start": 6,
|
||||||
@ -26,47 +26,47 @@ expression: actual
|
|||||||
"type": "PipeExpression",
|
"type": "PipeExpression",
|
||||||
"type": "PipeExpression",
|
"type": "PipeExpression",
|
||||||
"start": 17,
|
"start": 17,
|
||||||
"end": 74,
|
"end": 58,
|
||||||
"body": [
|
"body": [
|
||||||
{
|
{
|
||||||
"type": "CallExpression",
|
"type": "CallExpression",
|
||||||
"type": "CallExpression",
|
"type": "CallExpression",
|
||||||
"start": 17,
|
"start": 17,
|
||||||
"end": 56,
|
"end": 40,
|
||||||
"callee": {
|
"callee": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"start": 17,
|
"start": 17,
|
||||||
"end": 39,
|
"end": 23,
|
||||||
"name": "unstable_stdlib_circle"
|
"name": "circle"
|
||||||
},
|
},
|
||||||
"arguments": [
|
"arguments": [
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"start": 40,
|
"start": 24,
|
||||||
"end": 44,
|
"end": 28,
|
||||||
"value": "XY",
|
"value": "XY",
|
||||||
"raw": "'XY'"
|
"raw": "'XY'"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "ArrayExpression",
|
"type": "ArrayExpression",
|
||||||
"type": "ArrayExpression",
|
"type": "ArrayExpression",
|
||||||
"start": 46,
|
"start": 30,
|
||||||
"end": 51,
|
"end": 35,
|
||||||
"elements": [
|
"elements": [
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"start": 47,
|
"start": 31,
|
||||||
"end": 48,
|
"end": 32,
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"raw": "0"
|
"raw": "0"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"start": 49,
|
"start": 33,
|
||||||
"end": 50,
|
"end": 34,
|
||||||
"value": 0,
|
"value": 0,
|
||||||
"raw": "0"
|
"raw": "0"
|
||||||
}
|
}
|
||||||
@ -75,8 +75,8 @@ expression: actual
|
|||||||
{
|
{
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"start": 53,
|
"start": 37,
|
||||||
"end": 55,
|
"end": 39,
|
||||||
"value": 22,
|
"value": 22,
|
||||||
"raw": "22"
|
"raw": "22"
|
||||||
}
|
}
|
||||||
@ -86,28 +86,28 @@ expression: actual
|
|||||||
{
|
{
|
||||||
"type": "CallExpression",
|
"type": "CallExpression",
|
||||||
"type": "CallExpression",
|
"type": "CallExpression",
|
||||||
"start": 60,
|
"start": 44,
|
||||||
"end": 74,
|
"end": 58,
|
||||||
"callee": {
|
"callee": {
|
||||||
"type": "Identifier",
|
"type": "Identifier",
|
||||||
"start": 60,
|
"start": 44,
|
||||||
"end": 67,
|
"end": 51,
|
||||||
"name": "extrude"
|
"name": "extrude"
|
||||||
},
|
},
|
||||||
"arguments": [
|
"arguments": [
|
||||||
{
|
{
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"type": "Literal",
|
"type": "Literal",
|
||||||
"start": 68,
|
"start": 52,
|
||||||
"end": 70,
|
"end": 54,
|
||||||
"value": 14,
|
"value": 14,
|
||||||
"raw": "14"
|
"raw": "14"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"type": "PipeSubstitution",
|
"type": "PipeSubstitution",
|
||||||
"type": "PipeSubstitution",
|
"type": "PipeSubstitution",
|
||||||
"start": 72,
|
"start": 56,
|
||||||
"end": 73
|
"end": 57
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"optional": false
|
"optional": false
|
||||||
@ -121,34 +121,6 @@ expression: actual
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"kind": "const"
|
"kind": "const"
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ExpressionStatement",
|
|
||||||
"type": "ExpressionStatement",
|
|
||||||
"start": 75,
|
|
||||||
"end": 89,
|
|
||||||
"expression": {
|
|
||||||
"type": "CallExpression",
|
|
||||||
"type": "CallExpression",
|
|
||||||
"start": 75,
|
|
||||||
"end": 89,
|
|
||||||
"callee": {
|
|
||||||
"type": "Identifier",
|
|
||||||
"start": 75,
|
|
||||||
"end": 79,
|
|
||||||
"name": "show"
|
|
||||||
},
|
|
||||||
"arguments": [
|
|
||||||
{
|
|
||||||
"type": "Identifier",
|
|
||||||
"type": "Identifier",
|
|
||||||
"start": 80,
|
|
||||||
"end": 88,
|
|
||||||
"name": "cylinder"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"optional": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"nonCodeMeta": {
|
"nonCodeMeta": {
|
||||||
|
@ -110,6 +110,9 @@ impl From<ImportFormat> for kittycad::types::InputFormat {
|
|||||||
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
|
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
|
||||||
/// Otherwise you can specify the unit by passing in the options parameter.
|
/// Otherwise you can specify the unit by passing in the options parameter.
|
||||||
/// If you import a gltf file, we will try to find the bin file and import it as well.
|
/// If you import a gltf file, we will try to find the bin file and import it as well.
|
||||||
|
///
|
||||||
|
/// Import paths are relative to the current project directory. This only works in the desktop app
|
||||||
|
/// not in browser.
|
||||||
pub async fn import(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn import(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
|
let (file_path, options): (String, Option<ImportFormat>) = args.get_import_data()?;
|
||||||
|
|
||||||
@ -121,6 +124,9 @@ pub async fn import(args: Args) -> Result<MemoryItem, KclError> {
|
|||||||
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
|
/// For formats lacking unit data (STL, OBJ, PLY), the default import unit is millimeters.
|
||||||
/// Otherwise you can specify the unit by passing in the options parameter.
|
/// Otherwise you can specify the unit by passing in the options parameter.
|
||||||
/// If you import a gltf file, we will try to find the bin file and import it as well.
|
/// If you import a gltf file, we will try to find the bin file and import it as well.
|
||||||
|
///
|
||||||
|
/// Import paths are relative to the current project directory. This only works in the desktop app
|
||||||
|
/// not in browser.
|
||||||
#[stdlib {
|
#[stdlib {
|
||||||
name = "import",
|
name = "import",
|
||||||
}]
|
}]
|
||||||
|
@ -11,6 +11,9 @@ pub trait KclStdLibFn: StdLibFn {
|
|||||||
fn kcl_clone_box(&self) -> Box<dyn KclStdLibFn>;
|
fn kcl_clone_box(&self) -> Box<dyn KclStdLibFn>;
|
||||||
fn function(&self) -> &FunctionExpression;
|
fn function(&self) -> &FunctionExpression;
|
||||||
fn program(&self) -> &Program;
|
fn program(&self) -> &Program;
|
||||||
|
fn std_lib(&self) -> Box<dyn StdLibFn> {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ts_rs::TS for dyn KclStdLibFn {
|
impl ts_rs::TS for dyn KclStdLibFn {
|
||||||
|
@ -37,7 +37,6 @@ pub type FnMap = HashMap<String, StdFn>;
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
|
static ref CORE_FNS: Vec<Box<dyn StdLibFn>> = vec![
|
||||||
Box::new(Show),
|
|
||||||
Box::new(LegLen),
|
Box::new(LegLen),
|
||||||
Box::new(LegAngX),
|
Box::new(LegAngX),
|
||||||
Box::new(LegAngY),
|
Box::new(LegAngY),
|
||||||
@ -133,6 +132,15 @@ impl StdLib {
|
|||||||
Self { fns, kcl_fns }
|
Self { fns, kcl_fns }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the combined hashmaps.
|
||||||
|
pub fn combined(&self) -> HashMap<String, Box<dyn StdLibFn>> {
|
||||||
|
let mut combined = self.fns.clone();
|
||||||
|
for (k, v) in self.kcl_fns.clone() {
|
||||||
|
combined.insert(k, v.std_lib());
|
||||||
|
}
|
||||||
|
combined
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get(&self, name: &str) -> Option<Box<dyn StdLibFn>> {
|
pub fn get(&self, name: &str) -> Option<Box<dyn StdLibFn>> {
|
||||||
self.fns.get(name).cloned()
|
self.fns.get(name).cloned()
|
||||||
}
|
}
|
||||||
@ -705,21 +713,6 @@ impl Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Render a model.
|
|
||||||
// This never actually gets called so this is fine.
|
|
||||||
pub async fn show<'a>(args: Args) -> Result<MemoryItem, KclError> {
|
|
||||||
let sketch_group = args.get_sketch_group()?;
|
|
||||||
inner_show(sketch_group);
|
|
||||||
|
|
||||||
args.make_user_val_from_f64(0.0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Render a model.
|
|
||||||
#[stdlib {
|
|
||||||
name = "show",
|
|
||||||
}]
|
|
||||||
fn inner_show(_sketch: Box<SketchGroup>) {}
|
|
||||||
|
|
||||||
/// Returns the length of the given leg.
|
/// Returns the length of the given leg.
|
||||||
pub async fn leg_length(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn leg_length(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
let (hypotenuse, leg) = args.get_hypotenuse_leg()?;
|
||||||
@ -789,6 +782,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_generate_stdlib_markdown_docs() {
|
fn test_generate_stdlib_markdown_docs() {
|
||||||
let stdlib = StdLib::new();
|
let stdlib = StdLib::new();
|
||||||
|
let combined = stdlib.combined();
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
buf.push_str("<!--- DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED. -->\n\n");
|
buf.push_str("<!--- DO NOT EDIT THIS FILE. IT IS AUTOMATICALLY GENERATED. -->\n\n");
|
||||||
@ -800,8 +794,8 @@ mod tests {
|
|||||||
|
|
||||||
buf.push_str("* [Functions](#functions)\n");
|
buf.push_str("* [Functions](#functions)\n");
|
||||||
|
|
||||||
for key in stdlib.fns.keys().sorted() {
|
for key in combined.keys().sorted() {
|
||||||
let internal_fn = stdlib.fns.get(key).unwrap();
|
let internal_fn = combined.get(key).unwrap();
|
||||||
if internal_fn.unpublished() || internal_fn.deprecated() {
|
if internal_fn.unpublished() || internal_fn.deprecated() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -813,8 +807,8 @@ mod tests {
|
|||||||
|
|
||||||
buf.push_str("## Functions\n\n");
|
buf.push_str("## Functions\n\n");
|
||||||
|
|
||||||
for key in stdlib.fns.keys().sorted() {
|
for key in combined.keys().sorted() {
|
||||||
let internal_fn = stdlib.fns.get(key).unwrap();
|
let internal_fn = combined.get(key).unwrap();
|
||||||
if internal_fn.unpublished() {
|
if internal_fn.unpublished() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -874,11 +868,12 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_generate_stdlib_json_schema() {
|
fn test_generate_stdlib_json_schema() {
|
||||||
let stdlib = StdLib::new();
|
let stdlib = StdLib::new();
|
||||||
|
let combined = stdlib.combined();
|
||||||
|
|
||||||
let mut json_data = vec![];
|
let mut json_data = vec![];
|
||||||
|
|
||||||
for key in stdlib.fns.keys().sorted() {
|
for key in combined.keys().sorted() {
|
||||||
let internal_fn = stdlib.fns.get(key).unwrap();
|
let internal_fn = combined.get(key).unwrap();
|
||||||
json_data.push(internal_fn.to_json().unwrap());
|
json_data.push(internal_fn.to_json().unwrap());
|
||||||
}
|
}
|
||||||
expectorate::assert_contents(
|
expectorate::assert_contents(
|
||||||
|
@ -23,8 +23,8 @@ pub struct LinearPatternData {
|
|||||||
pub repetitions: usize,
|
pub repetitions: usize,
|
||||||
/// The distance between each repetition. This can also be referred to as spacing.
|
/// The distance between each repetition. This can also be referred to as spacing.
|
||||||
pub distance: f64,
|
pub distance: f64,
|
||||||
/// The axis of the pattern. This is a 3D vector.
|
/// The axis of the pattern. This is a 2D vector.
|
||||||
pub axis: [f64; 3],
|
pub axis: [f64; 2],
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data for a circular pattern.
|
/// Data for a circular pattern.
|
||||||
@ -36,8 +36,8 @@ pub struct CircularPatternData {
|
|||||||
/// This excludes the original entity. For example, if `repetitions` is 1,
|
/// This excludes the original entity. For example, if `repetitions` is 1,
|
||||||
/// the original entity will be copied once.
|
/// the original entity will be copied once.
|
||||||
pub repetitions: usize,
|
pub repetitions: usize,
|
||||||
/// The axis around which to make the pattern. This is a 3D vector.
|
/// The axis around which to make the pattern. This is a 2D vector.
|
||||||
pub axis: [f64; 3],
|
pub axis: [f64; 2],
|
||||||
/// The center about which to make th pattern. This is a 3D vector.
|
/// The center about which to make th pattern. This is a 3D vector.
|
||||||
pub center: [f64; 3],
|
pub center: [f64; 3],
|
||||||
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
/// The arc angle (in degrees) to place the repetitions. Must be greater than 0.
|
||||||
@ -50,7 +50,7 @@ pub struct CircularPatternData {
|
|||||||
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
|
let (data, geometry): (LinearPatternData, Geometry) = args.get_data_and_geometry()?;
|
||||||
|
|
||||||
if data.axis == [0.0, 0.0, 0.0] {
|
if data.axis == [0.0, 0.0] {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
message:
|
message:
|
||||||
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
"The axis of the linear pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||||
@ -70,7 +70,7 @@ pub async fn pattern_linear(args: Args) -> Result<MemoryItem, KclError> {
|
|||||||
pub async fn pattern_circular(args: Args) -> Result<MemoryItem, KclError> {
|
pub async fn pattern_circular(args: Args) -> Result<MemoryItem, KclError> {
|
||||||
let (data, geometry): (CircularPatternData, Geometry) = args.get_data_and_geometry()?;
|
let (data, geometry): (CircularPatternData, Geometry) = args.get_data_and_geometry()?;
|
||||||
|
|
||||||
if data.axis == [0.0, 0.0, 0.0] {
|
if data.axis == [0.0, 0.0] {
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
message:
|
message:
|
||||||
"The axis of the circular pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
"The axis of the circular pattern cannot be the zero vector. Otherwise they will just duplicate in place."
|
||||||
@ -97,7 +97,11 @@ async fn inner_pattern_linear(data: LinearPatternData, geometry: Geometry, args:
|
|||||||
.send_modeling_cmd(
|
.send_modeling_cmd(
|
||||||
id,
|
id,
|
||||||
ModelingCmd::EntityLinearPattern {
|
ModelingCmd::EntityLinearPattern {
|
||||||
axis: data.axis.into(),
|
axis: kittycad::types::Point3D {
|
||||||
|
x: data.axis[0],
|
||||||
|
y: data.axis[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
entity_id: geometry.id(),
|
entity_id: geometry.id(),
|
||||||
num_repetitions: data.repetitions as u32,
|
num_repetitions: data.repetitions as u32,
|
||||||
spacing: data.distance,
|
spacing: data.distance,
|
||||||
@ -154,7 +158,11 @@ async fn inner_pattern_circular(
|
|||||||
.send_modeling_cmd(
|
.send_modeling_cmd(
|
||||||
id,
|
id,
|
||||||
ModelingCmd::EntityCircularPattern {
|
ModelingCmd::EntityCircularPattern {
|
||||||
axis: data.axis.into(),
|
axis: kittycad::types::Point3D {
|
||||||
|
x: data.axis[0],
|
||||||
|
y: data.axis[1],
|
||||||
|
z: 0.0,
|
||||||
|
},
|
||||||
entity_id: geometry.id(),
|
entity_id: geometry.id(),
|
||||||
center: data.center.into(),
|
center: data.center.into(),
|
||||||
num_repetitions: data.repetitions as u32,
|
num_repetitions: data.repetitions as u32,
|
||||||
|
@ -48,7 +48,7 @@ impl std::fmt::Debug for Circle {
|
|||||||
/// TODO: Parse the KCL in a macro and generate these
|
/// TODO: Parse the KCL in a macro and generate these
|
||||||
impl StdLibFn for Circle {
|
impl StdLibFn for Circle {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"unstable_stdlib_circle".to_owned()
|
"circle".to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn summary(&self) -> String {
|
fn summary(&self) -> String {
|
||||||
@ -64,15 +64,56 @@ impl StdLibFn for Circle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
|
fn args(&self) -> Vec<crate::docs::StdLibFnArg> {
|
||||||
Vec::new() // TODO
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
let mut args = Vec::new();
|
||||||
|
for parameter in &self.function.params {
|
||||||
|
match parameter.identifier.name.as_str() {
|
||||||
|
"plane" => {
|
||||||
|
args.push(crate::docs::StdLibFnArg {
|
||||||
|
name: parameter.identifier.name.to_owned(),
|
||||||
|
type_: "SketchData".to_string(),
|
||||||
|
schema: <crate::std::sketch::SketchData>::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"center" => {
|
||||||
|
args.push(crate::docs::StdLibFnArg {
|
||||||
|
name: parameter.identifier.name.to_owned(),
|
||||||
|
type_: "[number, number]".to_string(),
|
||||||
|
schema: <[f64; 2]>::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
"radius" => {
|
||||||
|
args.push(crate::docs::StdLibFnArg {
|
||||||
|
name: parameter.identifier.name.to_owned(),
|
||||||
|
type_: "number".to_string(),
|
||||||
|
schema: <f64>::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => panic!("Unknown parameter: {:?}", parameter.identifier.name),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args
|
||||||
}
|
}
|
||||||
|
|
||||||
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
|
fn return_value(&self) -> Option<crate::docs::StdLibFnArg> {
|
||||||
None
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
|
settings.inline_subschemas = true;
|
||||||
|
let mut generator = schemars::gen::SchemaGenerator::new(settings);
|
||||||
|
Some(crate::docs::StdLibFnArg {
|
||||||
|
name: "SketchGroup".to_owned(),
|
||||||
|
type_: "SketchGroup".to_string(),
|
||||||
|
schema: <crate::executor::SketchGroup>::json_schema(&mut generator),
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unpublished(&self) -> bool {
|
fn unpublished(&self) -> bool {
|
||||||
true
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deprecated(&self) -> bool {
|
fn deprecated(&self) -> bool {
|
||||||
|
@ -53,7 +53,7 @@ fn block_comment(i: &mut Located<&str>) -> PResult<Token> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
|
fn line_comment(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
let inner = (r#"//"#, take_till(1.., ['\n', '\r'])).recognize();
|
let inner = (r#"//"#, take_till(0.., ['\n', '\r'])).recognize();
|
||||||
let (value, range) = inner.with_span().parse_next(i)?;
|
let (value, range) = inner.with_span().parse_next(i)?;
|
||||||
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
|
Ok(Token::from_range(range, TokenType::LineComment, value.to_string()))
|
||||||
}
|
}
|
||||||
@ -1463,13 +1463,13 @@ const things = "things"
|
|||||||
fn test_kitt() {
|
fn test_kitt() {
|
||||||
let program = include_str!("../../../tests/executor/inputs/kittycad_svg.kcl");
|
let program = include_str!("../../../tests/executor/inputs/kittycad_svg.kcl");
|
||||||
let actual = lexer(program).unwrap();
|
let actual = lexer(program).unwrap();
|
||||||
assert_eq!(actual.len(), 5098);
|
assert_eq!(actual.len(), 5093);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pipes_on_pipes() {
|
fn test_pipes_on_pipes() {
|
||||||
let program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl");
|
let program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||||
let actual = lexer(program).unwrap();
|
let actual = lexer(program).unwrap();
|
||||||
assert_eq!(actual.len(), 17846);
|
assert_eq!(actual.len(), 17841);
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lexer_negative_word() {
|
fn test_lexer_negative_word() {
|
||||||
|
@ -17,4 +17,3 @@ fn cube = (length, center) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const myCube = cube(40, [0,0])
|
const myCube = cube(40, [0,0])
|
||||||
show(myCube)
|
|
||||||
|
@ -1,2 +1 @@
|
|||||||
const cylinder = unstable_stdlib_circle('XY', [0,0], 22) |> extrude(14, %)
|
const cylinder = circle('XY', [0,0], 22) |> extrude(14, %)
|
||||||
show(cylinder)
|
|
||||||
|
@ -308,4 +308,3 @@ const svg = startSketchOn('XY')
|
|||||||
|> lineTo([13.44, -13.44], %) // VerticalLineHorizonal
|
|> lineTo([13.44, -13.44], %) // VerticalLineHorizonal
|
||||||
|> lineTo([14.28, -13.44], %) // HorizontalLineRelative
|
|> lineTo([14.28, -13.44], %) // HorizontalLineRelative
|
||||||
|> close(%)
|
|> close(%)
|
||||||
show(svg)
|
|
||||||
|
@ -468,4 +468,3 @@ const svg = startSketchOn('XY')
|
|||||||
|> bezierCurve({ control1: [0, -2], control2: [-2.68, -2.67], to: [-1.36, -2.34] }, %) // CubicBezierAbsolute
|
|> bezierCurve({ control1: [0, -2], control2: [-2.68, -2.67], to: [-1.36, -2.34] }, %) // CubicBezierAbsolute
|
||||||
|> bezierCurve({ control1: [0, -0], control2: [0, -1.34], to: [0, -0.68] }, %) // CubicBezierAbsolute
|
|> bezierCurve({ control1: [0, -0], control2: [0, -1.34], to: [0, -0.68] }, %) // CubicBezierAbsolute
|
||||||
|> close(%)
|
|> close(%)
|
||||||
show(svg)
|
|
||||||
|
@ -602,23 +602,14 @@ const part004 = startSketchOn('YZ')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_holes() {
|
async fn serial_test_holes() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const square = startSketchOn('XY')
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt(pos, %)
|
|
||||||
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
|
||||||
|> close(%)
|
|
||||||
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const square = startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([0, 10], %)
|
|> line([0, 10], %)
|
||||||
|> line([10, 0], %)
|
|> line([10, 0], %)
|
||||||
|> line([0, -10], %)
|
|> line([0, -10], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> hole(circle([2, 2], .5), %)
|
|> hole(circle('XY', [2, 2], .5), %)
|
||||||
|> hole(circle([2, 8], .5), %)
|
|> hole(circle('XY', [2, 8], .5), %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -631,7 +622,7 @@ const square = startSketchOn('XY')
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn optional_params() {
|
async fn optional_params() {
|
||||||
let code = r#"
|
let code = r#"
|
||||||
fn circle = (pos, radius, tag?) => {
|
fn other_circle = (pos, radius, tag?) => {
|
||||||
const sg = startSketchOn('XY')
|
const sg = startSketchOn('XY')
|
||||||
|> startProfileAt(pos, %)
|
|> startProfileAt(pos, %)
|
||||||
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
||||||
@ -640,7 +631,7 @@ async fn optional_params() {
|
|||||||
return sg
|
return sg
|
||||||
}
|
}
|
||||||
|
|
||||||
const thing = circle([2, 2], 20)
|
const thing = other_circle([2, 2], 20)
|
||||||
"#;
|
"#;
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
.await
|
.await
|
||||||
@ -650,19 +641,7 @@ const thing = circle([2, 2], 20)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_rounded_with_holes() {
|
async fn serial_test_rounded_with_holes() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"fn tarc = (to, sketchGroup, tag?) => {
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tarc = (to, sketchGroup, tag?) => {
|
|
||||||
return tangentialArcTo(to, sketchGroup, tag)
|
return tangentialArcTo(to, sketchGroup, tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,10 +664,10 @@ const holeRadius = 1
|
|||||||
const holeIndex = 6
|
const holeIndex = 6
|
||||||
|
|
||||||
const part = roundedRectangle([0, 0], 20, 20, 4)
|
const part = roundedRectangle([0, 0], 20, 20, 4)
|
||||||
|> hole(circle([-holeIndex, holeIndex], holeRadius), %)
|
|> hole(circle('XY', [-holeIndex, holeIndex], holeRadius), %)
|
||||||
|> hole(circle([holeIndex, holeIndex], holeRadius), %)
|
|> hole(circle('XY', [holeIndex, holeIndex], holeRadius), %)
|
||||||
|> hole(circle([-holeIndex, -holeIndex], holeRadius), %)
|
|> hole(circle('XY', [-holeIndex, -holeIndex], holeRadius), %)
|
||||||
|> hole(circle([holeIndex, -holeIndex], holeRadius), %)
|
|> hole(circle('XY', [holeIndex, -holeIndex], holeRadius), %)
|
||||||
|> extrude(2, %)
|
|> extrude(2, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@ -700,19 +679,7 @@ const part = roundedRectangle([0, 0], 20, 20, 4)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_top_level_expression() {
|
async fn serial_test_top_level_expression() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"circle('XY', [0,0], 22) |> extrude(14, %)"#;
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
circle([0,0], 22) |> extrude(14, %)"#;
|
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
.await
|
.await
|
||||||
@ -722,20 +689,8 @@ circle([0,0], 22) |> extrude(14, %)"#;
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_linear_basic() {
|
async fn serial_test_patterns_linear_basic() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = circle('XY', [0,0], 2)
|
||||||
const sg = startSketchOn('XY')
|
|> patternLinear({axis: [0,1], repetitions: 12, distance: 2}, %)
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = circle([0,0], 2)
|
|
||||||
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: 2}, %)
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -746,26 +701,14 @@ const part = circle([0,0], 2)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_linear_basic_3d() {
|
async fn serial_test_patterns_linear_basic_3d() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = startSketchOn('XY')
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([0,1], %)
|
|> line([0,1], %)
|
||||||
|> line([1, 0], %)
|
|> line([1, 0], %)
|
||||||
|> line([0, -1], %)
|
|> line([0, -1], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
|> patternLinear({axis: [1, 0,1], repetitions: 3, distance: 6}, %)
|
|> patternLinear({axis: [1, 0], repetitions: 3, distance: 6}, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -776,20 +719,8 @@ const part = startSketchOn('XY')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_linear_basic_negative_distance() {
|
async fn serial_test_patterns_linear_basic_negative_distance() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = circle('XY', [0,0], 2)
|
||||||
const sg = startSketchOn('XY')
|
|> patternLinear({axis: [0,1], repetitions: 12, distance: -2}, %)
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = circle([0,0], 2)
|
|
||||||
|> patternLinear({axis: [0,0,1], repetitions: 12, distance: -2}, %)
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -804,20 +735,8 @@ const part = circle([0,0], 2)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_linear_basic_negative_axis() {
|
async fn serial_test_patterns_linear_basic_negative_axis() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = circle('XY', [0,0], 2)
|
||||||
const sg = startSketchOn('XY')
|
|> patternLinear({axis: [0,-1], repetitions: 12, distance: 2}, %)
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = circle([0,0], 2)
|
|
||||||
|> patternLinear({axis: [0,0,-1], repetitions: 12, distance: 2}, %)
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -832,20 +751,8 @@ const part = circle([0,0], 2)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_linear_basic_holes() {
|
async fn serial_test_patterns_linear_basic_holes() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const circles = circle('XY', [5, 5], 1)
|
||||||
const sg = startSketchOn('XY')
|
|> patternLinear({axis: [1,1], repetitions: 12, distance: 3}, %)
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const circles = circle([5, 5], 1)
|
|
||||||
|> patternLinear({axis: [1,1,0], repetitions: 12, distance: 3}, %)
|
|
||||||
|
|
||||||
const rectangle = startSketchOn('XY')
|
const rectangle = startSketchOn('XY')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
@ -865,20 +772,8 @@ const rectangle = startSketchOn('XY')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_circular_basic_2d() {
|
async fn serial_test_patterns_circular_basic_2d() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = circle('XY', [0,0], 2)
|
||||||
const sg = startSketchOn('XY')
|
|> patternCircular({axis: [0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = circle([0,0], 2)
|
|
||||||
|> patternCircular({axis: [0,0,1], center: [20, 20, 20], repetitions: 12, arcDegrees: 210, rotateDuplicates: true}, %)
|
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -889,26 +784,14 @@ const part = circle([0,0], 2)
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_circular_basic_3d() {
|
async fn serial_test_patterns_circular_basic_3d() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = startSketchOn('XY')
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([0,1], %)
|
|> line([0,1], %)
|
||||||
|> line([1, 0], %)
|
|> line([1, 0], %)
|
||||||
|> line([0, -1], %)
|
|> line([0, -1], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
|> patternCircular({axis: [0,1,0], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|
|> patternCircular({axis: [0,1], center: [-20, -20, -20], repetitions: 40, arcDegrees: 360, rotateDuplicates: false}, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
@ -919,26 +802,14 @@ const part = startSketchOn('XY')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn serial_test_patterns_circular_3d_tilted_axis() {
|
async fn serial_test_patterns_circular_3d_tilted_axis() {
|
||||||
let code = r#"fn circle = (pos, radius) => {
|
let code = r#"const part = startSketchOn('XY')
|
||||||
const sg = startSketchOn('XY')
|
|
||||||
|> startProfileAt([pos[0] + radius, pos[1]], %)
|
|
||||||
|> arc({
|
|
||||||
angle_end: 360,
|
|
||||||
angle_start: 0,
|
|
||||||
radius: radius
|
|
||||||
}, %)
|
|
||||||
|> close(%)
|
|
||||||
return sg
|
|
||||||
}
|
|
||||||
|
|
||||||
const part = startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line([0,1], %)
|
|> line([0,1], %)
|
||||||
|> line([1, 0], %)
|
|> line([1, 0], %)
|
||||||
|> line([0, -1], %)
|
|> line([0, -1], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
|> extrude(1, %)
|
|> extrude(1, %)
|
||||||
|> patternCircular({axis: [1,1,-1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
|> patternCircular({axis: [1,1], center: [10, 0, 10], repetitions: 10, arcDegrees: 360, rotateDuplicates: true}, %)
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
let result = execute_and_snapshot(code, kittycad::types::UnitLength::Mm)
|
||||||
|
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 95 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 98 KiB After Width: | Height: | Size: 95 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
Before Width: | Height: | Size: 115 KiB After Width: | Height: | Size: 116 KiB |
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 96 KiB |