Compare commits
52 Commits
v0.43.0
...
kurt-delet
Author | SHA1 | Date | |
---|---|---|---|
d70ebca165 | |||
d8a9abba69 | |||
0fd18c14ef | |||
36d4830c34 | |||
4ce6054e64 | |||
ced49f8ddc | |||
e063622139 | |||
42178fa649 | |||
4bb23bc917 | |||
72272d5d98 | |||
5ef0a1e75f | |||
d8dc49b08a | |||
87eabef450 | |||
40e4f2236f | |||
663076f790 | |||
f2c76b0509 | |||
481bef859a | |||
1a67d344ee | |||
774e3efcb7 | |||
4ec44690bf | |||
d2f0865f95 | |||
84d17454e9 | |||
5a5138a703 | |||
33468c4c96 | |||
b3467bbe5a | |||
90086488b5 | |||
32e8975799 | |||
648616c667 | |||
482487cf57 | |||
5fe3023be9 | |||
30397ba7ab | |||
3344208c63 | |||
fcf3272ad2 | |||
d3e4b123d0 | |||
2bb548c000 | |||
b09c240e36 | |||
6c9d14af93 | |||
0642e49189 | |||
6add1d73ad | |||
68c89746c7 | |||
9f323c207c | |||
7197b6c85d | |||
913f2641c3 | |||
e9086c54ba | |||
9f93346dc6 | |||
1b9f5f20f5 | |||
3865637c61 | |||
2c40e8a97c | |||
c696f0837a | |||
30edf2ad56 | |||
e60cabb193 | |||
1e9cf6f256 |
@ -1,4 +1,5 @@
|
|||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
DEV=false
|
||||||
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
VITE_KC_API_WS_MODELING_URL=wss://api.zoo.dev/ws/modeling/commands
|
||||||
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
VITE_KC_API_BASE_URL=https://api.zoo.dev
|
||||||
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
VITE_KC_SITE_BASE_URL=https://zoo.dev
|
||||||
|
1
.github/workflows/e2e-tests.yml
vendored
@ -3,6 +3,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||||
|
@ -17,7 +17,7 @@ lastSegX(sketch: Sketch) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | The sketch whose line segment is being queried | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | A sketch is a collection of paths. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ lastSegY(sketch: Sketch) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | The sketch whose line segment is being queried | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | A sketch is a collection of paths. | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -57,55 +57,3 @@ Imported symbols can be renamed for convenience or to avoid name collisions.
|
|||||||
```
|
```
|
||||||
import increment as inc, decrement as dec from "util.kcl"
|
import increment as inc, decrement as dec from "util.kcl"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Importing files from other CAD systems
|
|
||||||
|
|
||||||
`import` can also be used to import files from other CAD systems. The format of the statement is the
|
|
||||||
same as for KCL files. You can only import the whole file, not items from it. E.g.,
|
|
||||||
|
|
||||||
```
|
|
||||||
import "tests/inputs/cube.obj"
|
|
||||||
|
|
||||||
// Use `cube` just like a KCL object.
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
|
||||||
import "tests/inputs/cube-2.sldprt" as cube
|
|
||||||
|
|
||||||
// Use `cube` just like a KCL object.
|
|
||||||
```
|
|
||||||
|
|
||||||
You can make the file format explicit using a format attribute (useful if using a different
|
|
||||||
extension), e.g.,
|
|
||||||
|
|
||||||
```
|
|
||||||
@(format = obj)
|
|
||||||
import "tests/inputs/cube"
|
|
||||||
```
|
|
||||||
|
|
||||||
For formats lacking unit data (such as STL, OBJ, or PLY files), the default
|
|
||||||
unit of measurement is millimeters. Alternatively you may specify the unit
|
|
||||||
by using an attirbute. Likewise, you can also specify a coordinate system. E.g.,
|
|
||||||
|
|
||||||
```
|
|
||||||
@(unitLength = ft, coords = opengl)
|
|
||||||
import "tests/inputs/cube.obj"
|
|
||||||
```
|
|
||||||
|
|
||||||
When importing a GLTF file, the bin file will be imported as well.
|
|
||||||
|
|
||||||
Import paths are relative to the current project directory. Imports currently only work when
|
|
||||||
using the native Modeling App, not in the browser.
|
|
||||||
|
|
||||||
### Supported values
|
|
||||||
|
|
||||||
File formats: `fbx`, `gltf`/`glb`, `obj`+, `ply`+, `sldprt`, `step`/`stp`, `stl`+. (Those marked with a
|
|
||||||
'+' support customising the length unit and coordinate system).
|
|
||||||
|
|
||||||
Length units: `mm` (the default), `cm`, `m`, `inch`, `ft`, `yd`.
|
|
||||||
|
|
||||||
Coordinate systems:
|
|
||||||
|
|
||||||
- `zoo` (the default), forward: -Y, up: +Z, handedness: right
|
|
||||||
- `opengl`, forward: +Z, up: +Y, handedness: right
|
|
||||||
- `vulkan`, forward: +Z, up: -Y, handedness: left
|
|
||||||
|
@ -17,8 +17,8 @@ offsetPlane(std_plane: StandardPlane, offset: number) -> Plane
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `std_plane` | [`StandardPlane`](/docs/kcl/types/StandardPlane) | Which standard plane (e.g. XY) should this new plane be created from? | Yes |
|
| `std_plane` | [`StandardPlane`](/docs/kcl/types/StandardPlane) | One of the standard planes. | Yes |
|
||||||
| `offset` | `number` | Distance from the standard plane this new plane will be created at. | Yes |
|
| `offset` | `number` | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ squareSketch = startSketchOn('XY')
|
|||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
|
|
||||||
circleSketch = startSketchOn(offsetPlane('XY', offset = 150))
|
circleSketch = startSketchOn(offsetPlane('XY', 150))
|
||||||
|> circle({ center = [0, 100], radius = 50 }, %)
|
|> circle({ center = [0, 100], radius = 50 }, %)
|
||||||
|
|
||||||
loft([squareSketch, circleSketch])
|
loft([squareSketch, circleSketch])
|
||||||
@ -55,7 +55,7 @@ squareSketch = startSketchOn('XZ')
|
|||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
|
|
||||||
circleSketch = startSketchOn(offsetPlane('XZ', offset = 150))
|
circleSketch = startSketchOn(offsetPlane('XZ', 150))
|
||||||
|> circle({ center = [0, 100], radius = 50 }, %)
|
|> circle({ center = [0, 100], radius = 50 }, %)
|
||||||
|
|
||||||
loft([squareSketch, circleSketch])
|
loft([squareSketch, circleSketch])
|
||||||
@ -73,7 +73,7 @@ squareSketch = startSketchOn('YZ')
|
|||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
|
|
||||||
circleSketch = startSketchOn(offsetPlane('YZ', offset = 150))
|
circleSketch = startSketchOn(offsetPlane('YZ', 150))
|
||||||
|> circle({ center = [0, 100], radius = 50 }, %)
|
|> circle({ center = [0, 100], radius = 50 }, %)
|
||||||
|
|
||||||
loft([squareSketch, circleSketch])
|
loft([squareSketch, circleSketch])
|
||||||
@ -91,7 +91,7 @@ squareSketch = startSketchOn('-XZ')
|
|||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
|
|
||||||
circleSketch = startSketchOn(offsetPlane('-XZ', offset = -150))
|
circleSketch = startSketchOn(offsetPlane('-XZ', -150))
|
||||||
|> circle({ center = [0, 100], radius = 50 }, %)
|
|> circle({ center = [0, 100], radius = 50 }, %)
|
||||||
|
|
||||||
loft([squareSketch, circleSketch])
|
loft([squareSketch, circleSketch])
|
||||||
@ -106,7 +106,7 @@ startSketchOn("XY")
|
|||||||
|> circle({ radius = 10, center = [0, 0] }, %)
|
|> circle({ radius = 10, center = [0, 0] }, %)
|
||||||
|
|
||||||
// Triangle on the plane 4 units above
|
// Triangle on the plane 4 units above
|
||||||
startSketchOn(offsetPlane("XY", offset = 4))
|
startSketchOn(offsetPlane("XY", 4))
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line(end = [10, 0])
|
|> line(end = [10, 0])
|
||||||
|> line(end = [0, 10])
|
|> line(end = [0, 10])
|
||||||
|
@ -9,7 +9,7 @@ Repeat a 2-dimensional sketch some number of times along a partial or
|
|||||||
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
|
complete circle some specified number of times. Each object may additionally be rotated along the circle, ensuring orentation of the solid with respect to the center of the circle is maintained.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Sketch]
|
patternCircular2d(data: CircularPattern2dData, sketch_set: SketchSet) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,12 +17,8 @@ patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], a
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
| `data` | [`CircularPattern2dData`](/docs/kcl/types/CircularPattern2dData) | Data for a circular pattern on a 2D sketch. | Yes |
|
||||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||||
| `center` | `[number]` | The center about which to make the pattern. This is a 2D vector. | Yes |
|
|
||||||
| `arc_degrees` | `number` | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
|
||||||
| `rotate_duplicates` | `bool` | Whether or not to rotate the duplicates as they are copied. | Yes |
|
|
||||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
@ -38,12 +34,12 @@ exampleSketch = startSketchOn('XZ')
|
|||||||
|> line(end = [-1, 0])
|
|> line(end = [-1, 0])
|
||||||
|> line(end = [0, -5])
|
|> line(end = [0, -5])
|
||||||
|> close()
|
|> close()
|
||||||
|> patternCircular2d(
|
|> patternCircular2d({
|
||||||
center = [0, 0],
|
center = [0, 0],
|
||||||
instances = 13,
|
instances = 13,
|
||||||
arcDegrees = 360,
|
arcDegrees = 360,
|
||||||
rotateDuplicates = true,
|
rotateDuplicates = true
|
||||||
)
|
}, %)
|
||||||
|
|
||||||
example = extrude(exampleSketch, length = 1)
|
example = extrude(exampleSketch, length = 1)
|
||||||
```
|
```
|
||||||
|
@ -9,7 +9,7 @@ Repeat a 2-dimensional sketch along some dimension, with a dynamic amount
|
|||||||
of distance between each repetition, some specified number of times.
|
of distance between each repetition, some specified number of times.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Sketch]
|
patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,11 +17,8 @@ patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axi
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
| `data` | [`LinearPattern2dData`](/docs/kcl/types/LinearPattern2dData) | Data for a linear pattern on a 2D sketch. | Yes |
|
||||||
| `instances` | `integer` | The number of total instances. Must be greater than or equal to 1. This includes the original entity. For example, if instances is 2, there will be two copies -- the original, and one new copy. If instances is 1, this has no effect. | Yes |
|
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||||
| `distance` | `number` | Distance between each repetition. Also known as 'spacing'. | Yes |
|
|
||||||
| `axis` | `[number]` | The axis of the pattern. A 2D vector. | Yes |
|
|
||||||
| `use_original` | `bool` | If the target was sketched on an extrusion, setting this will use the original sketch as the target, not the entire joined solid. Defaults to false. | No |
|
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
@ -33,7 +30,11 @@ patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axi
|
|||||||
```js
|
```js
|
||||||
exampleSketch = startSketchOn('XZ')
|
exampleSketch = startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 1 }, %)
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|> patternLinear2d(axis = [1, 0], instances = 7, distance = 4)
|
|> patternLinear2d({
|
||||||
|
axis = [1, 0],
|
||||||
|
instances = 7,
|
||||||
|
distance = 4
|
||||||
|
}, %)
|
||||||
|
|
||||||
example = extrude(exampleSketch, length = 1)
|
example = extrude(exampleSketch, length = 1)
|
||||||
```
|
```
|
||||||
|
@ -17,7 +17,7 @@ segAng(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segEnd(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segEndX(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segEndY(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segLen(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segStart(tag: TagIdentifier) -> [number]
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segStartX(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ segStartY(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
80484
docs/kcl/std.json
@ -17,7 +17,7 @@ tangentToEnd(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -20,6 +20,5 @@ Data for a circular pattern on a 2D sketch.
|
|||||||
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
|
| `center` |`[number, number]`| The center about which to make the pattern. This is a 2D vector. | No |
|
||||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||||
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -21,6 +21,5 @@ Data for a circular pattern on a 3D model.
|
|||||||
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
|
| `center` |`[number, number, number]`| The center about which to make the pattern. This is a 3D vector. | No |
|
||||||
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
| `arcDegrees` |`number`| The arc angle (in degrees) to place the repetitions. Must be greater than 0. | No |
|
||||||
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
| `rotateDuplicates` |`boolean`| Whether or not to rotate the duplicates as they are copied. | No |
|
||||||
| `useOriginal` |`boolean`| If the target being patterned is itself a pattern, then, should you use the original solid, or the pattern? | No |
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "EnvironmentRef"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
[`SnapshotRef`](/docs/kcl/types/SnapshotRef)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -19,8 +19,8 @@ A face.
|
|||||||
| `id` |`string`| The id of the face. | No |
|
| `id` |`string`| The id of the face. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |`string`| The tag of the face. | No |
|
| `value` |`string`| The tag of the face. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
||||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A face. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A face. | No |
|
||||||
|
@ -59,7 +59,23 @@ Any KCL value.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Number`| | No |
|
| `type` |enum: `Number`| | No |
|
||||||
| `value` |`number`| | No |
|
| `value` |`number`| | No |
|
||||||
| `ty` |[`NumericType`](/docs/kcl/types/NumericType)| Any KCL value. | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `Int`| | No |
|
||||||
|
| `value` |`integer`| | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
@ -295,7 +311,7 @@ Data for an imported geometry.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: `Function`| | No |
|
| `type` |enum: `Function`| | No |
|
||||||
| `memory` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| Any KCL value. | No |
|
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
@ -335,23 +351,6 @@ Data for an imported geometry.
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Tombstone`| | No |
|
|
||||||
| `value` |`null`| | No |
|
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,250 +0,0 @@
|
|||||||
---
|
|
||||||
title: "NumericType"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Count`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Mm`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Cm`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `M`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Inches`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Feet`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Yards`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Length`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Degrees`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Radians`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Angle`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Known`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Default`| | No |
|
|
||||||
| `len` |[`UnitLen`](/docs/kcl/types/UnitLen)| | No |
|
|
||||||
| `angle` |[`UnitAngle`](/docs/kcl/types/UnitAngle)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Unknown`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Any`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -98,29 +98,6 @@ a complete arc
|
|||||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
A base path.
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `CircleThreePoint`| | No |
|
|
||||||
| `p1` |`[number, number]`| Point 1 of the circle | No |
|
|
||||||
| `p2` |`[number, number]`| Point 2 of the circle | No |
|
|
||||||
| `p3` |`[number, number]`| Point 3 of the circle | No |
|
|
||||||
| `from` |`[number, number]`| The from point. | No |
|
|
||||||
| `to` |`[number, number]`| The to point. | No |
|
|
||||||
| `tag` |[`TagDeclarator`](/docs/kcl/types#tag-declaration)| The tag of the path. | No |
|
|
||||||
| `__geoMeta` |[`GeoMeta`](/docs/kcl/types/GeoMeta)| Metadata. | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
A path that is horizontal.
|
A path that is horizontal.
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ A plane.
|
|||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A plane. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s Y axis be? | No |
|
||||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A plane. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A plane. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
@ -17,5 +17,6 @@ layout: manual
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
||||||
| `currentEnv` |`integer`| | No |
|
| `currentEnv` |`integer`| | No |
|
||||||
|
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,7 +22,6 @@ A sketch is a collection of paths.
|
|||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||||
| `originalId` |`string`| | No |
|
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch is a collection of paths. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch is a collection of paths. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ A sketch is a collection of paths.
|
|||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
||||||
| `originalId` |`string`| | No |
|
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch or a group of sketches. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch or a group of sketches. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| Metadata. | No |
|
||||||
|
|
||||||
|
@ -29,8 +29,8 @@ A plane.
|
|||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A sketch type. | No |
|
| `value` |[`PlaneType`](/docs/kcl/types/PlaneType)| A sketch type. | No |
|
||||||
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
| `origin` |[`Point3d`](/docs/kcl/types/Point3d)| Origin of the plane. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane's Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the plane’s Y axis be? | No |
|
||||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch type. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch type. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
@ -53,8 +53,8 @@ A face.
|
|||||||
| `id` |`string`| The id of the face. | No |
|
| `id` |`string`| The id of the face. | No |
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID. | No |
|
||||||
| `value` |`string`| The tag of the face. | No |
|
| `value` |`string`| The tag of the face. | No |
|
||||||
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's X axis be? | No |
|
| `xAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s X axis be? | No |
|
||||||
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face's Y axis be? | No |
|
| `yAxis` |[`Point3d`](/docs/kcl/types/Point3d)| What should the face’s Y axis be? | No |
|
||||||
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
| `zAxis` |[`Point3d`](/docs/kcl/types/Point3d)| The z-axis (normal). | No |
|
||||||
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
| `solid` |[`Solid`](/docs/kcl/types/Solid)| The solid the face is on. | No |
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch type. | No |
|
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A sketch type. | No |
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SnapshotRef"
|
|
||||||
excerpt: "An index pointing to a snapshot within a specific (unspecified) environment."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
An index pointing to a snapshot within a specific (unspecified) environment.
|
|
||||||
|
|
||||||
**Type:** `integer` (`uint`)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "UnitAngle"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Degrees`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Radians`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,186 +0,0 @@
|
|||||||
---
|
|
||||||
title: "UnitType"
|
|
||||||
excerpt: ""
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Count`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Mm`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Cm`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `M`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Inches`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Feet`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Yards`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Length`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Degrees`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Radians`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Angle`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -19,8 +19,6 @@ test.describe(
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
// FIXME: Cannot use scene.waitForExecutionDone() since there is no KCL code
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
const coord =
|
const coord =
|
||||||
|
@ -10,7 +10,6 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
test('Typing KCL errors induces a badge on the code pane button', async ({
|
test('Typing KCL errors induces a badge on the code pane button', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
scene,
|
|
||||||
}) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
@ -31,7 +30,11 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// Ensure no badge is present
|
// Ensure no badge is present
|
||||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
@ -172,9 +175,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// FIXME: await scene.waitForExecutionDone() does not work. It still fails.
|
await page.waitForTimeout(1000)
|
||||||
// I needed to increase this timeout to get this to pass.
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
|
|
||||||
// Ensure badge is present
|
// Ensure badge is present
|
||||||
const codePaneButtonHolder = page.locator('#code-button-holder')
|
const codePaneButtonHolder = page.locator('#code-button-holder')
|
||||||
@ -186,7 +187,7 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
// click in the editor to focus it
|
// click in the editor to focus it
|
||||||
await page.locator('.cm-content').click()
|
await page.locator('.cm-content').click()
|
||||||
|
|
||||||
await page.waitForTimeout(2000)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
// go to the start of the editor and enter more text which will trigger
|
// go to the start of the editor and enter more text which will trigger
|
||||||
// a lint error.
|
// a lint error.
|
||||||
@ -203,9 +204,8 @@ test.describe('Code pane and errors', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('ArrowUp')
|
await page.keyboard.press('ArrowUp')
|
||||||
await page.keyboard.press('Home')
|
await page.keyboard.press('Home')
|
||||||
await page.keyboard.type('foo_bar = 1')
|
await page.keyboard.type('foo_bar = 1')
|
||||||
await page.waitForTimeout(2000)
|
await page.waitForTimeout(500)
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
await page.waitForTimeout(2000)
|
|
||||||
|
|
||||||
// ensure we have a lint error
|
// ensure we have a lint error
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
@ -301,7 +301,7 @@ test(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test.skip(
|
||||||
'external change of file contents are reflected in editor',
|
'external change of file contents are reflected in editor',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
|
@ -174,9 +174,6 @@ test.describe('Command bar tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// FIXME: No KCL code, unable to wait for engine execution
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
).not.toBeDisabled()
|
).not.toBeDisabled()
|
||||||
|
@ -9,8 +9,8 @@ import fsp from 'fs/promises'
|
|||||||
|
|
||||||
test(
|
test(
|
||||||
'export works on the first try',
|
'export works on the first try',
|
||||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
{ tag: '@electron' },
|
||||||
async ({ page, context, scene }, testInfo) => {
|
async ({ page, context }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
const bracketDir = path.join(dir, 'bracket')
|
||||||
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
await Promise.all([fsp.mkdir(bracketDir, { recursive: true })])
|
||||||
@ -118,9 +118,8 @@ test(
|
|||||||
// Close the file pane
|
// Close the file pane
|
||||||
await u.closeFilePanel()
|
await u.closeFilePanel()
|
||||||
|
|
||||||
// FIXME: await scene.waitForExecutionDone() does not work. The modeling indicator stays in -receive-reliable and not execution done
|
// wait for it to finish executing (todo: make this more robust)
|
||||||
await page.waitForTimeout(10000)
|
await page.waitForTimeout(1000)
|
||||||
|
|
||||||
// expect zero errors in guter
|
// expect zero errors in guter
|
||||||
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
await expect(page.locator('.cm-lint-marker-error')).not.toBeVisible()
|
||||||
|
|
||||||
|
@ -490,11 +490,6 @@ test.describe('Editor tests', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.press('ArrowLeft')
|
await page.keyboard.press('ArrowLeft')
|
||||||
await page.keyboard.press('ArrowRight')
|
await page.keyboard.press('ArrowRight')
|
||||||
|
|
||||||
// FIXME: lsp errors do not propagate to the frontend until engine is connected and code is executed
|
|
||||||
// This timeout is to wait for engine connection. LSP and code execution errors should be handled differently
|
|
||||||
// LSP can emit errors as fast as it waits and show them in the editor
|
|
||||||
await page.waitForTimeout(10000)
|
|
||||||
|
|
||||||
// error in guter
|
// error in guter
|
||||||
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
await expect(page.locator('.cm-lint-marker-info').first()).toBeVisible()
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ sketch001 = startSketchOn('XZ')
|
|||||||
revolve001 = revolve({ axis = "X" }, sketch001)
|
revolve001 = revolve({ axis = "X" }, sketch001)
|
||||||
triangle()
|
triangle()
|
||||||
|> extrude(length = 30)
|
|> extrude(length = 30)
|
||||||
plane001 = offsetPlane('XY', offset = 10)
|
plane001 = offsetPlane('XY', 10)
|
||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> startProfileAt([-20, 0], %)
|
|> startProfileAt([-20, 0], %)
|
||||||
|> line(end = [5, -15])
|
|> line(end = [5, -15])
|
||||||
@ -35,7 +35,7 @@ sketch002 = startSketchOn(plane001)
|
|||||||
extrude001 = extrude(sketch002, length = 10)
|
extrude001 = extrude(sketch002, length = 10)
|
||||||
`
|
`
|
||||||
|
|
||||||
const FEATURE_TREE_SKETCH_CODE = `sketch001 = startSketchOn('XZ')
|
const FEAUTRE_TREE_SKETCH_CODE = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> angledLine([0, 4], %, $rectangleSegmentA001)
|
|> angledLine([0, 4], %, $rectangleSegmentA001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
@ -54,7 +54,7 @@ sketch002 = startSketchOn(extrude001, rectangleSegmentB001)
|
|||||||
center = [-1, 2],
|
center = [-1, 2],
|
||||||
radius = .5
|
radius = .5
|
||||||
}, %)
|
}, %)
|
||||||
plane001 = offsetPlane('XZ', offset = -5)
|
plane001 = offsetPlane('XZ', -5)
|
||||||
sketch003 = startSketchOn(plane001)
|
sketch003 = startSketchOn(plane001)
|
||||||
|> circle({ center = [0, 0], radius = 5 }, %)
|
|> circle({ center = [0, 0], radius = 5 }, %)
|
||||||
`
|
`
|
||||||
@ -116,7 +116,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await testViewSource({
|
await testViewSource({
|
||||||
operationName: 'Offset Plane',
|
operationName: 'Offset Plane',
|
||||||
operationIndex: 0,
|
operationIndex: 0,
|
||||||
expectedActiveLine: "plane001 = offsetPlane('XY', offset = 10)",
|
expectedActiveLine: "plane001 = offsetPlane('XY', 10)",
|
||||||
})
|
})
|
||||||
await testViewSource({
|
await testViewSource({
|
||||||
operationName: 'Extrude',
|
operationName: 'Extrude',
|
||||||
@ -153,16 +153,33 @@ test.describe('Feature Tree pane', () => {
|
|||||||
`User can edit sketch (but not on offset plane yet) from the feature tree`,
|
`User can edit sketch (but not on offset plane yet) from the feature tree`,
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, homePage, scene, editor, toolbar, page }) => {
|
async ({ context, homePage, scene, editor, toolbar, page }) => {
|
||||||
await context.addInitScript((initialCode) => {
|
const unavailableToastMessage = page.getByText(
|
||||||
localStorage.setItem('persistCode', initialCode)
|
'Editing sketches on faces or offset planes through the feature tree is not yet supported'
|
||||||
}, FEATURE_TREE_SKETCH_CODE)
|
)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
await test.step('force re-exe', async () => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
await page.waitForTimeout(1000)
|
const bracketDir = join(dir, 'test-sample')
|
||||||
await editor.replaceCode('90', '91')
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
await page.waitForTimeout(1500)
|
await fsp.writeFile(
|
||||||
|
join(bracketDir, 'main.kcl'),
|
||||||
|
FEAUTRE_TREE_SKETCH_CODE,
|
||||||
|
'utf-8'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('setup test', async () => {
|
||||||
|
await homePage.expectState({
|
||||||
|
projectCards: [
|
||||||
|
{
|
||||||
|
title: 'test-sample',
|
||||||
|
fileCount: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sortBy: 'last-modified-desc',
|
||||||
|
})
|
||||||
|
await homePage.openProject('test-sample')
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
await toolbar.openFeatureTreePane()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('On a default plane should work', async () => {
|
await test.step('On a default plane should work', async () => {
|
||||||
@ -182,23 +199,24 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await test.step('On an extrude face should *not* work', async () => {
|
await test.step('On an extrude face should *not* work', async () => {
|
||||||
// Tooltip is getting in the way of clicking, so I'm first closing the pane
|
// Tooltip is getting in the way of clicking, so I'm first closing the pane
|
||||||
await toolbar.closeFeatureTreePane()
|
await toolbar.closeFeatureTreePane()
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
await editor.replaceCode('91', '90')
|
|
||||||
await page.waitForTimeout(2000)
|
|
||||||
await (await toolbar.getFeatureTreeOperation('Sketch', 1)).dblclick()
|
await (await toolbar.getFeatureTreeOperation('Sketch', 1)).dblclick()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
toolbar.exitSketchBtn,
|
unavailableToastMessage,
|
||||||
'We should be in sketch mode now'
|
'We should see a toast message about this'
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
await editor.expectState({
|
await unavailableToastMessage.waitFor({ state: 'detached' })
|
||||||
highlightedCode: '',
|
// TODO - turn on once we update the artifactGraph in Rust
|
||||||
diagnostics: [],
|
// to include the proper source location for the extrude face
|
||||||
activeLines: [
|
// await expect(
|
||||||
'sketch002=startSketchOn(extrude001,rectangleSegmentB001)',
|
// toolbar.exitSketchBtn,
|
||||||
],
|
// 'We should be in sketch mode now'
|
||||||
})
|
// ).toBeVisible()
|
||||||
await toolbar.exitSketchBtn.click()
|
// await editor.expectState({
|
||||||
|
// highlightedCode: '',
|
||||||
|
// diagnostics: [],
|
||||||
|
// activeLines: ['|>circle({center=[-1,2],radius=.5},%)'],
|
||||||
|
// })
|
||||||
|
// await toolbar.exitSketchBtn.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('On an offset plane should *not* work', async () => {
|
await test.step('On an offset plane should *not* work', async () => {
|
||||||
@ -208,7 +226,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: ['sketch003=startSketchOn(plane001)'],
|
activeLines: ['|>circle({center=[0,0],radius=5},%)'],
|
||||||
})
|
})
|
||||||
await expect(
|
await expect(
|
||||||
toolbar.exitSketchBtn,
|
toolbar.exitSketchBtn,
|
||||||
@ -324,8 +342,7 @@ test.describe('Feature Tree pane', () => {
|
|||||||
toolbar,
|
toolbar,
|
||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const testCode = (value: string) =>
|
const testCode = (value: string) => `p = offsetPlane('XY', ${value})`
|
||||||
`p = offsetPlane('XY', offset = ${value})`
|
|
||||||
const initialInput = '10'
|
const initialInput = '10'
|
||||||
const initialCode = testCode(initialInput)
|
const initialCode = testCode(initialInput)
|
||||||
const newInput = '5 + 10'
|
const newInput = '5 + 10'
|
||||||
|
@ -112,9 +112,6 @@ export class CmdBarFixture {
|
|||||||
* and assumes we are past the `pickCommand` step.
|
* and assumes we are past the `pickCommand` step.
|
||||||
*/
|
*/
|
||||||
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
|
progressCmdBar = async (shouldFuzzProgressMethod = true) => {
|
||||||
// FIXME: Progressing the command bar is a race condition. We have an async useEffect that reports the final state via useCalculateKclExpression. If this does not run quickly enough, it will not "fail" the continue because you can press continue if the state is not ready. E2E tests do not know this.
|
|
||||||
// Wait 1250ms to assume the await executeAst of the KCL input field is finished
|
|
||||||
await this.page.waitForTimeout(1250)
|
|
||||||
if (shouldFuzzProgressMethod || Math.random() > 0.5) {
|
if (shouldFuzzProgressMethod || Math.random() > 0.5) {
|
||||||
const arrowButton = this.page.getByRole('button', {
|
const arrowButton = this.page.getByRole('button', {
|
||||||
name: 'arrow right Continue',
|
name: 'arrow right Continue',
|
||||||
@ -131,23 +128,6 @@ export class CmdBarFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Added data-testid to the command bar buttons
|
|
||||||
// command-bar-continue are the buttons to go to the next step
|
|
||||||
// does not include the submit which is the final button press
|
|
||||||
// aka the right arrow button
|
|
||||||
continue = async () => {
|
|
||||||
const continueButton = this.page.getByTestId('command-bar-continue')
|
|
||||||
await continueButton.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added data-testid to the command bar buttons
|
|
||||||
// command-bar-submit is the button for the final step to submit
|
|
||||||
// the command bar flow aka the checkmark button.
|
|
||||||
submit = async () => {
|
|
||||||
const submitButton = this.page.getByTestId('command-bar-submit')
|
|
||||||
await submitButton.click()
|
|
||||||
}
|
|
||||||
|
|
||||||
openCmdBar = async (selectCmd?: 'promptToEdit') => {
|
openCmdBar = async (selectCmd?: 'promptToEdit') => {
|
||||||
// TODO why does this button not work in electron tests?
|
// TODO why does this button not work in electron tests?
|
||||||
// await this.cmdBarOpenBtn.click()
|
// await this.cmdBarOpenBtn.click()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { Page, Locator } from '@playwright/test'
|
import type { Page, Locator } from '@playwright/test'
|
||||||
import { expect } from '@playwright/test'
|
import { expect } from '@playwright/test'
|
||||||
import { isArray, uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
closeDebugPanel,
|
closeDebugPanel,
|
||||||
doAndWaitForImageDiff,
|
doAndWaitForImageDiff,
|
||||||
@ -255,7 +255,7 @@ export class SceneFixture {
|
|||||||
function isColourArray(
|
function isColourArray(
|
||||||
colour: [number, number, number] | [number, number, number][]
|
colour: [number, number, number] | [number, number, number][]
|
||||||
): colour is [number, number, number][] {
|
): colour is [number, number, number][] {
|
||||||
return isArray(colour[0])
|
return Array.isArray(colour[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function expectPixelColor(
|
export async function expectPixelColor(
|
||||||
|
@ -20,7 +20,6 @@ export class ToolbarFixture {
|
|||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
revolveButton!: Locator
|
revolveButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
helixButton!: Locator
|
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
tangentialArcBtn!: Locator
|
tangentialArcBtn!: Locator
|
||||||
@ -53,7 +52,6 @@ export class ToolbarFixture {
|
|||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.revolveButton = page.getByTestId('revolve')
|
this.revolveButton = page.getByTestId('revolve')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.helixButton = page.getByTestId('helix')
|
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
this.tangentialArcBtn = page.getByTestId('tangential-arc')
|
this.tangentialArcBtn = page.getByTestId('tangential-arc')
|
||||||
@ -135,16 +133,6 @@ export class ToolbarFixture {
|
|||||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
await this.page.getByTestId('dropdown-center-rectangle').click()
|
||||||
}
|
}
|
||||||
|
|
||||||
selectCircleThreePoint = async () => {
|
|
||||||
await this.page
|
|
||||||
.getByRole('button', { name: 'caret down Center circle:' })
|
|
||||||
.click()
|
|
||||||
await expect(
|
|
||||||
this.page.getByTestId('dropdown-circle-three-points')
|
|
||||||
).toBeVisible()
|
|
||||||
await this.page.getByTestId('dropdown-circle-three-points').click()
|
|
||||||
}
|
|
||||||
|
|
||||||
async closePane(paneId: SidebarType) {
|
async closePane(paneId: SidebarType) {
|
||||||
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
|
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
},
|
||||||
async ({ page, homePage }) => {
|
async ({ context, page, homePage }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
@ -68,7 +68,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
},
|
||||||
async ({ page }) => {
|
async ({ page, homePage }, testInfo) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
const viewportSize = { width: 1200, height: 500 }
|
const viewportSize = { width: 1200, height: 500 }
|
||||||
@ -154,7 +154,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'Click through each onboarding step and back',
|
'Click through each onboarding step',
|
||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
app: {
|
app: {
|
||||||
@ -187,21 +187,15 @@ test.describe('Onboarding tests', () => {
|
|||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
|
|
||||||
const nextButton = page.getByTestId('onboarding-next')
|
const nextButton = page.getByTestId('onboarding-next')
|
||||||
const prevButton = page.getByTestId('onboarding-prev')
|
|
||||||
|
|
||||||
while ((await nextButton.innerText()) !== 'Finish') {
|
while ((await nextButton.innerText()) !== 'Finish') {
|
||||||
await nextButton.hover()
|
await nextButton.hover()
|
||||||
await nextButton.click()
|
await nextButton.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
while ((await prevButton.innerText()) !== 'Dismiss') {
|
// Finish the onboarding
|
||||||
await prevButton.hover()
|
await nextButton.hover()
|
||||||
await prevButton.click()
|
await nextButton.click()
|
||||||
}
|
|
||||||
|
|
||||||
// Dismiss the onboarding
|
|
||||||
await prevButton.hover()
|
|
||||||
await prevButton.click()
|
|
||||||
|
|
||||||
// Test that the onboarding pane is gone
|
// Test that the onboarding pane is gone
|
||||||
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
await expect(page.getByTestId('onboarding-content')).not.toBeVisible()
|
||||||
@ -275,7 +269,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
async ({ page, homePage }) => {
|
async ({ context, page, homePage }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
const badCode = `// This is bad code we shouldn't see`
|
const badCode = `// This is bad code we shouldn't see`
|
||||||
|
|
||||||
@ -342,10 +336,10 @@ test.describe('Onboarding tests', () => {
|
|||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
// Test that the text in this step is correct
|
// Test that the text in this step is correct
|
||||||
const avatarLocator = page
|
const avatarLocator = await page
|
||||||
.getByTestId('user-sidebar-toggle')
|
.getByTestId('user-sidebar-toggle')
|
||||||
.locator('img')
|
.locator('img')
|
||||||
const onboardingOverlayLocator = page
|
const onboardingOverlayLocator = await page
|
||||||
.getByTestId('onboarding-content')
|
.getByTestId('onboarding-content')
|
||||||
.locator('div')
|
.locator('div')
|
||||||
.nth(1)
|
.nth(1)
|
||||||
@ -443,7 +437,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test.fixme(
|
test(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
@ -453,7 +447,7 @@ test.fixme(
|
|||||||
},
|
},
|
||||||
cleanProjectDir: true,
|
cleanProjectDir: true,
|
||||||
},
|
},
|
||||||
async ({ context, page }) => {
|
async ({ context, page, homePage }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const routerTemplateDir = join(dir, 'router-template-slate')
|
const routerTemplateDir = join(dir, 'router-template-slate')
|
||||||
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
await fsp.mkdir(routerTemplateDir, { recursive: true })
|
||||||
@ -492,6 +486,10 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Navigate into project', async () => {
|
await test.step('Navigate into project', async () => {
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('heading', { name: 'Your Projects' })
|
page.getByRole('heading', { name: 'Your Projects' })
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@ -516,10 +514,7 @@ test.fixme(
|
|||||||
const modelColor: [number, number, number] = [76, 76, 76]
|
const modelColor: [number, number, number] = [76, 76, 76]
|
||||||
|
|
||||||
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
await page.mouse.move(XYPlanePoint.x, XYPlanePoint.y)
|
||||||
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
|
||||||
await tutorialDismissButton.click()
|
await tutorialDismissButton.click()
|
||||||
// Make sure model still there.
|
|
||||||
await expectPixelColor(page, modelColor, XYPlanePoint, 8)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('Clear code and restart onboarding from settings', async () => {
|
await test.step('Clear code and restart onboarding from settings', async () => {
|
||||||
|
@ -29,13 +29,11 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
localStorage.setItem('persistCode', file)
|
localStorage.setItem('persistCode', file)
|
||||||
}, file)
|
}, file)
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
const [clickCircle, moveToCircle] = scene.makeMouseHelpers(582, 217)
|
||||||
|
|
||||||
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
await test.step('because there is sweepable geometry, verify extrude is enable when nothing is selected', async () => {
|
||||||
// FIXME: Do not click, clicking removes the activeLines in future checks
|
await scene.clickNoWhere()
|
||||||
// await scene.clickNoWhere()
|
|
||||||
await expect(toolbar.extrudeButton).toBeEnabled()
|
await expect(toolbar.extrudeButton).toBeEnabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -201,7 +199,6 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
}, file)
|
}, file)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||||
|
|
||||||
@ -224,8 +221,8 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)],%)
|
||||||
|>close()`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -251,8 +248,8 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
||||||
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)
|
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)
|
||||||
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)
|
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)
|
||||||
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)],%)
|
||||||
|>close()`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -273,8 +270,8 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004)
|
||||||
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)
|
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)
|
||||||
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)
|
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)
|
||||||
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)],%)|
|
||||||
|>close()`,
|
>close(%)`,
|
||||||
})
|
})
|
||||||
/// last one
|
/// last one
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -292,8 +289,8 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
||||||
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)
|
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)
|
||||||
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)
|
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)
|
||||||
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)],%)
|
||||||
|>close()`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||||
@ -309,9 +306,9 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001)
|
||||||
], %, $yo)
|
], %, $yo)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %, $seg02)
|
||||||
|> close()
|
|> close(%)
|
||||||
extrude001 = extrude(sketch001, length = 100)
|
extrude001 = extrude(100, sketch001)
|
||||||
|> chamfer({
|
|> chamfer({
|
||||||
length = 30,
|
length = 30,
|
||||||
tags = [getOppositeEdge(seg01)]
|
tags = [getOppositeEdge(seg01)]
|
||||||
@ -336,8 +333,8 @@ profile004 = startProfileAt([-23.43, 19.69], sketch005)
|
|||||||
segAng(rectangleSegmentA005),
|
segAng(rectangleSegmentA005),
|
||||||
-segLen(rectangleSegmentA005)
|
-segLen(rectangleSegmentA005)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
sketch004 = startSketchOn(extrude001, seg05)
|
sketch004 = startSketchOn(extrude001, seg05)
|
||||||
profile003 = startProfileAt([82.57, 322.96], sketch004)
|
profile003 = startProfileAt([82.57, 322.96], sketch004)
|
||||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||||
@ -349,8 +346,8 @@ profile003 = startProfileAt([82.57, 322.96], sketch004)
|
|||||||
segAng(rectangleSegmentA004),
|
segAng(rectangleSegmentA004),
|
||||||
-segLen(rectangleSegmentA004)
|
-segLen(rectangleSegmentA004)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
sketch003 = startSketchOn(extrude001, seg04)
|
sketch003 = startSketchOn(extrude001, seg04)
|
||||||
profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
||||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
@ -362,8 +359,8 @@ profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
|||||||
segAng(rectangleSegmentA003),
|
segAng(rectangleSegmentA003),
|
||||||
-segLen(rectangleSegmentA003)
|
-segLen(rectangleSegmentA003)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
@ -375,9 +372,8 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
|
|
||||||
`,
|
`,
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
@ -405,7 +401,6 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
}, file)
|
}, file)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
const sketchOnAChamfer = _sketchOnAChamfer(page, editor, toolbar, scene)
|
||||||
|
|
||||||
@ -428,8 +423,8 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)],%)
|
||||||
|>close()`,
|
|>close(%)`,
|
||||||
})
|
})
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
@ -469,8 +464,8 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute=[profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
`,
|
`,
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
@ -542,6 +537,101 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], sketch001)`,
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], sketch001)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
await toolbar.startSketchPlaneSelection()
|
||||||
|
await moveToXzPlane()
|
||||||
|
await clickOnXzPlane()
|
||||||
|
// timeout wait for engine animation is unavoidable
|
||||||
|
await page.waitForTimeout(600)
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.sketchOnXzPlane)
|
||||||
|
})
|
||||||
|
await test.step(`Place a point a few pixels off the middle, verify it still snaps to 0,0`, async () => {
|
||||||
|
await clickOriginSloppy()
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.pointAtOrigin)
|
||||||
|
})
|
||||||
|
await test.step(`Add a segment on x-axis after moving the mouse a bit, verify it snaps`, async () => {
|
||||||
|
await moveXAxisSloppy()
|
||||||
|
await clickXAxisSloppy()
|
||||||
|
await editor.expectEditor.toContain(expectedCodeSnippets.segmentOnXAxis)
|
||||||
|
})
|
||||||
|
await test.step(`Unequip line tool`, async () => {
|
||||||
|
await toolbar.lineBtn.click()
|
||||||
|
await expect(toolbar.lineBtn).not.toHaveAttribute('aria-pressed', 'true')
|
||||||
|
})
|
||||||
|
await test.step(`Drag the origin point up and to the right, verify it's past snapping`, async () => {
|
||||||
|
await dragToOffYAxis({
|
||||||
|
fromPoint: { x: originSloppy.screen[0], y: originSloppy.screen[1] },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
// yo
|
||||||
|
|
||||||
|
test(`Verify axis, origin, and horizontal snapping`, async ({
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
scene,
|
||||||
|
}) => {
|
||||||
|
const viewPortSize = { width: 1200, height: 500 }
|
||||||
|
|
||||||
|
await page.setBodyDimensions(viewPortSize)
|
||||||
|
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// Constants and locators
|
||||||
|
// These are mappings from screenspace to KCL coordinates,
|
||||||
|
// until we merge in our coordinate system helpers
|
||||||
|
const xzPlane = [
|
||||||
|
viewPortSize.width * 0.65,
|
||||||
|
viewPortSize.height * 0.3,
|
||||||
|
] as const
|
||||||
|
const originSloppy = {
|
||||||
|
screen: [
|
||||||
|
viewPortSize.width / 2 + 3, // 3px off the center of the screen
|
||||||
|
viewPortSize.height / 2,
|
||||||
|
],
|
||||||
|
kcl: [0, 0],
|
||||||
|
} as const
|
||||||
|
const xAxisSloppy = {
|
||||||
|
screen: [
|
||||||
|
viewPortSize.width * 0.75,
|
||||||
|
viewPortSize.height / 2 - 3, // 3px off the X-axis
|
||||||
|
],
|
||||||
|
kcl: [20.34, 0],
|
||||||
|
} as const
|
||||||
|
const offYAxis = {
|
||||||
|
screen: [
|
||||||
|
viewPortSize.width * 0.6, // Well off the Y-axis, out of snapping range
|
||||||
|
viewPortSize.height * 0.3,
|
||||||
|
],
|
||||||
|
kcl: [8.14, 6.78],
|
||||||
|
} as const
|
||||||
|
const yAxisSloppy = {
|
||||||
|
screen: [
|
||||||
|
viewPortSize.width / 2 + 5, // 5px off the Y-axis
|
||||||
|
viewPortSize.height * 0.3,
|
||||||
|
],
|
||||||
|
kcl: [0, 6.78],
|
||||||
|
} as const
|
||||||
|
const [clickOnXzPlane, moveToXzPlane] = scene.makeMouseHelpers(...xzPlane)
|
||||||
|
const [clickOriginSloppy] = scene.makeMouseHelpers(...originSloppy.screen)
|
||||||
|
const [clickXAxisSloppy, moveXAxisSloppy] = scene.makeMouseHelpers(
|
||||||
|
...xAxisSloppy.screen
|
||||||
|
)
|
||||||
|
const [dragToOffYAxis, dragFromOffAxis] = scene.makeDragHelpers(
|
||||||
|
...offYAxis.screen
|
||||||
|
)
|
||||||
|
|
||||||
|
const expectedCodeSnippets = {
|
||||||
|
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
||||||
|
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
|
||||||
|
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
||||||
|
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
|
||||||
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||||
|
}
|
||||||
|
|
||||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
await editor.closePane()
|
await editor.closePane()
|
||||||
await toolbar.startSketchPlaneSelection()
|
await toolbar.startSketchPlaneSelection()
|
||||||
@ -580,7 +670,6 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
expectedCodeSnippets.afterSegmentDraggedOnYAxis
|
expectedCodeSnippets.afterSegmentDraggedOnYAxis
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await editor.page.waitForTimeout(1000)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Verify user can double-click to edit a sketch`, async ({
|
test(`Verify user can double-click to edit a sketch`, async ({
|
||||||
@ -692,330 +781,6 @@ openSketch = startSketchOn('XY')
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test(`Shift-click to select and deselect edges and faces`, async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
// Code samples
|
|
||||||
const initialCode = `sketch001 = startSketchOn('XY')
|
|
||||||
|> startProfileAt([-12, -6], %)
|
|
||||||
|> line(end = [0, 12])
|
|
||||||
|> line(end = [24, 0])
|
|
||||||
|> line(end = [0, -12])
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
|> extrude(%, length = -12)`
|
|
||||||
|
|
||||||
// Locators
|
|
||||||
const upperEdgeLocation = { x: 600, y: 192 }
|
|
||||||
const lowerEdgeLocation = { x: 600, y: 383 }
|
|
||||||
const faceLocation = { x: 630, y: 290 }
|
|
||||||
|
|
||||||
// Click helpers
|
|
||||||
const [clickOnUpperEdge] = scene.makeMouseHelpers(
|
|
||||||
upperEdgeLocation.x,
|
|
||||||
upperEdgeLocation.y
|
|
||||||
)
|
|
||||||
const [clickOnLowerEdge] = scene.makeMouseHelpers(
|
|
||||||
lowerEdgeLocation.x,
|
|
||||||
lowerEdgeLocation.y
|
|
||||||
)
|
|
||||||
const [clickOnFace] = scene.makeMouseHelpers(faceLocation.x, faceLocation.y)
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
const edgeColorWhite: [number, number, number] = [220, 220, 220] // varies from 192 to 255
|
|
||||||
const edgeColorYellow: [number, number, number] = [251, 251, 40] // vaies from 12 to 67
|
|
||||||
const faceColorGray: [number, number, number] = [168, 168, 168]
|
|
||||||
const faceColorYellow: [number, number, number] = [155, 155, 155]
|
|
||||||
const tolerance = 40
|
|
||||||
const timeout = 150
|
|
||||||
|
|
||||||
// Setup
|
|
||||||
await test.step(`Initial test setup`, async () => {
|
|
||||||
await context.addInitScript((initialCode) => {
|
|
||||||
localStorage.setItem('persistCode', initialCode)
|
|
||||||
}, initialCode)
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Wait for the scene and stream to load
|
|
||||||
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Select and deselect a single edge', async () => {
|
|
||||||
await test.step('Click the edge', async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await clickOnUpperEdge()
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorYellow,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Shift-click the same edge to deselect', async () => {
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnUpperEdge()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Select and deselect multiple objects', async () => {
|
|
||||||
await test.step('Select both edges and the face', async () => {
|
|
||||||
await test.step('Select the upper edge', async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await clickOnUpperEdge()
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorYellow,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Select the lower edge (Shift-click)', async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
lowerEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnLowerEdge()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorYellow,
|
|
||||||
lowerEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Select the face (Shift-click)', async () => {
|
|
||||||
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnFace()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
await test.step('Deselect them one by one', async () => {
|
|
||||||
await test.step('Deselect the face (Shift-click)', async () => {
|
|
||||||
await scene.expectPixelColor(faceColorYellow, faceLocation, tolerance)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnFace()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(faceColorGray, faceLocation, tolerance)
|
|
||||||
})
|
|
||||||
await test.step('Deselect the lower edge (Shift-click)', async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorYellow,
|
|
||||||
lowerEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnLowerEdge()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
lowerEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Deselect the upper edge (Shift-click)', async () => {
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorYellow,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickOnUpperEdge()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
upperEdgeLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Shift-click to select and deselect sketch segments`, async ({
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
}) => {
|
|
||||||
// Locators
|
|
||||||
const firstPointLocation = { x: 200, y: 100 }
|
|
||||||
const secondPointLocation = { x: 800, y: 100 }
|
|
||||||
const thirdPointLocation = { x: 800, y: 400 }
|
|
||||||
const fristSegmentLocation = { x: 750, y: 100 }
|
|
||||||
const secondSegmentLocation = { x: 800, y: 150 }
|
|
||||||
const planeLocation = { x: 700, y: 200 }
|
|
||||||
|
|
||||||
// Click helpers
|
|
||||||
const [clickFirstPoint] = scene.makeMouseHelpers(
|
|
||||||
firstPointLocation.x,
|
|
||||||
firstPointLocation.y
|
|
||||||
)
|
|
||||||
const [clickSecondPoint] = scene.makeMouseHelpers(
|
|
||||||
secondPointLocation.x,
|
|
||||||
secondPointLocation.y
|
|
||||||
)
|
|
||||||
const [clickThirdPoint] = scene.makeMouseHelpers(
|
|
||||||
thirdPointLocation.x,
|
|
||||||
thirdPointLocation.y
|
|
||||||
)
|
|
||||||
const [clickFirstSegment] = scene.makeMouseHelpers(
|
|
||||||
fristSegmentLocation.x,
|
|
||||||
fristSegmentLocation.y
|
|
||||||
)
|
|
||||||
const [clickSecondSegment] = scene.makeMouseHelpers(
|
|
||||||
secondSegmentLocation.x,
|
|
||||||
secondSegmentLocation.y
|
|
||||||
)
|
|
||||||
const [clickPlane] = scene.makeMouseHelpers(
|
|
||||||
planeLocation.x,
|
|
||||||
planeLocation.y
|
|
||||||
)
|
|
||||||
|
|
||||||
// Colors
|
|
||||||
const edgeColorWhite: [number, number, number] = [220, 220, 220]
|
|
||||||
const edgeColorBlue: [number, number, number] = [20, 20, 200]
|
|
||||||
const backgroundColor: [number, number, number] = [30, 30, 30]
|
|
||||||
const tolerance = 40
|
|
||||||
const timeout = 150
|
|
||||||
|
|
||||||
// Setup
|
|
||||||
await test.step(`Initial test setup`, async () => {
|
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// Wait for the scene and stream to load
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
backgroundColor,
|
|
||||||
secondPointLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Select and deselect a single sketch segment', async () => {
|
|
||||||
await test.step('Get into sketch mode', async () => {
|
|
||||||
await editor.closePane()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.getByRole('button', { name: 'Start Sketch' }).click()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickPlane()
|
|
||||||
await page.waitForTimeout(1000)
|
|
||||||
})
|
|
||||||
await test.step('Draw sketch', async () => {
|
|
||||||
await clickFirstPoint()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickSecondPoint()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickThirdPoint()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
})
|
|
||||||
await test.step('Deselect line tool', async () => {
|
|
||||||
const btnLine = page.getByTestId('line')
|
|
||||||
const btnLineAriaPressed = await btnLine.getAttribute('aria-pressed')
|
|
||||||
if (btnLineAriaPressed === 'true') {
|
|
||||||
await btnLine.click()
|
|
||||||
}
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
})
|
|
||||||
await test.step('Select the first segment', async () => {
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickFirstSegment()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorBlue,
|
|
||||||
fristSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
secondSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Select the second segment (Shift-click)', async () => {
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickSecondSegment()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorBlue,
|
|
||||||
fristSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorBlue,
|
|
||||||
secondSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Deselect the first segment', async () => {
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickFirstSegment()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
fristSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorBlue,
|
|
||||||
secondSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
await test.step('Deselect the second segment', async () => {
|
|
||||||
await page.keyboard.down('Shift')
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await clickSecondSegment()
|
|
||||||
await page.waitForTimeout(timeout)
|
|
||||||
await page.keyboard.up('Shift')
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
fristSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
await scene.expectPixelColor(
|
|
||||||
edgeColorWhite,
|
|
||||||
secondSegmentLocation,
|
|
||||||
tolerance
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test(`Offset plane point-and-click`, async ({
|
test(`Offset plane point-and-click`, async ({
|
||||||
context,
|
context,
|
||||||
page,
|
page,
|
||||||
@ -1028,12 +793,9 @@ openSketch = startSketchOn('XY')
|
|||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 700, y: 150 }
|
const testPoint = { x: 700, y: 150 }
|
||||||
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnXzPlane] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const expectedOutput = `plane001 = offsetPlane('XZ', offset = 5)`
|
const expectedOutput = `plane001 = offsetPlane('XZ', 5)`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
// FIXME: Since there is no KCL code loaded. We need to wait for the scene to load before we continue.
|
|
||||||
// The engine may not be connected
|
|
||||||
await page.waitForTimeout(15000)
|
|
||||||
|
|
||||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||||
@ -1082,72 +844,6 @@ openSketch = startSketchOn('XY')
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Helix point-and-click', async ({
|
|
||||||
context,
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
editor,
|
|
||||||
toolbar,
|
|
||||||
cmdBar,
|
|
||||||
}) => {
|
|
||||||
// One dumb hardcoded screen pixel value
|
|
||||||
const testPoint = { x: 620, y: 257 }
|
|
||||||
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5,)`
|
|
||||||
const expectedLine = `revolutions=1,`
|
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
|
||||||
|
|
||||||
// await test.step(`Look for the red of the default plane`, async () => {
|
|
||||||
// await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
|
||||||
// })
|
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
|
||||||
await toolbar.helixButton.click()
|
|
||||||
await cmdBar.expectState({
|
|
||||||
stage: 'arguments',
|
|
||||||
currentArgKey: 'revolutions',
|
|
||||||
currentArgValue: '1',
|
|
||||||
headerArguments: {
|
|
||||||
AngleStart: '',
|
|
||||||
Axis: '',
|
|
||||||
CounterClockWise: '',
|
|
||||||
Length: '',
|
|
||||||
Radius: '',
|
|
||||||
Revolutions: '',
|
|
||||||
},
|
|
||||||
highlightedHeaderArg: 'revolutions',
|
|
||||||
commandName: 'Helix',
|
|
||||||
})
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
await cmdBar.progressCmdBar()
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
|
||||||
await editor.expectEditor.toContain(expectedOutput)
|
|
||||||
await editor.expectState({
|
|
||||||
diagnostics: [],
|
|
||||||
activeLines: [expectedLine],
|
|
||||||
highlightedCode: '',
|
|
||||||
})
|
|
||||||
// Red plane is now gone, white helix is there
|
|
||||||
await scene.expectPixelColor([250, 250, 250], testPoint, 15)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step('Delete offset plane via feature tree selection', async () => {
|
|
||||||
await editor.closePane()
|
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Helix', 0)
|
|
||||||
await operationButton.click({ button: 'left' })
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
// Red plane is back
|
|
||||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
const loftPointAndClickCases = [
|
||||||
{ shouldPreselect: true },
|
{ shouldPreselect: true },
|
||||||
{ shouldPreselect: false },
|
{ shouldPreselect: false },
|
||||||
@ -1164,7 +860,7 @@ openSketch = startSketchOn('XY')
|
|||||||
}) => {
|
}) => {
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
plane001 = offsetPlane('XZ', offset = 50)
|
plane001 = offsetPlane('XZ', 50)
|
||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
`
|
`
|
||||||
@ -1250,7 +946,7 @@ openSketch = startSketchOn('XY')
|
|||||||
}) => {
|
}) => {
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 30 }, %)
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
plane001 = offsetPlane('XZ', offset = 50)
|
plane001 = offsetPlane('XZ', 50)
|
||||||
sketch002 = startSketchOn(plane001)
|
sketch002 = startSketchOn(plane001)
|
||||||
|> circle({ center = [0, 0], radius = 20 }, %)
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
loft001 = loft([sketch001, sketch002])
|
loft001 = loft([sketch001, sketch002])
|
||||||
@ -1260,7 +956,6 @@ loft001 = loft([sketch001, sketch002])
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 575, y: 200 }
|
const testPoint = { x: 575, y: 200 }
|
||||||
@ -1297,7 +992,7 @@ loft001 = loft([sketch001, sketch002])
|
|||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
plane001 = offsetPlane('XZ', offset = 50)
|
plane001 = offsetPlane('XZ', 50)
|
||||||
`)
|
`)
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
// Check for sketch 1
|
// Check for sketch 1
|
||||||
@ -1338,7 +1033,7 @@ sketch002 = startSketchOn('XZ')
|
|||||||
testPoint.x - 50,
|
testPoint.x - 50,
|
||||||
testPoint.y
|
testPoint.y
|
||||||
)
|
)
|
||||||
const sweepDeclaration = 'sweep001 = sweep(sketch001, path = sketch002)'
|
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
||||||
|
|
||||||
await test.step(`Look for sketch001`, async () => {
|
await test.step(`Look for sketch001`, async () => {
|
||||||
await toolbar.closePane('code')
|
await toolbar.closePane('code')
|
||||||
@ -1373,12 +1068,27 @@ sketch002 = startSketchOn('XZ')
|
|||||||
await clickOnSketch2()
|
await clickOnSketch2()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await toolbar.openPane('code')
|
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
})
|
})
|
||||||
|
// // yo
|
||||||
|
// await clickOnSketch2()
|
||||||
|
// await page.waitForTimeout(500)
|
||||||
|
// await cmdBar.progressCmdBar()
|
||||||
|
// await toolbar.openPane('code')
|
||||||
|
// await page.waitForTimeout(500)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
// await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
||||||
|
// await editor.expectEditor.toContain(sweepDeclaration)
|
||||||
|
// await editor.expectState({
|
||||||
|
// diagnostics: [],
|
||||||
|
// activeLines: [sweepDeclaration],
|
||||||
|
// highlightedCode: '',
|
||||||
|
|
||||||
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
||||||
|
await toolbar.openPane('code')
|
||||||
await editor.expectEditor.toContain(sweepDeclaration)
|
await editor.expectEditor.toContain(sweepDeclaration)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
@ -1903,7 +1613,16 @@ extrude001 = extrude(sketch001, length = -12)
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
// verify modeling scene is loaded
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
backgroundColor,
|
||||||
|
secondEdgeLocation,
|
||||||
|
lowTolerance
|
||||||
|
)
|
||||||
|
|
||||||
|
// wait for stream to load
|
||||||
|
await scene.expectPixelColor(bodyColor, bodyLocation, highTolerance)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Test 1: Command bar flow with preselected edges
|
// Test 1: Command bar flow with preselected edges
|
||||||
@ -2128,7 +1847,6 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// verify modeling scene is loaded
|
// verify modeling scene is loaded
|
||||||
await scene.expectPixelColor(
|
await scene.expectPixelColor(
|
||||||
@ -2251,13 +1969,12 @@ chamfer04 = chamfer({ length = 5, tags = [getOppositeEdge(seg02)]}, extrude001
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 575, y: 200 }
|
const testPoint = { x: 575, y: 200 }
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell(extrude001, faces = ['end'], thickness = 5)"
|
"shell001 = shell({ faces = ['end'], thickness = 5 }, extrude001)"
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||||
@ -2350,7 +2067,6 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 580, y: 180 }
|
const testPoint = { x: 580, y: 180 }
|
||||||
@ -2358,7 +2074,8 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
const [clickOnWall] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 70)
|
||||||
const mutatedCode = 'xLine(-40, %, $seg01)'
|
const mutatedCode = 'xLine(-40, %, $seg01)'
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 5)"
|
"shell001 = shell({ faces = ['end', seg01], thickness = 5}, extrude001)"
|
||||||
|
const formattedOutLastLine = '}, extrude001)'
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
@ -2401,7 +2118,7 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
await editor.expectEditor.toContain(shellDeclaration)
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: [shellDeclaration],
|
activeLines: [formattedOutLastLine],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
@ -2448,18 +2165,20 @@ extrude002 = extrude(sketch002, length = 50)
|
|||||||
await context.addInitScript((initialCode) => {
|
await context.addInitScript((initialCode) => {
|
||||||
localStorage.setItem('persistCode', initialCode)
|
localStorage.setItem('persistCode', initialCode)
|
||||||
}, initialCode)
|
}, initialCode)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 580, y: 320 }
|
const testPoint = { x: 550, y: 295 }
|
||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const shellTarget = hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
||||||
const shellDeclaration = `shell001 = shell(${shellTarget}, faces = ['end'], thickness = 5)`
|
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||||
|
})`
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([113, 113, 113], testPoint, 15)
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
||||||
@ -2523,7 +2242,7 @@ extrude002 = extrude(sketch002, length = 50)
|
|||||||
sketch002 = startSketchOn('XZ')
|
sketch002 = startSketchOn('XZ')
|
||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> xLine(-2000, %)
|
|> xLine(-2000, %)
|
||||||
sweep001 = sweep(sketch001, path = sketch002)
|
sweep001 = sweep({ path = sketch002 }, sketch001)
|
||||||
`
|
`
|
||||||
await context.addInitScript((initialCode) => {
|
await context.addInitScript((initialCode) => {
|
||||||
localStorage.setItem('persistCode', initialCode)
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
@ -455,7 +455,7 @@ test.describe('Can export from electron app', () => {
|
|||||||
for (const method of exportMethods) {
|
for (const method of exportMethods) {
|
||||||
test(
|
test(
|
||||||
`Can export using ${method}`,
|
`Can export using ${method}`,
|
||||||
{ tag: ['@electron', '@skipLocalEngine'] },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
const bracketDir = path.join(dir, 'bracket')
|
const bracketDir = path.join(dir, 'bracket')
|
||||||
|
@ -35,8 +35,7 @@ sketch003 = startSketchOn('XY')
|
|||||||
extrude003 = extrude(sketch003, length = 20)
|
extrude003 = extrude(sketch003, length = 20)
|
||||||
`
|
`
|
||||||
|
|
||||||
test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
test.describe('Check the happy path, for basic changing color', () => {
|
||||||
test.fixme('Check the happy path, for basic changing color', () => {
|
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
desc: 'User accepts change',
|
desc: 'User accepts change',
|
||||||
@ -60,7 +59,6 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
localStorage.setItem('persistCode', file)
|
localStorage.setItem('persistCode', file)
|
||||||
}, file)
|
}, file)
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
const body1CapCoords = { x: 571, y: 351 }
|
const body1CapCoords = { x: 571, y: 351 }
|
||||||
const greenCheckCoords = { x: 565, y: 345 }
|
const greenCheckCoords = { x: 565, y: 345 }
|
||||||
@ -73,13 +71,9 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
const green: [number, number, number] = [108, 152, 75]
|
const green: [number, number, number] = [108, 152, 75]
|
||||||
const notGreen: [number, number, number] = [132, 132, 132]
|
const notGreen: [number, number, number] = [132, 132, 132]
|
||||||
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
const body2NotGreen: [number, number, number] = [88, 88, 88]
|
||||||
const submittingToast = page.getByText(
|
const submittingToast = page.getByText('Submitting to Text-to-CAD API...')
|
||||||
'Submitting to Text-to-CAD API...'
|
|
||||||
)
|
|
||||||
const successToast = page.getByText('Prompt to edit successful')
|
const successToast = page.getByText('Prompt to edit successful')
|
||||||
const acceptBtn = page.getByRole('button', {
|
const acceptBtn = page.getByRole('button', { name: 'checkmark Accept' })
|
||||||
name: 'checkmark Accept',
|
|
||||||
})
|
|
||||||
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
|
const rejectBtn = page.getByRole('button', { name: 'close Reject' })
|
||||||
|
|
||||||
await test.step('wait for scene to load select body and check selection came through', async () => {
|
await test.step('wait for scene to load select body and check selection came through', async () => {
|
||||||
@ -102,16 +96,14 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await cmdBar.progressCmdBar()
|
await cmdBar.progressCmdBar()
|
||||||
await expect(submittingToast).toBeVisible()
|
await expect(submittingToast).toBeVisible()
|
||||||
await expect(submittingToast).not.toBeVisible({
|
await expect(submittingToast).not.toBeVisible({ timeout: 2 * 60_000 }) // can take a while
|
||||||
timeout: 2 * 60_000,
|
|
||||||
}) // can take a while
|
|
||||||
await expect(successToast).toBeVisible()
|
await expect(successToast).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('verify initial change', async () => {
|
await test.step('verify initial change', async () => {
|
||||||
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
||||||
await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15)
|
await scene.expectPixelColor(body2NotGreen, body2WallCoords, 15)
|
||||||
await editor.expectEditor.toContain('appearance(')
|
await editor.expectEditor.toContain('appearance({')
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!shouldReject) {
|
if (!shouldReject) {
|
||||||
@ -120,13 +112,13 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
await expect(successToast).not.toBeVisible()
|
await expect(successToast).not.toBeVisible()
|
||||||
|
|
||||||
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
await scene.expectPixelColor(green, greenCheckCoords, 15)
|
||||||
await editor.expectEditor.toContain('appearance(')
|
await editor.expectEditor.toContain('appearance({')
|
||||||
|
|
||||||
// ctrl-z works after accepting
|
// ctrl-z works after accepting
|
||||||
await page.keyboard.down('ControlOrMeta')
|
await page.keyboard.down('ControlOrMeta')
|
||||||
await page.keyboard.press('KeyZ')
|
await page.keyboard.press('KeyZ')
|
||||||
await page.keyboard.up('ControlOrMeta')
|
await page.keyboard.up('ControlOrMeta')
|
||||||
await editor.expectEditor.not.toContain('appearance(')
|
await editor.expectEditor.not.toContain('appearance({')
|
||||||
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
@ -135,13 +127,14 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
await expect(successToast).not.toBeVisible()
|
await expect(successToast).not.toBeVisible()
|
||||||
|
|
||||||
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
await scene.expectPixelColor(notGreen, greenCheckCoords, 15)
|
||||||
await editor.expectEditor.not.toContain('appearance(')
|
await editor.expectEditor.not.toContain('appearance({')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test.describe('bad path', { tag: ['@skipWin'] }, () => {
|
||||||
test(`bad edit prompt`, async ({
|
test(`bad edit prompt`, async ({
|
||||||
context,
|
context,
|
||||||
homePage,
|
homePage,
|
||||||
@ -155,7 +148,6 @@ test.describe('Prompt-to-edit tests', { tag: '@skipWin' }, () => {
|
|||||||
localStorage.setItem('persistCode', file)
|
localStorage.setItem('persistCode', file)
|
||||||
}, file)
|
}, file)
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
|
||||||
const body1CapCoords = { x: 571, y: 351 }
|
const body1CapCoords = { x: 571, y: 351 }
|
||||||
const [clickBody1Cap] = scene.makeMouseHelpers(
|
const [clickBody1Cap] = scene.makeMouseHelpers(
|
||||||
|
@ -192,11 +192,11 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
|> line(end = [0, -1])
|
|> line(end = [0, -1])
|
||||||
|> close()
|
|> close()
|
||||||
|> extrude(length = 1)
|
|> extrude(length = 1)
|
||||||
|> patternLinear3d(
|
|> patternLinear3d({
|
||||||
axis = [1, 0, 1],
|
axis: [1, 0, 1],
|
||||||
repetitions = 3,
|
repetitions: 3,
|
||||||
distance = 6,
|
distance: 6
|
||||||
)`
|
}, %)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
@ -253,7 +253,7 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
|>
|
|>
|
||||||
|
|
||||||
example = extrude(exampleSketch, length = 5)
|
example = extrude(exampleSketch, length = 5)
|
||||||
shell(exampleSketch, faces = ['end'], thickness = 0.25)`
|
shell({ faces: ['end'], thickness: 0.25 }, exampleSketch)`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -313,10 +313,10 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test('when engine fails export we handle the failure and alert the user', async ({
|
||||||
'when engine fails export we handle the failure and alert the user',
|
page,
|
||||||
{ tag: '@skipLocalEngine' },
|
homePage,
|
||||||
async ({ scene, page, homePage }) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(
|
await page.addInitScript(
|
||||||
async ({ code }) => {
|
async ({ code }) => {
|
||||||
@ -383,7 +383,10 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
await page.keyboard.press('End')
|
await page.keyboard.press('End')
|
||||||
await page.keyboard.press('Enter')
|
await page.keyboard.press('Enter')
|
||||||
|
|
||||||
await scene.waitForExecutionDone()
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// Now try exporting
|
// Now try exporting
|
||||||
|
|
||||||
@ -411,8 +414,7 @@ extrude001 = extrude(sketch001, length = 50)
|
|||||||
|
|
||||||
const successToastMessage = page.getByText(`Exported successfully`)
|
const successToastMessage = page.getByText(`Exported successfully`)
|
||||||
await expect(successToastMessage).toBeVisible()
|
await expect(successToastMessage).toBeVisible()
|
||||||
}
|
})
|
||||||
)
|
|
||||||
test(
|
test(
|
||||||
'ensure you can not export while an export is already going',
|
'ensure you can not export while an export is already going',
|
||||||
{ tag: ['@skipLinux', '@skipWin'] },
|
{ tag: ['@skipLinux', '@skipWin'] },
|
||||||
|
@ -455,9 +455,7 @@ test(
|
|||||||
mask: [page.getByTestId('model-state-indicator')],
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
|
|
||||||
const lineEndClick = () =>
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
|
||||||
await lineEndClick()
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -468,11 +466,6 @@ test(
|
|||||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
// click on the end of the profile to continue it
|
|
||||||
await page.waitForTimeout(300)
|
|
||||||
await lineEndClick()
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// click to continue profile
|
// click to continue profile
|
||||||
await page.mouse.move(813, 392, { steps: 10 })
|
await page.mouse.move(813, 392, { steps: 10 })
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
@ -1202,12 +1195,14 @@ sweepSketch = startSketchOn('XY')
|
|||||||
angleStart = 0,
|
angleStart = 0,
|
||||||
radius = 2
|
radius = 2
|
||||||
}, %)
|
}, %)
|
||||||
|> sweep(path = sweepPath)
|
|> sweep({
|
||||||
|> appearance(
|
path = sweepPath,
|
||||||
|
}, %)
|
||||||
|
|> appearance({
|
||||||
color = "#bb00ff",
|
color = "#bb00ff",
|
||||||
metalness = 90,
|
metalness = 90,
|
||||||
roughness = 90
|
roughness = 90
|
||||||
)
|
}, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1248,12 +1243,14 @@ sweepSketch = startSketchOn('XY')
|
|||||||
angleStart = 0,
|
angleStart = 0,
|
||||||
radius = 2
|
radius = 2
|
||||||
}, %)
|
}, %)
|
||||||
|> sweep(path = sweepPath)
|
|> sweep({
|
||||||
|> appearance(
|
path = sweepPath,
|
||||||
|
}, %)
|
||||||
|
|> appearance({
|
||||||
color = "#bb00ff",
|
color = "#bb00ff",
|
||||||
metalness = 90,
|
metalness = 90,
|
||||||
roughness = 90
|
roughness = 90
|
||||||
)
|
}, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 51 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 66 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 145 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 129 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 53 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
@ -4,10 +4,10 @@ import { EngineCommand } from 'lang/std/artifactGraph'
|
|||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
|
||||||
test.describe('Test network and connection issues', () => {
|
test.describe('Test network and connection issues', () => {
|
||||||
test(
|
test('simulate network down and network little widget', async ({
|
||||||
'simulate network down and network little widget',
|
page,
|
||||||
{ tag: '@skipLocalEngine' },
|
homePage,
|
||||||
async ({ page, homePage }) => {
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
@ -77,13 +77,12 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
// (Second check) expect the network to be up
|
// (Second check) expect the network to be up
|
||||||
await expect(networkToggle).toContainText('Connected')
|
await expect(networkToggle).toContainText('Connected')
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
test('Engine disconnect & reconnect in sketch mode', async ({
|
||||||
'Engine disconnect & reconnect in sketch mode',
|
page,
|
||||||
{ tag: '@skipLocalEngine' },
|
homePage,
|
||||||
async ({ page, homePage }) => {
|
}) => {
|
||||||
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
// TODO: Don't skip Mac for these. After `window.tearDown` is working in Safari, these should work on webkit
|
||||||
const networkToggle = page.getByTestId('network-toggle')
|
const networkToggle = page.getByTestId('network-toggle')
|
||||||
|
|
||||||
@ -246,6 +245,5 @@ profile001 = startProfileAt([12.34, -12.34], sketch001)
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Exit Sketch' })
|
page.getByRole('button', { name: 'Exit Sketch' })
|
||||||
).not.toBeVisible()
|
).not.toBeVisible()
|
||||||
}
|
})
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
@ -109,8 +109,7 @@ test.describe('Testing Camera Movement', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.keyboard.down('Shift')
|
await page.keyboard.down('Shift')
|
||||||
await page.mouse.move(600, 200)
|
await page.mouse.move(600, 200)
|
||||||
await page.mouse.down({ button: 'right' })
|
await page.mouse.down({ button: 'right' })
|
||||||
// Gotcha: remove steps:2 from this 700,200 mouse move. This bricked the test on local host engine.
|
await page.mouse.move(700, 200, { steps: 2 })
|
||||||
await page.mouse.move(700, 200)
|
|
||||||
await page.mouse.up({ button: 'right' })
|
await page.mouse.up({ button: 'right' })
|
||||||
await page.keyboard.up('Shift')
|
await page.keyboard.up('Shift')
|
||||||
}, [-19, -85, -85])
|
}, [-19, -85, -85])
|
||||||
|
@ -708,8 +708,9 @@ part002 = startSketchOn('XZ')
|
|||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await u.waitForPageLoad()
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
await editor.scrollToText('line(end = [74.36, 130.4])', true)
|
await editor.scrollToText('line(end = [74.36, 130.4], %)', true)
|
||||||
await page.getByText('line(end = [74.36, 130.4])').click()
|
await page.getByText('line(end = [74.36, 130.4], %)').click()
|
||||||
|
await page.screenshot({ path: 'ok.png' })
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
const line3 = await u.getSegmentBodyCoords(
|
const line3 = await u.getSegmentBodyCoords(
|
||||||
|
@ -63,6 +63,10 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
await page.waitForTimeout(700) // wait for animation
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
|
// select a plane
|
||||||
|
await page.mouse.click(700, 200)
|
||||||
|
await page.waitForTimeout(700) // wait for animation
|
||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
@ -249,11 +253,7 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Solids should be select and deletable', async ({
|
test('Solids should be select and deletable', async ({ page, homePage }) => {
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
scene,
|
|
||||||
}) => {
|
|
||||||
test.setTimeout(90_000)
|
test.setTimeout(90_000)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -261,63 +261,63 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-79.26, 95.04], %)
|
|> startProfileAt([-79.26, 95.04], %)
|
||||||
|> line(end = [112.54, 127.64], tag = $seg02)
|
|> line(end=[112.54, 127.64], %, $seg02)
|
||||||
|> line(end = [170.36, -121.61], tag = $seg01)
|
|> line(end=[170.36, -121.61], %, $seg01)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
extrude001 = extrude(sketch001, length = 50)
|
extrude001 = extrude(50, sketch001)
|
||||||
sketch005 = startSketchOn(extrude001, 'END')
|
sketch005 = startSketchOn(extrude001, 'END')
|
||||||
|> startProfileAt([23.24, 136.52], %)
|
|> startProfileAt([23.24, 136.52], %)
|
||||||
|> line(end = [-8.44, 36.61])
|
|> line(end=[-8.44, 36.61], %)
|
||||||
|> line(end = [49.4, 2.05])
|
|> line(end=[49.4, 2.05], %)
|
||||||
|> line(end = [29.69, -46.95])
|
|> line(end=[29.69, -46.95], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
sketch003 = startSketchOn(extrude001, seg01)
|
sketch003 = startSketchOn(extrude001, seg01)
|
||||||
|> startProfileAt([21.23, 17.81], %)
|
|> startProfileAt([21.23, 17.81], %)
|
||||||
|> line(end = [51.97, 21.32])
|
|> line(end=[51.97, 21.32], %)
|
||||||
|> line(end = [4.07, -22.75])
|
|> line(end=[4.07, -22.75], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
sketch002 = startSketchOn(extrude001, seg02)
|
sketch002 = startSketchOn(extrude001, seg02)
|
||||||
|> startProfileAt([-100.54, 16.99], %)
|
|> startProfileAt([-100.54, 16.99], %)
|
||||||
|> line(end = [0, 20.03])
|
|> line(end=[0, 20.03], %)
|
||||||
|> line(end = [62.61, 0], tag = $seg03)
|
|> line(end=[62.61, 0], %, $seg03)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
extrude002 = extrude(sketch002, length = 50)
|
extrude002 = extrude(50, sketch002)
|
||||||
sketch004 = startSketchOn(extrude002, seg03)
|
sketch004 = startSketchOn(extrude002, seg03)
|
||||||
|> startProfileAt([57.07, 134.77], %)
|
|> startProfileAt([57.07, 134.77], %)
|
||||||
|> line(end = [-4.72, 22.84])
|
|> line(end=[-4.72, 22.84], %)
|
||||||
|> line(end = [28.8, 6.71])
|
|> line(end=[28.8, 6.71], %)
|
||||||
|> line(end = [9.19, -25.33])
|
|> line(end=[9.19, -25.33], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
extrude003 = extrude(sketch004, length = 20)
|
extrude003 = extrude(20, sketch004)
|
||||||
pipeLength = 40
|
pipeLength = 40
|
||||||
pipeSmallDia = 10
|
pipeSmallDia = 10
|
||||||
pipeLargeDia = 20
|
pipeLargeDia = 20
|
||||||
thickness = 0.5
|
thickness = 0.5
|
||||||
part009 = startSketchOn('XY')
|
part009 = startSketchOn('XY')
|
||||||
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
||||||
|> line(end = [thickness, 0])
|
|> line(end=[thickness, 0], %)
|
||||||
|> line(end = [0, -1])
|
|> line(end=[0, -1], %)
|
||||||
|> angledLineToX({
|
|> angledLineToX({
|
||||||
angle = 60,
|
angle = 60,
|
||||||
to = pipeSmallDia + thickness
|
to = pipeSmallDia + thickness
|
||||||
}, %)
|
}, %)
|
||||||
|> line(end = [0, -pipeLength])
|
|> line(end=[0, -pipeLength], %)
|
||||||
|> angledLineToX({
|
|> angledLineToX({
|
||||||
angle = -60,
|
angle = -60,
|
||||||
to = pipeLargeDia + thickness
|
to = pipeLargeDia + thickness
|
||||||
}, %)
|
}, %)
|
||||||
|> line(end = [0, -1])
|
|> line(end=[0, -1], %)
|
||||||
|> line(end = [-thickness, 0])
|
|> line(end=[-thickness, 0], %)
|
||||||
|> line(end = [0, 1])
|
|> line(end=[0, 1], %)
|
||||||
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
||||||
|> line(end = [0, pipeLength])
|
|> line(end=[0, pipeLength], %)
|
||||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||||
|> close()
|
|> close(%)
|
||||||
rev = revolve({ axis = 'y' }, part009)
|
rev = revolve({ axis = 'y' }, part009)
|
||||||
sketch006 = startSketchOn('XY')
|
sketch006 = startSketchOn('XY')
|
||||||
profile001 = circle({
|
profile001 = circle({
|
||||||
@ -334,12 +334,12 @@ profile002 = startProfileAt([86.92, -63.81], sketch006)
|
|||||||
segAng(rectangleSegmentA001),
|
segAng(rectangleSegmentA001),
|
||||||
-segLen(rectangleSegmentA001)
|
-segLen(rectangleSegmentA001)
|
||||||
], %)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
profile003 = startProfileAt([40.16, -120.48], sketch006)
|
profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||||
|> line(end = [26.95, 24.21])
|
|> line(end=[26.95, 24.21], %)
|
||||||
|> line(end = [20.91, -28.61])
|
|> line(end=[20.91, -28.61], %)
|
||||||
|> line(end = [32.46, 18.71])
|
|> line(end=[32.46, 18.71], %)
|
||||||
|
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
@ -347,7 +347,10 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
@ -390,47 +393,43 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
`rev = revolve({ axis: 'y' }, part009)`
|
`rev = revolve({ axis: 'y' }, part009)`
|
||||||
)
|
)
|
||||||
|
|
||||||
// FIXME (commented section below), this test would select a wall that had a sketch on it, and delete the underlying extrude
|
// DELETE PARENT EXTRUDE
|
||||||
// and replace the sketch on face with a hard coded custom plane, but since there was a sketch on that plane maybe it
|
await page.mouse.click(parentExtrude.x, parentExtrude.y)
|
||||||
// should have delete the sketch? it's broken atm, but not sure if worth fixing since desired behaviour is a little
|
await page.waitForTimeout(100)
|
||||||
// vague
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
// // DELETE PARENT EXTRUDE
|
'|> line(end = [170.36, -121.61], tag = $seg01)'
|
||||||
// await page.mouse.click(parentExtrude.x, parentExtrude.y)
|
)
|
||||||
// await page.waitForTimeout(100)
|
await u.clearCommandLogs()
|
||||||
// await expect(page.locator('.cm-activeLine')).toHaveText(
|
await page.keyboard.press('Backspace')
|
||||||
// '|> line(end = [170.36, -121.61], tag = $seg01)'
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||||
// )
|
await page.waitForTimeout(200)
|
||||||
// await u.clearCommandLogs()
|
await expect(u.codeLocator).not.toContainText(
|
||||||
// await page.keyboard.press('Backspace')
|
`extrude001 = extrude(sketch001, length = 50)`
|
||||||
// await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
)
|
||||||
// await page.waitForTimeout(200)
|
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
||||||
// await expect(u.codeLocator).not.toContainText(
|
plane = {
|
||||||
// `extrude001 = extrude(sketch001, length = 50)`
|
origin = { x = 0, y = -50, z = 0 },
|
||||||
// )
|
xAxis = { x = 1, y = 0, z = 0 },
|
||||||
// await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
yAxis = { x = 0, y = 0, z = 1 },
|
||||||
// plane = {
|
zAxis = { x = 0, y = -1, z = 0 }
|
||||||
// origin = { x = 0, y = -50, z = 0 },
|
}
|
||||||
// xAxis = { x = 1, y = 0, z = 0 },
|
})`)
|
||||||
// yAxis = { x = 0, y = 0, z = 1 },
|
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
||||||
// zAxis = { x = 0, y = -1, z = 0 }
|
plane = {
|
||||||
// }
|
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||||
// })`)
|
xAxis = { x = -0.81, y = 0, z = 0.58 },
|
||||||
// await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
// plane = {
|
zAxis = { x = 0.58, y = 0, z = 0.81 }
|
||||||
// origin = { x = 116.53, y = 0, z = 163.25 },
|
}
|
||||||
// xAxis = { x = -0.81, y = 0, z = 0.58 },
|
})`)
|
||||||
// yAxis = { x = 0, y = -1, z = 0 },
|
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
||||||
// zAxis = { x = 0.58, y = 0, z = 0.81 }
|
plane = {
|
||||||
// }
|
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||||
// })`)
|
xAxis = { x = -0.66, y = 0, z = -0.75 },
|
||||||
// await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
// plane = {
|
zAxis = { x = -0.75, y = 0, z = 0.66 }
|
||||||
// origin = { x = -91.74, y = 0, z = 80.89 },
|
}
|
||||||
// xAxis = { x = -0.66, y = 0, z = -0.75 },
|
})`)
|
||||||
// yAxis = { x = 0, y = -1, z = 0 },
|
|
||||||
// zAxis = { x = -0.75, y = 0, z = 0.66 }
|
|
||||||
// }
|
|
||||||
// })`)
|
|
||||||
|
|
||||||
// DELETE SOLID 2D
|
// DELETE SOLID 2D
|
||||||
await page.mouse.click(solid2d.x, solid2d.y)
|
await page.mouse.click(solid2d.x, solid2d.y)
|
||||||
@ -450,7 +449,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
const codeToBeDeletedSnippet =
|
const codeToBeDeletedSnippet =
|
||||||
'profile003 = startProfileAt([40.16, -120.48], sketch006)'
|
'profile003 = startProfileAt([40.16, -120.48], sketch006)'
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
' |> line(end = [20.91, -28.61])'
|
' |> line([20.91, -28.61], %)'
|
||||||
)
|
)
|
||||||
await u.clearCommandLogs()
|
await u.clearCommandLogs()
|
||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
@ -458,9 +457,10 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
|
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
|
||||||
})
|
})
|
||||||
test.fixme(
|
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
||||||
"Deleting solid that the AST mod can't handle results in a toast message",
|
page,
|
||||||
async ({ page, homePage }) => {
|
homePage,
|
||||||
|
}) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -521,8 +521,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.keyboard.press('Backspace')
|
await page.keyboard.press('Backspace')
|
||||||
|
|
||||||
await expect(page.getByText('Unable to delete selection')).toBeVisible()
|
await expect(page.getByText('Unable to delete selection')).toBeVisible()
|
||||||
}
|
})
|
||||||
)
|
|
||||||
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
|
test('Hovering over 3d features highlights code, clicking puts the cursor in the right place and sends selection id to engine', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
@ -945,7 +944,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
|
test('Testing selections (and hovers) work on sketches when NOT in sketch mode', async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
scene,
|
|
||||||
}) => {
|
}) => {
|
||||||
const cases = [
|
const cases = [
|
||||||
{
|
{
|
||||||
@ -981,7 +979,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
@ -1015,7 +1012,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
|
test("Hovering and selection of extruded faces works, and is not overridden shortly after user's click", async ({
|
||||||
page,
|
page,
|
||||||
homePage,
|
homePage,
|
||||||
scene,
|
|
||||||
}) => {
|
}) => {
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
@ -1034,7 +1030,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
|
|
||||||
await u.sendCustomCmd({
|
await u.sendCustomCmd({
|
||||||
@ -1068,19 +1063,19 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
.poll(() => u.getGreatestPixDiff(extrudeWall, noHoverColor))
|
.poll(() => u.getGreatestPixDiff(extrudeWall, noHoverColor))
|
||||||
.toBeLessThan(15)
|
.toBeLessThan(15)
|
||||||
await page.mouse.move(nothing.x, nothing.y)
|
await page.mouse.move(nothing.x, nothing.y)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.move(extrudeWall.x, extrudeWall.y)
|
await page.mouse.move(extrudeWall.x, extrudeWall.y)
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
|
await expect(page.getByTestId('hover-highlight').first()).toBeVisible()
|
||||||
await expect(page.getByTestId('hover-highlight').first()).toContainText(
|
await expect(page.getByTestId('hover-highlight').first()).toContainText(
|
||||||
removeAfterFirstParenthesis(extrudeText)
|
removeAfterFirstParenthesis(extrudeText)
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(200)
|
||||||
await expect(
|
await expect(
|
||||||
await u.getGreatestPixDiff(extrudeWall, hoverColor)
|
await u.getGreatestPixDiff(extrudeWall, hoverColor)
|
||||||
).toBeLessThan(15)
|
).toBeLessThan(15)
|
||||||
await page.mouse.click(extrudeWall.x, extrudeWall.y)
|
await page.mouse.click(extrudeWall.x, extrudeWall.y)
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${extrudeText}`)
|
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${extrudeText}`)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(200)
|
||||||
await expect(
|
await expect(
|
||||||
await u.getGreatestPixDiff(extrudeWall, selectColor)
|
await u.getGreatestPixDiff(extrudeWall, selectColor)
|
||||||
).toBeLessThan(15)
|
).toBeLessThan(15)
|
||||||
@ -1091,7 +1086,7 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
).toBeLessThan(15)
|
).toBeLessThan(15)
|
||||||
|
|
||||||
await page.mouse.move(nothing.x, nothing.y)
|
await page.mouse.move(nothing.x, nothing.y)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(300)
|
||||||
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
await expect(page.getByTestId('hover-highlight')).not.toBeVisible()
|
||||||
|
|
||||||
// because of shading, color is not exact everywhere on the face
|
// because of shading, color is not exact everywhere on the face
|
||||||
@ -1105,11 +1100,11 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await expect(page.getByTestId('hover-highlight').first()).toContainText(
|
await expect(page.getByTestId('hover-highlight').first()).toContainText(
|
||||||
removeAfterFirstParenthesis(capText)
|
removeAfterFirstParenthesis(capText)
|
||||||
)
|
)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(200)
|
||||||
await expect(await u.getGreatestPixDiff(cap, hoverColor)).toBeLessThan(15)
|
await expect(await u.getGreatestPixDiff(cap, hoverColor)).toBeLessThan(15)
|
||||||
await page.mouse.click(cap.x, cap.y)
|
await page.mouse.click(cap.x, cap.y)
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${capText}`)
|
await expect(page.locator('.cm-activeLine')).toHaveText(`|> ${capText}`)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(200)
|
||||||
await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15)
|
await expect(await u.getGreatestPixDiff(cap, selectColor)).toBeLessThan(15)
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
// check color stays there, i.e. not overridden (this was a bug previously)
|
// check color stays there, i.e. not overridden (this was a bug previously)
|
||||||
|
@ -896,53 +896,4 @@ test.describe('Testing settings', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(`Change inline units setting`, async ({
|
|
||||||
page,
|
|
||||||
homePage,
|
|
||||||
context,
|
|
||||||
editor,
|
|
||||||
}) => {
|
|
||||||
const initialInlineUnits = 'yd'
|
|
||||||
const editedInlineUnits = { short: 'mm', long: 'Millimeters' }
|
|
||||||
const inlineSettingsString = (s: string) =>
|
|
||||||
`@settings(defaultLengthUnit = ${s})`
|
|
||||||
const unitsIndicator = page.getByRole('button', {
|
|
||||||
name: 'Current units are:',
|
|
||||||
})
|
|
||||||
const unitsChangeButton = (name: string) =>
|
|
||||||
page.getByRole('button', { name, exact: true })
|
|
||||||
|
|
||||||
await context.folderSetupFn(async (dir) => {
|
|
||||||
const bracketDir = join(dir, 'project-000')
|
|
||||||
await fsp.mkdir(bracketDir, { recursive: true })
|
|
||||||
await fsp.copyFile(
|
|
||||||
executorInputPath('cube.kcl'),
|
|
||||||
join(bracketDir, 'main.kcl')
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Initial units from settings`, async () => {
|
|
||||||
await homePage.openProject('project-000')
|
|
||||||
await expect(unitsIndicator).toHaveText('Current units are: in')
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Manually write inline settings`, async () => {
|
|
||||||
await editor.openPane()
|
|
||||||
await editor.replaceCode(
|
|
||||||
`fn cube`,
|
|
||||||
`${inlineSettingsString(initialInlineUnits)}
|
|
||||||
fn cube`
|
|
||||||
)
|
|
||||||
await expect(unitsIndicator).toContainText(initialInlineUnits)
|
|
||||||
})
|
|
||||||
|
|
||||||
await test.step(`Change units setting via lower-right control`, async () => {
|
|
||||||
await unitsIndicator.click()
|
|
||||||
await unitsChangeButton(editedInlineUnits.long).click()
|
|
||||||
await expect(
|
|
||||||
page.getByText(`Updated per-file units to ${editedInlineUnits.short}`)
|
|
||||||
).toBeVisible()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
@ -3,7 +3,7 @@ import { getUtils, createProject } from './test-utils'
|
|||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import fs from 'fs'
|
import fs from 'fs'
|
||||||
|
|
||||||
test.describe('Text-to-CAD tests', { tag: ['@skipWin'] }, () => {
|
test.describe('Text-to-CAD tests', () => {
|
||||||
test('basic lego happy case', async ({ page, homePage }) => {
|
test('basic lego happy case', async ({ page, homePage }) => {
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
@ -32,10 +32,7 @@ test.fixme('Units menu', async ({ page, homePage }) => {
|
|||||||
await expect(unitsMenuButton).toContainText('mm')
|
await expect(unitsMenuButton).toContainText('mm')
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test('Successful export shows a success toast', async ({ page, homePage }) => {
|
||||||
'Successful export shows a success toast',
|
|
||||||
{ tag: '@skipLocalEngine' },
|
|
||||||
async ({ page, homePage }) => {
|
|
||||||
// FYI this test doesn't work with only engine running locally
|
// FYI this test doesn't work with only engine running locally
|
||||||
// And you will need to have the KittyCAD CLI installed
|
// And you will need to have the KittyCAD CLI installed
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
@ -100,8 +97,7 @@ part001 = startSketchOn('-XZ')
|
|||||||
},
|
},
|
||||||
page
|
page
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
)
|
|
||||||
|
|
||||||
test('Paste should not work unless an input is focused', async ({
|
test('Paste should not work unless an input is focused', async ({
|
||||||
page,
|
page,
|
||||||
@ -235,12 +231,8 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
await page.mouse.click(1000, 100)
|
await page.mouse.click(1000, 100)
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
||||||
await expect
|
|
||||||
.poll(async () => {
|
|
||||||
await page.keyboard.press('l')
|
await page.keyboard.press('l')
|
||||||
return lineButton.getAttribute('aria-pressed')
|
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
})
|
|
||||||
.toBe('true')
|
|
||||||
|
|
||||||
// Do not close the sketch.
|
// Do not close the sketch.
|
||||||
// On close it will exit sketch mode.
|
// On close it will exit sketch mode.
|
||||||
@ -465,7 +457,7 @@ test('Delete key does not navigate back', async ({ page, homePage }) => {
|
|||||||
await expect.poll(() => page.url()).not.toContain('/settings')
|
await expect.poll(() => page.url()).not.toContain('/settings')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('Sketch on face', async ({ page, homePage, scene, cmdBar }) => {
|
test('Sketch on face', async ({ page, homePage }) => {
|
||||||
test.setTimeout(90_000)
|
test.setTimeout(90_000)
|
||||||
const u = await getUtils(page)
|
const u = await getUtils(page)
|
||||||
await page.addInitScript(async () => {
|
await page.addInitScript(async () => {
|
||||||
@ -491,7 +483,11 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
await scene.waitForExecutionDone()
|
|
||||||
|
// wait for execution done
|
||||||
|
await u.openDebugPanel()
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]')
|
||||||
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByRole('button', { name: 'Start Sketch' })
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
@ -536,11 +532,11 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
|
|||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode).toContain(
|
await expect.poll(u.normalisedEditorCode).toContain(
|
||||||
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
||||||
profile001 = startProfileAt([-12.34, 12.34], sketch002)
|
profile001 = startProfileAt([-12.88, 6.66], sketch002)
|
||||||
|> line(end = [12.34, -12.34])
|
|> line(end = [2.71, -0.22], %)
|
||||||
|> line(end = [-12.34, -12.34])
|
|> line(end = [-2.87, -1.38], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> lineTo(endAbsolute = [profileStartX(%), profileStartY(%)], %)
|
||||||
|> close()
|
|> close(%)
|
||||||
`)
|
`)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -595,9 +591,10 @@ profile001 = startProfileAt([-12.34, 12.34], sketch002)
|
|||||||
await expect(page.getByTestId('command-bar')).toBeVisible()
|
await expect(page.getByTestId('command-bar')).toBeVisible()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await cmdBar.progressCmdBar()
|
await page.getByRole('button', { name: 'arrow right Continue' }).click()
|
||||||
|
await page.waitForTimeout(100)
|
||||||
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
await expect(page.getByText('Confirm Extrude')).toBeVisible()
|
||||||
await cmdBar.progressCmdBar()
|
await page.getByRole('button', { name: 'checkmark Submit command' }).click()
|
||||||
|
|
||||||
const result2 = result.genNext`
|
const result2 = result.genNext`
|
||||||
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
const sketch002 = extrude(sketch002, length = ${[5, 5]} + 7)`
|
||||||
|
@ -16,7 +16,8 @@ mac:
|
|||||||
arch:
|
arch:
|
||||||
- x64
|
- x64
|
||||||
- arm64
|
- arm64
|
||||||
notarize: true
|
notarize:
|
||||||
|
teamId: 92H8YB3B95
|
||||||
fileAssociations:
|
fileAssociations:
|
||||||
- ext: kcl
|
- ext: kcl
|
||||||
name: kcl
|
name: kcl
|
||||||
@ -31,10 +32,13 @@ win:
|
|||||||
arch:
|
arch:
|
||||||
- x64
|
- x64
|
||||||
- arm64
|
- arm64
|
||||||
signtoolOptions:
|
# - target: msi
|
||||||
sign: "./scripts/sign-win.js"
|
# arch:
|
||||||
|
# - x64
|
||||||
|
# - arm64
|
||||||
signingHashAlgorithms:
|
signingHashAlgorithms:
|
||||||
- sha256
|
- sha256
|
||||||
|
sign: "./scripts/sign-win.js"
|
||||||
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert
|
publisherName: "KittyCAD Inc" # needs to be exactly like on Digicert
|
||||||
icon: "assets/icon.ico"
|
icon: "assets/icon.ico"
|
||||||
fileAssociations:
|
fileAssociations:
|
||||||
@ -43,12 +47,15 @@ win:
|
|||||||
mimeType: text/vnd.zoo.kcl
|
mimeType: text/vnd.zoo.kcl
|
||||||
description: Zoo KCL File
|
description: Zoo KCL File
|
||||||
role: Editor
|
role: Editor
|
||||||
|
# msi:
|
||||||
|
# oneClick: false
|
||||||
|
# perMachine: true
|
||||||
nsis:
|
nsis:
|
||||||
oneClick: false
|
oneClick: false
|
||||||
perMachine: true
|
perMachine: true
|
||||||
allowElevation: true
|
allowElevation: true
|
||||||
installerIcon: "assets/icon.ico"
|
installerIcon: "assets/icon.ico"
|
||||||
include: "./scripts/installer.nsh"
|
include: "./installer.nsh"
|
||||||
linux:
|
linux:
|
||||||
artifactName: "${productName}-${version}-${arch}-${os}.${ext}"
|
artifactName: "${productName}-${version}-${arch}-${os}.${ext}"
|
||||||
target:
|
target:
|
||||||
|
27
package.json
@ -26,7 +26,7 @@
|
|||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.19",
|
"@headlessui/react": "^1.7.19",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "2.0.17",
|
"@kittycad/lib": "2.0.13",
|
||||||
"@lezer/highlight": "^1.2.1",
|
"@lezer/highlight": "^1.2.1",
|
||||||
"@lezer/lr": "^1.4.1",
|
"@lezer/lr": "^1.4.1",
|
||||||
"@react-hook/resize-observer": "^2.0.1",
|
"@react-hook/resize-observer": "^2.0.1",
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"codemirror": "^6.0.1",
|
"codemirror": "^6.0.1",
|
||||||
"decamelize": "^6.0.0",
|
"decamelize": "^6.0.0",
|
||||||
"diff": "^7.0.0",
|
"diff": "^7.0.0",
|
||||||
"electron-updater": "^6.5.0",
|
"electron-updater": "6.3.0",
|
||||||
"fuse.js": "^7.0.0",
|
"fuse.js": "^7.0.0",
|
||||||
"html2canvas-pro": "^1.5.8",
|
"html2canvas-pro": "^1.5.8",
|
||||||
"isomorphic-fetch": "^3.0.0",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
@ -85,7 +85,7 @@
|
|||||||
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt": "prettier --write ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
"fmt-check": "prettier --check ./src *.ts *.json *.js ./e2e ./packages",
|
||||||
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
"fetch:wasm": "./get-latest-wasm-bundle.sh",
|
||||||
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/achalmers/offset-plane-kwargs/manifest.json",
|
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/main/manifest.json",
|
||||||
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
"isomorphic-copy-wasm": "(copy src/wasm-lib/pkg/wasm_lib_bg.wasm public || cp src/wasm-lib/pkg/wasm_lib_bg.wasm public)",
|
||||||
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
"build:wasm-dev": "yarn wasm-prep && (cd src/wasm-lib && wasm-pack build --dev --target web --out-dir pkg && cargo test -p kcl-lib export_bindings) && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
@ -120,7 +120,6 @@
|
|||||||
"test:playwright:electron:windows:local": "yarn tronb:vite:dev && set NODE_ENV='development' && playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
"test:playwright:electron:windows:local": "yarn tronb:vite:dev && set NODE_ENV='development' && playwright test --config=playwright.electron.config.ts --grep-invert=\"@skipWin|@snapshot\"",
|
||||||
"test:playwright:electron:macos:local": "yarn tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
"test:playwright:electron:macos:local": "yarn tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipMacos|@snapshot'",
|
||||||
"test:playwright:electron:ubuntu:local": "yarn tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
"test:playwright:electron:ubuntu:local": "yarn tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot'",
|
||||||
"test:playwright:electron:ubuntu:engine:local": "yarn tronb:vite:dev && NODE_ENV=development playwright test --config=playwright.electron.config.ts --grep-invert='@skipLinux|@snapshot|@skipLocalEngine'",
|
|
||||||
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
"test:unit:local": "yarn simpleserver:bg && yarn test:unit; kill-port 3000",
|
||||||
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
"test:unit:kcl-samples:local": "yarn simpleserver:bg && yarn test:unit:kcl-samples; kill-port 3000"
|
||||||
},
|
},
|
||||||
@ -145,11 +144,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
"@babel/plugin-proposal-private-property-in-object": "^7.21.11",
|
||||||
"@babel/preset-env": "^7.25.4",
|
"@babel/preset-env": "^7.25.4",
|
||||||
"@electron-forge/cli": "^7.6.1",
|
"@electron-forge/cli": "7.4.0",
|
||||||
"@electron-forge/plugin-fuses": "^7.6.1",
|
"@electron-forge/plugin-fuses": "7.4.0",
|
||||||
"@electron-forge/plugin-vite": "^7.6.1",
|
"@electron-forge/plugin-vite": "7.4.0",
|
||||||
"@electron/fuses": "^1.8.0",
|
"@electron/fuses": "1.8.0",
|
||||||
"@electron/notarize": "^2.5.0",
|
|
||||||
"@iarna/toml": "^2.2.5",
|
"@iarna/toml": "^2.2.5",
|
||||||
"@lezer/generator": "^1.7.2",
|
"@lezer/generator": "^1.7.2",
|
||||||
"@nabla/vite-plugin-eslint": "^2.0.5",
|
"@nabla/vite-plugin-eslint": "^2.0.5",
|
||||||
@ -160,8 +158,8 @@
|
|||||||
"@types/electron": "^1.6.10",
|
"@types/electron": "^1.6.10",
|
||||||
"@types/isomorphic-fetch": "^0.0.39",
|
"@types/isomorphic-fetch": "^0.0.39",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
"@types/mocha": "^10.0.10",
|
"@types/mocha": "^10.0.6",
|
||||||
"@types/node": "^22.13.1",
|
"@types/node": "^22.7.8",
|
||||||
"@types/pixelmatch": "^5.2.6",
|
"@types/pixelmatch": "^5.2.6",
|
||||||
"@types/pngjs": "^6.0.4",
|
"@types/pngjs": "^6.0.4",
|
||||||
"@types/react": "^18.3.4",
|
"@types/react": "^18.3.4",
|
||||||
@ -176,8 +174,9 @@
|
|||||||
"@vitest/web-worker": "^1.5.0",
|
"@vitest/web-worker": "^1.5.0",
|
||||||
"@xstate/cli": "^0.5.17",
|
"@xstate/cli": "^0.5.17",
|
||||||
"autoprefixer": "^10.4.19",
|
"autoprefixer": "^10.4.19",
|
||||||
"electron": "^34.1.1",
|
"electron": "32.1.2",
|
||||||
"electron-builder": "^26.0.6",
|
"electron-builder": "24.13.3",
|
||||||
|
"electron-notarize": "1.2.2",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
@ -201,7 +200,7 @@
|
|||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.7.3",
|
"typescript": "^5.7.3",
|
||||||
"typescript-eslint": "^8.23.0",
|
"typescript-eslint": "^8.19.1",
|
||||||
"vite": "^5.4.12",
|
"vite": "^5.4.12",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
@precedence {
|
@precedence {
|
||||||
annotation
|
|
||||||
member
|
member
|
||||||
call
|
call
|
||||||
exp @left
|
exp @left
|
||||||
@ -21,12 +20,9 @@ statement[@isGroup=Statement] {
|
|||||||
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } |
|
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals? ParamList Arrow? Body } |
|
||||||
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
||||||
ReturnStatement { kw<"return"> expression } |
|
ReturnStatement { kw<"return"> expression } |
|
||||||
ExpressionStatement { expression } |
|
ExpressionStatement { expression }
|
||||||
Annotation { AnnotationName AnnotationList? }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AnnotationList { !annotation "(" commaSep<AnnotationProperty> ")" }
|
|
||||||
|
|
||||||
ParamList { "(" commaSep<Parameter { VariableDefinition "?"? (":" type)? }> ")" }
|
ParamList { "(" commaSep<Parameter { VariableDefinition "?"? (":" type)? }> ")" }
|
||||||
|
|
||||||
Body { "{" statement* "}" }
|
Body { "{" statement* "}" }
|
||||||
@ -63,15 +59,7 @@ UnaryOp { AddOp | BangOp }
|
|||||||
|
|
||||||
ObjectProperty { PropertyName (":" | Equals) expression }
|
ObjectProperty { PropertyName (":" | Equals) expression }
|
||||||
|
|
||||||
AnnotationProperty {
|
ArgumentList { "(" commaSep<expression> ")" }
|
||||||
PropertyName
|
|
||||||
( AddOp | MultOp | ExpOp | LogicOp | BangOp | CompOp | Equals | Arrow | PipeOperator | PipeSubstitution )
|
|
||||||
expression
|
|
||||||
}
|
|
||||||
|
|
||||||
LabeledArgument { ArgumentLabel Equals expression }
|
|
||||||
|
|
||||||
ArgumentList { "(" commaSep<LabeledArgument | expression> ")" }
|
|
||||||
|
|
||||||
type[@isGroup=Type] {
|
type[@isGroup=Type] {
|
||||||
@specialize[@name=PrimitiveType]<
|
@specialize[@name=PrimitiveType]<
|
||||||
@ -86,8 +74,6 @@ VariableDefinition { identifier }
|
|||||||
|
|
||||||
VariableName { identifier }
|
VariableName { identifier }
|
||||||
|
|
||||||
ArgumentLabel { identifier }
|
|
||||||
|
|
||||||
@skip { whitespace | LineComment | BlockComment }
|
@skip { whitespace | LineComment | BlockComment }
|
||||||
|
|
||||||
kw<term> { @specialize[@name={term}]<identifier, term> }
|
kw<term> { @specialize[@name={term}]<identifier, term> }
|
||||||
@ -115,7 +101,6 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
|||||||
PipeSubstitution { "%" }
|
PipeSubstitution { "%" }
|
||||||
|
|
||||||
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
|
identifier { (@asciiLetter | "_") (@asciiLetter | @digit | "_")* }
|
||||||
AnnotationName { "@" identifier? }
|
|
||||||
PropertyName { identifier }
|
PropertyName { identifier }
|
||||||
TagDeclarator { "$" identifier }
|
TagDeclarator { "$" identifier }
|
||||||
|
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
# alone
|
|
||||||
|
|
||||||
@a
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName))
|
|
||||||
|
|
||||||
# alone and anonymous
|
|
||||||
|
|
||||||
@
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName))
|
|
||||||
|
|
||||||
# empty
|
|
||||||
|
|
||||||
@ann()
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList))
|
|
||||||
|
|
||||||
# empty and anonymous
|
|
||||||
|
|
||||||
@()
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList))
|
|
||||||
|
|
||||||
# equals
|
|
||||||
|
|
||||||
@setting(a=1)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# operator
|
|
||||||
|
|
||||||
@ann(a*1)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
MultOp,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# anonymous
|
|
||||||
|
|
||||||
@(a=1)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# complex expr
|
|
||||||
|
|
||||||
@ann(a=(1+2+f('yes')))
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
ParenthesizedExpression(BinaryExpression(BinaryExpression(Number,
|
|
||||||
AddOp,
|
|
||||||
Number),
|
|
||||||
AddOp,
|
|
||||||
CallExpression(VariableName,
|
|
||||||
ArgumentList(String))))))))
|
|
||||||
|
|
||||||
# many args
|
|
||||||
|
|
||||||
@ann(a=1, b=2)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number),
|
|
||||||
AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# space around op
|
|
||||||
|
|
||||||
@ann(a / 1)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
MultOp,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# space around sep
|
|
||||||
|
|
||||||
@ann(a/1 , b/2)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
MultOp,
|
|
||||||
Number),
|
|
||||||
AnnotationProperty(PropertyName,
|
|
||||||
MultOp,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# trailing sep
|
|
||||||
|
|
||||||
@ann(a=1,)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# lone sep
|
|
||||||
|
|
||||||
@ann(,)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList))
|
|
||||||
|
|
||||||
# inside fn
|
|
||||||
|
|
||||||
fn f() {
|
|
||||||
@anno(b=2)
|
|
||||||
}
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(FunctionDeclaration(fn,
|
|
||||||
VariableDefinition,
|
|
||||||
ParamList,
|
|
||||||
Body(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))))
|
|
||||||
|
|
||||||
# laxer with space than the language parser is
|
|
||||||
|
|
||||||
@anno (b=2)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(Annotation(AnnotationName,
|
|
||||||
AnnotationList(AnnotationProperty(PropertyName,
|
|
||||||
Equals,
|
|
||||||
Number))))
|
|
@ -1,85 +0,0 @@
|
|||||||
# empty
|
|
||||||
|
|
||||||
f()
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(CallExpression(VariableName,
|
|
||||||
ArgumentList)))
|
|
||||||
|
|
||||||
# single anon arg
|
|
||||||
|
|
||||||
f(1)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(CallExpression(VariableName,
|
|
||||||
ArgumentList(Number))))
|
|
||||||
|
|
||||||
# deprecated multiple anon args
|
|
||||||
|
|
||||||
f(1, 2)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(CallExpression(VariableName,
|
|
||||||
ArgumentList(Number,
|
|
||||||
Number))))
|
|
||||||
|
|
||||||
# deprecated trailing %
|
|
||||||
|
|
||||||
startSketchOn('XY')
|
|
||||||
|> line([thickness, 0], %)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
|
||||||
ArgumentList(String)),
|
|
||||||
PipeOperator,
|
|
||||||
CallExpression(VariableName,
|
|
||||||
ArgumentList(ArrayExpression(VariableName,
|
|
||||||
Number),
|
|
||||||
PipeSubstitution)))))
|
|
||||||
|
|
||||||
# % and named arg
|
|
||||||
|
|
||||||
startSketchOn('XY')
|
|
||||||
|> line(%, end = [thickness, 0])
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
|
||||||
ArgumentList(String)),
|
|
||||||
PipeOperator,
|
|
||||||
CallExpression(VariableName,
|
|
||||||
ArgumentList(PipeSubstitution,
|
|
||||||
LabeledArgument(ArgumentLabel,
|
|
||||||
Equals,
|
|
||||||
ArrayExpression(VariableName,
|
|
||||||
Number)))))))
|
|
||||||
|
|
||||||
# implied % and named arg
|
|
||||||
|
|
||||||
startSketchOn('XY')
|
|
||||||
|> line(end = [thickness, 0])
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(PipeExpression(CallExpression(VariableName,
|
|
||||||
ArgumentList(String)),
|
|
||||||
PipeOperator,
|
|
||||||
CallExpression(VariableName,
|
|
||||||
ArgumentList(LabeledArgument(ArgumentLabel,
|
|
||||||
Equals,
|
|
||||||
ArrayExpression(VariableName,
|
|
||||||
Number)))))))
|
|
||||||
|
|
||||||
# multiple named arg
|
|
||||||
|
|
||||||
ngon(plane = "XY", numSides = 5, radius = pentR)
|
|
||||||
|
|
||||||
==>
|
|
||||||
Program(ExpressionStatement(CallExpression(VariableName,
|
|
||||||
ArgumentList(LabeledArgument(ArgumentLabel,
|
|
||||||
Equals,
|
|
||||||
String),
|
|
||||||
LabeledArgument(ArgumentLabel,
|
|
||||||
Equals,
|
|
||||||
Number),
|
|
||||||
LabeledArgument(ArgumentLabel,
|
|
||||||
Equals,
|
|
||||||
VariableName)))))
|
|
@ -29,7 +29,7 @@
|
|||||||
"vscode-uri": "^3.0.8"
|
"vscode-uri": "^3.0.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/node": "^22.13.1",
|
"@types/node": "^22.10.6",
|
||||||
"ts-node": "^10.9.2"
|
"ts-node": "^10.9.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,10 +109,10 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
|
resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9"
|
||||||
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
|
||||||
|
|
||||||
"@types/node@^22.13.1":
|
"@types/node@^22.10.6":
|
||||||
version "22.13.1"
|
version "22.10.6"
|
||||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.1.tgz#a2a3fefbdeb7ba6b89f40371842162fac0934f33"
|
resolved "https://registry.yarnpkg.com/@types/node/-/node-22.10.6.tgz#5c6795e71635876039f853cbccd59f523d9e4239"
|
||||||
integrity sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==
|
integrity sha512-qNiuwC4ZDAUNcY47xgaSuS92cjf8JbSUoaKS77bmLG1rU7MlATVSiw/IlrjtIyyskXBZ8KkNfjK/P5na7rgXbQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types "~6.20.0"
|
undici-types "~6.20.0"
|
||||||
|
|
||||||
|
@ -1,226 +1 @@
|
|||||||
[
|
404: Not Found
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "80-20-rail/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "80/20 Rail",
|
|
||||||
"description": "An 80/20 extruded aluminum linear rail. T-slot profile adjustable by profile height, rail length, and origin position"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "a-parametric-bearing-pillow-block/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "A Parametric Bearing Pillow Block",
|
|
||||||
"description": "A bearing pillow block, also known as a plummer block or pillow block bearing, is a pedestal used to provide support for a rotating shaft with the help of compatible bearings and various accessories. Housing a bearing, the pillow block provides a secure and stable foundation that allows the shaft to rotate smoothly within its machinery setup. These components are essential in a wide range of mechanical systems and machinery, playing a key role in reducing friction and supporting radial and axial loads."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "ball-bearing/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Ball Bearing",
|
|
||||||
"description": "A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "bracket/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Shelf Bracket",
|
|
||||||
"description": "This is a bracket that holds a shelf. It is made of aluminum and is designed to hold a force of 300 lbs. The bracket is 6 inches wide and the force is applied at the end of the shelf, 12 inches from the wall. The bracket has a factor of safety of 1.2. The legs of the bracket are 5 inches and 2 inches long. The thickness of the bracket is calculated from the constraints provided."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "car-wheel-assembly/main.kcl",
|
|
||||||
"multipleFiles": true,
|
|
||||||
"title": "Car Wheel Assembly",
|
|
||||||
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "cycloidal-gear/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Cycloidal Gear",
|
|
||||||
"description": "A cycloidal gear is a gear with a continuous, curved tooth profile. They are used in watchmaking and high precision robotics actuation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "dodecahedron/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Hollow Dodecahedron",
|
|
||||||
"description": "A regular dodecahedron or pentagonal dodecahedron is a dodecahedron composed of regular pentagonal faces, three meeting at each vertex. This example shows constructing the individual faces of the dodecahedron and extruding inwards."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "enclosure/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Enclosure",
|
|
||||||
"description": "An enclosure body and sealing lid for storing items"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "exhaust-manifold/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Exhaust Manifold",
|
|
||||||
"description": "A welded exhaust header for an inline 4-cylinder engine"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "flange-with-patterns/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Flange",
|
|
||||||
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "flange-xy/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Flange with XY coordinates",
|
|
||||||
"description": "A flange is a flat rim, collar, or rib, typically forged or cast, that is used to strengthen an object, guide it, or attach it to another object. Flanges are known for their use in various applications, including piping, plumbing, and mechanical engineering, among others."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "focusrite-scarlett-mounting-bracket/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "A mounting bracket for the Focusrite Scarlett Solo audio interface",
|
|
||||||
"description": "This is a bracket that holds an audio device underneath a desk or shelf. The audio device has dimensions of 144mm wide, 80mm length and 45mm depth with fillets of 6mm. This mounting bracket is designed to be 3D printed with PLA material"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "food-service-spatula/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Food Service Spatula",
|
|
||||||
"description": "Use these spatulas for mixing, flipping, and scraping."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "french-press/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "French Press",
|
|
||||||
"description": "A french press immersion coffee maker"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "gear/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Spur Gear",
|
|
||||||
"description": "A rotating machine part having cut teeth or, in the case of a cogwheel, inserted teeth (called cogs), which mesh with another toothed part to transmit torque. Geared devices can change the speed, torque, and direction of a power source. The two elements that define a gear are its circular shape and the teeth that are integrated into its outer edge, which are designed to fit into the teeth of another gear."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "gear-rack/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "100mm Gear Rack",
|
|
||||||
"description": "A flat bar or rail that is engraved with teeth along its length. These teeth are designed to mesh with the teeth of a gear, known as a pinion. When the pinion, a small cylindrical gear, rotates, its teeth engage with the teeth on the rack, causing the rack to move linearly. Conversely, linear motion applied to the rack will cause the pinion to rotate."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "hex-nut/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Hex nut",
|
|
||||||
"description": "A hex nut is a type of fastener with a threaded hole and a hexagonal outer shape, used in a wide variety of applications to secure parts together. The hexagonal shape allows for a greater torque to be applied with wrenches or tools, making it one of the most common nut types in hardware."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "i-beam/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "I-beam",
|
|
||||||
"description": "A structural metal beam with an I shaped cross section. Often used in construction"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "kitt/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Kitt",
|
|
||||||
"description": "The beloved KittyCAD mascot in a voxelized style."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "lego/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Lego Brick",
|
|
||||||
"description": "A standard Lego brick. This is a small, plastic construction block toy that can be interlocked with other blocks to build various structures, models, and figures. There are a lot of hacks used in this code."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "mounting-plate/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Mounting Plate",
|
|
||||||
"description": "A flat piece of material, often metal or plastic, that serves as a support or base for attaching, securing, or mounting various types of equipment, devices, or components."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "multi-axis-robot/main.kcl",
|
|
||||||
"multipleFiles": true,
|
|
||||||
"title": "Robot Arm",
|
|
||||||
"description": "A 4 axis robotic arm for industrial use. These machines can be used for assembly, packaging, organization of goods, and quality inspection processes"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "pipe/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Pipe",
|
|
||||||
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "pipe-flange-assembly/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Pipe and Flange Assembly",
|
|
||||||
"description": "A crucial component in various piping systems, designed to facilitate the connection, disconnection, and access to piping for inspection, cleaning, and modifications. This assembly combines pipes (long cylindrical conduits) with flanges (plate-like fittings) to create a secure yet detachable joint."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "pipe-with-bend/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Pipe with bend",
|
|
||||||
"description": "A tubular section or hollow cylinder, usually but not necessarily of circular cross-section, used mainly to convey substances that can flow."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "poopy-shoe/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Poopy Shoe",
|
|
||||||
"description": "poop shute for bambu labs printer - optimized for printing."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "router-template-cross-bar/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Router template for a cross bar",
|
|
||||||
"description": "A guide for routing a notch into a cross bar."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "router-template-slate/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Router template for a slate",
|
|
||||||
"description": "A guide for routing a slate for a cross bar."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "sheet-metal-bracket/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Sheet Metal Bracket",
|
|
||||||
"description": "A component typically made from flat sheet metal through various manufacturing processes such as bending, punching, cutting, and forming. These brackets are used to support, attach, or mount other hardware components, often providing a structural or functional base for assembly."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "socket-head-cap-screw/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Socket Head Cap Screw",
|
|
||||||
"description": "This is for a #10-24 screw that is 1.00 inches long. A socket head cap screw is a type of fastener that is widely used in a variety of applications requiring a high strength fastening solution. It is characterized by its cylindrical head and internal hexagonal drive, which allows for tightening with an Allen wrench or hex key."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "walkie-talkie/main.kcl",
|
|
||||||
"multipleFiles": true,
|
|
||||||
"title": "Walkie Talkie",
|
|
||||||
"description": "A portable, handheld two-way radio device that allows users to communicate wirelessly over short to medium distances. It operates on specific radio frequencies and features a push-to-talk button for transmitting messages, making it ideal for quick and reliable communication in outdoor, work, or emergency settings."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "main.kcl",
|
|
||||||
"pathFromProjectDirectoryToFirstFile": "washer/main.kcl",
|
|
||||||
"multipleFiles": false,
|
|
||||||
"title": "Washer",
|
|
||||||
"description": "A small, typically disk-shaped component with a hole in the middle, used in a wide range of applications, primarily in conjunction with fasteners like bolts and screws. Washers distribute the load of a fastener across a broader area. This is especially important when the fastening surface is soft or uneven, as it helps to prevent damage to the surface and ensures the load is evenly distributed, reducing the risk of the fastener becoming loose over time."
|
|
||||||
}
|
|
||||||
]
|
|
@ -11,7 +11,6 @@ echo "$PACKAGE" > package.json
|
|||||||
# electron-builder.yml
|
# electron-builder.yml
|
||||||
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
|
yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' electron-builder.yml
|
||||||
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
||||||
yq -i '.nsis.include = "./scripts/installer-nightly.nsh"' electron-builder.yml
|
|
||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
!macro preInit
|
|
||||||
SetRegView 64
|
|
||||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\Zoo Modeling App (Nightly)"
|
|
||||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\Zoo Modeling App (Nightly)"
|
|
||||||
SetRegView 32
|
|
||||||
WriteRegExpandStr HKLM "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\Zoo Modeling App (Nightly)"
|
|
||||||
WriteRegExpandStr HKCU "${INSTALL_REGISTRY_KEY}" InstallLocation "C:\Program Files\Zoo Modeling App (Nightly)"
|
|
||||||
!macroend
|
|
@ -31,6 +31,7 @@ import {
|
|||||||
recast,
|
recast,
|
||||||
defaultSourceRange,
|
defaultSourceRange,
|
||||||
resultIsOk,
|
resultIsOk,
|
||||||
|
ProgramMemory,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||||
@ -145,8 +146,7 @@ export const ClientSideScene = ({
|
|||||||
state.matches({ Sketch: 'Line tool' }) ||
|
state.matches({ Sketch: 'Line tool' }) ||
|
||||||
state.matches({ Sketch: 'Tangential arc to' }) ||
|
state.matches({ Sketch: 'Tangential arc to' }) ||
|
||||||
state.matches({ Sketch: 'Rectangle tool' }) ||
|
state.matches({ Sketch: 'Rectangle tool' }) ||
|
||||||
state.matches({ Sketch: 'Circle tool' }) ||
|
state.matches({ Sketch: 'Circle tool' })
|
||||||
state.matches({ Sketch: 'Circle three point tool' })
|
|
||||||
) {
|
) {
|
||||||
cursor = 'crosshair'
|
cursor = 'crosshair'
|
||||||
} else {
|
} else {
|
||||||
@ -427,7 +427,7 @@ export async function deleteSegment({
|
|||||||
modifiedAst = deleteSegmentFromPipeExpression(
|
modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
dependentRanges,
|
dependentRanges,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
codeManager.code,
|
codeManager.code,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
@ -441,8 +441,8 @@ export async function deleteSegment({
|
|||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
usePrevMemory: false,
|
programMemoryOverride: ProgramMemory.empty(),
|
||||||
})
|
})
|
||||||
if (testExecute.errors.length) {
|
if (testExecute.errors.length) {
|
||||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||||
@ -679,7 +679,7 @@ const ConstraintSymbol = ({
|
|||||||
shallowPath,
|
shallowPath,
|
||||||
argPosition,
|
argPosition,
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.variables
|
kclManager.programMemory
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!transform) return
|
if (!transform) return
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
BoxGeometry,
|
BoxGeometry,
|
||||||
|
Color,
|
||||||
DoubleSide,
|
DoubleSide,
|
||||||
Group,
|
Group,
|
||||||
Intersection,
|
Intersection,
|
||||||
|
Line,
|
||||||
|
LineDashedMaterial,
|
||||||
|
BufferGeometry,
|
||||||
Mesh,
|
Mesh,
|
||||||
MeshBasicMaterial,
|
MeshBasicMaterial,
|
||||||
Object3D,
|
Object3D,
|
||||||
@ -13,6 +17,7 @@ import {
|
|||||||
Points,
|
Points,
|
||||||
Quaternion,
|
Quaternion,
|
||||||
Scene,
|
Scene,
|
||||||
|
SphereGeometry,
|
||||||
Vector2,
|
Vector2,
|
||||||
Vector3,
|
Vector3,
|
||||||
} from 'three'
|
} from 'three'
|
||||||
@ -40,6 +45,7 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
Program,
|
Program,
|
||||||
|
ProgramMemory,
|
||||||
recast,
|
recast,
|
||||||
Sketch,
|
Sketch,
|
||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
@ -51,7 +57,6 @@ import {
|
|||||||
SourceRange,
|
SourceRange,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
CallExpressionKw,
|
CallExpressionKw,
|
||||||
VariableMap,
|
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
@ -65,6 +70,7 @@ import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
|||||||
import { executeAst, ToolTip } from 'lang/langHelpers'
|
import { executeAst, ToolTip } from 'lang/langHelpers'
|
||||||
import {
|
import {
|
||||||
createProfileStartHandle,
|
createProfileStartHandle,
|
||||||
|
createCircleGeometry,
|
||||||
SegmentUtils,
|
SegmentUtils,
|
||||||
segmentUtils,
|
segmentUtils,
|
||||||
} from './segments'
|
} from './segments'
|
||||||
@ -115,6 +121,8 @@ import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
|||||||
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
||||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import { LabeledArg } from 'wasm-lib/kcl/bindings/LabeledArg'
|
||||||
|
import { Literal } from 'wasm-lib/kcl/bindings/Literal'
|
||||||
import { radToDeg } from 'three/src/math/MathUtils'
|
import { radToDeg } from 'three/src/math/MathUtils'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { getArtifactFromRange, codeRefFromRange } from 'lang/std/artifactGraph'
|
import { getArtifactFromRange, codeRefFromRange } from 'lang/std/artifactGraph'
|
||||||
@ -164,6 +172,7 @@ type Vec3Array = [number, number, number]
|
|||||||
export class SceneEntities {
|
export class SceneEntities {
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
scene: Scene
|
scene: Scene
|
||||||
|
sceneProgramMemory: ProgramMemory = ProgramMemory.empty()
|
||||||
activeSegments: { [key: string]: Group } = {}
|
activeSegments: { [key: string]: Group } = {}
|
||||||
intersectionPlane: Mesh | null = null
|
intersectionPlane: Mesh | null = null
|
||||||
axisGroup: Group | null = null
|
axisGroup: Group | null = null
|
||||||
@ -575,25 +584,33 @@ export class SceneEntities {
|
|||||||
selectionRanges?: Selections
|
selectionRanges?: Selections
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
truncatedAst: Node<Program>
|
truncatedAst: Node<Program>
|
||||||
|
programMemoryOverride: ProgramMemory
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}> {
|
}> {
|
||||||
this.createIntersectionPlane()
|
this.createIntersectionPlane()
|
||||||
|
|
||||||
const prepared = this.prepareTruncatedAst(sketchNodePaths, maybeModdedAst)
|
const prepared = this.prepareTruncatedMemoryAndAst(
|
||||||
|
sketchNodePaths,
|
||||||
|
maybeModdedAst
|
||||||
|
)
|
||||||
if (err(prepared)) return Promise.reject(prepared)
|
if (err(prepared)) return Promise.reject(prepared)
|
||||||
const { truncatedAst, variableDeclarationName } = prepared
|
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||||
|
prepared
|
||||||
|
|
||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
|
const programMemory = execState.memory
|
||||||
const sketchesInfo = getSketchesInfo({
|
const sketchesInfo = getSketchesInfo({
|
||||||
sketchNodePaths,
|
sketchNodePaths,
|
||||||
ast: maybeModdedAst,
|
ast: maybeModdedAst,
|
||||||
variables: execState.variables,
|
programMemory,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
const group = new Group()
|
const group = new Group()
|
||||||
position && group.position.set(...position)
|
position && group.position.set(...position)
|
||||||
group.userData = {
|
group.userData = {
|
||||||
@ -764,6 +781,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
|
programMemoryOverride,
|
||||||
variableDeclarationName,
|
variableDeclarationName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -819,16 +837,16 @@ export class SceneEntities {
|
|||||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||||
|
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
kclManager.variables[variableDeclarationName],
|
kclManager.programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return Promise.reject(sg)
|
if (err(sg)) return Promise.reject(sg)
|
||||||
const lastSeg = sg?.paths?.slice(-1)[0] || sg.start
|
const lastSeg = sg?.paths?.slice(-1)[0] || sg.start
|
||||||
|
|
||||||
const index = sg.paths.length // because we've added a new segment that's not in the memory yet, no need for `.length -1`
|
const index = sg.paths.length // because we've added a new segment that's not in the memory yet, no need for `-1`
|
||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: _ast,
|
node: _ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
input: {
|
input: {
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
to: lastSeg.to,
|
to: lastSeg.to,
|
||||||
@ -847,7 +865,7 @@ export class SceneEntities {
|
|||||||
if (shouldTearDown) this.tearDownSketch({ removeAxis: false })
|
if (shouldTearDown) this.tearDownSketch({ removeAxis: false })
|
||||||
sceneInfra.resetMouseListeners()
|
sceneInfra.resetMouseListeners()
|
||||||
|
|
||||||
const { truncatedAst } = await this.setupSketch({
|
const { truncatedAst, programMemoryOverride } = await this.setupSketch({
|
||||||
sketchEntryNodePath,
|
sketchEntryNodePath,
|
||||||
sketchNodePaths,
|
sketchNodePaths,
|
||||||
forward,
|
forward,
|
||||||
@ -870,14 +888,14 @@ export class SceneEntities {
|
|||||||
let intersection2d = intersectionPoint?.twoD
|
let intersection2d = intersectionPoint?.twoD
|
||||||
const intersectsProfileStart = args.intersects
|
const intersectsProfileStart = args.intersects
|
||||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||||
.find(isGroupStartProfileForCurrentProfile(sketchEntryNodePath))
|
.find((a) => a?.name === PROFILE_START)
|
||||||
|
|
||||||
let modifiedAst: Program | Error = structuredClone(kclManager.ast)
|
let modifiedAst: Program | Error = structuredClone(kclManager.ast)
|
||||||
|
|
||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
pathToNode: sketchEntryNodePath,
|
pathToNode: sketchEntryNodePath,
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
if (err(sketch)) return Promise.reject(sketch)
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
if (!sketch) return Promise.reject(new Error('No sketch found'))
|
if (!sketch) return Promise.reject(new Error('No sketch found'))
|
||||||
@ -895,10 +913,10 @@ export class SceneEntities {
|
|||||||
])
|
])
|
||||||
modifiedAst = addCallExpressionsToPipe({
|
modifiedAst = addCallExpressionsToPipe({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
pathToNode: sketchEntryNodePath,
|
pathToNode: sketchEntryNodePath,
|
||||||
expressions: [
|
expressions: [
|
||||||
segmentName === 'tangentialArcTo'
|
lastSegment.type === 'TangentialArcTo'
|
||||||
? createCallExpressionStdLib('tangentialArcTo', [
|
? createCallExpressionStdLib('tangentialArcTo', [
|
||||||
originCoords,
|
originCoords,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
@ -911,7 +929,7 @@ export class SceneEntities {
|
|||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
modifiedAst = addCloseToPipe({
|
modifiedAst = addCloseToPipe({
|
||||||
node: modifiedAst,
|
node: modifiedAst,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
pathToNode: sketchEntryNodePath,
|
pathToNode: sketchEntryNodePath,
|
||||||
})
|
})
|
||||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||||
@ -965,7 +983,7 @@ export class SceneEntities {
|
|||||||
|
|
||||||
const tmp = addNewSketchLn({
|
const tmp = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
input: {
|
input: {
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||||
@ -1020,6 +1038,7 @@ export class SceneEntities {
|
|||||||
planeNodePath,
|
planeNodePath,
|
||||||
draftInfo: {
|
draftInfo: {
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
|
programMemoryOverride,
|
||||||
variableDeclarationName,
|
variableDeclarationName,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -1100,7 +1119,7 @@ export class SceneEntities {
|
|||||||
return Promise.reject(_recastAst)
|
return Promise.reject(_recastAst)
|
||||||
_ast = _recastAst.program
|
_ast = _recastAst.program
|
||||||
|
|
||||||
const { truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchEntryNodePath: updatedEntryNodePath,
|
sketchEntryNodePath: updatedEntryNodePath,
|
||||||
sketchNodePaths: updatedSketchNodePaths,
|
sketchNodePaths: updatedSketchNodePaths,
|
||||||
forward,
|
forward,
|
||||||
@ -1139,9 +1158,12 @@ export class SceneEntities {
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
const programMemory = execState.memory
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
|
const sketch = sketchFromKclValue(programMemory.get(varName), varName)
|
||||||
if (err(sketch)) return Promise.reject(sketch)
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
@ -1203,11 +1225,7 @@ export class SceneEntities {
|
|||||||
await codeManager.updateEditorWithAstAndWriteToFile(_ast)
|
await codeManager.updateEditorWithAstAndWriteToFile(_ast)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return { updatedEntryNodePath, updatedSketchNodePaths }
|
||||||
updatedEntryNodePath,
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
expressionIndexToDelete: insertIndex,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setupDraftCenterRectangle = async (
|
setupDraftCenterRectangle = async (
|
||||||
sketchEntryNodePath: PathToNode,
|
sketchEntryNodePath: PathToNode,
|
||||||
@ -1281,7 +1299,7 @@ export class SceneEntities {
|
|||||||
return Promise.reject(__recastAst)
|
return Promise.reject(__recastAst)
|
||||||
_ast = __recastAst.program
|
_ast = __recastAst.program
|
||||||
|
|
||||||
const { truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchEntryNodePath: updatedEntryNodePath,
|
sketchEntryNodePath: updatedEntryNodePath,
|
||||||
sketchNodePaths: updatedSketchNodePaths,
|
sketchNodePaths: updatedSketchNodePaths,
|
||||||
forward,
|
forward,
|
||||||
@ -1327,9 +1345,12 @@ export class SceneEntities {
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
const programMemory = execState.memory
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
|
const sketch = sketchFromKclValue(programMemory.get(varName), varName)
|
||||||
if (err(sketch)) return Promise.reject(sketch)
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
@ -1395,11 +1416,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return { updatedEntryNodePath, updatedSketchNodePaths }
|
||||||
updatedEntryNodePath,
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
expressionIndexToDelete: insertIndex,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setupDraftCircleThreePoint = async (
|
setupDraftCircleThreePoint = async (
|
||||||
sketchEntryNodePath: PathToNode,
|
sketchEntryNodePath: PathToNode,
|
||||||
@ -1452,7 +1469,7 @@ export class SceneEntities {
|
|||||||
// do a quick mock execution to get the program memory up-to-date
|
// do a quick mock execution to get the program memory up-to-date
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
|
|
||||||
const { truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchEntryNodePath: updatedEntryNodePath,
|
sketchEntryNodePath: updatedEntryNodePath,
|
||||||
sketchNodePaths: updatedSketchNodePaths,
|
sketchNodePaths: updatedSketchNodePaths,
|
||||||
forward,
|
forward,
|
||||||
@ -1464,13 +1481,12 @@ export class SceneEntities {
|
|||||||
|
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onMove: async (args) => {
|
onMove: async (args) => {
|
||||||
const firstProfileIndex = Number(updatedSketchNodePaths[0][1][0])
|
|
||||||
const nodePathWithCorrectedIndexForTruncatedAst =
|
const nodePathWithCorrectedIndexForTruncatedAst =
|
||||||
structuredClone(updatedEntryNodePath)
|
structuredClone(updatedEntryNodePath)
|
||||||
|
|
||||||
nodePathWithCorrectedIndexForTruncatedAst[1][0] =
|
nodePathWithCorrectedIndexForTruncatedAst[1][0] =
|
||||||
Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) -
|
Number(nodePathWithCorrectedIndexForTruncatedAst[1][0]) -
|
||||||
firstProfileIndex
|
Number(planeNodePath[1][0]) -
|
||||||
|
1
|
||||||
const _node = getNodeFromPath<VariableDeclaration>(
|
const _node = getNodeFromPath<VariableDeclaration>(
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
nodePathWithCorrectedIndexForTruncatedAst,
|
nodePathWithCorrectedIndexForTruncatedAst,
|
||||||
@ -1483,7 +1499,7 @@ export class SceneEntities {
|
|||||||
if (sketchInit.type === 'CallExpressionKw') {
|
if (sketchInit.type === 'CallExpressionKw') {
|
||||||
const moddedResult = changeSketchArguments(
|
const moddedResult = changeSketchArguments(
|
||||||
modded,
|
modded,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'path',
|
type: 'path',
|
||||||
pathToNode: nodePathWithCorrectedIndexForTruncatedAst,
|
pathToNode: nodePathWithCorrectedIndexForTruncatedAst,
|
||||||
@ -1505,9 +1521,12 @@ export class SceneEntities {
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
const programMemory = execState.memory
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
|
const sketch = sketchFromKclValue(programMemory.get(varName), varName)
|
||||||
if (err(sketch)) return
|
if (err(sketch)) return
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
@ -1548,7 +1567,7 @@ export class SceneEntities {
|
|||||||
if (sketchInit.type === 'CallExpressionKw') {
|
if (sketchInit.type === 'CallExpressionKw') {
|
||||||
const moddedResult = changeSketchArguments(
|
const moddedResult = changeSketchArguments(
|
||||||
modded,
|
modded,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'path',
|
type: 'path',
|
||||||
pathToNode: updatedEntryNodePath,
|
pathToNode: updatedEntryNodePath,
|
||||||
@ -1577,11 +1596,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return { updatedEntryNodePath, updatedSketchNodePaths }
|
||||||
updatedEntryNodePath,
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
expressionIndexToDelete: insertIndex,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setupDraftCircle = async (
|
setupDraftCircle = async (
|
||||||
sketchEntryNodePath: PathToNode,
|
sketchEntryNodePath: PathToNode,
|
||||||
@ -1635,7 +1650,7 @@ export class SceneEntities {
|
|||||||
// do a quick mock execution to get the program memory up-to-date
|
// do a quick mock execution to get the program memory up-to-date
|
||||||
await kclManager.executeAstMock(_ast)
|
await kclManager.executeAstMock(_ast)
|
||||||
|
|
||||||
const { truncatedAst } = await this.setupSketch({
|
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||||
sketchEntryNodePath: updatedEntryNodePath,
|
sketchEntryNodePath: updatedEntryNodePath,
|
||||||
sketchNodePaths: updatedSketchNodePaths,
|
sketchNodePaths: updatedSketchNodePaths,
|
||||||
forward,
|
forward,
|
||||||
@ -1668,7 +1683,7 @@ export class SceneEntities {
|
|||||||
if (sketchInit.type === 'CallExpression') {
|
if (sketchInit.type === 'CallExpression') {
|
||||||
const moddedResult = changeSketchArguments(
|
const moddedResult = changeSketchArguments(
|
||||||
modded,
|
modded,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'path',
|
type: 'path',
|
||||||
pathToNode: nodePathWithCorrectedIndexForTruncatedAst,
|
pathToNode: nodePathWithCorrectedIndexForTruncatedAst,
|
||||||
@ -1687,9 +1702,12 @@ export class SceneEntities {
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: modded,
|
ast: modded,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const sketch = sketchFromKclValue(execState.variables[varName], varName)
|
const programMemory = execState.memory
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
|
const sketch = sketchFromKclValue(programMemory.get(varName), varName)
|
||||||
if (err(sketch)) return
|
if (err(sketch)) return
|
||||||
const sgPaths = sketch.paths
|
const sgPaths = sketch.paths
|
||||||
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
const orthoFactor = orthoScale(sceneInfra.camControls.camera)
|
||||||
@ -1733,7 +1751,7 @@ export class SceneEntities {
|
|||||||
if (sketchInit.type === 'CallExpression') {
|
if (sketchInit.type === 'CallExpression') {
|
||||||
const moddedResult = changeSketchArguments(
|
const moddedResult = changeSketchArguments(
|
||||||
modded,
|
modded,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'path',
|
type: 'path',
|
||||||
pathToNode: updatedEntryNodePath,
|
pathToNode: updatedEntryNodePath,
|
||||||
@ -1762,11 +1780,7 @@ export class SceneEntities {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return {
|
return { updatedEntryNodePath, updatedSketchNodePaths }
|
||||||
updatedEntryNodePath,
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
expressionIndexToDelete: insertIndex,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
setupSketchIdleCallbacks = ({
|
setupSketchIdleCallbacks = ({
|
||||||
sketchEntryNodePath,
|
sketchEntryNodePath,
|
||||||
@ -1828,7 +1842,7 @@ export class SceneEntities {
|
|||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
pathToNode,
|
pathToNode,
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
if (trap(sketch)) return
|
if (trap(sketch)) return
|
||||||
if (!sketch) {
|
if (!sketch) {
|
||||||
@ -1841,7 +1855,7 @@ export class SceneEntities {
|
|||||||
const prevSegment = sketch.paths[pipeIndex - 2]
|
const prevSegment = sketch.paths[pipeIndex - 2]
|
||||||
const mod = addNewSketchLn({
|
const mod = addNewSketchLn({
|
||||||
node: kclManager.ast,
|
node: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
input: {
|
input: {
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||||
@ -1918,15 +1932,15 @@ export class SceneEntities {
|
|||||||
...this.mouseEnterLeaveCallbacks(),
|
...this.mouseEnterLeaveCallbacks(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
prepareTruncatedAst = (
|
prepareTruncatedMemoryAndAst = (
|
||||||
sketchNodePaths: PathToNode[],
|
sketchNodePaths: PathToNode[],
|
||||||
ast?: Node<Program>,
|
ast?: Node<Program>,
|
||||||
draftSegment?: DraftSegment
|
draftSegment?: DraftSegment
|
||||||
) =>
|
) =>
|
||||||
prepareTruncatedAst(
|
prepareTruncatedMemoryAndAst(
|
||||||
sketchNodePaths,
|
sketchNodePaths,
|
||||||
ast || kclManager.ast,
|
ast || kclManager.ast,
|
||||||
kclManager.lastSuccessfulVariables,
|
kclManager.lastSuccessfulProgramMemory,
|
||||||
draftSegment
|
draftSegment
|
||||||
)
|
)
|
||||||
onDragSegment({
|
onDragSegment({
|
||||||
@ -1946,6 +1960,7 @@ export class SceneEntities {
|
|||||||
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
||||||
draftInfo?: {
|
draftInfo?: {
|
||||||
truncatedAst: Node<Program>
|
truncatedAst: Node<Program>
|
||||||
|
programMemoryOverride: ProgramMemory
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}
|
}
|
||||||
}) {
|
}) {
|
||||||
@ -1953,7 +1968,7 @@ export class SceneEntities {
|
|||||||
draftInfo &&
|
draftInfo &&
|
||||||
intersects
|
intersects
|
||||||
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
.map(({ object }) => getParentGroup(object, [PROFILE_START]))
|
||||||
.find(isGroupStartProfileForCurrentProfile(sketchEntryNodePath))
|
.find((a) => a?.name === PROFILE_START)
|
||||||
const intersection2d = intersectsProfileStart
|
const intersection2d = intersectsProfileStart
|
||||||
? new Vector2(
|
? new Vector2(
|
||||||
intersectsProfileStart.position.x,
|
intersectsProfileStart.position.x,
|
||||||
@ -2090,12 +2105,12 @@ export class SceneEntities {
|
|||||||
to: dragTo,
|
to: dragTo,
|
||||||
from,
|
from,
|
||||||
},
|
},
|
||||||
variables: kclManager.variables,
|
previousProgramMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
modded = changeSketchArguments(
|
modded = changeSketchArguments(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: topLevelRange(node.start, node.end),
|
sourceRange: topLevelRange(node.start, node.end),
|
||||||
@ -2108,9 +2123,9 @@ export class SceneEntities {
|
|||||||
modifiedAst = modded.modifiedAst
|
modifiedAst = modded.modifiedAst
|
||||||
const info = draftInfo
|
const info = draftInfo
|
||||||
? draftInfo
|
? draftInfo
|
||||||
: this.prepareTruncatedAst(sketchNodePaths || [], modifiedAst)
|
: this.prepareTruncatedMemoryAndAst(sketchNodePaths || [], modifiedAst)
|
||||||
if (trap(info, { suppress: true })) return
|
if (trap(info, { suppress: true })) return
|
||||||
const { truncatedAst } = info
|
const { truncatedAst, programMemoryOverride } = info
|
||||||
;(async () => {
|
;(async () => {
|
||||||
const code = recast(modifiedAst)
|
const code = recast(modifiedAst)
|
||||||
if (trap(code)) return
|
if (trap(code)) return
|
||||||
@ -2121,13 +2136,15 @@ export class SceneEntities {
|
|||||||
const { execState } = await executeAst({
|
const { execState } = await executeAst({
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
|
programMemoryOverride,
|
||||||
})
|
})
|
||||||
const variables = execState.variables
|
const programMemory = execState.memory
|
||||||
|
this.sceneProgramMemory = programMemory
|
||||||
const sketchesInfo = getSketchesInfo({
|
const sketchesInfo = getSketchesInfo({
|
||||||
sketchNodePaths,
|
sketchNodePaths,
|
||||||
ast: truncatedAst,
|
ast: truncatedAst,
|
||||||
variables,
|
programMemory,
|
||||||
})
|
})
|
||||||
const callBacks: (() => SegmentOverlayPayload | null)[] = []
|
const callBacks: (() => SegmentOverlayPayload | null)[] = []
|
||||||
for (const sketchInfo of sketchesInfo) {
|
for (const sketchInfo of sketchesInfo) {
|
||||||
@ -2453,14 +2470,15 @@ export class SceneEntities {
|
|||||||
|
|
||||||
// calculations/pure-functions/easy to test so no excuse not to
|
// calculations/pure-functions/easy to test so no excuse not to
|
||||||
|
|
||||||
function prepareTruncatedAst(
|
function prepareTruncatedMemoryAndAst(
|
||||||
sketchNodePaths: PathToNode[],
|
sketchNodePaths: PathToNode[],
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
variables: VariableMap,
|
programMemory: ProgramMemory,
|
||||||
draftSegment?: DraftSegment
|
draftSegment?: DraftSegment
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
truncatedAst: Node<Program>
|
truncatedAst: Node<Program>
|
||||||
|
programMemoryOverride: ProgramMemory
|
||||||
// can I remove the below?
|
// can I remove the below?
|
||||||
variableDeclarationName: string
|
variableDeclarationName: string
|
||||||
}
|
}
|
||||||
@ -2479,7 +2497,7 @@ function prepareTruncatedAst(
|
|||||||
if (err(_node)) return _node
|
if (err(_node)) return _node
|
||||||
const variableDeclarationName = _node.node?.declaration?.id?.name || ''
|
const variableDeclarationName = _node.node?.declaration?.id?.name || ''
|
||||||
const sg = sketchFromKclValue(
|
const sg = sketchFromKclValue(
|
||||||
variables[variableDeclarationName],
|
programMemory.get(variableDeclarationName),
|
||||||
variableDeclarationName
|
variableDeclarationName
|
||||||
)
|
)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return sg
|
||||||
@ -2538,8 +2556,43 @@ function prepareTruncatedAst(
|
|||||||
body: structuredClone(_ast.body.slice(bodyStartIndex, bodyEndIndex + 1)),
|
body: structuredClone(_ast.body.slice(bodyStartIndex, bodyEndIndex + 1)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grab all the TagDeclarators and TagIdentifiers from memory.
|
||||||
|
let start = _node.node.start
|
||||||
|
const programMemoryOverride = programMemory.filterVariables(true, (value) => {
|
||||||
|
if (
|
||||||
|
!('__meta' in value) ||
|
||||||
|
value.__meta === undefined ||
|
||||||
|
value.__meta.length === 0 ||
|
||||||
|
value.__meta[0].sourceRange === undefined
|
||||||
|
) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value.__meta[0].sourceRange[0] >= start) {
|
||||||
|
// We only want things before our start point.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.type === 'TagIdentifier'
|
||||||
|
})
|
||||||
|
if (err(programMemoryOverride)) return programMemoryOverride
|
||||||
|
|
||||||
|
for (let i = 0; i < bodyStartIndex; i++) {
|
||||||
|
const node = _ast.body[i]
|
||||||
|
if (node.type !== 'VariableDeclaration') {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const name = node.declaration.id.name
|
||||||
|
const memoryItem = programMemory.get(name)
|
||||||
|
if (!memoryItem) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const error = programMemoryOverride.set(name, structuredClone(memoryItem))
|
||||||
|
if (err(error)) return error
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
truncatedAst,
|
truncatedAst,
|
||||||
|
programMemoryOverride,
|
||||||
variableDeclarationName,
|
variableDeclarationName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2556,14 +2609,14 @@ export function getParentGroup(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function sketchFromPathToNode({
|
export function sketchFromPathToNode({
|
||||||
pathToNode,
|
pathToNode,
|
||||||
ast,
|
ast,
|
||||||
variables,
|
programMemory,
|
||||||
}: {
|
}: {
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
ast: Program
|
ast: Program
|
||||||
variables: VariableMap
|
programMemory: ProgramMemory
|
||||||
}): Sketch | null | Error {
|
}): Sketch | null | Error {
|
||||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
@ -2572,7 +2625,7 @@ function sketchFromPathToNode({
|
|||||||
)
|
)
|
||||||
if (err(_varDec)) return _varDec
|
if (err(_varDec)) return _varDec
|
||||||
const varDec = _varDec.node
|
const varDec = _varDec.node
|
||||||
const result = variables[varDec?.id?.name || '']
|
const result = programMemory.get(varDec?.id?.name || '')
|
||||||
if (result?.type === 'Solid') {
|
if (result?.type === 'Solid') {
|
||||||
return result.value.sketch
|
return result.value.sketch
|
||||||
}
|
}
|
||||||
@ -2611,7 +2664,7 @@ export function getSketchQuaternion(
|
|||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
variables: kclManager.variables,
|
programMemory: kclManager.programMemory,
|
||||||
})
|
})
|
||||||
if (err(sketch)) return sketch
|
if (err(sketch)) return sketch
|
||||||
const zAxis = sketch?.on.zAxis || sketchNormalBackUp
|
const zAxis = sketch?.on.zAxis || sketchNormalBackUp
|
||||||
@ -2619,13 +2672,23 @@ export function getSketchQuaternion(
|
|||||||
|
|
||||||
return getQuaternionFromZAxis(massageFormats(zAxis))
|
return getQuaternionFromZAxis(massageFormats(zAxis))
|
||||||
}
|
}
|
||||||
export async function getSketchOrientationDetails(sketch: Sketch): Promise<{
|
export async function getSketchOrientationDetails(
|
||||||
|
sketchEntryNodePath: PathToNode
|
||||||
|
): Promise<{
|
||||||
quat: Quaternion
|
quat: Quaternion
|
||||||
sketchDetails: Omit<
|
sketchDetails: Omit<
|
||||||
SketchDetails & { faceId?: string },
|
SketchDetails & { faceId?: string },
|
||||||
'sketchNodePaths' | 'sketchEntryNodePath' | 'planeNodePath'
|
'sketchNodePaths' | 'sketchEntryNodePath' | 'planeNodePath'
|
||||||
>
|
>
|
||||||
}> {
|
}> {
|
||||||
|
const sketch = sketchFromPathToNode({
|
||||||
|
pathToNode: sketchEntryNodePath,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
programMemory: kclManager.programMemory,
|
||||||
|
})
|
||||||
|
if (err(sketch)) return Promise.reject(sketch)
|
||||||
|
if (!sketch) return Promise.reject('sketch not found')
|
||||||
|
|
||||||
if (sketch.on.type === 'plane') {
|
if (sketch.on.type === 'plane') {
|
||||||
const zAxis = sketch?.on.zAxis
|
const zAxis = sketch?.on.zAxis
|
||||||
return {
|
return {
|
||||||
@ -2638,6 +2701,8 @@ export async function getSketchOrientationDetails(sketch: Sketch): Promise<{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sketch.on.type === 'face') {
|
||||||
const faceInfo = await getFaceDetails(sketch.on.id)
|
const faceInfo = await getFaceDetails(sketch.on.id)
|
||||||
|
|
||||||
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
if (!faceInfo?.origin || !faceInfo?.z_axis || !faceInfo?.y_axis)
|
||||||
@ -2657,6 +2722,10 @@ export async function getSketchOrientationDetails(sketch: Sketch): Promise<{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Promise.reject(
|
||||||
|
'sketch.on.type not recognized, has a new type been added?'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves orientation details for a given entity representing a face (brep face or default plane).
|
* Retrieves orientation details for a given entity representing a face (brep face or default plane).
|
||||||
@ -2728,11 +2797,11 @@ function massageFormats(a: Vec3Array | Point3d): Vector3 {
|
|||||||
function getSketchesInfo({
|
function getSketchesInfo({
|
||||||
sketchNodePaths,
|
sketchNodePaths,
|
||||||
ast,
|
ast,
|
||||||
variables,
|
programMemory,
|
||||||
}: {
|
}: {
|
||||||
sketchNodePaths: PathToNode[]
|
sketchNodePaths: PathToNode[]
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
variables: VariableMap
|
programMemory: ProgramMemory
|
||||||
}): {
|
}): {
|
||||||
sketch: Sketch
|
sketch: Sketch
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
@ -2745,7 +2814,7 @@ function getSketchesInfo({
|
|||||||
const sketch = sketchFromPathToNode({
|
const sketch = sketchFromPathToNode({
|
||||||
pathToNode: path,
|
pathToNode: path,
|
||||||
ast,
|
ast,
|
||||||
variables,
|
programMemory,
|
||||||
})
|
})
|
||||||
if (err(sketch)) continue
|
if (err(sketch)) continue
|
||||||
if (!sketch) continue
|
if (!sketch) continue
|
||||||
@ -2779,13 +2848,3 @@ function computeSelectionFromSourceRangeAndAST(
|
|||||||
}
|
}
|
||||||
return selection
|
return selection
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGroupStartProfileForCurrentProfile(sketchEntryNodePath: PathToNode) {
|
|
||||||
return (group: Group<Object3DEventMap> | null) => {
|
|
||||||
if (group?.name !== PROFILE_START) return false
|
|
||||||
const groupExpressionIndex = Number(group.userData.pathToNode[1][0])
|
|
||||||
const isProfileStartOfCurrentExpr =
|
|
||||||
groupExpressionIndex === sketchEntryNodePath[1][0]
|
|
||||||
return isProfileStartOfCurrentExpr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -54,11 +54,13 @@ import {
|
|||||||
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
import { getTangentPointFromPreviousArc } from 'lib/utils2d'
|
||||||
import {
|
import {
|
||||||
ARROWHEAD,
|
ARROWHEAD,
|
||||||
|
CIRCLE_3_POINT_DRAFT_CIRCLE,
|
||||||
DRAFT_POINT,
|
DRAFT_POINT,
|
||||||
SceneInfra,
|
SceneInfra,
|
||||||
SEGMENT_LENGTH_LABEL,
|
SEGMENT_LENGTH_LABEL,
|
||||||
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
SEGMENT_LENGTH_LABEL_OFFSET_PX,
|
||||||
SEGMENT_LENGTH_LABEL_TEXT,
|
SEGMENT_LENGTH_LABEL_TEXT,
|
||||||
|
SKETCH_LAYER,
|
||||||
} from './sceneInfra'
|
} from './sceneInfra'
|
||||||
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
import { Themes, getThemeColorForThreeJs } from 'lib/theme'
|
||||||
import { normaliseAngle, roundOff } from 'lib/utils'
|
import { normaliseAngle, roundOff } from 'lib/utils'
|
||||||
@ -69,7 +71,7 @@ import {
|
|||||||
} from 'machines/modelingMachine'
|
} from 'machines/modelingMachine'
|
||||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { sceneInfra } from 'lib/singletons'
|
import { editorManager, sceneInfra } from 'lib/singletons'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { calculate_circle_from_3_points } from 'wasm-lib/pkg/wasm_lib'
|
import { calculate_circle_from_3_points } from 'wasm-lib/pkg/wasm_lib'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
@ -1010,7 +1012,10 @@ function createCircleCenterHandle(
|
|||||||
function createCircleThreePointHandle(
|
function createCircleThreePointHandle(
|
||||||
scale = 1,
|
scale = 1,
|
||||||
theme: Themes,
|
theme: Themes,
|
||||||
name: `circle-three-point-handle${'1' | '2' | '3'}`,
|
name:
|
||||||
|
| 'circle-three-point-handle1'
|
||||||
|
| 'circle-three-point-handle2'
|
||||||
|
| 'circle-three-point-handle3',
|
||||||
color?: number
|
color?: number
|
||||||
): Group {
|
): Group {
|
||||||
const circleCenterGroup = new Group()
|
const circleCenterGroup = new Group()
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import { parse, BinaryPart, Expr, resultIsOk, VariableMap } from '../lang/wasm'
|
import {
|
||||||
|
parse,
|
||||||
|
BinaryPart,
|
||||||
|
Expr,
|
||||||
|
ProgramMemory,
|
||||||
|
resultIsOk,
|
||||||
|
} from '../lang/wasm'
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -94,7 +100,7 @@ export function useCalc({
|
|||||||
newVariableInsertIndex: number
|
newVariableInsertIndex: number
|
||||||
setNewVariableName: (a: string) => void
|
setNewVariableName: (a: string) => void
|
||||||
} {
|
} {
|
||||||
const { variables } = useKclContext()
|
const { programMemory } = useKclContext()
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const selectionRange =
|
const selectionRange =
|
||||||
context.selectionRanges?.graphSelections[0]?.codeRef?.range
|
context.selectionRanges?.graphSelections[0]?.codeRef?.range
|
||||||
@ -121,7 +127,7 @@ export function useCalc({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (variables[newVariableName]) {
|
if (programMemory.has(newVariableName)) {
|
||||||
setIsNewVariableNameUnique(false)
|
setIsNewVariableNameUnique(false)
|
||||||
} else {
|
} else {
|
||||||
setIsNewVariableNameUnique(true)
|
setIsNewVariableNameUnique(true)
|
||||||
@ -129,14 +135,14 @@ export function useCalc({
|
|||||||
}, [newVariableName])
|
}, [newVariableName])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!variables || !selectionRange) return
|
if (!programMemory || !selectionRange) return
|
||||||
const varInfo = findAllPreviousVariables(
|
const varInfo = findAllPreviousVariables(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.variables,
|
kclManager.programMemory,
|
||||||
selectionRange
|
selectionRange
|
||||||
)
|
)
|
||||||
setAvailableVarInfo(varInfo)
|
setAvailableVarInfo(varInfo)
|
||||||
}, [kclManager.ast, kclManager.variables, selectionRange])
|
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
@ -144,9 +150,9 @@ export function useCalc({
|
|||||||
const pResult = parse(code)
|
const pResult = parse(code)
|
||||||
if (trap(pResult) || !resultIsOk(pResult)) return
|
if (trap(pResult) || !resultIsOk(pResult)) return
|
||||||
const ast = pResult.program
|
const ast = pResult.program
|
||||||
const _variables: VariableMap = {}
|
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
const error = (_variables[key] = {
|
const error = _programMem.set(key, {
|
||||||
type: 'String',
|
type: 'String',
|
||||||
value,
|
value,
|
||||||
__meta: [],
|
__meta: [],
|
||||||
@ -157,8 +163,8 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
isMock: true,
|
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||||
variables,
|
programMemoryOverride: kclManager.programMemory.clone(),
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
@ -168,7 +174,7 @@ export function useCalc({
|
|||||||
const init =
|
const init =
|
||||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
resultDeclaration?.declaration.init
|
resultDeclaration?.declaration.init
|
||||||
const result = execState.variables['__result__']?.value
|
const result = execState.memory?.get('__result__')?.value
|
||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
})
|
})
|
||||||
|
@ -196,7 +196,6 @@ function ReviewingButton() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
form="review-form"
|
form="review-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow"
|
className="w-fit !p-0 rounded-sm hover:shadow"
|
||||||
data-testid="command-bar-submit"
|
|
||||||
iconStart={{
|
iconStart={{
|
||||||
icon: 'checkmark',
|
icon: 'checkmark',
|
||||||
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
||||||
@ -215,7 +214,6 @@ function GatheringArgsButton() {
|
|||||||
type="submit"
|
type="submit"
|
||||||
form="arg-form"
|
form="arg-form"
|
||||||
className="w-fit !p-0 rounded-sm hover:shadow"
|
className="w-fit !p-0 rounded-sm hover:shadow"
|
||||||
data-testid="command-bar-continue"
|
|
||||||
iconStart={{
|
iconStart={{
|
||||||
icon: 'arrowRight',
|
icon: 'arrowRight',
|
||||||
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
bgClassName: 'p-1 rounded-sm !bg-primary hover:brightness-110',
|
||||||
|
@ -20,7 +20,6 @@ import { createIdentifier, createVariableDeclaration } from 'lang/modifyAst'
|
|||||||
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
|
import { useCodeMirror } from 'components/ModelingSidebar/ModelingPanes/CodeEditor'
|
||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
import { commandBarActor, useCommandBarState } from 'machines/commandBarMachine'
|
||||||
import toast from 'react-hot-toast'
|
|
||||||
|
|
||||||
const machineContextSelector = (snapshot?: {
|
const machineContextSelector = (snapshot?: {
|
||||||
context: Record<string, unknown>
|
context: Record<string, unknown>
|
||||||
@ -98,7 +97,6 @@ function CommandBarKclInput({
|
|||||||
value,
|
value,
|
||||||
initialVariableName,
|
initialVariableName,
|
||||||
})
|
})
|
||||||
|
|
||||||
const varMentionData: Completion[] = prevVariables.map((v) => ({
|
const varMentionData: Completion[] = prevVariables.map((v) => ({
|
||||||
label: v.key,
|
label: v.key,
|
||||||
detail: String(roundOff(v.value as number)),
|
detail: String(roundOff(v.value as number)),
|
||||||
@ -172,15 +170,7 @@ function CommandBarKclInput({
|
|||||||
|
|
||||||
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
|
function handleSubmit(e?: React.FormEvent<HTMLFormElement>) {
|
||||||
e?.preventDefault()
|
e?.preventDefault()
|
||||||
if (!canSubmit || valueNode === null) {
|
if (!canSubmit || valueNode === null) return
|
||||||
// Gotcha: Our application can attempt to submit a command value before the command bar kcl input is ready. Notify the scene and user.
|
|
||||||
if (!canSubmit) {
|
|
||||||
toast.error('Unable to submit command')
|
|
||||||
} else if (valueNode === null) {
|
|
||||||
toast.error('Unable to submit undefined command value')
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
onSubmit(
|
onSubmit(
|
||||||
createNewVariable
|
createNewVariable
|
||||||
|
@ -57,7 +57,7 @@ function CommandComboBox({
|
|||||||
onKeyDown={(event) => {
|
onKeyDown={(event) => {
|
||||||
if (
|
if (
|
||||||
(event.metaKey && event.key === 'k') ||
|
(event.metaKey && event.key === 'k') ||
|
||||||
event.key === 'Escape'
|
(event.key === 'Backspace' && !event.currentTarget.value)
|
||||||
) {
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
commandBarActor.send({ type: 'Close' })
|
commandBarActor.send({ type: 'Close' })
|
||||||
|