Compare commits
142 Commits
derive-doc
...
franknoiro
Author | SHA1 | Date | |
---|---|---|---|
df8977407c | |||
42016b7335 | |||
09f3e6e170 | |||
3747db01b8 | |||
bf1a42f6c0 | |||
b4d1a36bd9 | |||
b937934834 | |||
0208eecefa | |||
9999e4e62f | |||
b390e3e083 | |||
a572d7b0db | |||
1f405b9327 | |||
9a89926749 | |||
d7f834f23d | |||
0874891dd0 | |||
500d92d1fc | |||
59d0e079a1 | |||
bd10c65bdb | |||
b9862baed0 | |||
592c5259c6 | |||
039092ec24 | |||
950f5cebfd | |||
8a920b6cb7 | |||
c1db093e0f | |||
5f8ae22b04 | |||
dc7b901eb9 | |||
60dcf9d79e | |||
520f899ad4 | |||
41340c8bf0 | |||
a78ec6cd17 | |||
de526ae36e | |||
4a8e582865 | |||
1854064274 | |||
4a8897be4b | |||
4c0ea136e0 | |||
81c0035adc | |||
83f458fc36 | |||
c68e5d7774 | |||
ed8a0e4aaa | |||
322f398049 | |||
bc4d254297 | |||
10b7a772bf | |||
a686fe914b | |||
cba2349064 | |||
5ae92bcf5c | |||
ad8e306bdb | |||
508e1c919c | |||
58ec6100c4 | |||
11eceefedf | |||
11a678df5b | |||
22c0003eb1 | |||
8f0a40ba6e | |||
89b0ccb090 | |||
5d22308ad2 | |||
5580631c8f | |||
e1494c9f16 | |||
4a0d852a3c | |||
b213834316 | |||
1d8348c2cf | |||
2227287c9d | |||
680fc30682 | |||
40fb6a44d3 | |||
5713bfd9fa | |||
77902d550f | |||
db895d6801 | |||
3e1f8584ea | |||
2501a98cd9 | |||
e60b0e64ba | |||
3379cc489a | |||
a8b7328f65 | |||
ab6995bde3 | |||
6df5e70186 | |||
6b1cc36911 | |||
6a16e47491 | |||
f4f0533179 | |||
6360b8acac | |||
064a41d675 | |||
e075622a7f | |||
7a7929211a | |||
1f5f42963d | |||
235e6a1056 | |||
09cfbc1837 | |||
319029235c | |||
a7f4b0f037 | |||
d3afa38bd5 | |||
45416df215 | |||
ee54cdd27a | |||
84ae567f37 | |||
f94671f1bb | |||
bcf3790266 | |||
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 |
@ -33,7 +33,14 @@ helix(revolutions: number, angle_start: number, ccw?: bool, radius: number, axis
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Create a helix around the Z axis.
|
// Create a helix around the Z axis.
|
||||||
helixPath = helix(angleStart = 0, ccw = true, revolutions = 5, length = 10, radius = 5, axis = 'Z')
|
helixPath = helix(
|
||||||
|
angleStart = 0,
|
||||||
|
ccw = true,
|
||||||
|
revolutions = 5,
|
||||||
|
length = 10,
|
||||||
|
radius = 5,
|
||||||
|
axis = 'Z',
|
||||||
|
)
|
||||||
|
|
||||||
// Create a spring by sweeping around the helix path.
|
// Create a spring by sweeping around the helix path.
|
||||||
springSketch = startSketchOn('YZ')
|
springSketch = startSketchOn('YZ')
|
||||||
@ -49,7 +56,14 @@ helper001 = startSketchOn('XZ')
|
|||||||
|> startProfileAt([0, 0], %)
|
|> startProfileAt([0, 0], %)
|
||||||
|> line(end = [0, 10], tag = $edge001)
|
|> line(end = [0, 10], tag = $edge001)
|
||||||
|
|
||||||
helixPath = helix(angleStart = 0, ccw = true, revolutions = 5, length = 10, radius = 5, axis = edge001)
|
helixPath = helix(
|
||||||
|
angleStart = 0,
|
||||||
|
ccw = true,
|
||||||
|
revolutions = 5,
|
||||||
|
length = 10,
|
||||||
|
radius = 5,
|
||||||
|
axis = edge001,
|
||||||
|
)
|
||||||
|
|
||||||
// Create a spring by sweeping around the helix path.
|
// Create a spring by sweeping around the helix path.
|
||||||
springSketch = startSketchOn('XY')
|
springSketch = startSketchOn('XY')
|
||||||
@ -61,12 +75,19 @@ springSketch = startSketchOn('XY')
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
// Create a helix around a custom axis.
|
// Create a helix around a custom axis.
|
||||||
helixPath = helix(angleStart = 0, ccw = true, revolutions = 5, length = 10, radius = 5, axis = {
|
helixPath = helix(
|
||||||
|
angleStart = 0,
|
||||||
|
ccw = true,
|
||||||
|
revolutions = 5,
|
||||||
|
length = 10,
|
||||||
|
radius = 5,
|
||||||
|
axis = {
|
||||||
custom = {
|
custom = {
|
||||||
axis = [0, 0, 1.0],
|
axis = [0, 0, 1.0],
|
||||||
origin = [0, 0.25, 0]
|
origin = [0, 0.25, 0]
|
||||||
}
|
}
|
||||||
})
|
},
|
||||||
|
)
|
||||||
|
|
||||||
// Create a spring by sweeping around the helix path.
|
// Create a spring by sweeping around the helix path.
|
||||||
springSketch = startSketchOn('XY')
|
springSketch = startSketchOn('XY')
|
||||||
|
@ -17,7 +17,7 @@ lastSegX(sketch: Sketch) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | A sketch is a collection of paths. | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | The sketch whose line segment is being queried | 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) | A sketch is a collection of paths. | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | The sketch whose line segment is being queried | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
@ -57,3 +57,55 @@ 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
|
||||||
|
@ -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(data: CircularPattern2dData, sketch_set: SketchSet) -> [Sketch]
|
patternCircular2d(sketch_set: SketchSet, instances: integer, center: [number], arc_degrees: number, rotate_duplicates: bool, use_original?: bool) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,8 +17,12 @@ patternCircular2d(data: CircularPattern2dData, sketch_set: SketchSet) -> [Sketch
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `data` | [`CircularPattern2dData`](/docs/kcl/types/CircularPattern2dData) | Data for a circular pattern on a 2D sketch. | Yes |
|
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
||||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | 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 |
|
||||||
|
| `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
|
||||||
|
|
||||||
@ -34,12 +38,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(data: LinearPattern2dData, sketch_set: SketchSet, use_original?: bool) -> [Sketch]
|
patternLinear2d(sketch_set: SketchSet, instances: integer, distance: number, axis: [number], use_original?: bool) -> [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -17,9 +17,11 @@ patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet, use_original?:
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `data` | [`LinearPattern2dData`](/docs/kcl/types/LinearPattern2dData) | Data for a linear pattern on a 2D sketch. | Yes |
|
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
||||||
| `sketch_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | 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 |
|
||||||
| `use_original` | `bool` | | No |
|
| `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
|
||||||
|
|
||||||
@ -31,11 +33,7 @@ patternLinear2d(data: LinearPattern2dData, sketch_set: SketchSet, use_original?:
|
|||||||
```js
|
```js
|
||||||
exampleSketch = startSketchOn('XZ')
|
exampleSketch = startSketchOn('XZ')
|
||||||
|> circle({ center = [0, 0], radius = 1 }, %)
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|> patternLinear2d({
|
|> patternLinear2d(axis = [1, 0], instances = 7, distance = 4)
|
||||||
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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | 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) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
54550
docs/kcl/std.json
@ -59,7 +59,14 @@ sweepSketch = startSketchOn('XY')
|
|||||||
|
|
||||||
|
|
||||||
// Create a helix around the Z axis.
|
// Create a helix around the Z axis.
|
||||||
helixPath = helix(angleStart = 0, ccw = true, revolutions = 4, length = 10, radius = 5, axis = 'Z')
|
helixPath = helix(
|
||||||
|
angleStart = 0,
|
||||||
|
ccw = true,
|
||||||
|
revolutions = 4,
|
||||||
|
length = 10,
|
||||||
|
radius = 5,
|
||||||
|
axis = 'Z',
|
||||||
|
)
|
||||||
|
|
||||||
// Create a spring by sweeping around the helix path.
|
// Create a spring by sweeping around the helix path.
|
||||||
springSketch = startSketchOn('YZ')
|
springSketch = startSketchOn('YZ')
|
||||||
|
@ -17,7 +17,7 @@ tangentToEnd(tag: TagIdentifier) -> number
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | | Yes |
|
| `tag` | [`TagIdentifier`](/docs/kcl/types#tag-identifier) | The line segment being queried by its tag | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
16
docs/kcl/types/EnvironmentRef.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
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 |
|
||||||
|
@ -311,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` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
| `memory` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| Any KCL value. | No |
|
||||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
@ -351,6 +351,23 @@ 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 |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -98,6 +98,29 @@ 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,6 +17,5 @@ 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 |
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -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 |
|
||||||
|
16
docs/kcl/types/SnapshotRef.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
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`)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,23 +54,26 @@ async function doBasicSketch(
|
|||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator).toContainText(
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||||
} else {
|
} else {
|
||||||
@ -79,8 +82,10 @@ async function doBasicSketch(
|
|||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||||
@ -137,8 +142,10 @@ async function doBasicSketch(
|
|||||||
|
|
||||||
// Open the code pane.
|
// Open the code pane.
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
await expect(u.codeLocator)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|
commonPoints.startAt
|
||||||
|
}, sketch001)
|
||||||
|> xLine(${commonPoints.num1}, %, $seg01)
|
|> xLine(${commonPoints.num1}, %, $seg01)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(-segLen(seg01), %)`)
|
|> xLine(-segLen(seg01), %)`)
|
||||||
|
@ -43,8 +43,7 @@ test.describe(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn('${plane}')
|
const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.9, -1.22], sketch001)`
|
||||||
|> startProfileAt([0.9, -1.22], %)`
|
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
@ -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 { uuidv4 } from 'lib/utils'
|
import { isArray, uuidv4 } from 'lib/utils'
|
||||||
import {
|
import {
|
||||||
closeDebugPanel,
|
closeDebugPanel,
|
||||||
doAndWaitForImageDiff,
|
doAndWaitForImageDiff,
|
||||||
@ -9,13 +9,15 @@ import {
|
|||||||
sendCustomCmd,
|
sendCustomCmd,
|
||||||
} from '../test-utils'
|
} from '../test-utils'
|
||||||
|
|
||||||
type mouseParams = {
|
type MouseParams = {
|
||||||
pixelDiff?: number
|
pixelDiff?: number
|
||||||
|
shouldDbClick?: boolean
|
||||||
|
delay?: number
|
||||||
}
|
}
|
||||||
type mouseDragToParams = mouseParams & {
|
type MouseDragToParams = MouseParams & {
|
||||||
fromPoint: { x: number; y: number }
|
fromPoint: { x: number; y: number }
|
||||||
}
|
}
|
||||||
type mouseDragFromParams = mouseParams & {
|
type MouseDragFromParams = MouseParams & {
|
||||||
toPoint: { x: number; y: number }
|
toPoint: { x: number; y: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +28,12 @@ type SceneSerialised = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
type ClickHandler = (clickParams?: MouseParams) => Promise<void | boolean>
|
||||||
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
type MoveHandler = (moveParams?: MouseParams) => Promise<void | boolean>
|
||||||
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
type DblClickHandler = (clickParams?: MouseParams) => Promise<void | boolean>
|
||||||
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
type DragToHandler = (dragParams: MouseDragToParams) => Promise<void | boolean>
|
||||||
type DragFromHandler = (
|
type DragFromHandler = (
|
||||||
dragParams: mouseDragFromParams
|
dragParams: MouseDragFromParams
|
||||||
) => Promise<void | boolean>
|
) => Promise<void | boolean>
|
||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
@ -77,17 +79,26 @@ export class SceneFixture {
|
|||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: MouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
() => this.page.mouse.click(x, y),
|
() =>
|
||||||
|
clickParams?.shouldDbClick
|
||||||
|
? this.page.mouse.dblclick(x, y, {
|
||||||
|
delay: clickParams?.delay || 0,
|
||||||
|
})
|
||||||
|
: this.page.mouse.click(x, y, {
|
||||||
|
delay: clickParams?.delay || 0,
|
||||||
|
}),
|
||||||
clickParams.pixelDiff
|
clickParams.pixelDiff
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return this.page.mouse.click(x, y)
|
return clickParams?.shouldDbClick
|
||||||
|
? this.page.mouse.dblclick(x, y, { delay: clickParams?.delay || 0 })
|
||||||
|
: this.page.mouse.click(x, y, { delay: clickParams?.delay || 0 })
|
||||||
},
|
},
|
||||||
(moveParams?: mouseParams) => {
|
(moveParams?: MouseParams) => {
|
||||||
if (moveParams?.pixelDiff) {
|
if (moveParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -97,7 +108,7 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
(clickParams?: mouseParams) => {
|
(clickParams?: MouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -114,7 +125,7 @@ export class SceneFixture {
|
|||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [DragToHandler, DragFromHandler] =>
|
): [DragToHandler, DragFromHandler] =>
|
||||||
[
|
[
|
||||||
(dragToParams: mouseDragToParams) => {
|
(dragToParams: MouseDragToParams) => {
|
||||||
if (dragToParams?.pixelDiff) {
|
if (dragToParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -131,7 +142,7 @@ export class SceneFixture {
|
|||||||
targetPosition: { x, y },
|
targetPosition: { x, y },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
(dragFromParams: mouseDragFromParams) => {
|
(dragFromParams: MouseDragFromParams) => {
|
||||||
if (dragFromParams?.pixelDiff) {
|
if (dragFromParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -219,7 +230,7 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expectPixelColor = async (
|
expectPixelColor = async (
|
||||||
colour: [number, number, number],
|
colour: [number, number, number] | [number, number, number][],
|
||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) => {
|
) => {
|
||||||
@ -241,22 +252,36 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isColourArray(
|
||||||
|
colour: [number, number, number] | [number, number, number][]
|
||||||
|
): colour is [number, number, number][] {
|
||||||
|
return isArray(colour[0])
|
||||||
|
}
|
||||||
|
|
||||||
export async function expectPixelColor(
|
export async function expectPixelColor(
|
||||||
page: Page,
|
page: Page,
|
||||||
colour: [number, number, number],
|
colour: [number, number, number] | [number, number, number][],
|
||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) {
|
) {
|
||||||
let finalValue = colour
|
let finalValue = colour
|
||||||
await expect
|
await expect
|
||||||
.poll(async () => {
|
.poll(
|
||||||
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
async () => {
|
||||||
if (!pixel) return null
|
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
||||||
finalValue = pixel
|
if (!pixel) return null
|
||||||
return pixel.every(
|
finalValue = pixel
|
||||||
(channel, index) => Math.abs(channel - colour[index]) < diff
|
if (!isColourArray(colour)) {
|
||||||
)
|
return pixel.every(
|
||||||
})
|
(channel, index) => Math.abs(channel - colour[index]) < diff
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return colour.some((c) =>
|
||||||
|
c.every((channel, index) => Math.abs(pixel[index] - channel) < diff)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ timeout: 10_000 }
|
||||||
|
)
|
||||||
.toBeTruthy()
|
.toBeTruthy()
|
||||||
.catch((cause) => {
|
.catch((cause) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -23,7 +23,10 @@ export class ToolbarFixture {
|
|||||||
helixButton!: Locator
|
helixButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
|
tangentialArcBtn!: Locator
|
||||||
|
circleBtn!: Locator
|
||||||
rectangleBtn!: Locator
|
rectangleBtn!: Locator
|
||||||
|
lengthConstraintBtn!: Locator
|
||||||
exitSketchBtn!: Locator
|
exitSketchBtn!: Locator
|
||||||
editSketchBtn!: Locator
|
editSketchBtn!: Locator
|
||||||
fileTreeBtn!: Locator
|
fileTreeBtn!: Locator
|
||||||
@ -53,7 +56,10 @@ export class ToolbarFixture {
|
|||||||
this.helixButton = page.getByTestId('helix')
|
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.circleBtn = page.getByTestId('circle-center')
|
||||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||||
|
this.lengthConstraintBtn = page.getByTestId('constraint-length')
|
||||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||||
@ -119,6 +125,25 @@ export class ToolbarFixture {
|
|||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
selectCenterRectangle = async () => {
|
||||||
|
await this.page
|
||||||
|
.getByRole('button', { name: 'caret down Corner rectangle:' })
|
||||||
|
.click()
|
||||||
|
await expect(
|
||||||
|
this.page.getByTestId('dropdown-center-rectangle')
|
||||||
|
).toBeVisible()
|
||||||
|
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)
|
||||||
|
@ -219,18 +219,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
'startProfileAt([205.96, 254.59], sketch002)',
|
||||||
|> angledLine([
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
105.26
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
], %, $rectangleSegmentB001)
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
||||||
|> angledLine([
|
|>close()`,
|
||||||
segAng(rectangleSegmentA002),
|
|
||||||
-segLen(rectangleSegmentA002)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()`,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -251,19 +246,15 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
'startProfileAt([-209.64, 255.28], sketch003)',
|
||||||
|> angledLine([
|
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)
|
||||||
106.84
|
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)
|
||||||
], %, $rectangleSegmentB002)
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
||||||
|> angledLine([
|
|>close()`,
|
||||||
segAng(rectangleSegmentA003),
|
|
||||||
-segLen(rectangleSegmentA003)
|
|
||||||
], %, $rectangleSegmentC002)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()`,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
clickCoords: { x: 677, y: 87 },
|
clickCoords: { x: 677, y: 87 },
|
||||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||||
@ -276,19 +267,14 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
]
|
]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch003 = startSketchOn(extrude001, seg04)',
|
'sketch004 = startSketchOn(extrude001, seg05)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([75.8, 317.2], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
'startProfileAt([82.57, 322.96], sketch004)',
|
||||||
|> angledLine([
|
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004)
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)
|
||||||
106.84
|
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)
|
||||||
], %, $rectangleSegmentB002)
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
||||||
|> angledLine([
|
|>close()`,
|
||||||
segAng(rectangleSegmentA003),
|
|
||||||
-segLen(rectangleSegmentA003)
|
|
||||||
], %, $rectangleSegmentC002)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()`,
|
|
||||||
})
|
})
|
||||||
/// last one
|
/// last one
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -301,104 +287,98 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch005 = startSketchOn(extrude001, seg06)',
|
'sketch005 = startSketchOn(extrude001, seg06)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
'startProfileAt([-23.43, 19.69], sketch005)',
|
||||||
|
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
||||||
|> angledLine([
|
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)
|
||||||
segAng(rectangleSegmentA005) - 90,
|
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)
|
||||||
84.07
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
||||||
], %, $rectangleSegmentB004)
|
|>close()`,
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA005),
|
|
||||||
-segLen(rectangleSegmentA005)
|
|
||||||
], %, $rectangleSegmentC004)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> 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 () => {
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||||
|
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
217.26
|
||||||
|
], %, $seg01)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %, $yo)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
||||||
|
|> close()
|
||||||
|
extrude001 = extrude(sketch001, length = 100)
|
||||||
|
|> chamfer({
|
||||||
|
length = 30,
|
||||||
|
tags = [getOppositeEdge(seg01)]
|
||||||
|
}, %, $seg03)
|
||||||
|
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|
||||||
|
|> chamfer({
|
||||||
|
length = 30,
|
||||||
|
tags = [getNextAdjacentEdge(seg02)]
|
||||||
|
}, %, $seg05)
|
||||||
|
|> chamfer({
|
||||||
|
length = 30,
|
||||||
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
|
}, %, $seg06)
|
||||||
|
sketch005 = startSketchOn(extrude001, seg06)
|
||||||
|
profile004 = startProfileAt([-23.43, 19.69], sketch005)
|
||||||
|
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA005) - 90,
|
||||||
|
84.07
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA005),
|
||||||
|
-segLen(rectangleSegmentA005)
|
||||||
|
], %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
sketch004 = startSketchOn(extrude001, seg05)
|
||||||
|
profile003 = startProfileAt([82.57, 322.96], sketch004)
|
||||||
|
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA004) - 90,
|
||||||
|
103.07
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA004),
|
||||||
|
-segLen(rectangleSegmentA004)
|
||||||
|
], %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
sketch003 = startSketchOn(extrude001, seg04)
|
||||||
|
profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
||||||
|
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003) - 90,
|
||||||
|
106.84
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003),
|
||||||
|
-segLen(rectangleSegmentA003)
|
||||||
|
], %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||||
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|
105.26
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
|
||||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
`,
|
||||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|
||||||
217.26
|
|
||||||
], %, $seg01)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001),
|
|
||||||
-segLen(rectangleSegmentA001)
|
|
||||||
], %, $yo)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)], tag = $seg02)
|
|
||||||
|> close()
|
|
||||||
extrude001 = extrude(sketch001, length = 100)
|
|
||||||
|> chamfer({
|
|
||||||
length = 30,
|
|
||||||
tags = [getOppositeEdge(seg01)]
|
|
||||||
}, %, $seg03)
|
|
||||||
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|
|
||||||
|> chamfer({
|
|
||||||
length = 30,
|
|
||||||
tags = [getNextAdjacentEdge(seg02)]
|
|
||||||
}, %, $seg05)
|
|
||||||
|> chamfer({
|
|
||||||
length = 30,
|
|
||||||
tags = [getNextAdjacentEdge(yo)]
|
|
||||||
}, %, $seg06)
|
|
||||||
sketch005 = startSketchOn(extrude001, seg06)
|
|
||||||
|> startProfileAt([-23.43,19.69], %)
|
|
||||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA005) - 90,
|
|
||||||
84.07
|
|
||||||
], %, $rectangleSegmentB004)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA005),
|
|
||||||
-segLen(rectangleSegmentA005)
|
|
||||||
], %, $rectangleSegmentC004)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
sketch004 = startSketchOn(extrude001, seg05)
|
|
||||||
|> startProfileAt([82.57,322.96], %)
|
|
||||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA004) - 90,
|
|
||||||
103.07
|
|
||||||
], %, $rectangleSegmentB003)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA004),
|
|
||||||
-segLen(rectangleSegmentA004)
|
|
||||||
], %, $rectangleSegmentC003)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
sketch003 = startSketchOn(extrude001, seg04)
|
|
||||||
|> startProfileAt([-209.64,255.28], %)
|
|
||||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|
||||||
106.84
|
|
||||||
], %, $rectangleSegmentB002)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA003),
|
|
||||||
-segLen(rectangleSegmentA003)
|
|
||||||
], %, $rectangleSegmentC002)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
|
||||||
|> startProfileAt([205.96,254.59], %)
|
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|
||||||
105.26
|
|
||||||
], %, $rectangleSegmentB001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA002),
|
|
||||||
-segLen(rectangleSegmentA002)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()
|
|
||||||
`,
|
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -443,18 +423,13 @@ test.describe('Point-and-click tests', { tag: ['@skipWin'] }, () => {
|
|||||||
beforeChamferSnippetEnd: '}, extrude001)',
|
beforeChamferSnippetEnd: '}, extrude001)',
|
||||||
afterChamferSelectSnippet:
|
afterChamferSelectSnippet:
|
||||||
'sketch002 = startSketchOn(extrude001, seg03)',
|
'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
afterRectangle1stClickSnippet:
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
'startProfileAt([205.96, 254.59], sketch002)',
|
||||||
|> angledLine([
|
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
||||||
105.26
|
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
||||||
], %, $rectangleSegmentB001)
|
|>line(endAbsolute=[profileStartX(%),profileStartY(%)])
|
||||||
|> angledLine([
|
|>close()`,
|
||||||
segAng(rectangleSegmentA002),
|
|
||||||
-segLen(rectangleSegmentA002)
|
|
||||||
], %, $rectangleSegmentC001)
|
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|
||||||
|> close()`,
|
|
||||||
})
|
})
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
@ -484,17 +459,17 @@ chamf = chamfer({
|
|||||||
]
|
]
|
||||||
}, %)
|
}, %)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|> startProfileAt([205.96, 254.59], %)
|
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002) - 90,
|
segAng(rectangleSegmentA002) - 90,
|
||||||
105.26
|
105.26
|
||||||
], %, $rectangleSegmentB001)
|
], %)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %, $rectangleSegmentC001)
|
], %)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute=[profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
`,
|
`,
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
@ -561,10 +536,10 @@ sketch002 = startSketchOn(extrude001, seg03)
|
|||||||
|
|
||||||
const expectedCodeSnippets = {
|
const expectedCodeSnippets = {
|
||||||
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
||||||
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
|
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], sketch001)`,
|
||||||
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
||||||
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
|
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], sketch001)`,
|
||||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], sketch001)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
@ -605,6 +580,7 @@ sketch002 = startSketchOn(extrude001, seg03)
|
|||||||
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 ({
|
||||||
@ -1117,13 +1093,14 @@ openSketch = startSketchOn('XY')
|
|||||||
}) => {
|
}) => {
|
||||||
// One dumb hardcoded screen pixel value
|
// One dumb hardcoded screen pixel value
|
||||||
const testPoint = { x: 620, y: 257 }
|
const testPoint = { x: 620, y: 257 }
|
||||||
const expectedOutput = `helix001 = helix(revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5)`
|
const expectedOutput = `helix001 = helix( revolutions = 1, angleStart = 360, counterClockWise = false, radius = 5, axis = 'X', length = 5,)`
|
||||||
|
const expectedLine = `revolutions=1,`
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
await test.step(`Look for the red of the default plane`, async () => {
|
// await test.step(`Look for the red of the default plane`, async () => {
|
||||||
await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
// await scene.expectPixelColor([96, 52, 52], testPoint, 15)
|
||||||
})
|
// })
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
await toolbar.helixButton.click()
|
await toolbar.helixButton.click()
|
||||||
await cmdBar.expectState({
|
await cmdBar.expectState({
|
||||||
@ -1154,7 +1131,7 @@ openSketch = startSketchOn('XY')
|
|||||||
await editor.expectEditor.toContain(expectedOutput)
|
await editor.expectEditor.toContain(expectedOutput)
|
||||||
await editor.expectState({
|
await editor.expectState({
|
||||||
diagnostics: [],
|
diagnostics: [],
|
||||||
activeLines: [expectedOutput],
|
activeLines: [expectedLine],
|
||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
// Red plane is now gone, white helix is there
|
// Red plane is now gone, white helix is there
|
||||||
@ -1396,12 +1373,12 @@ 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)
|
||||||
})
|
})
|
||||||
|
|
||||||
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: [],
|
||||||
|
@ -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 })
|
||||||
|
@ -444,8 +444,7 @@ test(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||||
|> startProfileAt([7.19, -9.7], %)`
|
|
||||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -456,7 +455,9 @@ test(
|
|||||||
mask: [page.getByTestId('model-state-indicator')],
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
const lineEndClick = () =>
|
||||||
|
page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
await lineEndClick()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -467,6 +468,15 @@ 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
|
||||||
|
await page.mouse.move(813, 392, { steps: 10 })
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
@ -589,8 +599,7 @@ test(
|
|||||||
mask: [page.getByTestId('model-state-indicator')],
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')profile001 = circle({ center = [14.44, -2.44], radius = 1 }, sketch001)`
|
||||||
|> circle({ center = [14.44, -2.44], radius = 1 }, %)`
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -634,8 +643,7 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
||||||
|> startProfileAt([7.19, -9.7], %)`
|
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -653,6 +661,10 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.click(813, 392)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -739,8 +751,7 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `
|
code += `profile001 = startProfileAt([182.59, -246.32], sketch001)`
|
||||||
|> startProfileAt([182.59, -246.32], %)`
|
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -758,6 +769,10 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.click(813, 392)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 60 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 49 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 76 KiB |
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 127 KiB After Width: | Height: | Size: 127 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 |
@ -1,6 +1,7 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from './zoo-test'
|
||||||
|
|
||||||
import { commonPoints, getUtils } from './test-utils'
|
import { commonPoints, getUtils } from './test-utils'
|
||||||
|
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||||
|
import { uuidv4 } from 'lib/utils'
|
||||||
|
|
||||||
test.describe('Test network and connection issues', () => {
|
test.describe('Test network and connection issues', () => {
|
||||||
test(
|
test(
|
||||||
@ -111,18 +112,17 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|
||||||
|
|
||||||
// Expect the network to be up
|
// Expect the network to be up
|
||||||
await expect(networkToggle).toContainText('Connected')
|
await expect(networkToggle).toContainText('Connected')
|
||||||
@ -169,7 +169,9 @@ test.describe('Test network and connection issues', () => {
|
|||||||
await page.mouse.click(100, 100)
|
await page.mouse.click(100, 100)
|
||||||
|
|
||||||
// select a line
|
// select a line
|
||||||
await page.getByText(`startProfileAt(${commonPoints.startAt}, %)`).click()
|
await page
|
||||||
|
.getByText(`startProfileAt(${commonPoints.startAt}, sketch001)`)
|
||||||
|
.click()
|
||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await u.doAndWaitForCmd(
|
await u.doAndWaitForCmd(
|
||||||
@ -183,11 +185,36 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(150)
|
await page.waitForTimeout(150)
|
||||||
|
|
||||||
|
const camCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_look_at',
|
||||||
|
center: { x: 109, y: 0, z: -152 },
|
||||||
|
vantage: { x: 115, y: -505, z: -152 },
|
||||||
|
up: { x: 0, y: 0, z: 1 },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const updateCamCommand: EngineCommand = {
|
||||||
|
type: 'modeling_cmd_req',
|
||||||
|
cmd_id: uuidv4(),
|
||||||
|
cmd: {
|
||||||
|
type: 'default_camera_get_settings',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await u.sendCustomCmd(camCommand)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
await u.sendCustomCmd(updateCamCommand)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
|
// click to continue profile
|
||||||
|
await page.mouse.click(1007, 400)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
// Ensure we can continue sketching
|
// Ensure we can continue sketching
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
await expect.poll(u.normalisedEditorCode)
|
await expect.poll(u.normalisedEditorCode)
|
||||||
.toBe(`sketch001 = startSketchOn('XZ')
|
.toBe(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([12.34, -12.34], %)
|
profile001 = startProfileAt([12.34, -12.34], sketch001)
|
||||||
|> xLine(12.34, %)
|
|> xLine(12.34, %)
|
||||||
|> line(end = [-12.34, 12.34])
|
|> line(end = [-12.34, 12.34])
|
||||||
|
|
||||||
@ -197,7 +224,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode)
|
await expect.poll(u.normalisedEditorCode)
|
||||||
.toBe(`sketch001 = startSketchOn('XZ')
|
.toBe(`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([12.34, -12.34], %)
|
profile001 = startProfileAt([12.34, -12.34], sketch001)
|
||||||
|> xLine(12.34, %)
|
|> xLine(12.34, %)
|
||||||
|> line(end = [-12.34, 12.34])
|
|> line(end = [-12.34, 12.34])
|
||||||
|> xLine(-12.34, %)
|
|> xLine(-12.34, %)
|
||||||
|
@ -19,7 +19,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
|||||||
|> line(end = [20, 0])
|
|> line(end = [20, 0])
|
||||||
|> line(end = [0, 20])
|
|> line(end = [0, 20])
|
||||||
|> xLine(-20, %)
|
|> xLine(-20, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -673,7 +673,7 @@ test.describe('Testing constraints', { tag: ['@skipWin'] }, () => {
|
|||||||
},
|
},
|
||||||
] as const
|
] as const
|
||||||
for (const { testName, addVariable, value, constraint } of cases) {
|
for (const { testName, addVariable, value, constraint } of cases) {
|
||||||
test(`${testName}`, async ({ context, homePage, page }) => {
|
test(`${testName}`, async ({ context, homePage, page, editor }) => {
|
||||||
// constants and locators
|
// constants and locators
|
||||||
const cmdBarKclInput = page
|
const cmdBarKclInput = page
|
||||||
.getByTestId('cmd-bar-arg-value')
|
.getByTestId('cmd-bar-arg-value')
|
||||||
@ -706,7 +706,9 @@ part002 = startSketchOn('XZ')
|
|||||||
await page.setBodyDimensions({ width: 1200, height: 500 })
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
|
||||||
await homePage.goToModelingScene()
|
await homePage.goToModelingScene()
|
||||||
|
await u.waitForPageLoad()
|
||||||
|
|
||||||
|
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.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
|
|
||||||
|
@ -66,33 +66,34 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
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)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)`)
|
)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
commonPoints.startAt
|
||||||
|> xLine(${commonPoints.num1}, %)
|
}, sketch001)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')
|
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
||||||
|> startProfileAt(${commonPoints.startAt}, %)
|
commonPoints.startAt
|
||||||
|> xLine(${commonPoints.num1}, %)
|
}, sketch001)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
@ -259,66 +260,88 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'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], tag = $seg02)
|
||||||
|> line(end = [170.36, -121.61], tag = $seg01)
|
|> line(end = [170.36, -121.61], tag = $seg01)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
extrude001 = extrude(sketch001, length = 50)
|
extrude001 = extrude(sketch001, length = 50)
|
||||||
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], tag = $seg03)
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
extrude002 = extrude(sketch002, length = 50)
|
extrude002 = extrude(sketch002, length = 50)
|
||||||
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(sketch004, length = 20)
|
||||||
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')
|
||||||
|
profile001 = circle({
|
||||||
|
center = [42.91, -70.42],
|
||||||
|
radius = 17.96
|
||||||
|
}, sketch006)
|
||||||
|
profile002 = startProfileAt([86.92, -63.81], sketch006)
|
||||||
|
|> angledLine([0, 63.81], %, $rectangleSegmentA001)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001) - 90,
|
||||||
|
17.05
|
||||||
|
], %)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA001),
|
||||||
|
-segLen(rectangleSegmentA001)
|
||||||
|
], %)
|
||||||
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|
|> close()
|
||||||
|
profile003 = startProfileAt([40.16, -120.48], sketch006)
|
||||||
|
|> line(end = [26.95, 24.21])
|
||||||
|
|> line(end = [20.91, -28.61])
|
||||||
|
|> line(end = [32.46, 18.71])
|
||||||
|
|
||||||
|
`
|
||||||
)
|
)
|
||||||
}, KCL_DEFAULT_LENGTH)
|
}, KCL_DEFAULT_LENGTH)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
@ -347,9 +370,10 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const revolve = { x: 646, y: 248 }
|
const revolve = { x: 635, y: 253 }
|
||||||
const parentExtrude = { x: 915, y: 133 }
|
const parentExtrude = { x: 915, y: 133 }
|
||||||
const solid2d = { x: 770, y: 167 }
|
const solid2d = { x: 770, y: 167 }
|
||||||
|
const individualProfile = { x: 694, y: 432 }
|
||||||
|
|
||||||
// DELETE REVOLVE
|
// DELETE REVOLVE
|
||||||
await page.mouse.click(revolve.x, revolve.y)
|
await page.mouse.click(revolve.x, revolve.y)
|
||||||
@ -415,6 +439,20 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
|
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
|
||||||
|
|
||||||
|
// Delete a single profile
|
||||||
|
await page.mouse.click(individualProfile.x, individualProfile.y)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
const codeToBeDeletedSnippet =
|
||||||
|
'profile003 = startProfileAt([40.16, -120.48], sketch006)'
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(
|
||||||
|
' |> line(end = [20.91, -28.61])'
|
||||||
|
)
|
||||||
|
await u.clearCommandLogs()
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||||
|
await page.waitForTimeout(200)
|
||||||
|
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
|
||||||
})
|
})
|
||||||
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
||||||
page,
|
page,
|
||||||
@ -1216,12 +1254,15 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
|
const firstClickCoords = { x: 650, y: 200 } as const
|
||||||
// Place a point because the line tool will exit if no points are pressed
|
// Place a point because the line tool will exit if no points are pressed
|
||||||
await page.mouse.click(650, 200)
|
await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
|
||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
// Code before exiting the tool
|
// Code before exiting the tool
|
||||||
let previousCodeContent = await page.locator('.cm-content').innerText()
|
let previousCodeContent = (
|
||||||
|
await page.locator('.cm-content').innerText()
|
||||||
|
).replace(/\s+/g, '')
|
||||||
|
|
||||||
// deselect the line tool by clicking it
|
// deselect the line tool by clicking it
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
@ -1233,14 +1274,23 @@ test.describe('Testing selections', { tag: ['@skipWin'] }, () => {
|
|||||||
await page.mouse.click(750, 200)
|
await page.mouse.click(750, 200)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// expect no change
|
await expect
|
||||||
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
|
.poll(async () => {
|
||||||
|
let str = await page.locator('.cm-content').innerText()
|
||||||
|
str = str.replace(/\s+/g, '')
|
||||||
|
return str
|
||||||
|
})
|
||||||
|
.toBe(previousCodeContent)
|
||||||
|
|
||||||
// select line tool again
|
// select line tool again
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
|
// Click to continue profile
|
||||||
|
await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
|
||||||
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// line tool should work as expected again
|
// line tool should work as expected again
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(
|
await expect(page.locator('.cm-content')).not.toHaveText(
|
||||||
|
@ -209,8 +209,13 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
// Draw a line
|
// Draw a line
|
||||||
await page.mouse.move(700, 200, { steps: 5 })
|
await page.mouse.move(700, 200, { steps: 5 })
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
await page.mouse.move(800, 250, { steps: 5 })
|
|
||||||
await page.mouse.click(800, 250)
|
const secondMousePosition = { x: 800, y: 250 }
|
||||||
|
|
||||||
|
await page.mouse.move(secondMousePosition.x, secondMousePosition.y, {
|
||||||
|
steps: 5,
|
||||||
|
})
|
||||||
|
await page.mouse.click(secondMousePosition.x, secondMousePosition.y)
|
||||||
// Unequip line tool
|
// Unequip line tool
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
// Make sure we didn't pop out of sketch mode.
|
// Make sure we didn't pop out of sketch mode.
|
||||||
@ -219,11 +224,23 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
// Equip arc tool
|
// Equip arc tool
|
||||||
await page.keyboard.press('a')
|
await page.keyboard.press('a')
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
|
// click in the same position again to continue the profile
|
||||||
|
await page.mouse.move(secondMousePosition.x, secondMousePosition.y, {
|
||||||
|
steps: 5,
|
||||||
|
})
|
||||||
|
await page.mouse.click(secondMousePosition.x, secondMousePosition.y)
|
||||||
|
|
||||||
await page.mouse.move(1000, 100, { steps: 5 })
|
await page.mouse.move(1000, 100, { steps: 5 })
|
||||||
await page.mouse.click(1000, 100)
|
await page.mouse.click(1000, 100)
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
await page.keyboard.press('l')
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
||||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
await expect
|
||||||
|
.poll(async () => {
|
||||||
|
await page.keyboard.press('l')
|
||||||
|
return lineButton.getAttribute('aria-pressed')
|
||||||
|
})
|
||||||
|
.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.
|
||||||
@ -519,9 +536,9 @@ 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)
|
||||||
|> startProfileAt([-12.94, 6.6], %)
|
profile001 = startProfileAt([-12.34, 12.34], sketch002)
|
||||||
|> line(end = [2.45, -0.2])
|
|> line(end = [12.34, -12.34])
|
||||||
|> line(end = [-2.6, -1.25])
|
|> line(end = [-12.34, -12.34])
|
||||||
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
|> line(endAbsolute = [profileStartX(%), profileStartY(%)])
|
||||||
|> close()
|
|> close()
|
||||||
`)
|
`)
|
||||||
@ -537,9 +554,8 @@ extrude001 = extrude(sketch001, length = 5 + 7)`
|
|||||||
await page.getByText('startProfileAt([-12').click()
|
await page.getByText('startProfileAt([-12').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(400)
|
await page.waitForTimeout(500)
|
||||||
await page.waitForTimeout(150)
|
await page.setViewportSize({ width: 1200, height: 1200 })
|
||||||
await page.setBodyDimensions({ width: 1200, height: 1200 })
|
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.updateCamPosition([452, -152, 1166])
|
await u.updateCamPosition([452, -152, 1166])
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
@ -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.13",
|
"@kittycad/lib": "2.0.17",
|
||||||
"@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",
|
||||||
@ -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/kw-appearance/manifest.json",
|
"fetch:samples": "echo \"Fetching latest KCL samples...\" && curl -o public/kcl-samples-manifest-fallback.json https://raw.githubusercontent.com/KittyCAD/kcl-samples/achalmers/kw-pattern-transform2/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",
|
||||||
|
@ -34,6 +34,13 @@
|
|||||||
"title": "Car Wheel Assembly",
|
"title": "Car Wheel Assembly",
|
||||||
"description": "A car wheel assembly with a rotor, tire, and lug nuts."
|
"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",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "dodecahedron/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "dodecahedron/main.kcl",
|
||||||
@ -48,6 +55,13 @@
|
|||||||
"title": "Enclosure",
|
"title": "Enclosure",
|
||||||
"description": "An enclosure body and sealing lid for storing items"
|
"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",
|
"file": "main.kcl",
|
||||||
"pathFromProjectDirectoryToFirstFile": "flange-with-patterns/main.kcl",
|
"pathFromProjectDirectoryToFirstFile": "flange-with-patterns/main.kcl",
|
||||||
|
@ -5,7 +5,6 @@ import { useModelingContext } from 'hooks/useModelingContext'
|
|||||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
import { isSingleCursorInPipe } from 'lang/queryAst'
|
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
|
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
@ -21,6 +20,7 @@ import {
|
|||||||
} from 'lib/toolbar'
|
} from 'lib/toolbar'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
|
import { isCursorInFunctionDefinition } from 'lang/queryAst'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
import { isArray } from 'lib/utils'
|
import { isArray } from 'lib/utils'
|
||||||
|
|
||||||
@ -37,7 +37,12 @@ export function Toolbar({
|
|||||||
const buttonBorderClassName = '!border-transparent'
|
const buttonBorderClassName = '!border-transparent'
|
||||||
|
|
||||||
const sketchPathId = useMemo(() => {
|
const sketchPathId = useMemo(() => {
|
||||||
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast))
|
if (
|
||||||
|
isCursorInFunctionDefinition(
|
||||||
|
kclManager.ast,
|
||||||
|
context.selectionRanges.graphSelections[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
return isCursorInSketchCommandRange(
|
return isCursorInSketchCommandRange(
|
||||||
engineCommandManager.artifactGraph,
|
engineCommandManager.artifactGraph,
|
||||||
|
@ -31,7 +31,6 @@ 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'
|
||||||
@ -125,14 +124,7 @@ export const ClientSideScene = ({
|
|||||||
'mouseup',
|
'mouseup',
|
||||||
toSync(sceneInfra.onMouseUp, reportRejection)
|
toSync(sceneInfra.onMouseUp, reportRejection)
|
||||||
)
|
)
|
||||||
sceneEntitiesManager
|
sceneEntitiesManager.tearDownSketch({ removeAxis: true })
|
||||||
.tearDownSketch()
|
|
||||||
.then(() => {
|
|
||||||
// no op
|
|
||||||
})
|
|
||||||
.catch((e) => {
|
|
||||||
console.error(e)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@ -153,7 +145,8 @@ 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 {
|
||||||
@ -191,12 +184,15 @@ const Overlays = () => {
|
|||||||
style={{ zIndex: '99999999' }}
|
style={{ zIndex: '99999999' }}
|
||||||
>
|
>
|
||||||
{Object.entries(context.segmentOverlays)
|
{Object.entries(context.segmentOverlays)
|
||||||
.filter((a) => a[1].visible)
|
.flatMap((a) =>
|
||||||
.map(([pathToNodeString, overlay], index) => {
|
a[1].map((b) => ({ pathToNodeString: a[0], overlay: b }))
|
||||||
|
)
|
||||||
|
.filter((a) => a.overlay.visible)
|
||||||
|
.map(({ pathToNodeString, overlay }, index) => {
|
||||||
return (
|
return (
|
||||||
<Overlay
|
<Overlay
|
||||||
overlay={overlay}
|
overlay={overlay}
|
||||||
key={pathToNodeString}
|
key={pathToNodeString + String(index)}
|
||||||
pathToNodeString={pathToNodeString}
|
pathToNodeString={pathToNodeString}
|
||||||
overlayIndex={index}
|
overlayIndex={index}
|
||||||
/>
|
/>
|
||||||
@ -237,11 +233,17 @@ const Overlay = ({
|
|||||||
|
|
||||||
const constraints =
|
const constraints =
|
||||||
callExpression.type === 'CallExpression'
|
callExpression.type === 'CallExpression'
|
||||||
? getConstraintInfo(callExpression, codeManager.code, overlay.pathToNode)
|
? getConstraintInfo(
|
||||||
|
callExpression,
|
||||||
|
codeManager.code,
|
||||||
|
overlay.pathToNode,
|
||||||
|
overlay.filterValue
|
||||||
|
)
|
||||||
: getConstraintInfoKw(
|
: getConstraintInfoKw(
|
||||||
callExpression,
|
callExpression,
|
||||||
codeManager.code,
|
codeManager.code,
|
||||||
overlay.pathToNode
|
overlay.pathToNode,
|
||||||
|
overlay.filterValue
|
||||||
)
|
)
|
||||||
|
|
||||||
const offset = 20 // px
|
const offset = 20 // px
|
||||||
@ -261,7 +263,6 @@ const Overlay = ({
|
|||||||
state.matches({ Sketch: 'Tangential arc to' }) ||
|
state.matches({ Sketch: 'Tangential arc to' }) ||
|
||||||
state.matches({ Sketch: 'Rectangle tool' })
|
state.matches({ Sketch: 'Rectangle tool' })
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`absolute w-0 h-0`}>
|
<div className={`absolute w-0 h-0`}>
|
||||||
<div
|
<div
|
||||||
@ -319,17 +320,18 @@ const Overlay = ({
|
|||||||
this will likely change soon when we implement multi-profile so we'll leave it for now
|
this will likely change soon when we implement multi-profile so we'll leave it for now
|
||||||
issue: https://github.com/KittyCAD/modeling-app/issues/3910
|
issue: https://github.com/KittyCAD/modeling-app/issues/3910
|
||||||
*/}
|
*/}
|
||||||
{callExpression?.callee?.name !== 'circle' && (
|
{callExpression?.callee?.name !== 'circle' &&
|
||||||
<SegmentMenu
|
callExpression?.callee?.name !== 'circleThreePoint' && (
|
||||||
verticalPosition={
|
<SegmentMenu
|
||||||
overlay.windowCoords[1] > window.innerHeight / 2
|
verticalPosition={
|
||||||
? 'top'
|
overlay.windowCoords[1] > window.innerHeight / 2
|
||||||
: 'bottom'
|
? 'top'
|
||||||
}
|
: 'bottom'
|
||||||
pathToNode={overlay.pathToNode}
|
}
|
||||||
stdLibFnName={constraints[0]?.stdLibFnName}
|
pathToNode={overlay.pathToNode}
|
||||||
/>
|
stdLibFnName={constraints[0]?.stdLibFnName}
|
||||||
)}
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -425,7 +427,7 @@ export async function deleteSegment({
|
|||||||
modifiedAst = deleteSegmentFromPipeExpression(
|
modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
dependentRanges,
|
dependentRanges,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
kclManager.programMemory,
|
kclManager.variables,
|
||||||
codeManager.code,
|
codeManager.code,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
@ -439,8 +441,8 @@ export async function deleteSegment({
|
|||||||
const testExecute = await executeAst({
|
const testExecute = await executeAst({
|
||||||
ast: modifiedAst,
|
ast: modifiedAst,
|
||||||
engineCommandManager: engineCommandManager,
|
engineCommandManager: engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
isMock: true,
|
||||||
programMemoryOverride: ProgramMemory.empty(),
|
usePrevMemory: false,
|
||||||
})
|
})
|
||||||
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.')
|
||||||
@ -450,6 +452,8 @@ export async function deleteSegment({
|
|||||||
if (!sketchDetails) return
|
if (!sketchDetails) return
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
pathToNode,
|
pathToNode,
|
||||||
|
sketchDetails.sketchNodePaths,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -675,7 +679,7 @@ const ConstraintSymbol = ({
|
|||||||
shallowPath,
|
shallowPath,
|
||||||
argPosition,
|
argPosition,
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory
|
kclManager.variables
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!transform) return
|
if (!transform) return
|
||||||
|
@ -182,13 +182,15 @@ export class SceneInfra {
|
|||||||
callbacks: (() => SegmentOverlayPayload | null)[] = []
|
callbacks: (() => SegmentOverlayPayload | null)[] = []
|
||||||
_overlayCallbacks(callbacks: (() => SegmentOverlayPayload | null)[]) {
|
_overlayCallbacks(callbacks: (() => SegmentOverlayPayload | null)[]) {
|
||||||
const segmentOverlayPayload: SegmentOverlayPayload = {
|
const segmentOverlayPayload: SegmentOverlayPayload = {
|
||||||
type: 'set-many',
|
type: 'add-many',
|
||||||
overlays: {},
|
overlays: {},
|
||||||
}
|
}
|
||||||
callbacks.forEach((cb) => {
|
callbacks.forEach((cb) => {
|
||||||
const overlay = cb()
|
const overlay = cb()
|
||||||
if (overlay?.type === 'set-one') {
|
if (overlay?.type === 'set-one') {
|
||||||
segmentOverlayPayload.overlays[overlay.pathToNodeString] = overlay.seg
|
segmentOverlayPayload.overlays[overlay.pathToNodeString] = overlay.seg
|
||||||
|
} else if (overlay?.type === 'add-many') {
|
||||||
|
Object.assign(segmentOverlayPayload.overlays, overlay.overlays)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.modelingSend({
|
this.modelingSend({
|
||||||
@ -213,25 +215,27 @@ export class SceneInfra {
|
|||||||
|
|
||||||
overlayThrottleMap: { [pathToNodeString: string]: number } = {}
|
overlayThrottleMap: { [pathToNodeString: string]: number } = {}
|
||||||
updateOverlayDetails({
|
updateOverlayDetails({
|
||||||
arrowGroup,
|
handle,
|
||||||
group,
|
group,
|
||||||
isHandlesVisible,
|
isHandlesVisible,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
angle,
|
angle,
|
||||||
|
hasThreeDotMenu,
|
||||||
}: {
|
}: {
|
||||||
arrowGroup: Group
|
handle: Group
|
||||||
group: Group
|
group: Group
|
||||||
isHandlesVisible: boolean
|
isHandlesVisible: boolean
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
to: Coords2d
|
to: Coords2d
|
||||||
|
hasThreeDotMenu: boolean
|
||||||
angle?: number
|
angle?: number
|
||||||
}): SegmentOverlayPayload | null {
|
}): SegmentOverlayPayload | null {
|
||||||
if (!group.userData.draft && group.userData.pathToNode && arrowGroup) {
|
if (!group.userData.draft && group.userData.pathToNode && handle) {
|
||||||
const vector = new Vector3(0, 0, 0)
|
const vector = new Vector3(0, 0, 0)
|
||||||
|
|
||||||
// Get the position of the object3D in world space
|
// Get the position of the object3D in world space
|
||||||
arrowGroup.getWorldPosition(vector)
|
handle.getWorldPosition(vector)
|
||||||
|
|
||||||
// Project that position to screen space
|
// Project that position to screen space
|
||||||
vector.project(this.camControls.camera)
|
vector.project(this.camControls.camera)
|
||||||
@ -244,13 +248,16 @@ export class SceneInfra {
|
|||||||
return {
|
return {
|
||||||
type: 'set-one',
|
type: 'set-one',
|
||||||
pathToNodeString,
|
pathToNodeString,
|
||||||
seg: {
|
seg: [
|
||||||
windowCoords: [x, y],
|
{
|
||||||
angle: _angle,
|
windowCoords: [x, y],
|
||||||
group,
|
angle: _angle,
|
||||||
pathToNode: group.userData.pathToNode,
|
group,
|
||||||
visible: isHandlesVisible,
|
pathToNode: group.userData.pathToNode,
|
||||||
},
|
visible: isHandlesVisible,
|
||||||
|
hasThreeDotMenu,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
|
@ -31,6 +31,12 @@ import {
|
|||||||
CIRCLE_SEGMENT,
|
CIRCLE_SEGMENT,
|
||||||
CIRCLE_SEGMENT_BODY,
|
CIRCLE_SEGMENT_BODY,
|
||||||
CIRCLE_SEGMENT_DASH,
|
CIRCLE_SEGMENT_DASH,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE1,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE2,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE3,
|
||||||
|
CIRCLE_THREE_POINT_SEGMENT,
|
||||||
|
CIRCLE_THREE_POINT_SEGMENT_BODY,
|
||||||
|
CIRCLE_THREE_POINT_SEGMENT_DASH,
|
||||||
EXTRA_SEGMENT_HANDLE,
|
EXTRA_SEGMENT_HANDLE,
|
||||||
EXTRA_SEGMENT_OFFSET_PX,
|
EXTRA_SEGMENT_OFFSET_PX,
|
||||||
HIDE_HOVER_SEGMENT_LENGTH,
|
HIDE_HOVER_SEGMENT_LENGTH,
|
||||||
@ -56,11 +62,16 @@ import {
|
|||||||
} 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'
|
||||||
import { SegmentOverlayPayload } from 'machines/modelingMachine'
|
import {
|
||||||
|
SegmentOverlay,
|
||||||
|
SegmentOverlayPayload,
|
||||||
|
SegmentOverlays,
|
||||||
|
} 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 { editorManager, sceneInfra } from 'lib/singletons'
|
import { 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 { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
|
|
||||||
interface CreateSegmentArgs {
|
interface CreateSegmentArgs {
|
||||||
@ -307,11 +318,12 @@ class StraightSegment implements SegmentUtils {
|
|||||||
}
|
}
|
||||||
return () =>
|
return () =>
|
||||||
sceneInfra.updateOverlayDetails({
|
sceneInfra.updateOverlayDetails({
|
||||||
arrowGroup,
|
handle: arrowGroup,
|
||||||
group,
|
group,
|
||||||
isHandlesVisible,
|
isHandlesVisible,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
|
hasThreeDotMenu: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -483,12 +495,13 @@ class TangentialArcToSegment implements SegmentUtils {
|
|||||||
)
|
)
|
||||||
return () =>
|
return () =>
|
||||||
sceneInfra.updateOverlayDetails({
|
sceneInfra.updateOverlayDetails({
|
||||||
arrowGroup,
|
handle: arrowGroup,
|
||||||
group,
|
group,
|
||||||
isHandlesVisible,
|
isHandlesVisible,
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
angle,
|
angle,
|
||||||
|
hasThreeDotMenu: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -684,35 +697,255 @@ class CircleSegment implements SegmentUtils {
|
|||||||
}
|
}
|
||||||
return () =>
|
return () =>
|
||||||
sceneInfra.updateOverlayDetails({
|
sceneInfra.updateOverlayDetails({
|
||||||
arrowGroup,
|
handle: arrowGroup,
|
||||||
group,
|
group,
|
||||||
isHandlesVisible,
|
isHandlesVisible,
|
||||||
from: from,
|
from: from,
|
||||||
to: [center[0], center[1]],
|
to: [center[0], center[1]],
|
||||||
angle: Math.PI / 4,
|
angle: Math.PI / 4,
|
||||||
|
hasThreeDotMenu: true,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CircleThreePointSegment implements SegmentUtils {
|
||||||
|
init: SegmentUtils['init'] = ({
|
||||||
|
input,
|
||||||
|
id,
|
||||||
|
pathToNode,
|
||||||
|
isDraftSegment,
|
||||||
|
scale = 1,
|
||||||
|
theme,
|
||||||
|
isSelected = false,
|
||||||
|
sceneInfra,
|
||||||
|
prevSegment,
|
||||||
|
}) => {
|
||||||
|
if (input.type !== 'circle-three-point-segment') {
|
||||||
|
return new Error('Invalid segment type')
|
||||||
|
}
|
||||||
|
const { p1, p2, p3 } = input
|
||||||
|
const { center_x, center_y, radius } = calculate_circle_from_3_points(
|
||||||
|
p1[0],
|
||||||
|
p1[1],
|
||||||
|
p2[0],
|
||||||
|
p2[1],
|
||||||
|
p3[0],
|
||||||
|
p3[1]
|
||||||
|
)
|
||||||
|
const center: [number, number] = [center_x, center_y]
|
||||||
|
const baseColor = getThemeColorForThreeJs(theme)
|
||||||
|
const color = isSelected ? 0x0000ff : baseColor
|
||||||
|
|
||||||
|
const group = new Group()
|
||||||
|
const geometry = createArcGeometry({
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
startAngle: 0,
|
||||||
|
endAngle: Math.PI * 2,
|
||||||
|
ccw: true,
|
||||||
|
isDashed: isDraftSegment,
|
||||||
|
scale,
|
||||||
|
})
|
||||||
|
const mat = new MeshBasicMaterial({ color })
|
||||||
|
const arcMesh = new Mesh(geometry, mat)
|
||||||
|
const meshType = isDraftSegment
|
||||||
|
? CIRCLE_THREE_POINT_SEGMENT_DASH
|
||||||
|
: CIRCLE_THREE_POINT_SEGMENT_BODY
|
||||||
|
const handle1 = createCircleThreePointHandle(
|
||||||
|
scale,
|
||||||
|
theme,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE1,
|
||||||
|
color
|
||||||
|
)
|
||||||
|
const handle2 = createCircleThreePointHandle(
|
||||||
|
scale,
|
||||||
|
theme,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE2,
|
||||||
|
color
|
||||||
|
)
|
||||||
|
const handle3 = createCircleThreePointHandle(
|
||||||
|
scale,
|
||||||
|
theme,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE3,
|
||||||
|
color
|
||||||
|
)
|
||||||
|
|
||||||
|
arcMesh.userData.type = meshType
|
||||||
|
arcMesh.name = meshType
|
||||||
|
group.userData = {
|
||||||
|
type: CIRCLE_THREE_POINT_SEGMENT,
|
||||||
|
draft: isDraftSegment,
|
||||||
|
id,
|
||||||
|
p1,
|
||||||
|
p2,
|
||||||
|
p3,
|
||||||
|
ccw: true,
|
||||||
|
prevSegment,
|
||||||
|
pathToNode,
|
||||||
|
isSelected,
|
||||||
|
baseColor,
|
||||||
|
}
|
||||||
|
group.name = CIRCLE_THREE_POINT_SEGMENT
|
||||||
|
|
||||||
|
group.add(arcMesh, handle1, handle2, handle3)
|
||||||
|
const updateOverlaysCallback = this.update({
|
||||||
|
prevSegment,
|
||||||
|
input,
|
||||||
|
group,
|
||||||
|
scale,
|
||||||
|
sceneInfra,
|
||||||
|
})
|
||||||
|
if (err(updateOverlaysCallback)) return updateOverlaysCallback
|
||||||
|
|
||||||
|
return {
|
||||||
|
group,
|
||||||
|
updateOverlaysCallback,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
update: SegmentUtils['update'] = ({
|
||||||
|
input,
|
||||||
|
group,
|
||||||
|
scale = 1,
|
||||||
|
sceneInfra,
|
||||||
|
}) => {
|
||||||
|
if (input.type !== 'circle-three-point-segment') {
|
||||||
|
return new Error('Invalid segment type')
|
||||||
|
}
|
||||||
|
const { p1, p2, p3 } = input
|
||||||
|
group.userData.p1 = p1
|
||||||
|
group.userData.p2 = p2
|
||||||
|
group.userData.p3 = p3
|
||||||
|
const { center_x, center_y, radius } = calculate_circle_from_3_points(
|
||||||
|
p1[0],
|
||||||
|
p1[1],
|
||||||
|
p2[0],
|
||||||
|
p2[1],
|
||||||
|
p3[0],
|
||||||
|
p3[1]
|
||||||
|
)
|
||||||
|
const center: [number, number] = [center_x, center_y]
|
||||||
|
const points = [p1, p2, p3]
|
||||||
|
const handles = [
|
||||||
|
CIRCLE_THREE_POINT_HANDLE1,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE2,
|
||||||
|
CIRCLE_THREE_POINT_HANDLE3,
|
||||||
|
].map((handle) => group.getObjectByName(handle) as Group)
|
||||||
|
handles.forEach((handle, i) => {
|
||||||
|
const point = points[i]
|
||||||
|
if (handle && point) {
|
||||||
|
handle.position.set(point[0], point[1], 0)
|
||||||
|
handle.scale.set(scale, scale, scale)
|
||||||
|
handle.visible = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const pxLength = (2 * radius * Math.PI) / scale
|
||||||
|
const shouldHideIdle = pxLength < HIDE_SEGMENT_LENGTH
|
||||||
|
const shouldHideHover = pxLength < HIDE_HOVER_SEGMENT_LENGTH
|
||||||
|
|
||||||
|
const hoveredParent =
|
||||||
|
sceneInfra.hoveredObject &&
|
||||||
|
getParentGroup(sceneInfra.hoveredObject, [CIRCLE_SEGMENT])
|
||||||
|
let isHandlesVisible = !shouldHideIdle
|
||||||
|
if (hoveredParent && hoveredParent?.uuid === group?.uuid) {
|
||||||
|
isHandlesVisible = !shouldHideHover
|
||||||
|
}
|
||||||
|
|
||||||
|
const circleSegmentBody = group.children.find(
|
||||||
|
(child) => child.userData.type === CIRCLE_THREE_POINT_SEGMENT_BODY
|
||||||
|
) as Mesh
|
||||||
|
|
||||||
|
if (circleSegmentBody) {
|
||||||
|
const newGeo = createArcGeometry({
|
||||||
|
radius,
|
||||||
|
center,
|
||||||
|
startAngle: 0,
|
||||||
|
endAngle: Math.PI * 2,
|
||||||
|
ccw: true,
|
||||||
|
scale,
|
||||||
|
})
|
||||||
|
circleSegmentBody.geometry = newGeo
|
||||||
|
}
|
||||||
|
const circleSegmentBodyDashed = group.getObjectByName(
|
||||||
|
CIRCLE_THREE_POINT_SEGMENT_DASH
|
||||||
|
)
|
||||||
|
if (circleSegmentBodyDashed instanceof Mesh) {
|
||||||
|
// consider throttling the whole updateTangentialArcToSegment
|
||||||
|
// if there are more perf considerations going forward
|
||||||
|
circleSegmentBodyDashed.geometry = createArcGeometry({
|
||||||
|
center,
|
||||||
|
radius,
|
||||||
|
ccw: true,
|
||||||
|
// make the start end where the handle is
|
||||||
|
startAngle: Math.PI * 0.25,
|
||||||
|
endAngle: Math.PI * 2.25,
|
||||||
|
isDashed: true,
|
||||||
|
scale,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
const overlays: SegmentOverlays = {}
|
||||||
|
const points = [p1, p2, p3]
|
||||||
|
const overlayDetails = handles.map((handle, index) => {
|
||||||
|
const currentPoint = points[index]
|
||||||
|
const angle = Math.atan2(
|
||||||
|
currentPoint[1] - center[1],
|
||||||
|
currentPoint[0] - center[0]
|
||||||
|
)
|
||||||
|
return sceneInfra.updateOverlayDetails({
|
||||||
|
handle,
|
||||||
|
group,
|
||||||
|
isHandlesVisible,
|
||||||
|
from: [0, 0],
|
||||||
|
to: [center[0], center[1]],
|
||||||
|
angle: angle,
|
||||||
|
hasThreeDotMenu: index === 0,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
const segmentOverlays: SegmentOverlay[] = []
|
||||||
|
overlayDetails.forEach((payload, index) => {
|
||||||
|
if (payload?.type === 'set-one') {
|
||||||
|
overlays[payload.pathToNodeString] = payload.seg
|
||||||
|
segmentOverlays.push({
|
||||||
|
...payload.seg[0],
|
||||||
|
filterValue: index === 0 ? 'p1' : index === 1 ? 'p2' : 'p3',
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
const segmentOverlayPayload: SegmentOverlayPayload = {
|
||||||
|
type: 'set-one',
|
||||||
|
pathToNodeString:
|
||||||
|
overlayDetails[0]?.type === 'set-one'
|
||||||
|
? overlayDetails[0].pathToNodeString
|
||||||
|
: '',
|
||||||
|
seg: segmentOverlays,
|
||||||
|
}
|
||||||
|
return segmentOverlayPayload
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function createProfileStartHandle({
|
export function createProfileStartHandle({
|
||||||
from,
|
from,
|
||||||
isDraft = false,
|
isDraft = false,
|
||||||
scale = 1,
|
scale = 1,
|
||||||
theme,
|
theme,
|
||||||
isSelected,
|
isSelected,
|
||||||
|
size = 12,
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
scale?: number
|
scale?: number
|
||||||
theme: Themes
|
theme: Themes
|
||||||
isSelected?: boolean
|
isSelected?: boolean
|
||||||
|
size?: number
|
||||||
} & (
|
} & (
|
||||||
| { isDraft: true }
|
| { isDraft: true }
|
||||||
| { isDraft: false; id: string; pathToNode: PathToNode }
|
| { isDraft: false; id: string; pathToNode: PathToNode }
|
||||||
)) {
|
)) {
|
||||||
const group = new Group()
|
const group = new Group()
|
||||||
|
|
||||||
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
const geometry = new BoxGeometry(size, size, size) // in pixels scaled later
|
||||||
const baseColor = getThemeColorForThreeJs(theme)
|
const baseColor = getThemeColorForThreeJs(theme)
|
||||||
const color = isSelected ? 0x0000ff : baseColor
|
const color = isSelected ? 0x0000ff : baseColor
|
||||||
const body = new MeshBasicMaterial({ color })
|
const body = new MeshBasicMaterial({ color })
|
||||||
@ -774,6 +1007,29 @@ function createCircleCenterHandle(
|
|||||||
circleCenterGroup.scale.set(scale, scale, scale)
|
circleCenterGroup.scale.set(scale, scale, scale)
|
||||||
return circleCenterGroup
|
return circleCenterGroup
|
||||||
}
|
}
|
||||||
|
function createCircleThreePointHandle(
|
||||||
|
scale = 1,
|
||||||
|
theme: Themes,
|
||||||
|
name: `circle-three-point-handle${'1' | '2' | '3'}`,
|
||||||
|
color?: number
|
||||||
|
): Group {
|
||||||
|
const circleCenterGroup = new Group()
|
||||||
|
|
||||||
|
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
||||||
|
const baseColor = getThemeColorForThreeJs(theme)
|
||||||
|
const body = new MeshBasicMaterial({ color })
|
||||||
|
const mesh = new Mesh(geometry, body)
|
||||||
|
|
||||||
|
circleCenterGroup.add(mesh)
|
||||||
|
|
||||||
|
circleCenterGroup.userData = {
|
||||||
|
type: name,
|
||||||
|
baseColor,
|
||||||
|
}
|
||||||
|
circleCenterGroup.name = name
|
||||||
|
circleCenterGroup.scale.set(scale, scale, scale)
|
||||||
|
return circleCenterGroup
|
||||||
|
}
|
||||||
|
|
||||||
function createExtraSegmentHandle(
|
function createExtraSegmentHandle(
|
||||||
scale: number,
|
scale: number,
|
||||||
@ -1100,4 +1356,5 @@ export const segmentUtils = {
|
|||||||
straight: new StraightSegment(),
|
straight: new StraightSegment(),
|
||||||
tangentialArcTo: new TangentialArcToSegment(),
|
tangentialArcTo: new TangentialArcToSegment(),
|
||||||
circle: new CircleSegment(),
|
circle: new CircleSegment(),
|
||||||
|
circleThreePoint: new CircleThreePointSegment(),
|
||||||
} as const
|
} as const
|
||||||
|
@ -1,11 +1,5 @@
|
|||||||
import { useEffect, useState, useRef } from 'react'
|
import { useEffect, useState, useRef } from 'react'
|
||||||
import {
|
import { parse, BinaryPart, Expr, resultIsOk, VariableMap } from '../lang/wasm'
|
||||||
parse,
|
|
||||||
BinaryPart,
|
|
||||||
Expr,
|
|
||||||
ProgramMemory,
|
|
||||||
resultIsOk,
|
|
||||||
} from '../lang/wasm'
|
|
||||||
import {
|
import {
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createLiteral,
|
createLiteral,
|
||||||
@ -100,7 +94,7 @@ export function useCalc({
|
|||||||
newVariableInsertIndex: number
|
newVariableInsertIndex: number
|
||||||
setNewVariableName: (a: string) => void
|
setNewVariableName: (a: string) => void
|
||||||
} {
|
} {
|
||||||
const { programMemory } = useKclContext()
|
const { variables } = useKclContext()
|
||||||
const { context } = useModelingContext()
|
const { context } = useModelingContext()
|
||||||
const selectionRange =
|
const selectionRange =
|
||||||
context.selectionRanges?.graphSelections[0]?.codeRef?.range
|
context.selectionRanges?.graphSelections[0]?.codeRef?.range
|
||||||
@ -127,7 +121,7 @@ export function useCalc({
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (programMemory.has(newVariableName)) {
|
if (variables[newVariableName]) {
|
||||||
setIsNewVariableNameUnique(false)
|
setIsNewVariableNameUnique(false)
|
||||||
} else {
|
} else {
|
||||||
setIsNewVariableNameUnique(true)
|
setIsNewVariableNameUnique(true)
|
||||||
@ -135,14 +129,14 @@ export function useCalc({
|
|||||||
}, [newVariableName])
|
}, [newVariableName])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!programMemory || !selectionRange) return
|
if (!variables || !selectionRange) return
|
||||||
const varInfo = findAllPreviousVariables(
|
const varInfo = findAllPreviousVariables(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
kclManager.programMemory,
|
kclManager.variables,
|
||||||
selectionRange
|
selectionRange
|
||||||
)
|
)
|
||||||
setAvailableVarInfo(varInfo)
|
setAvailableVarInfo(varInfo)
|
||||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
}, [kclManager.ast, kclManager.variables, selectionRange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
try {
|
try {
|
||||||
@ -150,9 +144,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 _programMem: ProgramMemory = ProgramMemory.empty()
|
const _variables: VariableMap = {}
|
||||||
for (const { key, value } of availableVarInfo.variables) {
|
for (const { key, value } of availableVarInfo.variables) {
|
||||||
const error = _programMem.set(key, {
|
const error = (_variables[key] = {
|
||||||
type: 'String',
|
type: 'String',
|
||||||
value,
|
value,
|
||||||
__meta: [],
|
__meta: [],
|
||||||
@ -163,8 +157,8 @@ export function useCalc({
|
|||||||
executeAst({
|
executeAst({
|
||||||
ast,
|
ast,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
isMock: true,
|
||||||
programMemoryOverride: kclManager.programMemory.clone(),
|
variables,
|
||||||
}).then(({ execState }) => {
|
}).then(({ execState }) => {
|
||||||
const resultDeclaration = ast.body.find(
|
const resultDeclaration = ast.body.find(
|
||||||
(a) =>
|
(a) =>
|
||||||
@ -174,7 +168,7 @@ export function useCalc({
|
|||||||
const init =
|
const init =
|
||||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||||
resultDeclaration?.declaration.init
|
resultDeclaration?.declaration.init
|
||||||
const result = execState.memory?.get('__result__')?.value
|
const result = execState.variables['__result__']?.value
|
||||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||||
init && setValueNode(init)
|
init && setValueNode(init)
|
||||||
})
|
})
|
||||||
|
@ -157,8 +157,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
plugin.requestSemanticTokens()
|
plugin.requestSemanticTokens()
|
||||||
break
|
break
|
||||||
case 'kcl/memoryUpdated':
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
@ -25,7 +25,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import {
|
import {
|
||||||
isCursorInSketchCommandRange,
|
isCursorInSketchCommandRange,
|
||||||
updatePathToNodeFromMap,
|
updateSketchDetailsNodePaths,
|
||||||
} from 'lang/util'
|
} from 'lang/util'
|
||||||
import {
|
import {
|
||||||
kclManager,
|
kclManager,
|
||||||
@ -65,17 +65,30 @@ import {
|
|||||||
replaceValueAtNodePath,
|
replaceValueAtNodePath,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
|
splitPipedProfile,
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import { PathToNode, Program, parse, recast, resultIsOk } from 'lang/wasm'
|
import {
|
||||||
import { artifactIsPlaneWithPaths, isSingleCursorInPipe } from 'lang/queryAst'
|
PathToNode,
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
Program,
|
||||||
|
VariableDeclaration,
|
||||||
|
parse,
|
||||||
|
recast,
|
||||||
|
resultIsOk,
|
||||||
|
} from 'lang/wasm'
|
||||||
|
import {
|
||||||
|
artifactIsPlaneWithPaths,
|
||||||
|
doesSketchPipeNeedSplitting,
|
||||||
|
getNodeFromPath,
|
||||||
|
isCursorInFunctionDefinition,
|
||||||
|
traverse,
|
||||||
|
} from 'lang/queryAst'
|
||||||
import { exportFromEngine } from 'lib/exportFromEngine'
|
import { exportFromEngine } from 'lib/exportFromEngine'
|
||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap, reject } from 'lib/trap'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
@ -86,10 +99,16 @@ import { useFileContext } from 'hooks/useFileContext'
|
|||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
import {
|
||||||
|
getFaceCodeRef,
|
||||||
|
getPathsFromArtifact,
|
||||||
|
getPlaneFromArtifact,
|
||||||
|
} from 'lang/std/artifactGraph'
|
||||||
import { promptToEditFlow } from 'lib/promptToEdit'
|
import { promptToEditFlow } from 'lib/promptToEdit'
|
||||||
import { kclEditorActor } from 'machines/kclEditorMachine'
|
import { kclEditorActor } from 'machines/kclEditorMachine'
|
||||||
import { commandBarActor } from 'machines/commandBarMachine'
|
import { commandBarActor } from 'machines/commandBarMachine'
|
||||||
import { useToken } from 'machines/appMachine'
|
import { useToken } from 'machines/appMachine'
|
||||||
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
|
|
||||||
type MachineContext<T extends AnyStateMachine> = {
|
type MachineContext<T extends AnyStateMachine> = {
|
||||||
state: StateFrom<T>
|
state: StateFrom<T>
|
||||||
@ -254,7 +273,11 @@ export const ModelingMachineProvider = ({
|
|||||||
'Set Segment Overlays': assign({
|
'Set Segment Overlays': assign({
|
||||||
segmentOverlays: ({ context: { segmentOverlays }, event }) => {
|
segmentOverlays: ({ context: { segmentOverlays }, event }) => {
|
||||||
if (event.type !== 'Set Segment Overlays') return {}
|
if (event.type !== 'Set Segment Overlays') return {}
|
||||||
if (event.data.type === 'set-many') return event.data.overlays
|
if (event.data.type === 'add-many')
|
||||||
|
return {
|
||||||
|
...segmentOverlays,
|
||||||
|
...event.data.overlays,
|
||||||
|
}
|
||||||
if (event.data.type === 'set-one')
|
if (event.data.type === 'set-one')
|
||||||
return {
|
return {
|
||||||
...segmentOverlays,
|
...segmentOverlays,
|
||||||
@ -287,7 +310,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
sketchDetails: {
|
sketchDetails: {
|
||||||
...sketchDetails,
|
...sketchDetails,
|
||||||
sketchPathToNode: event.data,
|
sketchEntryNodePath: event.data,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -483,9 +506,17 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges: setSelections.selection,
|
selectionRanges: setSelections.selection,
|
||||||
sketchDetails: {
|
sketchDetails: {
|
||||||
...sketchDetails,
|
...sketchDetails,
|
||||||
sketchPathToNode:
|
sketchEntryNodePath:
|
||||||
setSelections.updatedPathToNode ||
|
setSelections.updatedSketchEntryNodePath ||
|
||||||
sketchDetails?.sketchPathToNode ||
|
sketchDetails?.sketchEntryNodePath ||
|
||||||
|
[],
|
||||||
|
sketchNodePaths:
|
||||||
|
setSelections.updatedSketchNodePaths ||
|
||||||
|
sketchDetails?.sketchNodePaths ||
|
||||||
|
[],
|
||||||
|
planeNodePath:
|
||||||
|
setSelections.updatedPlaneNodePath ||
|
||||||
|
sketchDetails?.planeNodePath ||
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -638,7 +669,12 @@ export const ModelingMachineProvider = ({
|
|||||||
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
if (
|
||||||
|
isCursorInFunctionDefinition(
|
||||||
|
kclManager.ast,
|
||||||
|
selectionRanges.graphSelections[0]
|
||||||
|
)
|
||||||
|
)
|
||||||
return false
|
return false
|
||||||
return !!isCursorInSketchCommandRange(
|
return !!isCursorInSketchCommandRange(
|
||||||
engineCommandManager.artifactGraph,
|
engineCommandManager.artifactGraph,
|
||||||
@ -666,13 +702,33 @@ export const ModelingMachineProvider = ({
|
|||||||
async ({ input: { sketchDetails } }) => {
|
async ({ input: { sketchDetails } }) => {
|
||||||
if (!sketchDetails) return
|
if (!sketchDetails) return
|
||||||
if (kclManager.ast.body.length) {
|
if (kclManager.ast.body.length) {
|
||||||
// this assumes no changes have been made to the sketch besides what we did when entering the sketch
|
|
||||||
// i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
|
|
||||||
const newAst = structuredClone(kclManager.ast)
|
const newAst = structuredClone(kclManager.ast)
|
||||||
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
const varDecIndex = sketchDetails.planeNodePath[1][0]
|
||||||
|
|
||||||
|
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||||
|
newAst,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(varDec)) return reject(new Error('No varDec'))
|
||||||
|
const variableName = varDec.node.declaration.id.name
|
||||||
|
let isIdentifierUsed = false
|
||||||
|
traverse(newAst, {
|
||||||
|
enter: (node) => {
|
||||||
|
if (
|
||||||
|
node.type === 'Identifier' &&
|
||||||
|
node.name === variableName
|
||||||
|
) {
|
||||||
|
isIdentifierUsed = true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if (isIdentifierUsed) return
|
||||||
|
|
||||||
// remove body item at varDecIndex
|
// remove body item at varDecIndex
|
||||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||||
await kclManager.executeAstMock(newAst)
|
await kclManager.executeAstMock(newAst)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(newAst)
|
||||||
}
|
}
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
@ -682,7 +738,7 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
'animate-to-face': fromPromise(async ({ input }) => {
|
'animate-to-face': fromPromise(async ({ input }) => {
|
||||||
if (!input) return undefined
|
if (!input) return null
|
||||||
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
||||||
const sketched =
|
const sketched =
|
||||||
input.type === 'extrudeFace'
|
input.type === 'extrudeFace'
|
||||||
@ -709,7 +765,9 @@ export const ModelingMachineProvider = ({
|
|||||||
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: pathToNewSketchNode,
|
sketchEntryNodePath: [],
|
||||||
|
planeNodePath: pathToNewSketchNode,
|
||||||
|
sketchNodePaths: [],
|
||||||
zAxis: input.zAxis,
|
zAxis: input.zAxis,
|
||||||
yAxis: input.yAxis,
|
yAxis: input.yAxis,
|
||||||
origin: input.position,
|
origin: input.position,
|
||||||
@ -730,7 +788,9 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: pathToNode,
|
sketchEntryNodePath: [],
|
||||||
|
planeNodePath: pathToNode,
|
||||||
|
sketchNodePaths: [],
|
||||||
zAxis: input.zAxis,
|
zAxis: input.zAxis,
|
||||||
yAxis: input.yAxis,
|
yAxis: input.yAxis,
|
||||||
origin: [0, 0, 0],
|
origin: [0, 0, 0],
|
||||||
@ -739,21 +799,49 @@ export const ModelingMachineProvider = ({
|
|||||||
}),
|
}),
|
||||||
'animate-to-sketch': fromPromise(
|
'animate-to-sketch': fromPromise(
|
||||||
async ({ input: { selectionRanges } }) => {
|
async ({ input: { selectionRanges } }) => {
|
||||||
const sourceRange =
|
const plane = getPlaneFromArtifact(
|
||||||
selectionRanges.graphSelections[0]?.codeRef?.range
|
selectionRanges.graphSelections[0].artifact,
|
||||||
const sketchPathToNode = getNodePathFromSourceRange(
|
engineCommandManager.artifactGraph
|
||||||
kclManager.ast,
|
|
||||||
sourceRange
|
|
||||||
)
|
)
|
||||||
const info = await getSketchOrientationDetails(
|
if (err(plane)) return Promise.reject(plane)
|
||||||
sketchPathToNode || []
|
|
||||||
|
const sketch = Object.values(kclManager.execState.variables).find(
|
||||||
|
(variable) =>
|
||||||
|
variable?.type === 'Sketch' &&
|
||||||
|
variable.value.artifactId === plane.pathIds[0]
|
||||||
)
|
)
|
||||||
|
if (!sketch || sketch.type !== 'Sketch')
|
||||||
|
return Promise.reject(new Error('No sketch'))
|
||||||
|
const info = await getSketchOrientationDetails(sketch.value)
|
||||||
|
|
||||||
await letEngineAnimateAndSyncCamAfter(
|
await letEngineAnimateAndSyncCamAfter(
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
info?.sketchDetails?.faceId || ''
|
info?.sketchDetails?.faceId || ''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const sketchArtifact = engineCommandManager.artifactGraph.get(
|
||||||
|
plane.pathIds[0]
|
||||||
|
)
|
||||||
|
if (sketchArtifact?.type !== 'path')
|
||||||
|
return Promise.reject(new Error('No sketch artifact'))
|
||||||
|
const sketchPaths = getPathsFromArtifact({
|
||||||
|
artifact: engineCommandManager.artifactGraph.get(plane.id),
|
||||||
|
sketchPathToNode: sketchArtifact?.codeRef?.pathToNode,
|
||||||
|
artifactGraph: engineCommandManager.artifactGraph,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
})
|
||||||
|
if (err(sketchPaths)) return Promise.reject(sketchPaths)
|
||||||
|
let codeRef = getFaceCodeRef(plane)
|
||||||
|
if (!codeRef) return Promise.reject(new Error('No plane codeRef'))
|
||||||
|
// codeRef.pathToNode is not always populated correctly
|
||||||
|
const planeNodePath = getNodePathFromSourceRange(
|
||||||
|
kclManager.ast,
|
||||||
|
codeRef.range
|
||||||
|
)
|
||||||
return {
|
return {
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
sketchEntryNodePath: sketchArtifact.codeRef.pathToNode || [],
|
||||||
|
sketchNodePaths: sketchPaths,
|
||||||
|
planeNodePath,
|
||||||
zAxis: info.sketchDetails.zAxis || null,
|
zAxis: info.sketchDetails.zAxis || null,
|
||||||
yAxis: info.sketchDetails.yAxis || null,
|
yAxis: info.sketchDetails.yAxis || null,
|
||||||
origin: info.sketchDetails.origin.map(
|
origin: info.sketchDetails.origin.map(
|
||||||
@ -766,7 +854,7 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
'Get horizontal info': fromPromise(
|
'Get horizontal info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
await applyConstraintHorzVertDistance({
|
await applyConstraintHorzVertDistance({
|
||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -778,13 +866,23 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -805,13 +903,15 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get vertical info': fromPromise(
|
'Get vertical info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
await applyConstraintHorzVertDistance({
|
await applyConstraintHorzVertDistance({
|
||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -822,13 +922,23 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -849,7 +959,9 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -859,14 +971,15 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
if (err(info)) return Promise.reject(info)
|
if (err(info)) return Promise.reject(info)
|
||||||
const { modifiedAst, pathToNodeMap } = await (info.enabled
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
? applyConstraintAngleBetween({
|
await (info.enabled
|
||||||
selectionRanges,
|
? applyConstraintAngleBetween({
|
||||||
})
|
selectionRanges,
|
||||||
: applyConstraintAngleLength({
|
})
|
||||||
selectionRanges,
|
: applyConstraintAngleLength({
|
||||||
angleOrLength: 'setAngle',
|
selectionRanges,
|
||||||
}))
|
angleOrLength: 'setAngle',
|
||||||
|
}))
|
||||||
const pResult = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
@ -875,13 +988,23 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -902,7 +1025,9 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -917,20 +1042,30 @@ export const ModelingMachineProvider = ({
|
|||||||
length: lengthValue,
|
length: lengthValue,
|
||||||
})
|
})
|
||||||
if (err(constraintResult)) return Promise.reject(constraintResult)
|
if (err(constraintResult)) return Promise.reject(constraintResult)
|
||||||
const { modifiedAst, pathToNodeMap } = constraintResult
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
|
constraintResult
|
||||||
const pResult = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -951,13 +1086,15 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get perpendicular distance info': fromPromise(
|
'Get perpendicular distance info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
await applyConstraintIntersect({
|
await applyConstraintIntersect({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
@ -967,13 +1104,22 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -994,13 +1140,15 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get ABS X info': fromPromise(
|
'Get ABS X info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
await applyConstraintAbsDistance({
|
await applyConstraintAbsDistance({
|
||||||
constraint: 'xAbs',
|
constraint: 'xAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -1011,13 +1159,22 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1038,13 +1195,15 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get ABS Y info': fromPromise(
|
'Get ABS Y info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap } =
|
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
||||||
await applyConstraintAbsDistance({
|
await applyConstraintAbsDistance({
|
||||||
constraint: 'yAbs',
|
constraint: 'yAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -1055,13 +1214,22 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
const updatedPathToNode = updatePathToNodeFromMap(
|
|
||||||
sketchDetails.sketchPathToNode,
|
const {
|
||||||
pathToNodeMap
|
updatedSketchEntryNodePath,
|
||||||
)
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex,
|
||||||
|
})
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1082,7 +1250,9 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -1102,9 +1272,11 @@ export const ModelingMachineProvider = ({
|
|||||||
let result: {
|
let result: {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToReplaced: PathToNode | null
|
pathToReplaced: PathToNode | null
|
||||||
|
exprInsertIndex: number
|
||||||
} = {
|
} = {
|
||||||
modifiedAst: parsed,
|
modifiedAst: parsed,
|
||||||
pathToReplaced: null,
|
pathToReplaced: null,
|
||||||
|
exprInsertIndex: -1,
|
||||||
}
|
}
|
||||||
// If the user provided a constant name,
|
// If the user provided a constant name,
|
||||||
// we need to insert the named constant
|
// we need to insert the named constant
|
||||||
@ -1134,6 +1306,7 @@ export const ModelingMachineProvider = ({
|
|||||||
result = {
|
result = {
|
||||||
modifiedAst: parseResultAfterInsertion.program,
|
modifiedAst: parseResultAfterInsertion.program,
|
||||||
pathToReplaced: astAfterReplacement.pathToReplaced,
|
pathToReplaced: astAfterReplacement.pathToReplaced,
|
||||||
|
exprInsertIndex: astAfterReplacement.exprInsertIndex,
|
||||||
}
|
}
|
||||||
} else if ('valueText' in data.namedValue) {
|
} else if ('valueText' in data.namedValue) {
|
||||||
// If they didn't provide a constant name,
|
// If they didn't provide a constant name,
|
||||||
@ -1164,10 +1337,22 @@ export const ModelingMachineProvider = ({
|
|||||||
parsed = parsed as Node<Program>
|
parsed = parsed as Node<Program>
|
||||||
if (!result.pathToReplaced)
|
if (!result.pathToReplaced)
|
||||||
return Promise.reject(new Error('No path to replaced node'))
|
return Promise.reject(new Error('No path to replaced node'))
|
||||||
|
const {
|
||||||
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
} = updateSketchDetailsNodePaths({
|
||||||
|
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
exprInsertIndex: result.exprInsertIndex,
|
||||||
|
})
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
result.pathToReplaced || [],
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
parsed,
|
parsed,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1188,7 +1373,191 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedPathToNode: result.pathToReplaced,
|
updatedSketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'set-up-draft-circle': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, data } }) => {
|
||||||
|
if (!sketchDetails || !data)
|
||||||
|
return reject('No sketch details or data')
|
||||||
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
|
|
||||||
|
const result = await sceneEntitiesManager.setupDraftCircle(
|
||||||
|
sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchDetails.sketchNodePaths,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
|
sketchDetails.zAxis,
|
||||||
|
sketchDetails.yAxis,
|
||||||
|
sketchDetails.origin,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if (err(result)) return reject(result)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'set-up-draft-circle-three-point': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, data } }) => {
|
||||||
|
if (!sketchDetails || !data)
|
||||||
|
return reject('No sketch details or data')
|
||||||
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
|
|
||||||
|
const result =
|
||||||
|
await sceneEntitiesManager.setupDraftCircleThreePoint(
|
||||||
|
sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchDetails.sketchNodePaths,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
|
sketchDetails.zAxis,
|
||||||
|
sketchDetails.yAxis,
|
||||||
|
sketchDetails.origin,
|
||||||
|
data.p1,
|
||||||
|
data.p2
|
||||||
|
)
|
||||||
|
if (err(result)) return reject(result)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'set-up-draft-rectangle': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, data } }) => {
|
||||||
|
if (!sketchDetails || !data)
|
||||||
|
return reject('No sketch details or data')
|
||||||
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
|
|
||||||
|
const result = await sceneEntitiesManager.setupDraftRectangle(
|
||||||
|
sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchDetails.sketchNodePaths,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
|
sketchDetails.zAxis,
|
||||||
|
sketchDetails.yAxis,
|
||||||
|
sketchDetails.origin,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if (err(result)) return reject(result)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'set-up-draft-center-rectangle': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, data } }) => {
|
||||||
|
if (!sketchDetails || !data)
|
||||||
|
return reject('No sketch details or data')
|
||||||
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
|
const result = await sceneEntitiesManager.setupDraftCenterRectangle(
|
||||||
|
sketchDetails.sketchEntryNodePath,
|
||||||
|
sketchDetails.sketchNodePaths,
|
||||||
|
sketchDetails.planeNodePath,
|
||||||
|
sketchDetails.zAxis,
|
||||||
|
sketchDetails.yAxis,
|
||||||
|
sketchDetails.origin,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
if (err(result)) return reject(result)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'setup-client-side-sketch-segments': fromPromise(
|
||||||
|
async ({ input: { sketchDetails, selectionRanges } }) => {
|
||||||
|
if (!sketchDetails) return
|
||||||
|
if (!sketchDetails.sketchEntryNodePath.length) return
|
||||||
|
if (Object.keys(sceneEntitiesManager.activeSegments).length > 0) {
|
||||||
|
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
||||||
|
}
|
||||||
|
sceneInfra.resetMouseListeners()
|
||||||
|
await sceneEntitiesManager.setupSketch({
|
||||||
|
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
forward: sketchDetails.zAxis,
|
||||||
|
up: sketchDetails.yAxis,
|
||||||
|
position: sketchDetails.origin,
|
||||||
|
maybeModdedAst: kclManager.ast,
|
||||||
|
selectionRanges,
|
||||||
|
})
|
||||||
|
sceneInfra.resetMouseListeners()
|
||||||
|
|
||||||
|
sceneEntitiesManager.setupSketchIdleCallbacks({
|
||||||
|
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
||||||
|
forward: sketchDetails.zAxis,
|
||||||
|
up: sketchDetails.yAxis,
|
||||||
|
position: sketchDetails.origin,
|
||||||
|
sketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
planeNodePath: sketchDetails.planeNodePath,
|
||||||
|
// We will want to pass sketchTools here
|
||||||
|
// to add their interactions
|
||||||
|
})
|
||||||
|
|
||||||
|
// We will want to update the context with sketchTools.
|
||||||
|
// They'll be used for their .destroy() in tearDownSketch
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
),
|
||||||
|
'split-sketch-pipe-if-needed': fromPromise(
|
||||||
|
async ({ input: { sketchDetails } }) => {
|
||||||
|
if (!sketchDetails) return reject('No sketch details')
|
||||||
|
const existingSketchInfoNoOp = {
|
||||||
|
updatedEntryNodePath: sketchDetails.sketchEntryNodePath,
|
||||||
|
updatedSketchNodePaths: sketchDetails.sketchNodePaths,
|
||||||
|
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
||||||
|
expressionIndexToDelete: -1,
|
||||||
|
} as const
|
||||||
|
if (
|
||||||
|
!sketchDetails.sketchNodePaths.length &&
|
||||||
|
sketchDetails.planeNodePath.length
|
||||||
|
) {
|
||||||
|
// new sketch, no profiles yet
|
||||||
|
return existingSketchInfoNoOp
|
||||||
|
}
|
||||||
|
const doesNeedSplitting = doesSketchPipeNeedSplitting(
|
||||||
|
kclManager.ast,
|
||||||
|
sketchDetails.sketchEntryNodePath
|
||||||
|
)
|
||||||
|
if (err(doesNeedSplitting)) return reject(doesNeedSplitting)
|
||||||
|
let moddedAst: Program = structuredClone(kclManager.ast)
|
||||||
|
let pathToProfile = sketchDetails.sketchEntryNodePath
|
||||||
|
let updatedSketchNodePaths = sketchDetails.sketchNodePaths
|
||||||
|
if (doesNeedSplitting) {
|
||||||
|
const splitResult = splitPipedProfile(
|
||||||
|
moddedAst,
|
||||||
|
sketchDetails.sketchEntryNodePath
|
||||||
|
)
|
||||||
|
if (err(splitResult)) return reject(splitResult)
|
||||||
|
moddedAst = splitResult.modifiedAst
|
||||||
|
pathToProfile = splitResult.pathToProfile
|
||||||
|
updatedSketchNodePaths = [pathToProfile]
|
||||||
|
}
|
||||||
|
|
||||||
|
const indexToDelete = sketchDetails?.expressionIndexToDelete || -1
|
||||||
|
if (indexToDelete >= 0) {
|
||||||
|
// this is the expression that was added when as sketch tool was used but not completed
|
||||||
|
// i.e first click for the center of the circle, but not the second click for the radius
|
||||||
|
// we added a circle to editor, but they bailed out early so we should remove it
|
||||||
|
moddedAst.body.splice(indexToDelete, 1)
|
||||||
|
// make sure the deleted expression is removed from the sketchNodePaths
|
||||||
|
updatedSketchNodePaths = updatedSketchNodePaths.filter(
|
||||||
|
(path) => path[1][0] !== indexToDelete
|
||||||
|
)
|
||||||
|
// if the deleted expression was the entryNodePath, we should just make it the first sketchNodePath
|
||||||
|
// as a safe default
|
||||||
|
pathToProfile =
|
||||||
|
pathToProfile[1][0] !== indexToDelete
|
||||||
|
? pathToProfile
|
||||||
|
: updatedSketchNodePaths[0]
|
||||||
|
}
|
||||||
|
await kclManager.executeAstMock(moddedAst)
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(moddedAst)
|
||||||
|
return {
|
||||||
|
updatedEntryNodePath: pathToProfile,
|
||||||
|
updatedSketchNodePaths: updatedSketchNodePaths,
|
||||||
|
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
||||||
|
expressionIndexToDelete: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -1199,6 +1568,7 @@ export const ModelingMachineProvider = ({
|
|||||||
selections: input.selection,
|
selections: input.selection,
|
||||||
token,
|
token,
|
||||||
artifactGraph: engineCommandManager.artifactGraph,
|
artifactGraph: engineCommandManager.artifactGraph,
|
||||||
|
projectName: context.project.name,
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -13,12 +13,7 @@ import {
|
|||||||
getOperationLabel,
|
getOperationLabel,
|
||||||
stdLibMap,
|
stdLibMap,
|
||||||
} from 'lib/operations'
|
} from 'lib/operations'
|
||||||
import {
|
import { editorManager, engineCommandManager, kclManager } from 'lib/singletons'
|
||||||
codeManager,
|
|
||||||
editorManager,
|
|
||||||
engineCommandManager,
|
|
||||||
kclManager,
|
|
||||||
} from 'lib/singletons'
|
|
||||||
import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react'
|
import { ComponentProps, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||||
import { Actor, Prop } from 'xstate'
|
import { Actor, Prop } from 'xstate'
|
||||||
@ -67,7 +62,7 @@ export const FeatureTreePane = () => {
|
|||||||
)
|
)
|
||||||
: null
|
: null
|
||||||
|
|
||||||
if (!artifact || !('codeRef' in artifact)) {
|
if (!artifact) {
|
||||||
modelingSend({
|
modelingSend({
|
||||||
type: 'Set selection',
|
type: 'Set selection',
|
||||||
data: {
|
data: {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { processMemory } from './MemoryPane'
|
import { processMemory } from './MemoryPane'
|
||||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||||
import { assertParse, initPromise, ProgramMemory } from '../../../lang/wasm'
|
import { assertParse, initPromise } from '../../../lang/wasm'
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initPromise
|
await initPromise
|
||||||
@ -29,15 +29,11 @@ describe('processMemory', () => {
|
|||||||
|> line(endAbsolute = [2.15, 4.32])
|
|> line(endAbsolute = [2.15, 4.32])
|
||||||
// |> rx(90, %)`
|
// |> rx(90, %)`
|
||||||
const ast = assertParse(code)
|
const ast = assertParse(code)
|
||||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
const execState = await enginelessExecutor(ast)
|
||||||
const output = processMemory(execState.memory)
|
const output = processMemory(execState.variables)
|
||||||
expect(output.myVar).toEqual(5)
|
expect(output.myVar).toEqual(5)
|
||||||
expect(output.otherVar).toEqual(3)
|
expect(output.otherVar).toEqual(3)
|
||||||
expect(output).toEqual({
|
expect(output).toEqual({
|
||||||
HALF_TURN: 180,
|
|
||||||
QUARTER_TURN: 90,
|
|
||||||
THREE_QUARTER_TURN: 270,
|
|
||||||
ZERO: 0,
|
|
||||||
myVar: 5,
|
myVar: 5,
|
||||||
myFn: '__function(a)__',
|
myFn: '__function(a)__',
|
||||||
otherVar: 3,
|
otherVar: 3,
|
||||||
|
@ -2,10 +2,10 @@ import toast from 'react-hot-toast'
|
|||||||
import ReactJson from 'react-json-view'
|
import ReactJson from 'react-json-view'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
import {
|
import {
|
||||||
ProgramMemory,
|
|
||||||
Path,
|
Path,
|
||||||
ExtrudeSurface,
|
ExtrudeSurface,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
|
VariableMap,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { useResolvedTheme } from 'hooks/useResolvedTheme'
|
import { useResolvedTheme } from 'hooks/useResolvedTheme'
|
||||||
@ -15,12 +15,12 @@ import Tooltip from 'components/Tooltip'
|
|||||||
import { useModelingContext } from 'hooks/useModelingContext'
|
import { useModelingContext } from 'hooks/useModelingContext'
|
||||||
|
|
||||||
export const MemoryPaneMenu = () => {
|
export const MemoryPaneMenu = () => {
|
||||||
const { programMemory } = useKclContext()
|
const { variables } = useKclContext()
|
||||||
|
|
||||||
function copyProgramMemoryToClipboard() {
|
function copyProgramMemoryToClipboard() {
|
||||||
if (globalThis && 'navigator' in globalThis) {
|
if (globalThis && 'navigator' in globalThis) {
|
||||||
navigator.clipboard
|
navigator.clipboard
|
||||||
.writeText(JSON.stringify(programMemory))
|
.writeText(JSON.stringify(variables))
|
||||||
.then(() => toast.success('Program memory copied to clipboard'))
|
.then(() => toast.success('Program memory copied to clipboard'))
|
||||||
.catch((e) =>
|
.catch((e) =>
|
||||||
trap(new Error('Failed to copy program memory to clipboard'))
|
trap(new Error('Failed to copy program memory to clipboard'))
|
||||||
@ -50,12 +50,9 @@ export const MemoryPaneMenu = () => {
|
|||||||
|
|
||||||
export const MemoryPane = () => {
|
export const MemoryPane = () => {
|
||||||
const theme = useResolvedTheme()
|
const theme = useResolvedTheme()
|
||||||
const { programMemory } = useKclContext()
|
const { variables } = useKclContext()
|
||||||
const { state } = useModelingContext()
|
const { state } = useModelingContext()
|
||||||
const ProcessedMemory = useMemo(
|
const ProcessedMemory = useMemo(() => processMemory(variables), [variables])
|
||||||
() => processMemory(programMemory),
|
|
||||||
[programMemory]
|
|
||||||
)
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full relative">
|
<div className="h-full relative">
|
||||||
<div className="absolute inset-0 p-2 flex flex-col items-start">
|
<div className="absolute inset-0 p-2 flex flex-col items-start">
|
||||||
@ -85,9 +82,10 @@ export const MemoryPane = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const processMemory = (programMemory: ProgramMemory) => {
|
export const processMemory = (variables: VariableMap) => {
|
||||||
const processedMemory: any = {}
|
const processedMemory: any = {}
|
||||||
for (const [key, val] of programMemory?.visibleEntries()) {
|
for (const [key, val] of Object.entries(variables)) {
|
||||||
|
if (val === undefined) continue
|
||||||
if (
|
if (
|
||||||
val.type === 'Sketch' ||
|
val.type === 'Sketch' ||
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -19,7 +19,6 @@ import { commandBarActor } from 'machines/commandBarMachine'
|
|||||||
import { useSelector } from '@xstate/react'
|
import { useSelector } from '@xstate/react'
|
||||||
import { copyFileShareLink } from 'lib/links'
|
import { copyFileShareLink } from 'lib/links'
|
||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
|
||||||
import { useToken } from 'machines/appMachine'
|
import { useToken } from 'machines/appMachine'
|
||||||
|
|
||||||
const ProjectSidebarMenu = ({
|
const ProjectSidebarMenu = ({
|
||||||
@ -194,7 +193,7 @@ function ProjectMenuPopover({
|
|||||||
id: 'share-link',
|
id: 'share-link',
|
||||||
Element: 'button',
|
Element: 'button',
|
||||||
children: 'Share current part (via Zoo link)',
|
children: 'Share current part (via Zoo link)',
|
||||||
disabled: !(IS_NIGHTLY_OR_DEBUG && findCommand(shareCommandInfo)),
|
disabled: !findCommand(shareCommandInfo),
|
||||||
onClick: async () => {
|
onClick: async () => {
|
||||||
await copyFileShareLink({
|
await copyFileShareLink({
|
||||||
token: token ?? '',
|
token: token ?? '',
|
||||||
|
@ -2,7 +2,12 @@ import { SVGProps } from 'react'
|
|||||||
|
|
||||||
export const Spinner = (props: SVGProps<SVGSVGElement>) => {
|
export const Spinner = (props: SVGProps<SVGSVGElement>) => {
|
||||||
return (
|
return (
|
||||||
<svg viewBox="0 0 10 10" className={'w-8 h-8'} {...props}>
|
<svg
|
||||||
|
data-testid="spinner"
|
||||||
|
viewBox="0 0 10 10"
|
||||||
|
className={'w-8 h-8'}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
<circle
|
<circle
|
||||||
cx="5"
|
cx="5"
|
||||||
cy="5"
|
cy="5"
|
||||||
|
@ -95,7 +95,7 @@ export function applyConstraintEqualAngle({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
})
|
})
|
||||||
if (err(transform)) return transform
|
if (err(transform)) return transform
|
||||||
const { modifiedAst, pathToNodeMap } = transform
|
const { modifiedAst, pathToNodeMap } = transform
|
||||||
|
@ -93,7 +93,7 @@ export function applyConstraintEqualLength({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
})
|
})
|
||||||
if (err(transform)) return transform
|
if (err(transform)) return transform
|
||||||
const { modifiedAst, pathToNodeMap } = transform
|
const { modifiedAst, pathToNodeMap } = transform
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { toolTips } from 'lang/langHelpers'
|
import { toolTips } from 'lang/langHelpers'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { Program, ProgramMemory, Expr } from '../../lang/wasm'
|
import { Program, Expr, VariableMap } from '../../lang/wasm'
|
||||||
import { getNodeFromPath } from '../../lang/queryAst'
|
import { getNodeFromPath } from '../../lang/queryAst'
|
||||||
import {
|
import {
|
||||||
PathToNodeMap,
|
PathToNodeMap,
|
||||||
@ -51,7 +51,7 @@ export function applyConstraintHorzVert(
|
|||||||
selectionRanges: Selections,
|
selectionRanges: Selections,
|
||||||
horOrVert: 'vertical' | 'horizontal',
|
horOrVert: 'vertical' | 'horizontal',
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory
|
memVars: VariableMap
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
@ -66,7 +66,7 @@ export function applyConstraintHorzVert(
|
|||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory,
|
memVars,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ export function intersectInfo({
|
|||||||
isLinesParallelAndConstrained(
|
isLinesParallelAndConstrained(
|
||||||
kclManager.ast,
|
kclManager.ast,
|
||||||
engineCommandManager.artifactGraph,
|
engineCommandManager.artifactGraph,
|
||||||
kclManager.programMemory,
|
kclManager.variables,
|
||||||
selectionRanges.graphSelections[0],
|
selectionRanges.graphSelections[0],
|
||||||
selectionRanges.graphSelections[1]
|
selectionRanges.graphSelections[1]
|
||||||
)
|
)
|
||||||
@ -136,6 +136,7 @@ export async function applyConstraintIntersect({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
}> {
|
}> {
|
||||||
const info = intersectInfo({
|
const info = intersectInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -147,7 +148,7 @@ export async function applyConstraintIntersect({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges: forcedSelectionRanges,
|
selectionRanges: forcedSelectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
})
|
})
|
||||||
if (err(transform1)) return Promise.reject(transform1)
|
if (err(transform1)) return Promise.reject(transform1)
|
||||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||||
@ -174,6 +175,7 @@ export async function applyConstraintIntersect({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
@ -184,7 +186,7 @@ export async function applyConstraintIntersect({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges: forcedSelectionRanges,
|
selectionRanges: forcedSelectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
forceSegName: segName,
|
forceSegName: segName,
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
@ -192,6 +194,7 @@ export async function applyConstraintIntersect({
|
|||||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||||
transform2
|
transform2
|
||||||
|
|
||||||
|
let exprInsertIndex = -1
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -204,9 +207,11 @@ export async function applyConstraintIntersect({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
|
exprInsertIndex = newVariableInsertIndex
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap: _pathToNodeMap,
|
pathToNodeMap: _pathToNodeMap,
|
||||||
|
exprInsertIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export function removeConstrainingValuesInfo({
|
|||||||
| Error {
|
| Error {
|
||||||
const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => {
|
const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => {
|
||||||
const tmp = getNodeFromPath<Expr>(kclManager.ast, codeRef.pathToNode)
|
const tmp = getNodeFromPath<Expr>(kclManager.ast, codeRef.pathToNode)
|
||||||
if (err(tmp)) return tmp
|
if (tmp instanceof Error) return tmp
|
||||||
return tmp.node
|
return tmp.node
|
||||||
})
|
})
|
||||||
const _err1 = _nodes.find(err)
|
const _err1 = _nodes.find(err)
|
||||||
@ -88,7 +88,7 @@ export function applyRemoveConstrainingValues({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges: updatedSelectionRanges,
|
selectionRanges: updatedSelectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ export async function applyConstraintAbsDistance({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
}> {
|
}> {
|
||||||
const info = absDistanceInfo({
|
const info = absDistanceInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -104,7 +105,7 @@ export async function applyConstraintAbsDistance({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
if (err(transform1)) return Promise.reject(transform1)
|
if (err(transform1)) return Promise.reject(transform1)
|
||||||
@ -124,13 +125,14 @@ export async function applyConstraintAbsDistance({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
if (err(transform2)) return Promise.reject(transform2)
|
if (err(transform2)) return Promise.reject(transform2)
|
||||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transform2
|
const { modifiedAst: _modifiedAst, pathToNodeMap } = transform2
|
||||||
|
|
||||||
|
let exprInsertIndex = -1
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -143,8 +145,9 @@ export async function applyConstraintAbsDistance({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
|
exprInsertIndex = newVariableInsertIndex
|
||||||
}
|
}
|
||||||
return { modifiedAst: _modifiedAst, pathToNodeMap }
|
return { modifiedAst: _modifiedAst, pathToNodeMap, exprInsertIndex }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyConstraintAxisAlign({
|
export function applyConstraintAxisAlign({
|
||||||
@ -172,7 +175,7 @@ export function applyConstraintAxisAlign({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
|
@ -86,6 +86,7 @@ export async function applyConstraintAngleBetween({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
}> {
|
}> {
|
||||||
const info = angleBetweenInfo({ selectionRanges })
|
const info = angleBetweenInfo({ selectionRanges })
|
||||||
if (err(info)) return Promise.reject(info)
|
if (err(info)) return Promise.reject(info)
|
||||||
@ -95,7 +96,7 @@ export async function applyConstraintAngleBetween({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
})
|
})
|
||||||
if (err(transformed1)) return Promise.reject(transformed1)
|
if (err(transformed1)) return Promise.reject(transformed1)
|
||||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||||
@ -122,6 +123,7 @@ export async function applyConstraintAngleBetween({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +135,7 @@ export async function applyConstraintAngleBetween({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
forceSegName: segName,
|
forceSegName: segName,
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
@ -141,6 +143,7 @@ export async function applyConstraintAngleBetween({
|
|||||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||||
transformed2
|
transformed2
|
||||||
|
|
||||||
|
let exprInsertIndex = -1
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -153,9 +156,11 @@ export async function applyConstraintAngleBetween({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
|
exprInsertIndex = newVariableInsertIndex
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap: _pathToNodeMap,
|
pathToNodeMap: _pathToNodeMap,
|
||||||
|
exprInsertIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,15 +87,13 @@ export function horzVertDistanceInfo({
|
|||||||
export async function applyConstraintHorzVertDistance({
|
export async function applyConstraintHorzVertDistance({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint,
|
constraint,
|
||||||
// TODO align will always be false (covered by synconous applyConstraintHorzVertAlign), remove it
|
|
||||||
isAlign = false,
|
|
||||||
}: {
|
}: {
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
constraint: 'setHorzDistance' | 'setVertDistance'
|
constraint: 'setHorzDistance' | 'setVertDistance'
|
||||||
isAlign?: false
|
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
}> {
|
}> {
|
||||||
const info = horzVertDistanceInfo({
|
const info = horzVertDistanceInfo({
|
||||||
selectionRanges: selectionRanges,
|
selectionRanges: selectionRanges,
|
||||||
@ -107,7 +105,7 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
})
|
})
|
||||||
if (err(transformed)) return Promise.reject(transformed)
|
if (err(transformed)) return Promise.reject(transformed)
|
||||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||||
@ -133,25 +131,25 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex: -1,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isExprBinaryPart(valueNode))
|
if (!isExprBinaryPart(valueNode))
|
||||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
let finalValue = isAlign
|
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
? createLiteral(0)
|
|
||||||
: removeDoubleNegatives(valueNode, sign, variableName)
|
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const transformed = transformSecondarySketchLinesTagFirst({
|
const transformed = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
forceSegName: segName,
|
forceSegName: segName,
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
|
|
||||||
if (err(transformed)) return Promise.reject(transformed)
|
if (err(transformed)) return Promise.reject(transformed)
|
||||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transformed
|
const { modifiedAst: _modifiedAst, pathToNodeMap } = transformed
|
||||||
|
let exprInsertIndex = -1
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -164,10 +162,12 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
|
exprInsertIndex = newVariableInsertIndex
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,7 +195,7 @@ export function applyConstraintHorzVertAlign({
|
|||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos,
|
transformInfos,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
if (err(retval)) return retval
|
if (err(retval)) return retval
|
||||||
|
@ -74,10 +74,14 @@ export async function applyConstraintLength({
|
|||||||
}: {
|
}: {
|
||||||
length: KclCommandValue
|
length: KclCommandValue
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
}) {
|
}): Promise<{
|
||||||
|
modifiedAst: Program
|
||||||
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
|
}> {
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const angleLength = angleLengthInfo({ selectionRanges })
|
const angleLength = angleLengthInfo({ selectionRanges })
|
||||||
if (err(angleLength)) return angleLength
|
if (err(angleLength)) return Promise.reject(angleLength)
|
||||||
const { transforms } = angleLength
|
const { transforms } = angleLength
|
||||||
|
|
||||||
let distanceExpression: Expr = length.valueAst
|
let distanceExpression: Expr = length.valueAst
|
||||||
@ -98,14 +102,14 @@ export async function applyConstraintLength({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isExprBinaryPart(distanceExpression)) {
|
if (!isExprBinaryPart(distanceExpression)) {
|
||||||
return new Error('Invalid valueNode, is not a BinaryPart')
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
}
|
}
|
||||||
|
|
||||||
const retval = transformAstSketchLines({
|
const retval = transformAstSketchLines({
|
||||||
ast,
|
ast,
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
forceValueUsedInTransform: distanceExpression,
|
forceValueUsedInTransform: distanceExpression,
|
||||||
})
|
})
|
||||||
@ -116,6 +120,12 @@ export async function applyConstraintLength({
|
|||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex:
|
||||||
|
'variableName' in length &&
|
||||||
|
length.variableName &&
|
||||||
|
length.insertIndex !== undefined
|
||||||
|
? length.insertIndex
|
||||||
|
: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,6 +138,7 @@ export async function applyConstraintAngleLength({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
|
exprInsertIndex: number
|
||||||
}> {
|
}> {
|
||||||
const angleLength = angleLengthInfo({ selectionRanges, angleOrLength })
|
const angleLength = angleLengthInfo({ selectionRanges, angleOrLength })
|
||||||
if (err(angleLength)) return Promise.reject(angleLength)
|
if (err(angleLength)) return Promise.reject(angleLength)
|
||||||
@ -137,7 +148,7 @@ export async function applyConstraintAngleLength({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
if (err(sketched)) return Promise.reject(sketched)
|
if (err(sketched)) return Promise.reject(sketched)
|
||||||
@ -189,7 +200,7 @@ export async function applyConstraintAngleLength({
|
|||||||
ast: structuredClone(kclManager.ast),
|
ast: structuredClone(kclManager.ast),
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
transformInfos: transforms,
|
transformInfos: transforms,
|
||||||
programMemory: kclManager.programMemory,
|
memVars: kclManager.variables,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
forceValueUsedInTransform: finalValue,
|
forceValueUsedInTransform: finalValue,
|
||||||
})
|
})
|
||||||
@ -212,5 +223,6 @@ export async function applyConstraintAngleLength({
|
|||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
|
exprInsertIndex: variableName ? newVariableInsertIndex : -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ export function useConvertToVariable(range?: SourceRange) {
|
|||||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||||
moveValueIntoNewVariable(
|
moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
kclManager.programMemory,
|
kclManager.variables,
|
||||||
range || context.selectionRanges.graphSelections[0]?.codeRef?.range,
|
range || context.selectionRanges.graphSelections[0]?.codeRef?.range,
|
||||||
variableName
|
variableName
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,7 @@ import { KCLError } from './errors'
|
|||||||
|
|
||||||
const KclContext = createContext({
|
const KclContext = createContext({
|
||||||
code: codeManager?.code || '',
|
code: codeManager?.code || '',
|
||||||
programMemory: kclManager?.programMemory,
|
variables: kclManager?.variables,
|
||||||
ast: kclManager?.ast,
|
ast: kclManager?.ast,
|
||||||
isExecuting: kclManager?.isExecuting,
|
isExecuting: kclManager?.isExecuting,
|
||||||
diagnostics: kclManager?.diagnostics,
|
diagnostics: kclManager?.diagnostics,
|
||||||
@ -31,7 +31,7 @@ export function KclContextProvider({
|
|||||||
// Both the code state and the editor state start off with the same code.
|
// Both the code state and the editor state start off with the same code.
|
||||||
const [code, setCode] = useState(loadedCode || codeManager.code)
|
const [code, setCode] = useState(loadedCode || codeManager.code)
|
||||||
|
|
||||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
const [variables, setVariables] = useState(kclManager.variables)
|
||||||
const [ast, setAst] = useState(kclManager.ast)
|
const [ast, setAst] = useState(kclManager.ast)
|
||||||
const [isExecuting, setIsExecuting] = useState(false)
|
const [isExecuting, setIsExecuting] = useState(false)
|
||||||
const [diagnostics, setDiagnostics] = useState<Diagnostic[]>([])
|
const [diagnostics, setDiagnostics] = useState<Diagnostic[]>([])
|
||||||
@ -44,7 +44,7 @@ export function KclContextProvider({
|
|||||||
setCode,
|
setCode,
|
||||||
})
|
})
|
||||||
kclManager.registerCallBacks({
|
kclManager.registerCallBacks({
|
||||||
setProgramMemory,
|
setVariables,
|
||||||
setAst,
|
setAst,
|
||||||
setLogs,
|
setLogs,
|
||||||
setErrors,
|
setErrors,
|
||||||
@ -58,7 +58,7 @@ export function KclContextProvider({
|
|||||||
<KclContext.Provider
|
<KclContext.Provider
|
||||||
value={{
|
value={{
|
||||||
code,
|
code,
|
||||||
programMemory,
|
variables,
|
||||||
ast,
|
ast,
|
||||||
isExecuting,
|
isExecuting,
|
||||||
diagnostics,
|
diagnostics,
|
||||||
|
@ -16,14 +16,16 @@ import {
|
|||||||
clearSceneAndBustCache,
|
clearSceneAndBustCache,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
|
getKclVersion,
|
||||||
initPromise,
|
initPromise,
|
||||||
|
KclValue,
|
||||||
parse,
|
parse,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
Program,
|
Program,
|
||||||
ProgramMemory,
|
|
||||||
recast,
|
recast,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
|
VariableMap,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { getNodeFromPath, getSettingsAnnotation } from './queryAst'
|
import { getNodeFromPath, getSettingsAnnotation } from './queryAst'
|
||||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||||
@ -58,11 +60,12 @@ export class KclManager {
|
|||||||
nonCodeNodes: {},
|
nonCodeNodes: {},
|
||||||
startNodes: [],
|
startNodes: [],
|
||||||
},
|
},
|
||||||
trivia: [],
|
innerAttrs: [],
|
||||||
|
outerAttrs: [],
|
||||||
}
|
}
|
||||||
private _execState: ExecState = emptyExecState()
|
private _execState: ExecState = emptyExecState()
|
||||||
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
private _variables: VariableMap = {}
|
||||||
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
lastSuccessfulVariables: VariableMap = {}
|
||||||
lastSuccessfulOperations: Operation[] = []
|
lastSuccessfulOperations: Operation[] = []
|
||||||
private _logs: string[] = []
|
private _logs: string[] = []
|
||||||
private _errors: KCLError[] = []
|
private _errors: KCLError[] = []
|
||||||
@ -73,12 +76,15 @@ export class KclManager {
|
|||||||
private _hasErrors = false
|
private _hasErrors = false
|
||||||
private _switchedFiles = false
|
private _switchedFiles = false
|
||||||
private _fileSettings: KclSettingsAnnotation = {}
|
private _fileSettings: KclSettingsAnnotation = {}
|
||||||
|
private _kclVersion: string | undefined = undefined
|
||||||
|
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
|
|
||||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||||
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
private _variablesCallBack: (arg: {
|
||||||
|
[key in string]?: KclValue | undefined
|
||||||
|
}) => void = () => {}
|
||||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||||
private _kclErrorsCallBack: (errors: KCLError[]) => void = () => {}
|
private _kclErrorsCallBack: (errors: KCLError[]) => void = () => {}
|
||||||
private _diagnosticsCallback: (errors: Diagnostic[]) => void = () => {}
|
private _diagnosticsCallback: (errors: Diagnostic[]) => void = () => {}
|
||||||
@ -97,24 +103,34 @@ export class KclManager {
|
|||||||
this._switchedFiles = switchedFiles
|
this._switchedFiles = switchedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
get programMemory() {
|
get variables() {
|
||||||
return this._programMemory
|
return this._variables
|
||||||
}
|
}
|
||||||
// This is private because callers should be setting the entire execState.
|
// This is private because callers should be setting the entire execState.
|
||||||
private set programMemory(programMemory) {
|
private set variables(variables) {
|
||||||
this._programMemory = programMemory
|
this._variables = variables
|
||||||
this._programMemoryCallBack(programMemory)
|
this._variablesCallBack(variables)
|
||||||
}
|
}
|
||||||
|
|
||||||
private set execState(execState) {
|
private set execState(execState) {
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this.programMemory = execState.memory
|
this.variables = execState.variables
|
||||||
}
|
}
|
||||||
|
|
||||||
get execState() {
|
get execState() {
|
||||||
return this._execState
|
return this._execState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the kcl version from the wasm module
|
||||||
|
// and store it in the singleton
|
||||||
|
// so we don't waste time getting it multiple times
|
||||||
|
get kclVersion() {
|
||||||
|
if (this._kclVersion === undefined) {
|
||||||
|
this._kclVersion = getKclVersion()
|
||||||
|
}
|
||||||
|
return this._kclVersion
|
||||||
|
}
|
||||||
|
|
||||||
get errors() {
|
get errors() {
|
||||||
return this._errors
|
return this._errors
|
||||||
}
|
}
|
||||||
@ -201,7 +217,7 @@ export class KclManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
registerCallBacks({
|
registerCallBacks({
|
||||||
setProgramMemory,
|
setVariables,
|
||||||
setAst,
|
setAst,
|
||||||
setLogs,
|
setLogs,
|
||||||
setErrors,
|
setErrors,
|
||||||
@ -209,7 +225,7 @@ export class KclManager {
|
|||||||
setIsExecuting,
|
setIsExecuting,
|
||||||
setWasmInitFailed,
|
setWasmInitFailed,
|
||||||
}: {
|
}: {
|
||||||
setProgramMemory: (arg: ProgramMemory) => void
|
setVariables: (arg: VariableMap) => void
|
||||||
setAst: (arg: Node<Program>) => void
|
setAst: (arg: Node<Program>) => void
|
||||||
setLogs: (arg: string[]) => void
|
setLogs: (arg: string[]) => void
|
||||||
setErrors: (errors: KCLError[]) => void
|
setErrors: (errors: KCLError[]) => void
|
||||||
@ -217,7 +233,7 @@ export class KclManager {
|
|||||||
setIsExecuting: (arg: boolean) => void
|
setIsExecuting: (arg: boolean) => void
|
||||||
setWasmInitFailed: (arg: boolean) => void
|
setWasmInitFailed: (arg: boolean) => void
|
||||||
}) {
|
}) {
|
||||||
this._programMemoryCallBack = setProgramMemory
|
this._variablesCallBack = setVariables
|
||||||
this._astCallBack = setAst
|
this._astCallBack = setAst
|
||||||
this._logsCallBack = setLogs
|
this._logsCallBack = setLogs
|
||||||
this._kclErrorsCallBack = setErrors
|
this._kclErrorsCallBack = setErrors
|
||||||
@ -240,7 +256,8 @@ export class KclManager {
|
|||||||
nonCodeNodes: {},
|
nonCodeNodes: {},
|
||||||
startNodes: [],
|
startNodes: [],
|
||||||
},
|
},
|
||||||
trivia: [],
|
innerAttrs: [],
|
||||||
|
outerAttrs: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +346,7 @@ export class KclManager {
|
|||||||
ast,
|
ast,
|
||||||
path: codeManager.currentFilePath || undefined,
|
path: codeManager.currentFilePath || undefined,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
|
isMock: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Program was not interrupted, setup the scene
|
// Program was not interrupted, setup the scene
|
||||||
@ -385,17 +403,16 @@ export class KclManager {
|
|||||||
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
||||||
this.execState = execState
|
this.execState = execState
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulVariables = execState.variables
|
||||||
this.lastSuccessfulOperations = execState.operations
|
this.lastSuccessfulOperations = execState.operations
|
||||||
}
|
}
|
||||||
this.ast = { ...ast }
|
this.ast = { ...ast }
|
||||||
// updateArtifactGraph relies on updated executeState/programMemory
|
// updateArtifactGraph relies on updated executeState/variables
|
||||||
this.engineCommandManager.updateArtifactGraph(execState.artifactGraph)
|
this.engineCommandManager.updateArtifactGraph(execState.artifactGraph)
|
||||||
this._executeCallback()
|
this._executeCallback()
|
||||||
if (!isInterrupted) {
|
if (!isInterrupted) {
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
}
|
}
|
||||||
|
|
||||||
this.engineCommandManager.addCommandLog({
|
this.engineCommandManager.addCommandLog({
|
||||||
type: 'execution-done',
|
type: 'execution-done',
|
||||||
data: null,
|
data: null,
|
||||||
@ -442,16 +459,16 @@ export class KclManager {
|
|||||||
const { logs, errors, execState } = await executeAst({
|
const { logs, errors, execState } = await executeAst({
|
||||||
ast: newAst,
|
ast: newAst,
|
||||||
engineCommandManager: this.engineCommandManager,
|
engineCommandManager: this.engineCommandManager,
|
||||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
isMock: true,
|
||||||
programMemoryOverride: ProgramMemory.empty(),
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this._logs = logs
|
this._logs = logs
|
||||||
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
||||||
|
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this._programMemory = execState.memory
|
this._variables = execState.variables
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
this.lastSuccessfulProgramMemory = execState.memory
|
this.lastSuccessfulVariables = execState.variables
|
||||||
this.lastSuccessfulOperations = execState.operations
|
this.lastSuccessfulOperations = execState.operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
import { assertParse, initPromise, parse } from './wasm'
|
|
||||||
import { err } from 'lib/trap'
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await initPromise
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('testing AST', () => {
|
|
||||||
test('5 + 6', () => {
|
|
||||||
const ast = assertParse('5 +6')
|
|
||||||
delete (ast as any).nonCodeMeta
|
|
||||||
expect(ast.body).toEqual([
|
|
||||||
{
|
|
||||||
type: 'ExpressionStatement',
|
|
||||||
start: 0,
|
|
||||||
end: 4,
|
|
||||||
|
|
||||||
expression: {
|
|
||||||
type: 'BinaryExpression',
|
|
||||||
start: 0,
|
|
||||||
end: 4,
|
|
||||||
|
|
||||||
left: {
|
|
||||||
type: 'Literal',
|
|
||||||
start: 0,
|
|
||||||
end: 1,
|
|
||||||
value: {
|
|
||||||
suffix: 'None',
|
|
||||||
value: 5,
|
|
||||||
},
|
|
||||||
raw: '5',
|
|
||||||
},
|
|
||||||
operator: '+',
|
|
||||||
right: {
|
|
||||||
type: 'Literal',
|
|
||||||
start: 3,
|
|
||||||
end: 4,
|
|
||||||
value: {
|
|
||||||
suffix: 'None',
|
|
||||||
value: 6,
|
|
||||||
},
|
|
||||||
raw: '6',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('parsing errors', () => {
|
|
||||||
it('should return an error when there is a unexpected closed curly brace', async () => {
|
|
||||||
const code = `const myVar = startSketchAt([}], %)`
|
|
||||||
const result = parse(code)
|
|
||||||
if (err(result)) throw result
|
|
||||||
const error = result.errors[0]
|
|
||||||
expect(error.message).toBe('Array is missing a closing bracket(`]`)')
|
|
||||||
expect(error.sourceRange).toEqual([28, 29, 0])
|
|
||||||
})
|
|
||||||
})
|
|
@ -15,8 +15,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
|> line(endAbsolute = [0.46, -5.82])
|
|> line(endAbsolute = [0.46, -5.82])
|
||||||
// |> rx(45, %)`
|
// |> rx(45, %)`
|
||||||
const execState = await enginelessExecutor(assertParse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
const sketch001 = execState.variables['mySketch001']
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
type: 'Sketch',
|
type: 'Sketch',
|
||||||
value: {
|
value: {
|
||||||
@ -73,8 +72,7 @@ const mySketch001 = startSketchOn('XY')
|
|||||||
// |> rx(45, %)
|
// |> rx(45, %)
|
||||||
|> extrude(length = 2)`
|
|> extrude(length = 2)`
|
||||||
const execState = await enginelessExecutor(assertParse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
// @ts-ignore
|
const sketch001 = execState.variables['mySketch001']
|
||||||
const sketch001 = execState.memory.get('mySketch001')
|
|
||||||
expect(sketch001).toEqual({
|
expect(sketch001).toEqual({
|
||||||
type: 'Solid',
|
type: 'Solid',
|
||||||
value: {
|
value: {
|
||||||
@ -165,9 +163,9 @@ const sk2 = startSketchOn('XY')
|
|||||||
|
|
||||||
`
|
`
|
||||||
const execState = await enginelessExecutor(assertParse(code))
|
const execState = await enginelessExecutor(assertParse(code))
|
||||||
const programMemory = execState.memory
|
const variables = execState.variables
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
const geos = [variables['theExtrude'], variables['sk2']]
|
||||||
expect(geos).toEqual([
|
expect(geos).toEqual([
|
||||||
{
|
{
|
||||||
type: 'Solid',
|
type: 'Solid',
|
||||||
|
@ -2,12 +2,12 @@ import fs from 'node:fs'
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
assertParse,
|
assertParse,
|
||||||
ProgramMemory,
|
|
||||||
Sketch,
|
Sketch,
|
||||||
initPromise,
|
initPromise,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
defaultArtifactGraph,
|
defaultArtifactGraph,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
|
VariableMap,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
@ -21,13 +21,13 @@ describe('test executor', () => {
|
|||||||
const code = `const myVar = 5
|
const code = `const myVar = 5
|
||||||
const newVar = myVar + 1`
|
const newVar = myVar + 1`
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(5)
|
expect(mem['myVar']?.value).toBe(5)
|
||||||
expect(mem.get('newVar')?.value).toBe(6)
|
expect(mem['newVar']?.value).toBe(6)
|
||||||
})
|
})
|
||||||
it('test assigning a var with a string', async () => {
|
it('test assigning a var with a string', async () => {
|
||||||
const code = `const myVar = "a str"`
|
const code = `const myVar = "a str"`
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe('a str')
|
expect(mem['myVar']?.value).toBe('a str')
|
||||||
})
|
})
|
||||||
it('test assigning a var by cont concatenating two strings string execute', async () => {
|
it('test assigning a var by cont concatenating two strings string execute', async () => {
|
||||||
const code = fs.readFileSync(
|
const code = fs.readFileSync(
|
||||||
@ -35,7 +35,7 @@ const newVar = myVar + 1`
|
|||||||
'utf-8'
|
'utf-8'
|
||||||
)
|
)
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe('a str another str')
|
expect(mem['myVar']?.value).toBe('a str another str')
|
||||||
})
|
})
|
||||||
it('fn funcN = () => {} execute', async () => {
|
it('fn funcN = () => {} execute', async () => {
|
||||||
const mem = await exe(
|
const mem = await exe(
|
||||||
@ -47,8 +47,8 @@ const newVar = myVar + 1`
|
|||||||
'const magicNum = funcN(9, theVar)',
|
'const magicNum = funcN(9, theVar)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
)
|
)
|
||||||
expect(mem.get('theVar')?.value).toBe(60)
|
expect(mem['theVar']?.value).toBe(60)
|
||||||
expect(mem.get('magicNum')?.value).toBe(69)
|
expect(mem['magicNum']?.value).toBe(69)
|
||||||
})
|
})
|
||||||
it('sketch declaration', async () => {
|
it('sketch declaration', async () => {
|
||||||
let code = `const mySketch = startSketchOn('XY')
|
let code = `const mySketch = startSketchOn('XY')
|
||||||
@ -60,7 +60,7 @@ const newVar = myVar + 1`
|
|||||||
`
|
`
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
// geo is three js buffer geometry and is very bloated to have in tests
|
// geo is three js buffer geometry and is very bloated to have in tests
|
||||||
const sk = mem.get('mySketch')
|
const sk = mem['mySketch']
|
||||||
expect(sk?.type).toEqual('Sketch')
|
expect(sk?.type).toEqual('Sketch')
|
||||||
if (sk?.type !== 'Sketch') {
|
if (sk?.type !== 'Sketch') {
|
||||||
return
|
return
|
||||||
@ -117,7 +117,7 @@ const newVar = myVar + 1`
|
|||||||
'const myVar = 5 + 1 |> myFn(%)',
|
'const myVar = 5 + 1 |> myFn(%)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(7)
|
expect(mem['myVar']?.value).toBe(7)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Enable rotations #152
|
// Enable rotations #152
|
||||||
@ -130,15 +130,15 @@ const newVar = myVar + 1`
|
|||||||
// 'const rotated = rx(90, mySk1)',
|
// 'const rotated = rx(90, mySk1)',
|
||||||
// ].join('\n')
|
// ].join('\n')
|
||||||
// const mem = await exe(code)
|
// const mem = await exe(code)
|
||||||
// expect(mem.get('mySk1')?.value).toHaveLength(3)
|
// expect(mem['mySk1']?.value).toHaveLength(3)
|
||||||
// expect(mem.get('rotated')?.type).toBe('Sketch')
|
// expect(mem['rotated')?.type).toBe('Sketch']
|
||||||
// if (
|
// if (
|
||||||
// mem.get('mySk1')?.type !== 'Sketch' ||
|
// mem['mySk1']?.type !== 'Sketch' ||
|
||||||
// mem.get('rotated')?.type !== 'Sketch'
|
// mem['rotated']?.type !== 'Sketch'
|
||||||
// )
|
// )
|
||||||
// throw new Error('not a sketch')
|
// throw new Error('not a sketch')
|
||||||
// expect(mem.get('mySk1')?.rotation).toEqual([0, 0, 0, 1])
|
// expect(mem['mySk1']?.rotation).toEqual([0, 0, 0, 1])
|
||||||
// expect(mem.get('rotated')?.rotation.map((a) => a.toFixed(4))).toEqual([
|
// expect(mem['rotated']?.rotation.map((a) => a.toFixed(4))).toEqual([
|
||||||
// '0.7071',
|
// '0.7071',
|
||||||
// '0.0000',
|
// '0.0000',
|
||||||
// '0.0000',
|
// '0.0000',
|
||||||
@ -157,7 +157,7 @@ const newVar = myVar + 1`
|
|||||||
// ' |> rx(90, %)',
|
// ' |> rx(90, %)',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('mySk1')).toEqual({
|
expect(mem['mySk1']).toEqual({
|
||||||
type: 'Sketch',
|
type: 'Sketch',
|
||||||
value: {
|
value: {
|
||||||
type: 'Sketch',
|
type: 'Sketch',
|
||||||
@ -236,7 +236,7 @@ const newVar = myVar + 1`
|
|||||||
)
|
)
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
// TODO path to node is probably wrong here, zero indexes are not correct
|
// TODO path to node is probably wrong here, zero indexes are not correct
|
||||||
expect(mem.get('three')).toEqual({
|
expect(mem['three']).toEqual({
|
||||||
type: 'Number',
|
type: 'Number',
|
||||||
value: 3,
|
value: 3,
|
||||||
__meta: [
|
__meta: [
|
||||||
@ -245,7 +245,7 @@ const newVar = myVar + 1`
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
expect(mem.get('yo')).toEqual({
|
expect(mem['yo']).toEqual({
|
||||||
type: 'Array',
|
type: 'Array',
|
||||||
value: [
|
value: [
|
||||||
{ type: 'Number', value: 1, __meta: [{ sourceRange: [28, 29, 0] }] },
|
{ type: 'Number', value: 1, __meta: [{ sourceRange: [28, 29, 0] }] },
|
||||||
@ -263,9 +263,6 @@ const newVar = myVar + 1`
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
// Check that there are no other variables or environments.
|
|
||||||
expect(mem.numEnvironments()).toBe(1)
|
|
||||||
expect(mem.numVariables(0)).toBe(2)
|
|
||||||
})
|
})
|
||||||
it('execute object expression', async () => {
|
it('execute object expression', async () => {
|
||||||
const code = [
|
const code = [
|
||||||
@ -273,7 +270,7 @@ const newVar = myVar + 1`
|
|||||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('yo')).toEqual({
|
expect(mem['yo']).toEqual({
|
||||||
type: 'Object',
|
type: 'Object',
|
||||||
value: {
|
value: {
|
||||||
aStr: {
|
aStr: {
|
||||||
@ -309,7 +306,7 @@ const newVar = myVar + 1`
|
|||||||
'\n'
|
'\n'
|
||||||
)
|
)
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')).toEqual({
|
expect(mem['myVar']).toEqual({
|
||||||
type: 'String',
|
type: 'String',
|
||||||
value: '123',
|
value: '123',
|
||||||
__meta: [
|
__meta: [
|
||||||
@ -325,80 +322,80 @@ describe('testing math operators', () => {
|
|||||||
it('can sum', async () => {
|
it('can sum', async () => {
|
||||||
const code = ['const myVar = 1 + 2'].join('\n')
|
const code = ['const myVar = 1 + 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(3)
|
expect(mem['myVar']?.value).toBe(3)
|
||||||
})
|
})
|
||||||
it('can subtract', async () => {
|
it('can subtract', async () => {
|
||||||
const code = ['const myVar = 1 - 2'].join('\n')
|
const code = ['const myVar = 1 - 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(-1)
|
expect(mem['myVar']?.value).toBe(-1)
|
||||||
})
|
})
|
||||||
it('can multiply', async () => {
|
it('can multiply', async () => {
|
||||||
const code = ['const myVar = 1 * 2'].join('\n')
|
const code = ['const myVar = 1 * 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(2)
|
expect(mem['myVar']?.value).toBe(2)
|
||||||
})
|
})
|
||||||
it('can divide', async () => {
|
it('can divide', async () => {
|
||||||
const code = ['const myVar = 1 / 2'].join('\n')
|
const code = ['const myVar = 1 / 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(0.5)
|
expect(mem['myVar']?.value).toBe(0.5)
|
||||||
})
|
})
|
||||||
it('can modulus', async () => {
|
it('can modulus', async () => {
|
||||||
const code = ['const myVar = 5 % 2'].join('\n')
|
const code = ['const myVar = 5 % 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(1)
|
expect(mem['myVar']?.value).toBe(1)
|
||||||
})
|
})
|
||||||
it('can do multiple operations', async () => {
|
it('can do multiple operations', async () => {
|
||||||
const code = ['const myVar = 1 + 2 * 3'].join('\n')
|
const code = ['const myVar = 1 + 2 * 3'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(7)
|
expect(mem['myVar']?.value).toBe(7)
|
||||||
})
|
})
|
||||||
it('big example with parans', async () => {
|
it('big example with parans', async () => {
|
||||||
const code = ['const myVar = 1 + 2 * (3 - 4) / -5 + 6'].join('\n')
|
const code = ['const myVar = 1 + 2 * (3 - 4) / -5 + 6'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(7.4)
|
expect(mem['myVar']?.value).toBe(7.4)
|
||||||
})
|
})
|
||||||
it('with identifier', async () => {
|
it('with identifier', async () => {
|
||||||
const code = ['const yo = 6', 'const myVar = yo / 2'].join('\n')
|
const code = ['const yo = 6', 'const myVar = yo / 2'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(3)
|
expect(mem['myVar']?.value).toBe(3)
|
||||||
})
|
})
|
||||||
it('with lots of testing', async () => {
|
it('with lots of testing', async () => {
|
||||||
const code = ['const myVar = 2 * ((2 + 3 ) / 4 + 5)'].join('\n')
|
const code = ['const myVar = 2 * ((2 + 3 ) / 4 + 5)'].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(12.5)
|
expect(mem['myVar']?.value).toBe(12.5)
|
||||||
})
|
})
|
||||||
it('with callExpression at start', async () => {
|
it('with callExpression at start', async () => {
|
||||||
const code = 'const myVar = min(4, 100) + 2'
|
const code = 'const myVar = min(4, 100) + 2'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(6)
|
expect(mem['myVar']?.value).toBe(6)
|
||||||
})
|
})
|
||||||
it('with callExpression at end', async () => {
|
it('with callExpression at end', async () => {
|
||||||
const code = 'const myVar = 2 + min(4, 100)'
|
const code = 'const myVar = 2 + min(4, 100)'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(6)
|
expect(mem['myVar']?.value).toBe(6)
|
||||||
})
|
})
|
||||||
it('with nested callExpression', async () => {
|
it('with nested callExpression', async () => {
|
||||||
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(6)
|
expect(mem['myVar']?.value).toBe(6)
|
||||||
})
|
})
|
||||||
it('with unaryExpression', async () => {
|
it('with unaryExpression', async () => {
|
||||||
const code = 'const myVar = -min(100, 3)'
|
const code = 'const myVar = -min(100, 3)'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(-3)
|
expect(mem['myVar']?.value).toBe(-3)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in callExpression', async () => {
|
it('with unaryExpression in callExpression', async () => {
|
||||||
const code = 'const myVar = min(-legLen(5, 4), 5)'
|
const code = 'const myVar = min(-legLen(5, 4), 5)'
|
||||||
const code2 = 'const myVar = min(5 , -legLen(5, 4))'
|
const code2 = 'const myVar = min(5 , -legLen(5, 4))'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
const mem2 = await exe(code2)
|
const mem2 = await exe(code2)
|
||||||
expect(mem.get('myVar')?.value).toBe(-3)
|
expect(mem['myVar']?.value).toBe(-3)
|
||||||
expect(mem.get('myVar')?.value).toBe(mem2.get('myVar')?.value)
|
expect(mem['myVar']?.value).toBe(mem2['myVar']?.value)
|
||||||
})
|
})
|
||||||
it('with unaryExpression in ArrayExpression', async () => {
|
it('with unaryExpression in ArrayExpression', async () => {
|
||||||
const code = 'const myVar = [1,-legLen(5, 4)]'
|
const code = 'const myVar = [1,-legLen(5, 4)]'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toEqual([
|
expect(mem['myVar']?.value).toEqual([
|
||||||
{
|
{
|
||||||
__meta: [
|
__meta: [
|
||||||
{
|
{
|
||||||
@ -426,7 +423,7 @@ describe('testing math operators', () => {
|
|||||||
'|> line(end = [-2.21, -legLen(5, min(3, 999))])',
|
'|> line(end = [-2.21, -legLen(5, min(3, 999))])',
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
const sketch = sketchFromKclValue(mem['part001'], 'part001')
|
||||||
// result of `-legLen(5, min(3, 999))` should be -4
|
// result of `-legLen(5, min(3, 999))` should be -4
|
||||||
const yVal = (sketch as Sketch).paths?.[0]?.to?.[1]
|
const yVal = (sketch as Sketch).paths?.[0]?.to?.[1]
|
||||||
expect(yVal).toBe(-4)
|
expect(yVal).toBe(-4)
|
||||||
@ -444,7 +441,7 @@ describe('testing math operators', () => {
|
|||||||
``,
|
``,
|
||||||
].join('\n')
|
].join('\n')
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
const sketch = sketchFromKclValue(mem['part001'], 'part001')
|
||||||
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
||||||
expect((sketch as Sketch).paths?.[1]?.from).toEqual([3, 4])
|
expect((sketch as Sketch).paths?.[1]?.from).toEqual([3, 4])
|
||||||
expect((sketch as Sketch).paths?.[1]?.to).toEqual([6, 0])
|
expect((sketch as Sketch).paths?.[1]?.to).toEqual([6, 0])
|
||||||
@ -454,7 +451,7 @@ describe('testing math operators', () => {
|
|||||||
)
|
)
|
||||||
const removedUnaryExpMem = await exe(removedUnaryExp)
|
const removedUnaryExpMem = await exe(removedUnaryExp)
|
||||||
const removedUnaryExpMemSketch = sketchFromKclValue(
|
const removedUnaryExpMemSketch = sketchFromKclValue(
|
||||||
removedUnaryExpMem.get('part001'),
|
removedUnaryExpMem['part001'],
|
||||||
'part001'
|
'part001'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -464,12 +461,12 @@ describe('testing math operators', () => {
|
|||||||
it('with nested callExpression and binaryExpression', async () => {
|
it('with nested callExpression and binaryExpression', async () => {
|
||||||
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myVar')?.value).toBe(5)
|
expect(mem['myVar']?.value).toBe(5)
|
||||||
})
|
})
|
||||||
it('can do power of math', async () => {
|
it('can do power of math', async () => {
|
||||||
const code = 'const myNeg2 = 4 ^ 2 - 3 ^ 2 * 2'
|
const code = 'const myNeg2 = 4 ^ 2 - 3 ^ 2 * 2'
|
||||||
const mem = await exe(code)
|
const mem = await exe(code)
|
||||||
expect(mem.get('myNeg2')?.value).toBe(-2)
|
expect(mem['myNeg2']?.value).toBe(-2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -498,12 +495,9 @@ const theExtrude = startSketchOn('XY')
|
|||||||
|
|
||||||
// helpers
|
// helpers
|
||||||
|
|
||||||
async function exe(
|
async function exe(code: string, variables: VariableMap = {}) {
|
||||||
code: string,
|
|
||||||
programMemory: ProgramMemory = ProgramMemory.empty()
|
|
||||||
) {
|
|
||||||
const ast = assertParse(code)
|
const ast = assertParse(code)
|
||||||
|
|
||||||
const execState = await enginelessExecutor(ast, programMemory)
|
const execState = await enginelessExecutor(ast, true, undefined, variables)
|
||||||
return execState.memory
|
return execState.variables
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { assertParse, initPromise, programMemoryInit } from './wasm'
|
import { assertParse, initPromise } from './wasm'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
|
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
@ -32,7 +32,7 @@ child_process.spawnSync('git', [
|
|||||||
'clone',
|
'clone',
|
||||||
'--single-branch',
|
'--single-branch',
|
||||||
'--branch',
|
'--branch',
|
||||||
'achalmers/kw-appearance',
|
'achalmers/kw-pattern-transform2',
|
||||||
URL_GIT_KCL_SAMPLES,
|
URL_GIT_KCL_SAMPLES,
|
||||||
DIR_KCL_SAMPLES,
|
DIR_KCL_SAMPLES,
|
||||||
])
|
])
|
||||||
@ -72,7 +72,7 @@ describe('Test KCL Samples from public Github repository', () => {
|
|||||||
const ast = assertParse(code)
|
const ast = assertParse(code)
|
||||||
await enginelessExecutor(
|
await enginelessExecutor(
|
||||||
ast,
|
ast,
|
||||||
programMemoryInit(),
|
false,
|
||||||
file.pathFromProjectDirectoryToFirstFile
|
file.pathFromProjectDirectoryToFirstFile
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
Program,
|
Program,
|
||||||
executor,
|
executeWithEngine,
|
||||||
ProgramMemory,
|
executeMock,
|
||||||
kclLint,
|
kclLint,
|
||||||
emptyExecState,
|
emptyExecState,
|
||||||
ExecState,
|
ExecState,
|
||||||
|
VariableMap,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { enginelessExecutor } from 'lib/testHelpers'
|
|
||||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||||
import { KCLError } from 'lang/errors'
|
import { KCLError } from 'lang/errors'
|
||||||
import { Diagnostic } from '@codemirror/lint'
|
import { Diagnostic } from '@codemirror/lint'
|
||||||
@ -27,6 +27,7 @@ export type ToolTip =
|
|||||||
| 'angledLineThatIntersects'
|
| 'angledLineThatIntersects'
|
||||||
| 'tangentialArcTo'
|
| 'tangentialArcTo'
|
||||||
| 'circle'
|
| 'circle'
|
||||||
|
| 'circleThreePoint'
|
||||||
|
|
||||||
export const toolTips: Array<ToolTip> = [
|
export const toolTips: Array<ToolTip> = [
|
||||||
'line',
|
'line',
|
||||||
@ -42,20 +43,23 @@ export const toolTips: Array<ToolTip> = [
|
|||||||
'yLineTo',
|
'yLineTo',
|
||||||
'angledLineThatIntersects',
|
'angledLineThatIntersects',
|
||||||
'tangentialArcTo',
|
'tangentialArcTo',
|
||||||
|
'circleThreePoint',
|
||||||
]
|
]
|
||||||
|
|
||||||
export async function executeAst({
|
export async function executeAst({
|
||||||
ast,
|
ast,
|
||||||
path,
|
path,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
// If you set programMemoryOverride we assume you mean mock mode. Since that
|
isMock,
|
||||||
// is the only way to go about it.
|
usePrevMemory,
|
||||||
programMemoryOverride,
|
variables,
|
||||||
}: {
|
}: {
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
path?: string
|
path?: string
|
||||||
engineCommandManager: EngineCommandManager
|
engineCommandManager: EngineCommandManager
|
||||||
programMemoryOverride?: ProgramMemory
|
isMock: boolean
|
||||||
|
usePrevMemory?: boolean
|
||||||
|
variables?: VariableMap
|
||||||
isInterrupted?: boolean
|
isInterrupted?: boolean
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
logs: string[]
|
logs: string[]
|
||||||
@ -64,12 +68,11 @@ export async function executeAst({
|
|||||||
isInterrupted: boolean
|
isInterrupted: boolean
|
||||||
}> {
|
}> {
|
||||||
try {
|
try {
|
||||||
const execState = await (programMemoryOverride
|
const execState = await (isMock
|
||||||
? enginelessExecutor(ast, programMemoryOverride, path)
|
? executeMock(ast, usePrevMemory, path, variables)
|
||||||
: executor(ast, engineCommandManager, path))
|
: executeWithEngine(ast, engineCommandManager, path))
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -3,7 +3,6 @@ import {
|
|||||||
recast,
|
recast,
|
||||||
initPromise,
|
initPromise,
|
||||||
Identifier,
|
Identifier,
|
||||||
SourceRange,
|
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
LiteralValue,
|
LiteralValue,
|
||||||
Literal,
|
Literal,
|
||||||
@ -25,6 +24,7 @@ import {
|
|||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
removeSingleConstraintInfo,
|
removeSingleConstraintInfo,
|
||||||
deleteFromSelection,
|
deleteFromSelection,
|
||||||
|
splitPipedProfile,
|
||||||
} from './modifyAst'
|
} from './modifyAst'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { findUsesOfTagInPipe } from './queryAst'
|
import { findUsesOfTagInPipe } from './queryAst'
|
||||||
@ -134,7 +134,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -142,7 +142,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -150,7 +150,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -158,7 +158,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -166,7 +166,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -174,7 +174,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -182,7 +182,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -190,7 +190,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
@ -198,7 +198,7 @@ describe('Testing findUniqueName', () => {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
] satisfies Node<Identifier>[]),
|
] satisfies Node<Identifier>[]),
|
||||||
'yo',
|
'yo',
|
||||||
@ -217,7 +217,8 @@ describe('Testing addSketchTo', () => {
|
|||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
|
||||||
trivia: [],
|
innerAttrs: [],
|
||||||
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
'yz'
|
'yz'
|
||||||
)
|
)
|
||||||
@ -315,7 +316,7 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
const startIndex = code.indexOf('100 + 100') + 1
|
const startIndex = code.indexOf('100 + 100') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(startIndex, startIndex),
|
topLevelRange(startIndex, startIndex),
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
@ -329,7 +330,7 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
const startIndex = code.indexOf('2.8') + 1
|
const startIndex = code.indexOf('2.8') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(startIndex, startIndex),
|
topLevelRange(startIndex, startIndex),
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
@ -343,7 +344,7 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
const startIndex = code.indexOf('def(')
|
const startIndex = code.indexOf('def(')
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(startIndex, startIndex),
|
topLevelRange(startIndex, startIndex),
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
@ -357,7 +358,7 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
const startIndex = code.indexOf('jkl(') + 1
|
const startIndex = code.indexOf('jkl(') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(startIndex, startIndex),
|
topLevelRange(startIndex, startIndex),
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
@ -371,7 +372,7 @@ yo2 = hmm([identifierGuy + 5])`
|
|||||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||||
const { modifiedAst } = moveValueIntoNewVariable(
|
const { modifiedAst } = moveValueIntoNewVariable(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(startIndex, startIndex),
|
topLevelRange(startIndex, startIndex),
|
||||||
'newVar'
|
'newVar'
|
||||||
)
|
)
|
||||||
@ -557,7 +558,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
|||||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
[],
|
[],
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
code,
|
code,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
@ -639,7 +640,7 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
|||||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||||
dependentSegments,
|
dependentSegments,
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
code,
|
code,
|
||||||
pathToNode
|
pathToNode
|
||||||
)
|
)
|
||||||
@ -745,7 +746,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
argPosition,
|
argPosition,
|
||||||
ast,
|
ast,
|
||||||
execState.memory
|
execState.variables
|
||||||
)
|
)
|
||||||
if (!mod) return new Error('mod is undefined')
|
if (!mod) return new Error('mod is undefined')
|
||||||
const recastCode = recast(mod.modifiedAst)
|
const recastCode = recast(mod.modifiedAst)
|
||||||
@ -794,7 +795,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
argPosition,
|
argPosition,
|
||||||
ast,
|
ast,
|
||||||
execState.memory
|
execState.variables
|
||||||
)
|
)
|
||||||
if (!mod) return new Error('mod is undefined')
|
if (!mod) return new Error('mod is undefined')
|
||||||
const recastCode = recast(mod.modifiedAst)
|
const recastCode = recast(mod.modifiedAst)
|
||||||
@ -978,7 +979,7 @@ sketch002 = startSketchOn({
|
|||||||
codeRef: codeRefFromRange(range, ast),
|
codeRef: codeRefFromRange(range, ast),
|
||||||
artifact,
|
artifact,
|
||||||
},
|
},
|
||||||
execState.memory,
|
execState.variables,
|
||||||
async () => {
|
async () => {
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||||
return {
|
return {
|
||||||
@ -995,3 +996,63 @@ sketch002 = startSketchOn({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('Testing splitPipedProfile', () => {
|
||||||
|
it('should split the pipe expression correctly', () => {
|
||||||
|
const codeBefore = `part001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([1, 2], %)
|
||||||
|
|> line([3, 4], %)
|
||||||
|
|> line([5, 6], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, part001)
|
||||||
|
`
|
||||||
|
|
||||||
|
const expectedCodeAfter = `sketch001 = startSketchOn('XZ')
|
||||||
|
part001 = startProfileAt([1, 2], sketch001)
|
||||||
|
|> line([3, 4], %)
|
||||||
|
|> line([5, 6], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, part001)
|
||||||
|
`
|
||||||
|
|
||||||
|
const ast = assertParse(codeBefore)
|
||||||
|
|
||||||
|
const codeOfInterest = `startSketchOn('XZ')`
|
||||||
|
const range: [number, number, number] = [
|
||||||
|
codeBefore.indexOf(codeOfInterest),
|
||||||
|
codeBefore.indexOf(codeOfInterest) + codeOfInterest.length,
|
||||||
|
0,
|
||||||
|
]
|
||||||
|
const pathToPipe = getNodePathFromSourceRange(ast, range)
|
||||||
|
|
||||||
|
const result = splitPipedProfile(ast, pathToPipe)
|
||||||
|
|
||||||
|
if (err(result)) throw result
|
||||||
|
|
||||||
|
const newCode = recast(result.modifiedAst)
|
||||||
|
if (err(newCode)) throw newCode
|
||||||
|
expect(newCode.trim()).toBe(expectedCodeAfter.trim())
|
||||||
|
})
|
||||||
|
it('should return error for already split pipe', () => {
|
||||||
|
const codeBefore = `sketch001 = startSketchOn('XZ')
|
||||||
|
part001 = startProfileAt([1, 2], sketch001)
|
||||||
|
|> line([3, 4], %)
|
||||||
|
|> line([5, 6], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, part001)
|
||||||
|
`
|
||||||
|
|
||||||
|
const ast = assertParse(codeBefore)
|
||||||
|
|
||||||
|
const codeOfInterest = `startProfileAt([1, 2], sketch001)`
|
||||||
|
const range: [number, number, number] = [
|
||||||
|
codeBefore.indexOf(codeOfInterest),
|
||||||
|
codeBefore.indexOf(codeOfInterest) + codeOfInterest.length,
|
||||||
|
0,
|
||||||
|
]
|
||||||
|
const pathToPipe = getNodePathFromSourceRange(ast, range)
|
||||||
|
|
||||||
|
const result = splitPipedProfile(ast, pathToPipe)
|
||||||
|
expect(result instanceof Error).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
@ -18,11 +18,14 @@ import {
|
|||||||
UnaryExpression,
|
UnaryExpression,
|
||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
PathToNode,
|
PathToNode,
|
||||||
ProgramMemory,
|
|
||||||
SourceRange,
|
SourceRange,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
isPathToNodeNumber,
|
isPathToNodeNumber,
|
||||||
|
parse,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
|
ArtifactGraph,
|
||||||
|
VariableMap,
|
||||||
|
KclValue,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import {
|
import {
|
||||||
isNodeSafeToReplacePath,
|
isNodeSafeToReplacePath,
|
||||||
@ -31,6 +34,8 @@ import {
|
|||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
traverse,
|
traverse,
|
||||||
|
getBodyIndex,
|
||||||
|
isCallExprWithName,
|
||||||
ARG_INDEX_FIELD,
|
ARG_INDEX_FIELD,
|
||||||
LABELED_ARG_FIELD,
|
LABELED_ARG_FIELD,
|
||||||
} from './queryAst'
|
} from './queryAst'
|
||||||
@ -48,7 +53,7 @@ import {
|
|||||||
transformAstSketchLines,
|
transformAstSketchLines,
|
||||||
} from './std/sketchcombos'
|
} from './std/sketchcombos'
|
||||||
import { DefaultPlaneStr } from 'lib/planes'
|
import { DefaultPlaneStr } from 'lib/planes'
|
||||||
import { isOverlap, roundOff } from 'lib/utils'
|
import { isArray, isOverlap, roundOff } from 'lib/utils'
|
||||||
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
import { KCL_DEFAULT_CONSTANT_PREFIXES } from 'lib/constants'
|
||||||
import { SimplifiedArgDetails } from './std/stdTypes'
|
import { SimplifiedArgDetails } from './std/stdTypes'
|
||||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
@ -56,8 +61,19 @@ import { Models } from '@kittycad/lib'
|
|||||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
||||||
|
import {
|
||||||
|
Artifact,
|
||||||
|
expandCap,
|
||||||
|
expandPlane,
|
||||||
|
expandWall,
|
||||||
|
getArtifactOfTypes,
|
||||||
|
getArtifactsOfTypes,
|
||||||
|
getPathsFromArtifact,
|
||||||
|
} from './std/artifactGraph'
|
||||||
|
import { BodyItem } from 'wasm-lib/kcl/bindings/BodyItem'
|
||||||
import { findKwArg } from './util'
|
import { findKwArg } from './util'
|
||||||
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
import { deleteEdgeTreatment } from './modifyAst/addEdgeTreatment'
|
||||||
|
import { engineCommandManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
@ -90,41 +106,54 @@ export function startSketchOnDefault(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addStartProfileAt(
|
export function insertNewStartProfileAt(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
sketchEntryNodePath: PathToNode,
|
||||||
at: [number, number]
|
sketchNodePaths: PathToNode[],
|
||||||
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
planeNodePath: PathToNode,
|
||||||
const _node1 = getNodeFromPath<VariableDeclaration>(
|
at: [number, number],
|
||||||
|
insertType: 'start' | 'end' = 'end'
|
||||||
|
):
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
updatedSketchNodePaths: PathToNode[]
|
||||||
|
updatedEntryNodePath: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
planeNodePath,
|
||||||
'VariableDeclaration'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(_node1)) return _node1
|
if (err(varDec)) return varDec
|
||||||
const variableDeclaration = _node1.node
|
if (varDec.node.type !== 'VariableDeclarator') return new Error('not a var')
|
||||||
if (variableDeclaration.type !== 'VariableDeclaration') {
|
|
||||||
return new Error('variableDeclaration.init.type !== PipeExpression')
|
const newExpression = createVariableDeclaration(
|
||||||
}
|
findUniqueName(node, 'profile'),
|
||||||
const _node = { ...node }
|
createCallExpressionStdLib('startProfileAt', [
|
||||||
const init = variableDeclaration.declaration.init
|
createArrayExpression([
|
||||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
createLiteral(roundOff(at[0])),
|
||||||
createArrayExpression([
|
createLiteral(roundOff(at[1])),
|
||||||
createLiteral(roundOff(at[0])),
|
]),
|
||||||
createLiteral(roundOff(at[1])),
|
createIdentifier(varDec.node.id.name),
|
||||||
]),
|
|
||||||
createPipeSubstitution(),
|
|
||||||
])
|
|
||||||
if (init.type === 'PipeExpression') {
|
|
||||||
init.body.splice(1, 0, startProfileAt)
|
|
||||||
} else {
|
|
||||||
variableDeclaration.declaration.init = createPipeExpression([
|
|
||||||
init,
|
|
||||||
startProfileAt,
|
|
||||||
])
|
])
|
||||||
}
|
)
|
||||||
|
const insertIndex = getInsertIndex(sketchNodePaths, planeNodePath, insertType)
|
||||||
|
|
||||||
|
const _node = structuredClone(node)
|
||||||
|
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
||||||
|
_node.body.splice(insertIndex, 0, newExpression)
|
||||||
|
|
||||||
|
const { updatedEntryNodePath, updatedSketchNodePaths } =
|
||||||
|
updateSketchNodePathsWithInsertIndex({
|
||||||
|
insertIndex,
|
||||||
|
insertType,
|
||||||
|
sketchNodePaths,
|
||||||
|
})
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
updatedSketchNodePaths,
|
||||||
|
updatedEntryNodePath,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,8 +253,21 @@ export function mutateKwArg(
|
|||||||
for (let i = 0; i < node.arguments.length; i++) {
|
for (let i = 0; i < node.arguments.length; i++) {
|
||||||
const arg = node.arguments[i]
|
const arg = node.arguments[i]
|
||||||
if (arg.label.name === label) {
|
if (arg.label.name === label) {
|
||||||
node.arguments[i].arg = val
|
if (isLiteralArrayOrStatic(val) && isLiteralArrayOrStatic(arg.arg)) {
|
||||||
return true
|
node.arguments[i].arg = val
|
||||||
|
return true
|
||||||
|
} else if (
|
||||||
|
arg.arg.type === 'ArrayExpression' &&
|
||||||
|
val.type === 'ArrayExpression'
|
||||||
|
) {
|
||||||
|
const arrExp = arg.arg
|
||||||
|
arrExp.elements.forEach((element, i) => {
|
||||||
|
if (isLiteralArrayOrStatic(element)) {
|
||||||
|
arrExp.elements[i] = val.elements[i]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.arguments.push(createLabeledArg(label, val))
|
node.arguments.push(createLabeledArg(label, val))
|
||||||
@ -278,7 +320,7 @@ export function mutateObjExpProp(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -288,15 +330,17 @@ export function mutateObjExpProp(
|
|||||||
export function extrudeSketch({
|
export function extrudeSketch({
|
||||||
node,
|
node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
shouldPipe = false,
|
|
||||||
distance = createLiteral(4),
|
distance = createLiteral(4),
|
||||||
extrudeName,
|
extrudeName,
|
||||||
|
artifact,
|
||||||
|
artifactGraph,
|
||||||
}: {
|
}: {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
shouldPipe?: boolean
|
|
||||||
distance: Expr
|
distance: Expr
|
||||||
extrudeName?: string
|
extrudeName?: string
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
artifact?: Artifact
|
||||||
}):
|
}):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
@ -304,10 +348,16 @@ export function extrudeSketch({
|
|||||||
pathToExtrudeArg: PathToNode
|
pathToExtrudeArg: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
const orderedSketchNodePaths = getPathsFromArtifact({
|
||||||
|
artifact: artifact,
|
||||||
|
sketchPathToNode: pathToNode,
|
||||||
|
artifactGraph,
|
||||||
|
ast: node,
|
||||||
|
})
|
||||||
|
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
||||||
const _node = structuredClone(node)
|
const _node = structuredClone(node)
|
||||||
const _node1 = getNodeFromPath(_node, pathToNode)
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
||||||
if (err(_node1)) return _node1
|
if (err(_node1)) return _node1
|
||||||
const { node: sketchExpression } = _node1
|
|
||||||
|
|
||||||
// determine if sketchExpression is in a pipeExpression or not
|
// determine if sketchExpression is in a pipeExpression or not
|
||||||
const _node2 = getNodeFromPath<PipeExpression>(
|
const _node2 = getNodeFromPath<PipeExpression>(
|
||||||
@ -316,9 +366,6 @@ export function extrudeSketch({
|
|||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
)
|
)
|
||||||
if (err(_node2)) return _node2
|
if (err(_node2)) return _node2
|
||||||
const { node: pipeExpression } = _node2
|
|
||||||
|
|
||||||
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
|
||||||
|
|
||||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||||
_node,
|
_node,
|
||||||
@ -326,54 +373,27 @@ export function extrudeSketch({
|
|||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(_node3)) return _node3
|
if (err(_node3)) return _node3
|
||||||
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
const { node: variableDeclarator } = _node3
|
||||||
|
|
||||||
const sketchToExtrude = shouldPipe
|
const extrudeCall = createCallExpressionStdLibKw(
|
||||||
? createPipeSubstitution()
|
'extrude',
|
||||||
: createIdentifier(variableDeclarator.id.name)
|
createIdentifier(variableDeclarator.id.name),
|
||||||
const extrudeCall = createCallExpressionStdLibKw('extrude', sketchToExtrude, [
|
[createLabeledArg('length', distance)]
|
||||||
createLabeledArg('length', distance),
|
)
|
||||||
])
|
|
||||||
// index of the 'length' arg above. If you reorder the labeled args above,
|
// index of the 'length' arg above. If you reorder the labeled args above,
|
||||||
// make sure to update this too.
|
// make sure to update this too.
|
||||||
const argIndex = 0
|
const argIndex = 0
|
||||||
|
|
||||||
if (shouldPipe) {
|
|
||||||
const pipeChain = createPipeExpression(
|
|
||||||
isInPipeExpression
|
|
||||||
? [...pipeExpression.body, extrudeCall]
|
|
||||||
: [sketchExpression as any, extrudeCall]
|
|
||||||
)
|
|
||||||
|
|
||||||
variableDeclarator.init = pipeChain
|
|
||||||
const pathToExtrudeArg: PathToNode = [
|
|
||||||
...pathToDecleration,
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['body', ''],
|
|
||||||
[pipeChain.body.length - 1, 'index'],
|
|
||||||
['arguments', 'CallExpressionKw'],
|
|
||||||
[argIndex, ARG_INDEX_FIELD],
|
|
||||||
['arg', LABELED_ARG_FIELD],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst: _node,
|
|
||||||
pathToNode,
|
|
||||||
pathToExtrudeArg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
// We're not creating a pipe expression,
|
||||||
// but rather a separate constant for the extrusion
|
// but rather a separate constant for the extrusion
|
||||||
const name =
|
const name =
|
||||||
extrudeName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
extrudeName ?? findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
||||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
||||||
|
|
||||||
const sketchIndexInPathToNode =
|
const lastSketchNodePath =
|
||||||
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
||||||
const sketchIndexInBody = pathToDecleration[
|
|
||||||
sketchIndexInPathToNode
|
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
||||||
][0] as number
|
|
||||||
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||||
|
|
||||||
const pathToExtrudeArg: PathToNode = [
|
const pathToExtrudeArg: PathToNode = [
|
||||||
@ -891,7 +911,7 @@ export function createLiteral(value: LiteralValue | number): Node<Literal> {
|
|||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
value,
|
value,
|
||||||
raw,
|
raw,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -901,7 +921,7 @@ export function createTagDeclarator(value: string): Node<TagDeclarator> {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
@ -913,7 +933,7 @@ export function createIdentifier(name: string): Node<Identifier> {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
name,
|
name,
|
||||||
}
|
}
|
||||||
@ -925,7 +945,7 @@ export function createPipeSubstitution(): Node<PipeSubstitution> {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -938,13 +958,13 @@ export function createCallExpressionStdLib(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
@ -962,13 +982,13 @@ export function createCallExpressionStdLibKw(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
@ -986,13 +1006,13 @@ export function createCallExpression(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
callee: {
|
callee: {
|
||||||
type: 'Identifier',
|
type: 'Identifier',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
@ -1008,7 +1028,7 @@ export function createArrayExpression(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
nonCodeMeta: nonCodeMetaEmpty(),
|
nonCodeMeta: nonCodeMetaEmpty(),
|
||||||
elements,
|
elements,
|
||||||
@ -1023,7 +1043,7 @@ export function createPipeExpression(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
body,
|
body,
|
||||||
nonCodeMeta: nonCodeMetaEmpty(),
|
nonCodeMeta: nonCodeMetaEmpty(),
|
||||||
@ -1041,14 +1061,14 @@ export function createVariableDeclaration(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
declaration: {
|
declaration: {
|
||||||
type: 'VariableDeclarator',
|
type: 'VariableDeclarator',
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
id: createIdentifier(varName),
|
id: createIdentifier(varName),
|
||||||
init,
|
init,
|
||||||
@ -1066,7 +1086,7 @@ export function createObjectExpression(properties: {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
nonCodeMeta: nonCodeMetaEmpty(),
|
nonCodeMeta: nonCodeMetaEmpty(),
|
||||||
properties: Object.entries(properties).map(([key, value]) => ({
|
properties: Object.entries(properties).map(([key, value]) => ({
|
||||||
@ -1074,7 +1094,7 @@ export function createObjectExpression(properties: {
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
key: createIdentifier(key),
|
key: createIdentifier(key),
|
||||||
|
|
||||||
value,
|
value,
|
||||||
@ -1091,7 +1111,7 @@ export function createUnaryExpression(
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
operator,
|
operator,
|
||||||
argument,
|
argument,
|
||||||
@ -1108,7 +1128,7 @@ export function createBinaryExpression([left, operator, right]: [
|
|||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
moduleId: 0,
|
moduleId: 0,
|
||||||
trivia: [],
|
outerAttrs: [],
|
||||||
|
|
||||||
operator,
|
operator,
|
||||||
left,
|
left,
|
||||||
@ -1218,7 +1238,7 @@ export function replaceValueAtNodePath({
|
|||||||
|
|
||||||
export function moveValueIntoNewVariablePath(
|
export function moveValueIntoNewVariablePath(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
variableName: string
|
variableName: string
|
||||||
): {
|
): {
|
||||||
@ -1231,11 +1251,7 @@ export function moveValueIntoNewVariablePath(
|
|||||||
|
|
||||||
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
||||||
|
|
||||||
const { insertIndex } = findAllPreviousVariablesPath(
|
const { insertIndex } = findAllPreviousVariablesPath(ast, memVars, pathToNode)
|
||||||
ast,
|
|
||||||
programMemory,
|
|
||||||
pathToNode
|
|
||||||
)
|
|
||||||
let _node = structuredClone(ast)
|
let _node = structuredClone(ast)
|
||||||
const boop = replacer(_node, variableName)
|
const boop = replacer(_node, variableName)
|
||||||
if (trap(boop)) return { modifiedAst: ast }
|
if (trap(boop)) return { modifiedAst: ast }
|
||||||
@ -1251,7 +1267,7 @@ export function moveValueIntoNewVariablePath(
|
|||||||
|
|
||||||
export function moveValueIntoNewVariable(
|
export function moveValueIntoNewVariable(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
variableName: string
|
variableName: string
|
||||||
): {
|
): {
|
||||||
@ -1263,11 +1279,7 @@ export function moveValueIntoNewVariable(
|
|||||||
const { isSafe, value, replacer } = meta
|
const { isSafe, value, replacer } = meta
|
||||||
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
||||||
|
|
||||||
const { insertIndex } = findAllPreviousVariables(
|
const { insertIndex } = findAllPreviousVariables(ast, memVars, sourceRange)
|
||||||
ast,
|
|
||||||
programMemory,
|
|
||||||
sourceRange
|
|
||||||
)
|
|
||||||
let _node = structuredClone(ast)
|
let _node = structuredClone(ast)
|
||||||
const replaced = replacer(_node, variableName)
|
const replaced = replacer(_node, variableName)
|
||||||
if (trap(replaced)) return { modifiedAst: ast }
|
if (trap(replaced)) return { modifiedAst: ast }
|
||||||
@ -1289,7 +1301,7 @@ export function moveValueIntoNewVariable(
|
|||||||
export function deleteSegmentFromPipeExpression(
|
export function deleteSegmentFromPipeExpression(
|
||||||
dependentRanges: SourceRange[],
|
dependentRanges: SourceRange[],
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
): Node<Program> | Error {
|
): Node<Program> | Error {
|
||||||
@ -1321,7 +1333,7 @@ export function deleteSegmentFromPipeExpression(
|
|||||||
callExp.shallowPath,
|
callExp.shallowPath,
|
||||||
constraintInfo.argPosition,
|
constraintInfo.argPosition,
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
programMemory
|
memVars
|
||||||
)
|
)
|
||||||
if (!transform) return
|
if (!transform) return
|
||||||
_modifiedAst = transform.modifiedAst
|
_modifiedAst = transform.modifiedAst
|
||||||
@ -1350,7 +1362,7 @@ export function removeSingleConstraintInfo(
|
|||||||
pathToCallExp: PathToNode,
|
pathToCallExp: PathToNode,
|
||||||
argDetails: SimplifiedArgDetails,
|
argDetails: SimplifiedArgDetails,
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
programMemory: ProgramMemory
|
memVars: VariableMap
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
@ -1367,7 +1379,7 @@ export function removeSingleConstraintInfo(
|
|||||||
ast,
|
ast,
|
||||||
selectionRanges: [pathToCallExp],
|
selectionRanges: [pathToCallExp],
|
||||||
transformInfos: [transform],
|
transformInfos: [transform],
|
||||||
programMemory,
|
memVars,
|
||||||
referenceSegName: '',
|
referenceSegName: '',
|
||||||
})
|
})
|
||||||
if (err(retval)) return false
|
if (err(retval)) return false
|
||||||
@ -1377,11 +1389,44 @@ export function removeSingleConstraintInfo(
|
|||||||
export async function deleteFromSelection(
|
export async function deleteFromSelection(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
selection: Selection,
|
selection: Selection,
|
||||||
programMemory: ProgramMemory,
|
variables: VariableMap,
|
||||||
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
||||||
({} as any)
|
({} as any)
|
||||||
): Promise<Node<Program> | Error> {
|
): Promise<Node<Program> | Error> {
|
||||||
const astClone = structuredClone(ast)
|
const astClone = structuredClone(ast)
|
||||||
|
if (
|
||||||
|
(selection.artifact?.type === 'plane' ||
|
||||||
|
selection.artifact?.type === 'cap' ||
|
||||||
|
selection.artifact?.type === 'wall') &&
|
||||||
|
selection.artifact?.pathIds?.length
|
||||||
|
) {
|
||||||
|
const plane =
|
||||||
|
selection.artifact.type === 'plane'
|
||||||
|
? expandPlane(selection.artifact, engineCommandManager.artifactGraph)
|
||||||
|
: selection.artifact.type === 'wall'
|
||||||
|
? expandWall(selection.artifact, engineCommandManager.artifactGraph)
|
||||||
|
: expandCap(selection.artifact, engineCommandManager.artifactGraph)
|
||||||
|
for (const path of plane.paths.sort(
|
||||||
|
(a, b) => b.codeRef.range[0] - a.codeRef.range[0]
|
||||||
|
)) {
|
||||||
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
path.codeRef.pathToNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(varDec)) return varDec
|
||||||
|
const bodyIndex = Number(varDec.shallowPath[1][0])
|
||||||
|
astClone.body.splice(bodyIndex, 1)
|
||||||
|
}
|
||||||
|
// If it's a cap, we're not going to continue and try to
|
||||||
|
// delete the extrusion
|
||||||
|
if (
|
||||||
|
selection.artifact.type === 'cap' ||
|
||||||
|
selection.artifact.type === 'wall'
|
||||||
|
) {
|
||||||
|
return astClone
|
||||||
|
}
|
||||||
|
}
|
||||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
ast,
|
ast,
|
||||||
selection?.codeRef?.pathToNode,
|
selection?.codeRef?.pathToNode,
|
||||||
@ -1460,59 +1505,108 @@ export async function deleteFromSelection(
|
|||||||
if (extrudeNameToDelete) {
|
if (extrudeNameToDelete) {
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
let currentVariableName = ''
|
|
||||||
const pathsDependingOnExtrude: Array<{
|
const pathsDependingOnExtrude: Array<{
|
||||||
path: PathToNode
|
path: PathToNode
|
||||||
sketchName: string
|
variable: KclValue
|
||||||
}> = []
|
}> = []
|
||||||
traverse(astClone, {
|
|
||||||
leave: (node) => {
|
|
||||||
if (node.type === 'VariableDeclaration') {
|
|
||||||
currentVariableName = ''
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enter: (node, path) => {
|
|
||||||
;(async () => {
|
|
||||||
if (node.type === 'VariableDeclaration') {
|
|
||||||
currentVariableName = node.declaration.id.name
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
// match startSketchOn(${extrudeNameToDelete})
|
|
||||||
node.type === 'CallExpression' &&
|
|
||||||
node.callee.name === 'startSketchOn' &&
|
|
||||||
node.arguments[0].type === 'Identifier' &&
|
|
||||||
node.arguments[0].name === extrudeNameToDelete
|
|
||||||
) {
|
|
||||||
pathsDependingOnExtrude.push({
|
|
||||||
path,
|
|
||||||
sketchName: currentVariableName,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})().catch(reportRejection)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const roundLiteral = (x: number) => createLiteral(roundOff(x))
|
const roundLiteral = (x: number) => createLiteral(roundOff(x))
|
||||||
const modificationDetails: {
|
const modificationDetails: {
|
||||||
parent: PipeExpression['body']
|
parentPipe: PipeExpression['body']
|
||||||
|
parentInit: VariableDeclarator
|
||||||
faceDetails: Models['FaceIsPlanar_type']
|
faceDetails: Models['FaceIsPlanar_type']
|
||||||
lastKey: number
|
lastKey: number | string
|
||||||
}[] = []
|
}[] = []
|
||||||
for (const { path, sketchName } of pathsDependingOnExtrude) {
|
const wallArtifact =
|
||||||
const parent = getNodeFromPath<PipeExpression['body']>(
|
selection.artifact?.type === 'wall'
|
||||||
|
? selection.artifact
|
||||||
|
: selection.artifact?.type === 'segment' &&
|
||||||
|
selection.artifact.surfaceId
|
||||||
|
? getArtifactOfTypes(
|
||||||
|
{ key: selection.artifact.surfaceId, types: ['wall'] },
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
: null
|
||||||
|
if (err(wallArtifact)) return
|
||||||
|
if (wallArtifact) {
|
||||||
|
const sweep = getArtifactOfTypes(
|
||||||
|
{ key: wallArtifact.sweepId, types: ['sweep'] },
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (err(sweep)) return
|
||||||
|
const wallsWithDependencies = Array.from(
|
||||||
|
getArtifactsOfTypes(
|
||||||
|
{ keys: sweep.surfaceIds, types: ['wall', 'cap'] },
|
||||||
|
engineCommandManager.artifactGraph
|
||||||
|
).values()
|
||||||
|
).filter((wall) => wall?.pathIds?.length)
|
||||||
|
const wallIds = wallsWithDependencies.map((wall) => wall.id)
|
||||||
|
Object.entries(variables).forEach(([key, _var]) => {
|
||||||
|
if (
|
||||||
|
_var?.type === 'Face' &&
|
||||||
|
wallIds.includes(_var.value.artifactId)
|
||||||
|
) {
|
||||||
|
const pathToStartSketchOn = getNodePathFromSourceRange(
|
||||||
|
astClone,
|
||||||
|
_var.value.__meta[0].sourceRange
|
||||||
|
)
|
||||||
|
pathsDependingOnExtrude.push({
|
||||||
|
path: pathToStartSketchOn,
|
||||||
|
variable: _var,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
_var?.type === 'Sketch' &&
|
||||||
|
_var.value.on.type === 'face' &&
|
||||||
|
wallIds.includes(_var.value.on.artifactId)
|
||||||
|
) {
|
||||||
|
const pathToStartSketchOn = getNodePathFromSourceRange(
|
||||||
|
astClone,
|
||||||
|
_var.value.on.__meta[0].sourceRange
|
||||||
|
)
|
||||||
|
pathsDependingOnExtrude.push({
|
||||||
|
path: pathToStartSketchOn,
|
||||||
|
variable: {
|
||||||
|
type: 'Face',
|
||||||
|
value: _var.value.on,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const { path, variable } of pathsDependingOnExtrude) {
|
||||||
|
// `parentPipe` and `parentInit` are the exact same node, but because it could either be an array or on object node
|
||||||
|
// putting them in two different variables was the only way to get TypeScript to stop complaining
|
||||||
|
// the reason why we're grabbing the parent and the last key is because we want to mutate the ast
|
||||||
|
// so `parent[lastKey]` does the trick, if there's a better way of doing this I'm all years
|
||||||
|
const parentPipe = getNodeFromPath<PipeExpression['body']>(
|
||||||
astClone,
|
astClone,
|
||||||
path.slice(0, -1)
|
path.slice(0, -1)
|
||||||
)
|
)
|
||||||
if (err(parent)) {
|
const parentInit = getNodeFromPath<VariableDeclarator>(
|
||||||
|
astClone,
|
||||||
|
path.slice(0, -1)
|
||||||
|
)
|
||||||
|
if (err(parentPipe) || err(parentInit)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const sketchToPreserve = sketchFromKclValue(
|
if (!variable) return new Error('Could not find sketch')
|
||||||
programMemory.get(sketchName),
|
const artifactId =
|
||||||
sketchName
|
variable.type === 'Sketch'
|
||||||
)
|
? variable.value.artifactId
|
||||||
if (err(sketchToPreserve)) return sketchToPreserve
|
: variable.type === 'Face'
|
||||||
|
? variable.value.artifactId
|
||||||
|
: ''
|
||||||
|
if (!artifactId) return new Error('Sketch not on anything')
|
||||||
|
const onId =
|
||||||
|
variable.type === 'Sketch'
|
||||||
|
? variable.value.on.id
|
||||||
|
: variable.type === 'Face'
|
||||||
|
? variable.value.id
|
||||||
|
: ''
|
||||||
|
if (!onId) return new Error('Sketch not on anything')
|
||||||
// Can't kick off multiple requests at once as getFaceDetails
|
// Can't kick off multiple requests at once as getFaceDetails
|
||||||
// is three engine calls in one and they conflict
|
// is three engine calls in one and they conflict
|
||||||
const faceDetails = await getFaceDetails(sketchToPreserve.on.id)
|
const faceDetails = await getFaceDetails(onId)
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
faceDetails.origin &&
|
faceDetails.origin &&
|
||||||
@ -1523,14 +1617,20 @@ export async function deleteFromSelection(
|
|||||||
) {
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const lastKey = Number(path.slice(-1)[0][0])
|
const lastKey = path.slice(-1)[0][0]
|
||||||
modificationDetails.push({
|
modificationDetails.push({
|
||||||
parent: parent.node,
|
parentPipe: parentPipe.node,
|
||||||
|
parentInit: parentInit.node,
|
||||||
faceDetails,
|
faceDetails,
|
||||||
lastKey,
|
lastKey,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
for (const { parent, faceDetails, lastKey } of modificationDetails) {
|
for (const {
|
||||||
|
parentInit,
|
||||||
|
parentPipe,
|
||||||
|
faceDetails,
|
||||||
|
lastKey,
|
||||||
|
} of modificationDetails) {
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
faceDetails.origin &&
|
faceDetails.origin &&
|
||||||
@ -1541,7 +1641,7 @@ export async function deleteFromSelection(
|
|||||||
) {
|
) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
parent[lastKey] = createCallExpressionStdLib('startSketchOn', [
|
const expression = createCallExpressionStdLib('startSketchOn', [
|
||||||
createObjectExpression({
|
createObjectExpression({
|
||||||
plane: createObjectExpression({
|
plane: createObjectExpression({
|
||||||
origin: createObjectExpression({
|
origin: createObjectExpression({
|
||||||
@ -1567,6 +1667,14 @@ export async function deleteFromSelection(
|
|||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
if (
|
||||||
|
parentInit.type === 'VariableDeclarator' &&
|
||||||
|
lastKey === 'init'
|
||||||
|
) {
|
||||||
|
parentInit[lastKey] = expression
|
||||||
|
} else if (isArray(parentPipe) && typeof lastKey === 'number') {
|
||||||
|
parentPipe[lastKey] = expression
|
||||||
|
}
|
||||||
}
|
}
|
||||||
resolve(true)
|
resolve(true)
|
||||||
})().catch(reportRejection)
|
})().catch(reportRejection)
|
||||||
@ -1578,15 +1686,29 @@ export async function deleteFromSelection(
|
|||||||
return deleteEdgeTreatment(astClone, selection)
|
return deleteEdgeTreatment(astClone, selection)
|
||||||
} else if (varDec.node.init.type === 'PipeExpression') {
|
} else if (varDec.node.init.type === 'PipeExpression') {
|
||||||
const pipeBody = varDec.node.init.body
|
const pipeBody = varDec.node.init.body
|
||||||
|
const doNotDeleteProfileIfItHasBeenExtruded = !(
|
||||||
|
selection?.artifact?.type === 'segment' && selection?.artifact?.surfaceId
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
pipeBody[0].type === 'CallExpression' &&
|
pipeBody[0].type === 'CallExpression' &&
|
||||||
pipeBody[0].callee.name === 'startSketchOn'
|
doNotDeleteProfileIfItHasBeenExtruded &&
|
||||||
|
(pipeBody[0].callee.name === 'startSketchOn' ||
|
||||||
|
pipeBody[0].callee.name === 'startProfileAt')
|
||||||
) {
|
) {
|
||||||
// remove varDec
|
// remove varDec
|
||||||
const varDecIndex = varDec.shallowPath[1][0] as number
|
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||||
astClone.body.splice(varDecIndex, 1)
|
astClone.body.splice(varDecIndex, 1)
|
||||||
return astClone
|
return astClone
|
||||||
}
|
}
|
||||||
|
} else if (
|
||||||
|
// single expression profiles
|
||||||
|
(varDec.node.init.type === 'CallExpressionKw' ||
|
||||||
|
varDec.node.init.type === 'CallExpression') &&
|
||||||
|
['circleThreePoint', 'circle'].includes(varDec.node.init.callee.name)
|
||||||
|
) {
|
||||||
|
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||||
|
astClone.body.splice(varDecIndex, 1)
|
||||||
|
return astClone
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Error('Selection not recognised, could not delete')
|
return new Error('Selection not recognised, could not delete')
|
||||||
@ -1596,6 +1718,167 @@ const nonCodeMetaEmpty = () => {
|
|||||||
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createLabeledArg = (name: string, arg: Expr): LabeledArg => {
|
export function getInsertIndex(
|
||||||
return { label: createIdentifier(name), arg, type: 'LabeledArg' }
|
sketchNodePaths: PathToNode[],
|
||||||
|
planeNodePath: PathToNode,
|
||||||
|
insertType: 'start' | 'end'
|
||||||
|
) {
|
||||||
|
let minIndex = 0
|
||||||
|
let maxIndex = 0
|
||||||
|
for (const path of sketchNodePaths) {
|
||||||
|
const index = Number(path[1][0])
|
||||||
|
if (index < minIndex) minIndex = index
|
||||||
|
if (index > maxIndex) maxIndex = index
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertIndex = !sketchNodePaths.length
|
||||||
|
? Number(planeNodePath[1][0]) + 1
|
||||||
|
: insertType === 'start'
|
||||||
|
? minIndex
|
||||||
|
: maxIndex + 1
|
||||||
|
return insertIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateSketchNodePathsWithInsertIndex({
|
||||||
|
insertIndex,
|
||||||
|
insertType,
|
||||||
|
sketchNodePaths,
|
||||||
|
}: {
|
||||||
|
insertIndex: number
|
||||||
|
insertType: 'start' | 'end'
|
||||||
|
sketchNodePaths: PathToNode[]
|
||||||
|
}): {
|
||||||
|
updatedEntryNodePath: PathToNode
|
||||||
|
updatedSketchNodePaths: PathToNode[]
|
||||||
|
} {
|
||||||
|
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
||||||
|
const newExpressionPathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[insertIndex, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
]
|
||||||
|
let updatedSketchNodePaths = structuredClone(sketchNodePaths)
|
||||||
|
if (insertType === 'start') {
|
||||||
|
updatedSketchNodePaths = updatedSketchNodePaths.map((path) => {
|
||||||
|
path[1][0] = Number(path[1][0]) + 1
|
||||||
|
return path
|
||||||
|
})
|
||||||
|
updatedSketchNodePaths.unshift(newExpressionPathToNode)
|
||||||
|
} else {
|
||||||
|
updatedSketchNodePaths.push(newExpressionPathToNode)
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
updatedSketchNodePaths,
|
||||||
|
updatedEntryNodePath: newExpressionPathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Split the following pipe expression into
|
||||||
|
* ```ts
|
||||||
|
* part001 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([1, 2], %)
|
||||||
|
|> line([3, 4], %)
|
||||||
|
|> line([5, 6], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, part001)
|
||||||
|
```
|
||||||
|
into
|
||||||
|
```ts
|
||||||
|
sketch001 = startSketchOn('XZ')
|
||||||
|
part001 = startProfileAt([1, 2], sketch001)
|
||||||
|
|> line([3, 4], %)
|
||||||
|
|> line([5, 6], %)
|
||||||
|
|> close(%)
|
||||||
|
extrude001 = extrude(5, part001)
|
||||||
|
```
|
||||||
|
Notice that the `startSketchOn` is what gets the new variable name, this is so part001 still has the same data as before
|
||||||
|
making it safe for later code that uses part001 (the extrude in this example)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
export function splitPipedProfile(
|
||||||
|
ast: Program,
|
||||||
|
pathToPipe: PathToNode
|
||||||
|
):
|
||||||
|
| {
|
||||||
|
modifiedAst: Program
|
||||||
|
pathToProfile: PathToNode
|
||||||
|
pathToPlane: PathToNode
|
||||||
|
}
|
||||||
|
| Error {
|
||||||
|
const _ast = structuredClone(ast)
|
||||||
|
const varDec = getNodeFromPath<VariableDeclaration>(
|
||||||
|
_ast,
|
||||||
|
pathToPipe,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(varDec)) return varDec
|
||||||
|
if (
|
||||||
|
varDec.node.type !== 'VariableDeclaration' ||
|
||||||
|
varDec.node.declaration.init.type !== 'PipeExpression'
|
||||||
|
) {
|
||||||
|
return new Error('pathToNode does not point to pipe')
|
||||||
|
}
|
||||||
|
const init = varDec.node.declaration.init
|
||||||
|
const firstCall = init.body[0]
|
||||||
|
if (!isCallExprWithName(firstCall, 'startSketchOn'))
|
||||||
|
return new Error('First call is not startSketchOn')
|
||||||
|
const secondCall = init.body[1]
|
||||||
|
if (!isCallExprWithName(secondCall, 'startProfileAt'))
|
||||||
|
return new Error('Second call is not startProfileAt')
|
||||||
|
|
||||||
|
const varName = varDec.node.declaration.id.name
|
||||||
|
const newVarName = findUniqueName(_ast, 'sketch')
|
||||||
|
const secondCallArgs = structuredClone(secondCall.arguments)
|
||||||
|
secondCallArgs[1] = createIdentifier(newVarName)
|
||||||
|
const firstCallOfNewPipe = createCallExpression(
|
||||||
|
'startProfileAt',
|
||||||
|
secondCallArgs
|
||||||
|
)
|
||||||
|
const newSketch = createVariableDeclaration(
|
||||||
|
newVarName,
|
||||||
|
varDec.node.declaration.init.body[0]
|
||||||
|
)
|
||||||
|
const newProfile = createVariableDeclaration(
|
||||||
|
varName,
|
||||||
|
varDec.node.declaration.init.body.length <= 2
|
||||||
|
? firstCallOfNewPipe
|
||||||
|
: createPipeExpression([
|
||||||
|
firstCallOfNewPipe,
|
||||||
|
...varDec.node.declaration.init.body.slice(2),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
const index = getBodyIndex(pathToPipe)
|
||||||
|
if (err(index)) return index
|
||||||
|
_ast.body.splice(index, 1, newSketch, newProfile)
|
||||||
|
const pathToPlane = structuredClone(pathToPipe)
|
||||||
|
const pathToProfile = structuredClone(pathToPipe)
|
||||||
|
pathToProfile[1][0] = index + 1
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: _ast,
|
||||||
|
pathToProfile,
|
||||||
|
pathToPlane,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createNodeFromExprSnippet(
|
||||||
|
strings: TemplateStringsArray,
|
||||||
|
...expressions: any[]
|
||||||
|
): Node<BodyItem> | Error {
|
||||||
|
const code = strings.reduce(
|
||||||
|
(acc, str, i) => acc + str + (expressions[i] || ''),
|
||||||
|
''
|
||||||
|
)
|
||||||
|
let program = parse(code)
|
||||||
|
if (err(program)) return program
|
||||||
|
const node = program.program?.body[0]
|
||||||
|
if (!node) return new Error('No node found')
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createLabeledArg = (label: string, arg: Expr): LabeledArg => {
|
||||||
|
return { label: createIdentifier(label), arg, type: 'LabeledArg' }
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
const sketchVar = varDecNode.node.declaration.id.name
|
const sketchVar = varDecNode.node.declaration.id.name
|
||||||
|
|
||||||
const sketch = sketchFromKclValue(
|
const sketch = sketchFromKclValue(
|
||||||
dependencies.kclManager.programMemory.get(sketchVar),
|
dependencies.kclManager.variables[sketchVar],
|
||||||
sketchVar
|
sketchVar
|
||||||
)
|
)
|
||||||
if (trap(sketch)) return sketch
|
if (trap(sketch)) return sketch
|
||||||
|
@ -5,9 +5,9 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
Expr,
|
Expr,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
PipeExpression,
|
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
CallExpressionKw,
|
CallExpressionKw,
|
||||||
|
ArtifactGraph,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
@ -16,7 +16,6 @@ import {
|
|||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
createObjectExpression,
|
createObjectExpression,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
createPipeExpression,
|
|
||||||
findUniqueName,
|
findUniqueName,
|
||||||
createVariableDeclaration,
|
createVariableDeclaration,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
@ -26,14 +25,18 @@ import {
|
|||||||
mutateAstWithTagForSketchSegment,
|
mutateAstWithTagForSketchSegment,
|
||||||
getEdgeTagCall,
|
getEdgeTagCall,
|
||||||
} from 'lang/modifyAst/addEdgeTreatment'
|
} from 'lang/modifyAst/addEdgeTreatment'
|
||||||
|
import { Artifact, getPathsFromArtifact } from 'lang/std/artifactGraph'
|
||||||
|
import { kclManager } from 'lib/singletons'
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pathToSketchNode: PathToNode,
|
pathToSketchNode: PathToNode,
|
||||||
shouldPipe = false,
|
|
||||||
angle: Expr = createLiteral(4),
|
angle: Expr = createLiteral(4),
|
||||||
axisOrEdge: string,
|
axisOrEdge: string,
|
||||||
axis: string,
|
axis: string,
|
||||||
edge: Selections
|
edge: Selections,
|
||||||
|
artifactGraph: ArtifactGraph,
|
||||||
|
artifact?: Artifact
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
@ -41,6 +44,13 @@ export function revolveSketch(
|
|||||||
pathToRevolveArg: PathToNode
|
pathToRevolveArg: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
|
const orderedSketchNodePaths = getPathsFromArtifact({
|
||||||
|
artifact: artifact,
|
||||||
|
sketchPathToNode: pathToSketchNode,
|
||||||
|
artifactGraph,
|
||||||
|
ast: kclManager.ast,
|
||||||
|
})
|
||||||
|
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
||||||
const clonedAst = structuredClone(ast)
|
const clonedAst = structuredClone(ast)
|
||||||
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
|
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
|
||||||
if (err(sketchNode)) return sketchNode
|
if (err(sketchNode)) return sketchNode
|
||||||
@ -82,29 +92,13 @@ export function revolveSketch(
|
|||||||
generatedAxis = createLiteral(axis)
|
generatedAxis = createLiteral(axis)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Original Code */
|
|
||||||
const { node: sketchExpression } = sketchNode
|
|
||||||
|
|
||||||
// determine if sketchExpression is in a pipeExpression or not
|
|
||||||
const sketchPipeExpressionNode = getNodeFromPath<PipeExpression>(
|
|
||||||
clonedAst,
|
|
||||||
pathToSketchNode,
|
|
||||||
'PipeExpression'
|
|
||||||
)
|
|
||||||
if (err(sketchPipeExpressionNode)) return sketchPipeExpressionNode
|
|
||||||
const { node: sketchPipeExpression } = sketchPipeExpressionNode
|
|
||||||
const isInPipeExpression = sketchPipeExpression.type === 'PipeExpression'
|
|
||||||
|
|
||||||
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
|
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
|
||||||
clonedAst,
|
clonedAst,
|
||||||
pathToSketchNode,
|
pathToSketchNode,
|
||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
|
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
|
||||||
const {
|
const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode
|
||||||
node: sketchVariableDeclarator,
|
|
||||||
shallowPath: sketchPathToDecleration,
|
|
||||||
} = sketchVariableDeclaratorNode
|
|
||||||
|
|
||||||
if (!generatedAxis) return new Error('Generated axis selection is missing.')
|
if (!generatedAxis) return new Error('Generated axis selection is missing.')
|
||||||
|
|
||||||
@ -116,41 +110,16 @@ export function revolveSketch(
|
|||||||
createIdentifier(sketchVariableDeclarator.id.name),
|
createIdentifier(sketchVariableDeclarator.id.name),
|
||||||
])
|
])
|
||||||
|
|
||||||
if (shouldPipe) {
|
|
||||||
const pipeChain = createPipeExpression(
|
|
||||||
isInPipeExpression
|
|
||||||
? [...sketchPipeExpression.body, revolveCall]
|
|
||||||
: [sketchExpression as any, revolveCall]
|
|
||||||
)
|
|
||||||
|
|
||||||
sketchVariableDeclarator.init = pipeChain
|
|
||||||
const pathToRevolveArg: PathToNode = [
|
|
||||||
...sketchPathToDecleration,
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
['body', ''],
|
|
||||||
[pipeChain.body.length - 1, 'index'],
|
|
||||||
['arguments', 'CallExpression'],
|
|
||||||
[0, 'index'],
|
|
||||||
]
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst: clonedAst,
|
|
||||||
pathToSketchNode,
|
|
||||||
pathToRevolveArg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
// We're not creating a pipe expression,
|
||||||
// but rather a separate constant for the extrusion
|
// but rather a separate constant for the extrusion
|
||||||
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
||||||
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
||||||
const sketchIndexInPathToNode =
|
const lastSketchNodePath =
|
||||||
sketchPathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
||||||
const sketchIndexInBody = sketchPathToDecleration[sketchIndexInPathToNode][0]
|
let sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
||||||
let insertIndex = sketchIndexInBody
|
if (typeof sketchIndexInBody !== 'number') {
|
||||||
|
return new Error('expected sketchIndexInBody to be a number')
|
||||||
if (typeof insertIndex !== 'number')
|
}
|
||||||
return new Error('expected insertIndex to be a number')
|
|
||||||
|
|
||||||
// If an axis was selected in KCL, find the max index to insert the revolve command
|
// If an axis was selected in KCL, find the max index to insert the revolve command
|
||||||
if (axisDeclaration) {
|
if (axisDeclaration) {
|
||||||
@ -161,14 +130,14 @@ export function revolveSketch(
|
|||||||
if (typeof axisIndex !== 'number')
|
if (typeof axisIndex !== 'number')
|
||||||
return new Error('expected axisIndex to be a number')
|
return new Error('expected axisIndex to be a number')
|
||||||
|
|
||||||
insertIndex = Math.max(insertIndex, axisIndex)
|
sketchIndexInBody = Math.max(sketchIndexInBody, axisIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
clonedAst.body.splice(insertIndex + 1, 0, VariableDeclaration)
|
clonedAst.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||||
|
|
||||||
const pathToRevolveArg: PathToNode = [
|
const pathToRevolveArg: PathToNode = [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[insertIndex + 1, 'index'],
|
[sketchIndexInBody + 1, 'index'],
|
||||||
['declaration', 'VariableDeclaration'],
|
['declaration', 'VariableDeclaration'],
|
||||||
['init', 'VariableDeclarator'],
|
['init', 'VariableDeclarator'],
|
||||||
['arguments', 'CallExpression'],
|
['arguments', 'CallExpression'],
|
||||||
|
@ -9,7 +9,6 @@ import {
|
|||||||
CallExpression,
|
CallExpression,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { ProgramMemory } from 'lang/wasm'
|
|
||||||
import {
|
import {
|
||||||
findAllPreviousVariables,
|
findAllPreviousVariables,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
@ -63,7 +62,7 @@ variableBelowShouldNotBeIncluded = 3
|
|||||||
|
|
||||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
topLevelRange(rangeStart, rangeStart)
|
topLevelRange(rangeStart, rangeStart)
|
||||||
)
|
)
|
||||||
expect(variables).toEqual([
|
expect(variables).toEqual([
|
||||||
@ -398,7 +397,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
memVars: execState.variables,
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
})
|
})
|
||||||
@ -418,7 +417,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
memVars: execState.variables,
|
||||||
})
|
})
|
||||||
expect(result).toEqual(true)
|
expect(result).toEqual(true)
|
||||||
})
|
})
|
||||||
@ -432,7 +431,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
|||||||
selection: {
|
selection: {
|
||||||
codeRef: codeRefFromRange(topLevelRange(10, 11), ast),
|
codeRef: codeRefFromRange(topLevelRange(10, 11), ast),
|
||||||
},
|
},
|
||||||
programMemory: execState.memory,
|
memVars: execState.variables,
|
||||||
})
|
})
|
||||||
expect(result).toEqual(false)
|
expect(result).toEqual(false)
|
||||||
})
|
})
|
||||||
@ -722,7 +721,7 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
|||||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
const modifiedAst = addCallExpressionsToPipe({
|
const modifiedAst = addCallExpressionsToPipe({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory: ProgramMemory.empty(),
|
variables: {},
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
expressions: [
|
expressions: [
|
||||||
createCallExpressionStdLib(
|
createCallExpressionStdLib(
|
||||||
@ -777,7 +776,7 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
|||||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||||
const modifiedAst = addCloseToPipe({
|
const modifiedAst = addCloseToPipe({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory: ProgramMemory.empty(),
|
variables: {},
|
||||||
pathToNode: sketchPathToNode,
|
pathToNode: sketchPathToNode,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ import { ToolTip } from 'lang/langHelpers'
|
|||||||
import { Selection, Selections } from 'lib/selections'
|
import { Selection, Selections } from 'lib/selections'
|
||||||
import {
|
import {
|
||||||
ArrayExpression,
|
ArrayExpression,
|
||||||
ArtifactGraph,
|
|
||||||
BinaryExpression,
|
BinaryExpression,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
CallExpressionKw,
|
CallExpressionKw,
|
||||||
@ -13,7 +12,6 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
PipeExpression,
|
PipeExpression,
|
||||||
Program,
|
Program,
|
||||||
ProgramMemory,
|
|
||||||
ReturnStatement,
|
ReturnStatement,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
sketchFromKclValueOptional,
|
sketchFromKclValueOptional,
|
||||||
@ -23,9 +21,11 @@ import {
|
|||||||
VariableDeclaration,
|
VariableDeclaration,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
recast,
|
recast,
|
||||||
|
ArtifactGraph,
|
||||||
kclSettings,
|
kclSettings,
|
||||||
unitLenToUnitLength,
|
unitLenToUnitLength,
|
||||||
unitAngToUnitAngle,
|
unitAngToUnitAngle,
|
||||||
|
VariableMap,
|
||||||
} from './wasm'
|
} from './wasm'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||||
@ -37,10 +37,11 @@ import {
|
|||||||
getConstraintType,
|
getConstraintType,
|
||||||
} from './std/sketchcombos'
|
} from './std/sketchcombos'
|
||||||
import { err, Reason } from 'lib/trap'
|
import { err, Reason } from 'lib/trap'
|
||||||
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { findKwArg } from './util'
|
import { findKwArg } from './util'
|
||||||
import { codeRefFromRange } from './std/artifactGraph'
|
import { codeRefFromRange } from './std/artifactGraph'
|
||||||
|
import { FunctionExpression } from 'wasm-lib/kcl/bindings/FunctionExpression'
|
||||||
|
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
||||||
import { KclSettingsAnnotation } from 'lib/settings/settingsTypes'
|
import { KclSettingsAnnotation } from 'lib/settings/settingsTypes'
|
||||||
|
|
||||||
export const LABELED_ARG_FIELD = 'LabeledArg -> Arg'
|
export const LABELED_ARG_FIELD = 'LabeledArg -> Arg'
|
||||||
@ -287,7 +288,7 @@ export interface PrevVariable<T> {
|
|||||||
|
|
||||||
export function findAllPreviousVariablesPath(
|
export function findAllPreviousVariablesPath(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
path: PathToNode,
|
path: PathToNode,
|
||||||
type: 'number' | 'string' = 'number'
|
type: 'number' | 'string' = 'number'
|
||||||
): {
|
): {
|
||||||
@ -325,7 +326,7 @@ export function findAllPreviousVariablesPath(
|
|||||||
bodyItems?.forEach?.((item) => {
|
bodyItems?.forEach?.((item) => {
|
||||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
||||||
const varName = item.declaration.id.name
|
const varName = item.declaration.id.name
|
||||||
const varValue = programMemory?.get(varName)
|
const varValue = memVars[varName]
|
||||||
if (!varValue || typeof varValue?.value !== type) return
|
if (!varValue || typeof varValue?.value !== type) return
|
||||||
variables.push({
|
variables.push({
|
||||||
key: varName,
|
key: varName,
|
||||||
@ -342,7 +343,7 @@ export function findAllPreviousVariablesPath(
|
|||||||
|
|
||||||
export function findAllPreviousVariables(
|
export function findAllPreviousVariables(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
type: 'number' | 'string' = 'number'
|
type: 'number' | 'string' = 'number'
|
||||||
): {
|
): {
|
||||||
@ -351,13 +352,19 @@ export function findAllPreviousVariables(
|
|||||||
insertIndex: number
|
insertIndex: number
|
||||||
} {
|
} {
|
||||||
const path = getNodePathFromSourceRange(ast, sourceRange)
|
const path = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
return findAllPreviousVariablesPath(ast, programMemory, path, type)
|
return findAllPreviousVariablesPath(ast, memVars, path, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReplacerFn = (
|
type ReplacerFn = (
|
||||||
_ast: Node<Program>,
|
_ast: Node<Program>,
|
||||||
varName: string
|
varName: string
|
||||||
) => { modifiedAst: Node<Program>; pathToReplaced: PathToNode } | Error
|
) =>
|
||||||
|
| {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToReplaced: PathToNode
|
||||||
|
exprInsertIndex: number
|
||||||
|
}
|
||||||
|
| Error
|
||||||
|
|
||||||
export function isNodeSafeToReplacePath(
|
export function isNodeSafeToReplacePath(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
@ -409,7 +416,7 @@ export function isNodeSafeToReplacePath(
|
|||||||
if (err(_nodeToReplace)) return _nodeToReplace
|
if (err(_nodeToReplace)) return _nodeToReplace
|
||||||
const nodeToReplace = _nodeToReplace.node as any
|
const nodeToReplace = _nodeToReplace.node as any
|
||||||
nodeToReplace[last[0]] = identifier
|
nodeToReplace[last[0]] = identifier
|
||||||
return { modifiedAst: _ast, pathToReplaced }
|
return { modifiedAst: _ast, pathToReplaced, exprInsertIndex: index }
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPipeSub = isTypeInValue(finVal as Expr, 'PipeSubstitution')
|
const hasPipeSub = isTypeInValue(finVal as Expr, 'PipeSubstitution')
|
||||||
@ -479,7 +486,7 @@ function isTypeInArrayExp(
|
|||||||
export function isLinesParallelAndConstrained(
|
export function isLinesParallelAndConstrained(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
artifactGraph: ArtifactGraph,
|
artifactGraph: ArtifactGraph,
|
||||||
programMemory: ProgramMemory,
|
memVars: VariableMap,
|
||||||
primaryLine: Selection,
|
primaryLine: Selection,
|
||||||
secondaryLine: Selection
|
secondaryLine: Selection
|
||||||
):
|
):
|
||||||
@ -509,7 +516,7 @@ export function isLinesParallelAndConstrained(
|
|||||||
if (err(_varDec)) return _varDec
|
if (err(_varDec)) return _varDec
|
||||||
const varDec = _varDec.node
|
const varDec = _varDec.node
|
||||||
const varName = (varDec as VariableDeclaration)?.declaration.id?.name
|
const varName = (varDec as VariableDeclaration)?.declaration.id?.name
|
||||||
const sg = sketchFromKclValue(programMemory?.get(varName), varName)
|
const sg = sketchFromKclValue(memVars[varName], varName)
|
||||||
if (err(sg)) return sg
|
if (err(sg)) return sg
|
||||||
const _primarySegment = getSketchSegmentFromSourceRange(
|
const _primarySegment = getSketchSegmentFromSourceRange(
|
||||||
sg,
|
sg,
|
||||||
@ -518,8 +525,15 @@ export function isLinesParallelAndConstrained(
|
|||||||
if (err(_primarySegment)) return _primarySegment
|
if (err(_primarySegment)) return _primarySegment
|
||||||
const primarySegment = _primarySegment.segment
|
const primarySegment = _primarySegment.segment
|
||||||
|
|
||||||
|
const _varDec2 = getNodeFromPath(ast, secondaryPath, 'VariableDeclaration')
|
||||||
|
if (err(_varDec2)) return _varDec2
|
||||||
|
const varDec2 = _varDec2.node
|
||||||
|
const varName2 = (varDec2 as VariableDeclaration)?.declaration.id?.name
|
||||||
|
const sg2 = sketchFromKclValue(memVars[varName2], varName2)
|
||||||
|
if (err(sg2)) return sg2
|
||||||
|
|
||||||
const _segment = getSketchSegmentFromSourceRange(
|
const _segment = getSketchSegmentFromSourceRange(
|
||||||
sg,
|
sg2,
|
||||||
secondaryLine?.codeRef?.range
|
secondaryLine?.codeRef?.range
|
||||||
)
|
)
|
||||||
if (err(_segment)) return _segment
|
if (err(_segment)) return _segment
|
||||||
@ -589,11 +603,11 @@ export function isLinesParallelAndConstrained(
|
|||||||
export function hasExtrudeSketch({
|
export function hasExtrudeSketch({
|
||||||
ast,
|
ast,
|
||||||
selection,
|
selection,
|
||||||
programMemory,
|
memVars,
|
||||||
}: {
|
}: {
|
||||||
ast: Program
|
ast: Program
|
||||||
selection: Selection
|
selection: Selection
|
||||||
programMemory: ProgramMemory
|
memVars: VariableMap
|
||||||
}): boolean {
|
}): boolean {
|
||||||
const varDecMeta = getNodeFromPath<VariableDeclaration>(
|
const varDecMeta = getNodeFromPath<VariableDeclaration>(
|
||||||
ast,
|
ast,
|
||||||
@ -607,7 +621,7 @@ export function hasExtrudeSketch({
|
|||||||
const varDec = varDecMeta.node
|
const varDec = varDecMeta.node
|
||||||
if (varDec.type !== 'VariableDeclaration') return false
|
if (varDec.type !== 'VariableDeclaration') return false
|
||||||
const varName = varDec.declaration.id.name
|
const varName = varDec.declaration.id.name
|
||||||
const varValue = programMemory?.get(varName)
|
const varValue = memVars[varName]
|
||||||
return (
|
return (
|
||||||
varValue?.type === 'Solid' ||
|
varValue?.type === 'Solid' ||
|
||||||
!(sketchFromKclValueOptional(varValue, varName) instanceof Reason)
|
!(sketchFromKclValueOptional(varValue, varName) instanceof Reason)
|
||||||
@ -871,6 +885,59 @@ export function getObjExprProperty(
|
|||||||
return { expr: node.properties[index].value, index }
|
return { expr: node.properties[index].value, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCursorInFunctionDefinition(
|
||||||
|
ast: Node<Program>,
|
||||||
|
selectionRanges: Selection
|
||||||
|
): boolean {
|
||||||
|
if (!selectionRanges?.codeRef?.pathToNode) return false
|
||||||
|
const node = getNodeFromPath<FunctionExpression>(
|
||||||
|
ast,
|
||||||
|
selectionRanges.codeRef.pathToNode,
|
||||||
|
'FunctionExpression'
|
||||||
|
)
|
||||||
|
if (err(node)) return false
|
||||||
|
if (node.node.type === 'FunctionExpression') return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBodyIndex(pathToNode: PathToNode): number | Error {
|
||||||
|
const index = Number(pathToNode[1][0])
|
||||||
|
if (Number.isInteger(index)) return index
|
||||||
|
return new Error('Expected number index')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isCallExprWithName(
|
||||||
|
expr: Expr | CallExpression,
|
||||||
|
name: string
|
||||||
|
): expr is CallExpression {
|
||||||
|
if (expr.type === 'CallExpression' && expr.callee.type === 'Identifier') {
|
||||||
|
return expr.callee.name === name
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doesSketchPipeNeedSplitting(
|
||||||
|
ast: Node<Program>,
|
||||||
|
pathToPipe: PathToNode
|
||||||
|
): boolean | Error {
|
||||||
|
const varDec = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
pathToPipe,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(varDec)) return varDec
|
||||||
|
if (varDec.node.type !== 'VariableDeclarator') return new Error('Not a var')
|
||||||
|
const pipeExpression = varDec.node.init
|
||||||
|
if (pipeExpression.type !== 'PipeExpression') return false
|
||||||
|
const [firstPipe, secondPipe] = pipeExpression.body
|
||||||
|
if (!firstPipe || !secondPipe) return false
|
||||||
|
if (
|
||||||
|
isCallExprWithName(firstPipe, 'startSketchOn') &&
|
||||||
|
isCallExprWithName(secondPipe, 'startProfileAt')
|
||||||
|
)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Given KCL, returns the settings annotation object if it exists.
|
* Given KCL, returns the settings annotation object if it exists.
|
||||||
*/
|
*/
|
||||||
|
@ -82,6 +82,7 @@ function moreNodePathFromSourceRange(
|
|||||||
return moreNodePathFromSourceRange(arg, sourceRange, path)
|
return moreNodePathFromSourceRange(arg, sourceRange, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return path
|
||||||
}
|
}
|
||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
Expr,
|
||||||
Artifact,
|
Artifact,
|
||||||
ArtifactGraph,
|
ArtifactGraph,
|
||||||
ArtifactId,
|
ArtifactId,
|
||||||
@ -18,7 +19,8 @@ import {
|
|||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { codeManager } from 'lib/singletons'
|
import { Cap, Plane, Wall } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
|
import { CapSubType } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
|
|
||||||
export type { Artifact, ArtifactId, SegmentArtifact } from 'lang/wasm'
|
export type { Artifact, ArtifactId, SegmentArtifact } from 'lang/wasm'
|
||||||
|
|
||||||
@ -37,10 +39,28 @@ export interface PlaneArtifactRich extends BaseArtifact {
|
|||||||
codeRef: CodeRef
|
codeRef: CodeRef
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CapArtifactRich extends BaseArtifact {
|
||||||
|
type: 'cap'
|
||||||
|
subType: CapSubType
|
||||||
|
faceCodeRef: CodeRef
|
||||||
|
edgeCuts: Array<EdgeCut>
|
||||||
|
paths: Array<PathArtifact>
|
||||||
|
sweep?: SweepArtifact
|
||||||
|
}
|
||||||
|
export interface WallArtifactRich extends BaseArtifact {
|
||||||
|
type: 'wall'
|
||||||
|
id: ArtifactId
|
||||||
|
segment: PathArtifact
|
||||||
|
edgeCuts: Array<EdgeCut>
|
||||||
|
sweep: SweepArtifact
|
||||||
|
paths: Array<PathArtifact>
|
||||||
|
faceCodeRef: CodeRef
|
||||||
|
}
|
||||||
|
|
||||||
export interface PathArtifactRich extends BaseArtifact {
|
export interface PathArtifactRich extends BaseArtifact {
|
||||||
type: 'path'
|
type: 'path'
|
||||||
/** A path must always lie on a plane */
|
/** A path must always lie on a plane */
|
||||||
plane: PlaneArtifact | WallArtifact
|
plane: PlaneArtifact | WallArtifact | CapArtifact
|
||||||
/** A path must always contain 0 or more segments */
|
/** A path must always contain 0 or more segments */
|
||||||
segments: Array<SegmentArtifact>
|
segments: Array<SegmentArtifact>
|
||||||
/** A path may not result in a sweep artifact */
|
/** A path may not result in a sweep artifact */
|
||||||
@ -51,7 +71,7 @@ export interface PathArtifactRich extends BaseArtifact {
|
|||||||
interface SegmentArtifactRich extends BaseArtifact {
|
interface SegmentArtifactRich extends BaseArtifact {
|
||||||
type: 'segment'
|
type: 'segment'
|
||||||
path: PathArtifact
|
path: PathArtifact
|
||||||
surf?: WallArtifact
|
surf: WallArtifact
|
||||||
edges: Array<SweepEdge>
|
edges: Array<SweepEdge>
|
||||||
edgeCut?: EdgeCut
|
edgeCut?: EdgeCut
|
||||||
codeRef: CodeRef
|
codeRef: CodeRef
|
||||||
@ -151,6 +171,73 @@ export function expandPlane(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function expandWall(
|
||||||
|
wall: WallArtifact,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): WallArtifactRich {
|
||||||
|
const { pathIds, sweepId: _s, edgeCutEdgeIds, ...keptProperties } = wall
|
||||||
|
const paths = pathIds?.length
|
||||||
|
? Array.from(
|
||||||
|
getArtifactsOfTypes(
|
||||||
|
{ keys: wall.pathIds, types: ['path'] },
|
||||||
|
artifactGraph
|
||||||
|
).values()
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
const sweep = artifactGraph.get(wall.sweepId) as SweepArtifact
|
||||||
|
const edgeCuts = edgeCutEdgeIds?.length
|
||||||
|
? Array.from(
|
||||||
|
getArtifactsOfTypes(
|
||||||
|
{ keys: wall.edgeCutEdgeIds, types: ['edgeCut'] },
|
||||||
|
artifactGraph
|
||||||
|
).values()
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
const segment = artifactGraph.get(wall.segId) as PathArtifact
|
||||||
|
return {
|
||||||
|
type: 'wall',
|
||||||
|
...keptProperties,
|
||||||
|
paths,
|
||||||
|
sweep,
|
||||||
|
segment,
|
||||||
|
edgeCuts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function expandCap(
|
||||||
|
cap: CapArtifact,
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
): CapArtifactRich {
|
||||||
|
const { pathIds, sweepId: _s, edgeCutEdgeIds, ...keptProperties } = cap
|
||||||
|
const paths = pathIds?.length
|
||||||
|
? Array.from(
|
||||||
|
getArtifactsOfTypes(
|
||||||
|
{ keys: cap.pathIds, types: ['path'] },
|
||||||
|
artifactGraph
|
||||||
|
).values()
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
const maybeSweep = getArtifactOfTypes(
|
||||||
|
{ key: cap.sweepId, types: ['sweep'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
const sweep = err(maybeSweep) ? undefined : maybeSweep
|
||||||
|
const edgeCuts = edgeCutEdgeIds?.length
|
||||||
|
? Array.from(
|
||||||
|
getArtifactsOfTypes(
|
||||||
|
{ keys: cap.edgeCutEdgeIds, types: ['edgeCut'] },
|
||||||
|
artifactGraph
|
||||||
|
).values()
|
||||||
|
)
|
||||||
|
: []
|
||||||
|
return {
|
||||||
|
type: 'cap',
|
||||||
|
...keptProperties,
|
||||||
|
paths,
|
||||||
|
sweep,
|
||||||
|
edgeCuts,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function expandPath(
|
export function expandPath(
|
||||||
path: PathArtifact,
|
path: PathArtifact,
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
@ -239,6 +326,7 @@ export function expandSegment(
|
|||||||
if (err(path)) return path
|
if (err(path)) return path
|
||||||
if (err(surf)) return surf
|
if (err(surf)) return surf
|
||||||
if (err(edgeCut)) return edgeCut
|
if (err(edgeCut)) return edgeCut
|
||||||
|
if (!surf) return new Error('Segment does not have a surface')
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'segment',
|
type: 'segment',
|
||||||
@ -410,6 +498,186 @@ export function codeRefFromRange(range: SourceRange, ast: Program): CodeRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPlaneFromPath(
|
||||||
|
path: PathArtifact,
|
||||||
|
graph: ArtifactGraph
|
||||||
|
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
||||||
|
const plane = getArtifactOfTypes(
|
||||||
|
{ key: path.planeId, types: ['plane', 'wall', 'cap'] },
|
||||||
|
graph
|
||||||
|
)
|
||||||
|
if (err(plane)) return plane
|
||||||
|
return plane
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlaneFromSegment(
|
||||||
|
segment: SegmentArtifact,
|
||||||
|
graph: ArtifactGraph
|
||||||
|
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
||||||
|
const path = getArtifactOfTypes(
|
||||||
|
{ key: segment.pathId, types: ['path'] },
|
||||||
|
graph
|
||||||
|
)
|
||||||
|
if (err(path)) return path
|
||||||
|
return getPlaneFromPath(path, graph)
|
||||||
|
}
|
||||||
|
function getPlaneFromSolid2D(
|
||||||
|
solid2D: Solid2D,
|
||||||
|
graph: ArtifactGraph
|
||||||
|
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
||||||
|
const path = getArtifactOfTypes(
|
||||||
|
{ key: solid2D.pathId, types: ['path'] },
|
||||||
|
graph
|
||||||
|
)
|
||||||
|
if (err(path)) return path
|
||||||
|
return getPlaneFromPath(path, graph)
|
||||||
|
}
|
||||||
|
function getPlaneFromSweepEdge(edge: SweepEdge, graph: ArtifactGraph) {
|
||||||
|
const sweep = getArtifactOfTypes(
|
||||||
|
{ key: edge.sweepId, types: ['sweep'] },
|
||||||
|
graph
|
||||||
|
)
|
||||||
|
if (err(sweep)) return sweep
|
||||||
|
const path = getArtifactOfTypes({ key: sweep.pathId, types: ['path'] }, graph)
|
||||||
|
if (err(path)) return path
|
||||||
|
return getPlaneFromPath(path, graph)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlaneFromArtifact(
|
||||||
|
artifact: Artifact | undefined,
|
||||||
|
graph: ArtifactGraph
|
||||||
|
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
||||||
|
if (!artifact) return new Error(`Artifact is undefined`)
|
||||||
|
if (artifact.type === 'plane') return artifact
|
||||||
|
if (artifact.type === 'path') return getPlaneFromPath(artifact, graph)
|
||||||
|
if (artifact.type === 'segment') return getPlaneFromSegment(artifact, graph)
|
||||||
|
if (artifact.type === 'solid2d') return getPlaneFromSolid2D(artifact, graph)
|
||||||
|
if (artifact.type === 'wall' || artifact.type === 'cap') return artifact
|
||||||
|
if (artifact.type === 'sweepEdge')
|
||||||
|
return getPlaneFromSweepEdge(artifact, graph)
|
||||||
|
return new Error(`Artifact type ${artifact.type} does not have a plane`)
|
||||||
|
}
|
||||||
|
|
||||||
|
const onlyConsecutivePaths = (
|
||||||
|
orderedNodePaths: PathToNode[],
|
||||||
|
originalPath: PathToNode,
|
||||||
|
ast: Program
|
||||||
|
): PathToNode[] => {
|
||||||
|
const isExprSafe = (index: number, ast: Program): boolean => {
|
||||||
|
// we allow expressions between profiles, but only basic math expressions 5 + 6 etc
|
||||||
|
// because 5 + doSomeMath() might be okay, but we can't know if it's an abstraction on a stdlib
|
||||||
|
// call that involves a engine call, and we can't have that in sketch-mode/mock-execution
|
||||||
|
const expr = ast.body?.[index]
|
||||||
|
if (!expr) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (expr.type === 'ImportStatement' || expr.type === 'ReturnStatement') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (expr.type === 'VariableDeclaration') {
|
||||||
|
const init = expr.declaration?.init
|
||||||
|
if (!init) return false
|
||||||
|
if (init.type === 'CallExpression') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (init.type === 'BinaryExpression' && isNodeSafe(init)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (init.type === 'Literal' || init.type === 'MemberExpression') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const originalIndex = Number(
|
||||||
|
orderedNodePaths.find(
|
||||||
|
(path) => path[1][0] === originalPath[1][0]
|
||||||
|
)?.[1]?.[0] || 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const minIndex = Number(orderedNodePaths[0][1][0])
|
||||||
|
const maxIndex = Number(orderedNodePaths[orderedNodePaths.length - 1][1][0])
|
||||||
|
const pathIndexMap: any = {}
|
||||||
|
orderedNodePaths.forEach((path) => {
|
||||||
|
const bodyIndex = Number(path[1][0])
|
||||||
|
pathIndexMap[bodyIndex] = path
|
||||||
|
})
|
||||||
|
const safePaths: PathToNode[] = []
|
||||||
|
|
||||||
|
// traverse expressions in either direction from the profile selected
|
||||||
|
// when the user entered sketch mode
|
||||||
|
for (let i = originalIndex; i <= maxIndex; i++) {
|
||||||
|
if (pathIndexMap[i]) {
|
||||||
|
safePaths.push(pathIndexMap[i])
|
||||||
|
} else if (!isExprSafe(i, ast)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (let i = originalIndex - 1; i >= minIndex; i--) {
|
||||||
|
if (pathIndexMap[i]) {
|
||||||
|
safePaths.unshift(pathIndexMap[i])
|
||||||
|
} else if (!isExprSafe(i, ast)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return safePaths
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPathsFromPlaneArtifact(
|
||||||
|
planeArtifact: PlaneArtifact,
|
||||||
|
artifactGraph: ArtifactGraph,
|
||||||
|
ast: Program
|
||||||
|
): PathToNode[] {
|
||||||
|
const nodePaths: PathToNode[] = []
|
||||||
|
for (const pathId of planeArtifact.pathIds) {
|
||||||
|
const path = artifactGraph.get(pathId)
|
||||||
|
if (!path) continue
|
||||||
|
if ('codeRef' in path && path.codeRef) {
|
||||||
|
// TODO should figure out why upstream the path is bad
|
||||||
|
const isNodePathBad = path.codeRef.pathToNode.length < 2
|
||||||
|
nodePaths.push(
|
||||||
|
isNodePathBad
|
||||||
|
? getNodePathFromSourceRange(ast, path.codeRef.range)
|
||||||
|
: path.codeRef.pathToNode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return onlyConsecutivePaths(nodePaths, nodePaths[0], ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPathsFromArtifact({
|
||||||
|
sketchPathToNode,
|
||||||
|
artifact,
|
||||||
|
artifactGraph,
|
||||||
|
ast,
|
||||||
|
}: {
|
||||||
|
sketchPathToNode: PathToNode
|
||||||
|
artifact?: Artifact
|
||||||
|
artifactGraph: ArtifactGraph
|
||||||
|
ast: Program
|
||||||
|
}): PathToNode[] | Error {
|
||||||
|
const plane = getPlaneFromArtifact(artifact, artifactGraph)
|
||||||
|
if (err(plane)) return plane
|
||||||
|
const paths = getArtifactsOfTypes(
|
||||||
|
{ keys: plane.pathIds, types: ['path'] },
|
||||||
|
artifactGraph
|
||||||
|
)
|
||||||
|
let nodePaths = [...paths.values()]
|
||||||
|
.map((path) => path.codeRef.pathToNode)
|
||||||
|
.sort((a, b) => Number(a[1][0]) - Number(b[1][0]))
|
||||||
|
return onlyConsecutivePaths(nodePaths, sketchPathToNode, ast)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isNodeSafe(node: Expr): boolean {
|
||||||
|
if (node.type === 'Literal' || node.type === 'MemberExpression') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (node.type === 'BinaryExpression') {
|
||||||
|
return isNodeSafe(node.left) && isNodeSafe(node.right)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an artifact from a code source range
|
* Get an artifact from a code source range
|
||||||
*/
|
*/
|
||||||
@ -418,12 +686,24 @@ export function getArtifactFromRange(
|
|||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): Artifact | null {
|
): Artifact | null {
|
||||||
for (const artifact of artifactGraph.values()) {
|
for (const artifact of artifactGraph.values()) {
|
||||||
if ('codeRef' in artifact) {
|
const codeRef = getFaceCodeRef(artifact)
|
||||||
|
if (codeRef) {
|
||||||
const match =
|
const match =
|
||||||
artifact.codeRef?.range[0] === range[0] &&
|
codeRef?.range[0] === range[0] && codeRef.range[1] === range[1]
|
||||||
artifact.codeRef.range[1] === range[1]
|
|
||||||
if (match) return artifact
|
if (match) return artifact
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getFaceCodeRef(
|
||||||
|
artifact: Artifact | Plane | Wall | Cap
|
||||||
|
): CodeRef | null {
|
||||||
|
if ('faceCodeRef' in artifact) {
|
||||||
|
return artifact.faceCodeRef
|
||||||
|
}
|
||||||
|
if ('codeRef' in artifact) {
|
||||||
|
return artifact.codeRef
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 569 KiB After Width: | Height: | Size: 560 KiB |
@ -124,7 +124,7 @@ describe('testing changeSketchArguments', () => {
|
|||||||
const sourceStart = code.indexOf(lineToChange)
|
const sourceStart = code.indexOf(lineToChange)
|
||||||
const changeSketchArgsRetVal = changeSketchArguments(
|
const changeSketchArgsRetVal = changeSketchArguments(
|
||||||
ast,
|
ast,
|
||||||
execState.memory,
|
execState.variables,
|
||||||
{
|
{
|
||||||
type: 'sourceRange',
|
type: 'sourceRange',
|
||||||
sourceRange: topLevelRange(
|
sourceRange: topLevelRange(
|
||||||
@ -160,7 +160,7 @@ mySketch001 = startSketchOn('XY')
|
|||||||
expect(sourceStart).toBe(89)
|
expect(sourceStart).toBe(89)
|
||||||
const newSketchLnRetVal = addNewSketchLn({
|
const newSketchLnRetVal = addNewSketchLn({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory: execState.memory,
|
variables: execState.variables,
|
||||||
input: {
|
input: {
|
||||||
type: 'straight-segment',
|
type: 'straight-segment',
|
||||||
from: [0, 0],
|
from: [0, 0],
|
||||||
@ -190,7 +190,7 @@ mySketch001 = startSketchOn('XY')
|
|||||||
|
|
||||||
const modifiedAst2 = addCloseToPipe({
|
const modifiedAst2 = addCloseToPipe({
|
||||||
node: ast,
|
node: ast,
|
||||||
programMemory: execState.memory,
|
variables: execState.variables,
|
||||||
pathToNode: [
|
pathToNode: [
|
||||||
['body', ''],
|
['body', ''],
|
||||||
[0, 'index'],
|
[0, 'index'],
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import {
|
import {
|
||||||
ProgramMemory,
|
|
||||||
Path,
|
Path,
|
||||||
Sketch,
|
Sketch,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
@ -14,6 +13,7 @@ import {
|
|||||||
Identifier,
|
Identifier,
|
||||||
sketchFromKclValue,
|
sketchFromKclValue,
|
||||||
topLevelRange,
|
topLevelRange,
|
||||||
|
VariableMap,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import {
|
import {
|
||||||
getNodeFromPath,
|
getNodeFromPath,
|
||||||
@ -66,7 +66,12 @@ import { perpendicularDistance } from 'sketch-helpers'
|
|||||||
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
import { TagDeclarator } from 'wasm-lib/kcl/bindings/TagDeclarator'
|
||||||
import { EdgeCutInfo } from 'machines/modelingMachine'
|
import { EdgeCutInfo } from 'machines/modelingMachine'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { findKwArg, findKwArgAny, findKwArgAnyIndex } from 'lang/util'
|
import {
|
||||||
|
findKwArg,
|
||||||
|
findKwArgWithIndex,
|
||||||
|
findKwArgAny,
|
||||||
|
findKwArgAnyIndex,
|
||||||
|
} from 'lang/util'
|
||||||
|
|
||||||
export const ARG_TAG = 'tag'
|
export const ARG_TAG = 'tag'
|
||||||
export const ARG_END = 'end'
|
export const ARG_END = 'end'
|
||||||
@ -76,6 +81,9 @@ const STRAIGHT_SEGMENT_ERR = new Error(
|
|||||||
'Invalid input, expected "straight-segment"'
|
'Invalid input, expected "straight-segment"'
|
||||||
)
|
)
|
||||||
const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"')
|
const ARC_SEGMENT_ERR = new Error('Invalid input, expected "arc-segment"')
|
||||||
|
const CIRCLE_THREE_POINT_SEGMENT_ERR = new Error(
|
||||||
|
'Invalid input, expected "circle-three-point-segment"'
|
||||||
|
)
|
||||||
|
|
||||||
export type Coords2d = [number, number]
|
export type Coords2d = [number, number]
|
||||||
|
|
||||||
@ -171,7 +179,8 @@ const commonConstraintInfoHelper = (
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode,
|
||||||
|
filterValue?: string
|
||||||
) => {
|
) => {
|
||||||
if (callExp.type !== 'CallExpression' && callExp.type !== 'CallExpressionKw')
|
if (callExp.type !== 'CallExpression' && callExp.type !== 'CallExpressionKw')
|
||||||
return []
|
return []
|
||||||
@ -295,7 +304,8 @@ const horzVertConstraintInfoHelper = (
|
|||||||
stdLibFnName: ConstrainInfo['stdLibFnName'],
|
stdLibFnName: ConstrainInfo['stdLibFnName'],
|
||||||
abbreviatedInput: AbbreviatedInput,
|
abbreviatedInput: AbbreviatedInput,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode,
|
||||||
|
filterValue?: string
|
||||||
) => {
|
) => {
|
||||||
if (callExp.type !== 'CallExpression') return []
|
if (callExp.type !== 'CallExpression') return []
|
||||||
const firstArg = callExp.arguments?.[0]
|
const firstArg = callExp.arguments?.[0]
|
||||||
@ -355,7 +365,7 @@ function getTagKwArg(): SketchLineHelperKw['getTag'] {
|
|||||||
export const line: SketchLineHelperKw = {
|
export const line: SketchLineHelperKw = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
variables,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
replaceExistingCallback,
|
replaceExistingCallback,
|
||||||
@ -494,7 +504,7 @@ export const line: SketchLineHelperKw = {
|
|||||||
export const lineTo: SketchLineHelperKw = {
|
export const lineTo: SketchLineHelperKw = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
variables,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
replaceExistingCallback,
|
replaceExistingCallback,
|
||||||
@ -502,13 +512,14 @@ export const lineTo: SketchLineHelperKw = {
|
|||||||
}) => {
|
}) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const to = segmentInput.to
|
const to = segmentInput.to
|
||||||
const _node = { ...node }
|
const _node = structuredClone(node)
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression | CallExpressionKw>(
|
const nodeMeta = getNodeFromPath<PipeExpression | CallExpressionKw>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
)
|
)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
|
|
||||||
const { node: pipe } = nodeMeta
|
const { node: pipe } = nodeMeta
|
||||||
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
const nodeMeta2 = getNodeFromPath<VariableDeclarator>(
|
||||||
_node,
|
_node,
|
||||||
@ -783,11 +794,11 @@ export const xLine: SketchLineHelper = {
|
|||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { from, to } = segmentInput
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = structuredClone(node)
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const varDec = getNode<VariableDeclaration>('VariableDeclaration')
|
||||||
if (err(_node1)) return _node1
|
if (err(varDec)) return varDec
|
||||||
const { node: pipe } = _node1
|
const dec = varDec.node.declaration
|
||||||
|
|
||||||
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||||
|
|
||||||
@ -802,7 +813,11 @@ export const xLine: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { callExp, valueUsedInTransform } = result
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
if (dec.init.type === 'PipeExpression') {
|
||||||
|
dec.init.body[callIndex] = callExp
|
||||||
|
} else {
|
||||||
|
dec.init = callExp
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -814,7 +829,11 @@ export const xLine: SketchLineHelper = {
|
|||||||
newVal,
|
newVal,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
pipe.body = [...pipe.body, newLine]
|
if (dec.init.type === 'PipeExpression') {
|
||||||
|
dec.init.body = [...dec.init.body, newLine]
|
||||||
|
} else {
|
||||||
|
dec.init = createPipeExpression([dec.init, newLine])
|
||||||
|
}
|
||||||
return { modifiedAst: _node, pathToNode }
|
return { modifiedAst: _node, pathToNode }
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
@ -851,11 +870,11 @@ export const yLine: SketchLineHelper = {
|
|||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { from, to } = segmentInput
|
const { from, to } = segmentInput
|
||||||
const _node = { ...node }
|
const _node = structuredClone(node)
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const _node1 = getNode<PipeExpression>('PipeExpression')
|
const varDec = getNode<VariableDeclaration>('VariableDeclaration')
|
||||||
if (err(_node1)) return _node1
|
if (err(varDec)) return varDec
|
||||||
const { node: pipe } = _node1
|
const dec = varDec.node.declaration
|
||||||
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||||
if (replaceExistingCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
@ -868,7 +887,11 @@ export const yLine: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { callExp, valueUsedInTransform } = result
|
const { callExp, valueUsedInTransform } = result
|
||||||
pipe.body[callIndex] = callExp
|
if (dec.init.type === 'PipeExpression') {
|
||||||
|
dec.init.body[callIndex] = callExp
|
||||||
|
} else {
|
||||||
|
dec.init = callExp
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -880,7 +903,11 @@ export const yLine: SketchLineHelper = {
|
|||||||
newVal,
|
newVal,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
pipe.body = [...pipe.body, newLine]
|
if (dec.init.type === 'PipeExpression') {
|
||||||
|
dec.init.body = [...dec.init.body, newLine]
|
||||||
|
} else {
|
||||||
|
dec.init = createPipeExpression([dec.init, newLine])
|
||||||
|
}
|
||||||
return { modifiedAst: _node, pathToNode }
|
return { modifiedAst: _node, pathToNode }
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
@ -1220,6 +1247,295 @@ export const circle: SketchLineHelper = {
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
export const circleThreePoint: SketchLineHelperKw = {
|
||||||
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
|
if (segmentInput.type !== 'circle-three-point-segment') {
|
||||||
|
return CIRCLE_THREE_POINT_SEGMENT_ERR
|
||||||
|
}
|
||||||
|
|
||||||
|
const { p1, p2, p3 } = segmentInput
|
||||||
|
const _node = structuredClone(node)
|
||||||
|
const nodeMeta = getNodeFromPath<VariableDeclaration>(
|
||||||
|
_node,
|
||||||
|
pathToNode,
|
||||||
|
'VariableDeclaration'
|
||||||
|
)
|
||||||
|
if (err(nodeMeta)) return nodeMeta
|
||||||
|
|
||||||
|
const { node: varDec } = nodeMeta
|
||||||
|
|
||||||
|
const createRoundedLiteral = (val: number) =>
|
||||||
|
createLiteral(roundOff(val, 2))
|
||||||
|
if (replaceExistingCallback) {
|
||||||
|
const result = replaceExistingCallback([
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p1',
|
||||||
|
argType: 'xAbsolute',
|
||||||
|
expr: createRoundedLiteral(p1[0]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p1',
|
||||||
|
argType: 'yAbsolute',
|
||||||
|
expr: createRoundedLiteral(p1[1]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p2',
|
||||||
|
argType: 'xAbsolute',
|
||||||
|
expr: createRoundedLiteral(p2[0]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p2',
|
||||||
|
argType: 'yAbsolute',
|
||||||
|
expr: createRoundedLiteral(p2[1]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p3',
|
||||||
|
argType: 'xAbsolute',
|
||||||
|
expr: createRoundedLiteral(p3[0]),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p3',
|
||||||
|
argType: 'yAbsolute',
|
||||||
|
expr: createRoundedLiteral(p3[1]),
|
||||||
|
},
|
||||||
|
])
|
||||||
|
if (err(result)) return result
|
||||||
|
const { callExp, valueUsedInTransform } = result
|
||||||
|
|
||||||
|
varDec.declaration.init = callExp
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
valueUsedInTransform,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Error('replaceExistingCallback is missing')
|
||||||
|
},
|
||||||
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
|
if (input.type !== 'circle-three-point-segment') {
|
||||||
|
return CIRCLE_THREE_POINT_SEGMENT_ERR
|
||||||
|
}
|
||||||
|
const { p1, p2, p3 } = input
|
||||||
|
const _node = { ...node }
|
||||||
|
const nodeMeta = getNodeFromPath<CallExpressionKw>(_node, pathToNode)
|
||||||
|
if (err(nodeMeta)) return nodeMeta
|
||||||
|
|
||||||
|
const { node: callExpression, shallowPath } = nodeMeta
|
||||||
|
const createRounded2DPointArr = (point: [number, number]) =>
|
||||||
|
createArrayExpression([
|
||||||
|
createLiteral(roundOff(point[0], 2)),
|
||||||
|
createLiteral(roundOff(point[1], 2)),
|
||||||
|
])
|
||||||
|
|
||||||
|
const newP1 = createRounded2DPointArr(p1)
|
||||||
|
const newP2 = createRounded2DPointArr(p2)
|
||||||
|
const newP3 = createRounded2DPointArr(p3)
|
||||||
|
mutateKwArg('p1', callExpression, newP1)
|
||||||
|
mutateKwArg('p2', callExpression, newP2)
|
||||||
|
mutateKwArg('p3', callExpression, newP3)
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode: shallowPath,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getTag: getTagKwArg(),
|
||||||
|
addTag: addTagKw(),
|
||||||
|
getConstraintInfo: (callExp, code, pathToNode, filterValue) => {
|
||||||
|
if (callExp.type !== 'CallExpressionKw') return []
|
||||||
|
const p1Details = findKwArgWithIndex('p1', callExp)
|
||||||
|
const p2Details = findKwArgWithIndex('p2', callExp)
|
||||||
|
const p3Details = findKwArgWithIndex('p3', callExp)
|
||||||
|
if (!p1Details || !p2Details || !p3Details) return []
|
||||||
|
if (
|
||||||
|
p1Details.expr.type !== 'ArrayExpression' ||
|
||||||
|
p2Details.expr.type !== 'ArrayExpression' ||
|
||||||
|
p3Details.expr.type !== 'ArrayExpression'
|
||||||
|
)
|
||||||
|
return []
|
||||||
|
|
||||||
|
const pathToP1ArrayExpression: PathToNode = [
|
||||||
|
...pathToNode,
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[p1Details.argIndex, 'arg index'],
|
||||||
|
['arg', 'labeledArg -> Arg'],
|
||||||
|
['elements', 'ArrayExpression'],
|
||||||
|
]
|
||||||
|
const pathToP2ArrayExpression: PathToNode = [
|
||||||
|
...pathToNode,
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[p2Details.argIndex, 'arg index'],
|
||||||
|
['arg', 'labeledArg -> Arg'],
|
||||||
|
['elements', 'ArrayExpression'],
|
||||||
|
]
|
||||||
|
const pathToP3ArrayExpression: PathToNode = [
|
||||||
|
...pathToNode,
|
||||||
|
['arguments', 'CallExpressionKw'],
|
||||||
|
[p3Details.argIndex, 'arg index'],
|
||||||
|
['arg', 'labeledArg -> Arg'],
|
||||||
|
['elements', 'ArrayExpression'],
|
||||||
|
]
|
||||||
|
|
||||||
|
const pathToP1XArg: PathToNode = [...pathToP1ArrayExpression, [0, 'index']]
|
||||||
|
const pathToP1YArg: PathToNode = [...pathToP1ArrayExpression, [1, 'index']]
|
||||||
|
const pathToP2XArg: PathToNode = [...pathToP2ArrayExpression, [0, 'index']]
|
||||||
|
const pathToP2YArg: PathToNode = [...pathToP2ArrayExpression, [1, 'index']]
|
||||||
|
const pathToP3XArg: PathToNode = [...pathToP3ArrayExpression, [0, 'index']]
|
||||||
|
const pathToP3YArg: PathToNode = [...pathToP3ArrayExpression, [1, 'index']]
|
||||||
|
|
||||||
|
const constraints: (ConstrainInfo & { filterValue: string })[] = [
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'xAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p1Details.expr.elements[0]),
|
||||||
|
sourceRange: [
|
||||||
|
p1Details.expr.elements[0].start,
|
||||||
|
p1Details.expr.elements[0].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP1XArg,
|
||||||
|
value: code.slice(
|
||||||
|
p1Details.expr.elements[0].start,
|
||||||
|
p1Details.expr.elements[0].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p1',
|
||||||
|
},
|
||||||
|
filterValue: 'p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'yAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p1Details.expr.elements[1]),
|
||||||
|
sourceRange: [
|
||||||
|
p1Details.expr.elements[1].start,
|
||||||
|
p1Details.expr.elements[1].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP1YArg,
|
||||||
|
value: code.slice(
|
||||||
|
p1Details.expr.elements[1].start,
|
||||||
|
p1Details.expr.elements[1].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p1',
|
||||||
|
},
|
||||||
|
filterValue: 'p1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'xAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p2Details.expr.elements[0]),
|
||||||
|
sourceRange: [
|
||||||
|
p2Details.expr.elements[0].start,
|
||||||
|
p2Details.expr.elements[0].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP2XArg,
|
||||||
|
value: code.slice(
|
||||||
|
p2Details.expr.elements[0].start,
|
||||||
|
p2Details.expr.elements[0].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p2',
|
||||||
|
},
|
||||||
|
filterValue: 'p2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'yAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p2Details.expr.elements[1]),
|
||||||
|
sourceRange: [
|
||||||
|
p2Details.expr.elements[1].start,
|
||||||
|
p2Details.expr.elements[1].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP2YArg,
|
||||||
|
value: code.slice(
|
||||||
|
p2Details.expr.elements[1].start,
|
||||||
|
p2Details.expr.elements[1].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p2',
|
||||||
|
},
|
||||||
|
filterValue: 'p2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'xAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p3Details.expr.elements[0]),
|
||||||
|
sourceRange: [
|
||||||
|
p3Details.expr.elements[0].start,
|
||||||
|
p3Details.expr.elements[0].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP3XArg,
|
||||||
|
value: code.slice(
|
||||||
|
p3Details.expr.elements[0].start,
|
||||||
|
p3Details.expr.elements[0].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 0,
|
||||||
|
key: 'p3',
|
||||||
|
},
|
||||||
|
filterValue: 'p3',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
stdLibFnName: 'circleThreePoint',
|
||||||
|
type: 'yAbsolute',
|
||||||
|
isConstrained: isNotLiteralArrayOrStatic(p3Details.expr.elements[1]),
|
||||||
|
sourceRange: [
|
||||||
|
p3Details.expr.elements[1].start,
|
||||||
|
p3Details.expr.elements[1].end,
|
||||||
|
0,
|
||||||
|
],
|
||||||
|
pathToNode: pathToP3YArg,
|
||||||
|
value: code.slice(
|
||||||
|
p3Details.expr.elements[1].start,
|
||||||
|
p3Details.expr.elements[1].end
|
||||||
|
),
|
||||||
|
argPosition: {
|
||||||
|
type: 'arrayInObject',
|
||||||
|
index: 1,
|
||||||
|
key: 'p3',
|
||||||
|
},
|
||||||
|
filterValue: 'p3',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const finalConstraints: ConstrainInfo[] = []
|
||||||
|
constraints.forEach((constraint) => {
|
||||||
|
if (!filterValue) {
|
||||||
|
finalConstraints.push(constraint)
|
||||||
|
}
|
||||||
|
if (filterValue && constraint.filterValue === filterValue) {
|
||||||
|
finalConstraints.push(constraint)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return finalConstraints
|
||||||
|
},
|
||||||
|
}
|
||||||
export const angledLine: SketchLineHelper = {
|
export const angledLine: SketchLineHelper = {
|
||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
@ -1313,7 +1629,7 @@ export const angledLine: SketchLineHelper = {
|
|||||||
export const angledLineOfXLength: SketchLineHelper = {
|
export const angledLineOfXLength: SketchLineHelper = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
variables,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
replaceExistingCallback,
|
replaceExistingCallback,
|
||||||
@ -1337,10 +1653,7 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
const { node: varDec } = nodeMeta2
|
const { node: varDec } = nodeMeta2
|
||||||
|
|
||||||
const variableName = varDec.id.name
|
const variableName = varDec.id.name
|
||||||
const sketch = sketchFromKclValue(
|
const sketch = sketchFromKclValue(variables[variableName], variableName)
|
||||||
previousProgramMemory?.get(variableName),
|
|
||||||
variableName
|
|
||||||
)
|
|
||||||
if (err(sketch)) {
|
if (err(sketch)) {
|
||||||
return sketch
|
return sketch
|
||||||
}
|
}
|
||||||
@ -1429,7 +1742,7 @@ export const angledLineOfXLength: SketchLineHelper = {
|
|||||||
export const angledLineOfYLength: SketchLineHelper = {
|
export const angledLineOfYLength: SketchLineHelper = {
|
||||||
add: ({
|
add: ({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
variables,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
replaceExistingCallback,
|
replaceExistingCallback,
|
||||||
@ -1452,10 +1765,7 @@ export const angledLineOfYLength: SketchLineHelper = {
|
|||||||
if (err(nodeMeta2)) return nodeMeta2
|
if (err(nodeMeta2)) return nodeMeta2
|
||||||
const { node: varDec } = nodeMeta2
|
const { node: varDec } = nodeMeta2
|
||||||
const variableName = varDec.id.name
|
const variableName = varDec.id.name
|
||||||
const sketch = sketchFromKclValue(
|
const sketch = sketchFromKclValue(variables[variableName], variableName)
|
||||||
previousProgramMemory?.get(variableName),
|
|
||||||
variableName
|
|
||||||
)
|
|
||||||
if (err(sketch)) return sketch
|
if (err(sketch)) return sketch
|
||||||
|
|
||||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||||
@ -1793,7 +2103,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
}
|
}
|
||||||
return new Error('not implemented')
|
return new Error('not implemented')
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input, previousProgramMemory }) => {
|
updateArgs: ({ node, pathToNode, input, variables }) => {
|
||||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { to, from } = input
|
const { to, from } = input
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
@ -1820,10 +2130,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
|||||||
|
|
||||||
const { node: varDec } = nodeMeta2
|
const { node: varDec } = nodeMeta2
|
||||||
const varName = varDec.declaration.id.name
|
const varName = varDec.declaration.id.name
|
||||||
const sketch = sketchFromKclValue(
|
const sketch = sketchFromKclValue(variables[varName], varName)
|
||||||
previousProgramMemory.get(varName),
|
|
||||||
varName
|
|
||||||
)
|
|
||||||
if (err(sketch)) return sketch
|
if (err(sketch)) return sketch
|
||||||
const intersectPath = sketch.paths.find(
|
const intersectPath = sketch.paths.find(
|
||||||
({ tag }: Path) => tag && tag.value === intersectTagName
|
({ tag }: Path) => tag && tag.value === intersectTagName
|
||||||
@ -1954,7 +2261,8 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
|
|||||||
startNodes: [],
|
startNodes: [],
|
||||||
nonCodeNodes: [],
|
nonCodeNodes: [],
|
||||||
},
|
},
|
||||||
trivia: [],
|
innerAttrs: [],
|
||||||
|
outerAttrs: [],
|
||||||
},
|
},
|
||||||
pathToNode,
|
pathToNode,
|
||||||
}
|
}
|
||||||
@ -1992,11 +2300,12 @@ export const sketchLineHelperMap: { [key: string]: SketchLineHelper } = {
|
|||||||
export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
|
export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
|
||||||
line,
|
line,
|
||||||
lineTo,
|
lineTo,
|
||||||
|
circleThreePoint,
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
export function changeSketchArguments(
|
export function changeSketchArguments(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
programMemory: ProgramMemory,
|
variables: VariableMap,
|
||||||
sourceRangeOrPath:
|
sourceRangeOrPath:
|
||||||
| {
|
| {
|
||||||
type: 'sourceRange'
|
type: 'sourceRange'
|
||||||
@ -2030,7 +2339,7 @@ export function changeSketchArguments(
|
|||||||
|
|
||||||
return updateArgs({
|
return updateArgs({
|
||||||
node: _node,
|
node: _node,
|
||||||
previousProgramMemory: programMemory,
|
variables,
|
||||||
pathToNode: shallowPath,
|
pathToNode: shallowPath,
|
||||||
input,
|
input,
|
||||||
})
|
})
|
||||||
@ -2047,7 +2356,7 @@ export function changeSketchArguments(
|
|||||||
|
|
||||||
return updateArgs({
|
return updateArgs({
|
||||||
node: _node,
|
node: _node,
|
||||||
previousProgramMemory: programMemory,
|
variables,
|
||||||
pathToNode: shallowPath,
|
pathToNode: shallowPath,
|
||||||
input,
|
input,
|
||||||
})
|
})
|
||||||
@ -2059,30 +2368,36 @@ export function changeSketchArguments(
|
|||||||
export function getConstraintInfo(
|
export function getConstraintInfo(
|
||||||
callExpression: Node<CallExpression>,
|
callExpression: Node<CallExpression>,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode,
|
||||||
|
filterValue?: string
|
||||||
): ConstrainInfo[] {
|
): ConstrainInfo[] {
|
||||||
const fnName = callExpression?.callee?.name || ''
|
const fnName = callExpression?.callee?.name || ''
|
||||||
if (!(fnName in sketchLineHelperMap)) return []
|
if (!(fnName in sketchLineHelperMap)) return []
|
||||||
return sketchLineHelperMap[fnName].getConstraintInfo(
|
return sketchLineHelperMap[fnName].getConstraintInfo(
|
||||||
callExpression,
|
callExpression,
|
||||||
code,
|
code,
|
||||||
pathToNode
|
pathToNode,
|
||||||
|
filterValue
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getConstraintInfoKw(
|
export function getConstraintInfoKw(
|
||||||
callExpression: Node<CallExpressionKw>,
|
callExpression: Node<CallExpressionKw>,
|
||||||
code: string,
|
code: string,
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode,
|
||||||
|
filterValue?: string
|
||||||
): ConstrainInfo[] {
|
): ConstrainInfo[] {
|
||||||
const fnName = callExpression?.callee?.name || ''
|
const fnName = callExpression?.callee?.name || ''
|
||||||
const isAbsolute = findKwArg('endAbsolute', callExpression) !== undefined
|
const isAbsolute =
|
||||||
|
fnName === 'circleThreePoint' ||
|
||||||
|
findKwArg('endAbsolute', callExpression) !== undefined
|
||||||
if (!(fnName in sketchLineHelperMapKw)) return []
|
if (!(fnName in sketchLineHelperMapKw)) return []
|
||||||
const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
|
const correctFnName = fnName === 'line' && isAbsolute ? 'lineTo' : fnName
|
||||||
return sketchLineHelperMapKw[correctFnName].getConstraintInfo(
|
return sketchLineHelperMapKw[correctFnName].getConstraintInfo(
|
||||||
callExpression,
|
callExpression,
|
||||||
code,
|
code,
|
||||||
pathToNode
|
pathToNode,
|
||||||
|
filterValue
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2112,7 +2427,7 @@ export function compareVec2Epsilon2(
|
|||||||
|
|
||||||
interface CreateLineFnCallArgs {
|
interface CreateLineFnCallArgs {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
variables: VariableMap
|
||||||
input: SegmentInputs
|
input: SegmentInputs
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
@ -2121,7 +2436,7 @@ interface CreateLineFnCallArgs {
|
|||||||
|
|
||||||
export function addNewSketchLn({
|
export function addNewSketchLn({
|
||||||
node: _node,
|
node: _node,
|
||||||
programMemory: previousProgramMemory,
|
variables,
|
||||||
fnName,
|
fnName,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
input: segmentInput,
|
input: segmentInput,
|
||||||
@ -2151,7 +2466,7 @@ export function addNewSketchLn({
|
|||||||
)
|
)
|
||||||
return add({
|
return add({
|
||||||
node,
|
node,
|
||||||
previousProgramMemory,
|
variables,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
spliceBetween,
|
spliceBetween,
|
||||||
@ -2164,7 +2479,7 @@ export function addCallExpressionsToPipe({
|
|||||||
expressions,
|
expressions,
|
||||||
}: {
|
}: {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
variables: VariableMap
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
expressions: Node<CallExpression | CallExpressionKw>[]
|
expressions: Node<CallExpression | CallExpressionKw>[]
|
||||||
}) {
|
}) {
|
||||||
@ -2188,7 +2503,7 @@ export function addCloseToPipe({
|
|||||||
pathToNode,
|
pathToNode,
|
||||||
}: {
|
}: {
|
||||||
node: Program
|
node: Program
|
||||||
programMemory: ProgramMemory
|
variables: VariableMap
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
}) {
|
}) {
|
||||||
const _node = { ...node }
|
const _node = { ...node }
|
||||||
@ -2209,7 +2524,7 @@ export function addCloseToPipe({
|
|||||||
|
|
||||||
export function replaceSketchLine({
|
export function replaceSketchLine({
|
||||||
node,
|
node,
|
||||||
programMemory,
|
variables,
|
||||||
pathToNode: _pathToNode,
|
pathToNode: _pathToNode,
|
||||||
fnName,
|
fnName,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
@ -2217,7 +2532,7 @@ export function replaceSketchLine({
|
|||||||
referencedSegment,
|
referencedSegment,
|
||||||
}: {
|
}: {
|
||||||
node: Node<Program>
|
node: Node<Program>
|
||||||
programMemory: ProgramMemory
|
variables: VariableMap
|
||||||
pathToNode: PathToNode
|
pathToNode: PathToNode
|
||||||
fnName: ToolTip
|
fnName: ToolTip
|
||||||
segmentInput: SegmentInputs
|
segmentInput: SegmentInputs
|
||||||
@ -2241,7 +2556,7 @@ export function replaceSketchLine({
|
|||||||
: sketchLineHelperMap[fnName]
|
: sketchLineHelperMap[fnName]
|
||||||
const addRetVal = add({
|
const addRetVal = add({
|
||||||
node: _node,
|
node: _node,
|
||||||
previousProgramMemory: programMemory,
|
variables,
|
||||||
pathToNode: _pathToNode,
|
pathToNode: _pathToNode,
|
||||||
referencedSegment,
|
referencedSegment,
|
||||||
segmentInput,
|
segmentInput,
|
||||||
@ -2306,8 +2621,6 @@ function addTagToChamfer(
|
|||||||
if (err(variableDec)) return variableDec
|
if (err(variableDec)) return variableDec
|
||||||
const isPipeExpression = pipeExpr.node.type === 'PipeExpression'
|
const isPipeExpression = pipeExpr.node.type === 'PipeExpression'
|
||||||
|
|
||||||
console.log('pipeExpr', pipeExpr, variableDec)
|
|
||||||
// const callExpr = isPipeExpression ? pipeExpr.node.body[pipeIndex] : variableDec.node.init
|
|
||||||
const callExpr = isPipeExpression
|
const callExpr = isPipeExpression
|
||||||
? pipeExpr.node.body[pipeIndex]
|
? pipeExpr.node.body[pipeIndex]
|
||||||
: variableDec.node.init
|
: variableDec.node.init
|
||||||
@ -2388,7 +2701,6 @@ function addTagToChamfer(
|
|||||||
if (isPipeExpression) {
|
if (isPipeExpression) {
|
||||||
pipeExpr.node.body.splice(pipeIndex, 0, newExpressionToInsert)
|
pipeExpr.node.body.splice(pipeIndex, 0, newExpressionToInsert)
|
||||||
} else {
|
} else {
|
||||||
console.log('yo', createPipeExpression([newExpressionToInsert, callExpr]))
|
|
||||||
callExpr.arguments[1] = createPipeSubstitution()
|
callExpr.arguments[1] = createPipeSubstitution()
|
||||||
variableDec.node.init = createPipeExpression([
|
variableDec.node.init = createPipeExpression([
|
||||||
newExpressionToInsert,
|
newExpressionToInsert,
|
||||||
@ -2536,7 +2848,7 @@ function addTagKw(): addTagFn {
|
|||||||
start: callExpr.node.start,
|
start: callExpr.node.start,
|
||||||
end: callExpr.node.end,
|
end: callExpr.node.end,
|
||||||
moduleId: callExpr.node.moduleId,
|
moduleId: callExpr.node.moduleId,
|
||||||
trivia: callExpr.node.trivia,
|
outerAttrs: callExpr.node.outerAttrs,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2727,6 +3039,8 @@ export function isAbsoluteLine(lineCall: CallExpressionKw): boolean | Error {
|
|||||||
return new Error(
|
return new Error(
|
||||||
`line call has neither ${ARG_END} nor ${ARG_END_ABSOLUTE} params`
|
`line call has neither ${ARG_END} nor ${ARG_END_ABSOLUTE} params`
|
||||||
)
|
)
|
||||||
|
case 'circleThreePoint':
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return new Error(`Unknown sketch function ${name}`)
|
return new Error(`Unknown sketch function ${name}`)
|
||||||
}
|
}
|
||||||
|