@ -10,11 +10,11 @@ This will work on any solid, including extruded solids, revolved solids, and she
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
appearance(
|
appearance(
|
||||||
solidSet: SolidSet,
|
solids: [Solid],
|
||||||
color: String,
|
color: String,
|
||||||
metalness?: number,
|
metalness?: number,
|
||||||
roughness?: number,
|
roughness?: number,
|
||||||
): SolidSet
|
): [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -22,14 +22,14 @@ appearance(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) whose appearance is being set | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | The solid(s) whose appearance is being set | Yes |
|
||||||
| `color` | `String` | Color of the new material, a hex string like '#ff0000' | Yes |
|
| `color` | `String` | Color of the new material, a hex string like '#ff0000' | Yes |
|
||||||
| `metalness` | [`number`](/docs/kcl/types/number) | Metalness of the new material, a percentage like 95.7. | No |
|
| `metalness` | [`number`](/docs/kcl/types/number) | Metalness of the new material, a percentage like 95.7. | No |
|
||||||
| `roughness` | [`number`](/docs/kcl/types/number) | Roughness of the new material, a percentage like 95.7. | No |
|
| `roughness` | [`number`](/docs/kcl/types/number) | Roughness of the new material, a percentage like 95.7. | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
[`[Solid]`](/docs/kcl/types/Solid)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@ -10,9 +10,9 @@ You can provide more than one sketch to extrude, and they will all be extruded i
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
extrude(
|
extrude(
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
length: number,
|
length: number,
|
||||||
): SolidSet
|
): [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -20,12 +20,12 @@ extrude(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch or set of sketches should be extruded | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketch or sketches should be extruded | Yes |
|
||||||
| `length` | [`number`](/docs/kcl/types/number) | How far to extrude the given sketches | Yes |
|
| `length` | [`number`](/docs/kcl/types/number) | How far to extrude the given sketches | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
[`[Solid]`](/docs/kcl/types/Solid)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Use a 2-dimensional sketch to cut a hole in another 2-dimensional sketch.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
hole(
|
hole(
|
||||||
holeSketch: SketchSet,
|
holeSketch: [Sketch],
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
): Sketch
|
): Sketch
|
||||||
```
|
```
|
||||||
@ -20,7 +20,7 @@ hole(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `holeSketch` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
| `holeSketch` | [`[Sketch]`](/docs/kcl/types/Sketch) | | Yes |
|
||||||
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
| `sketch` | [`Sketch`](/docs/kcl/types/Sketch) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|||||||
@ -13,7 +13,7 @@ Mirror occurs around a local sketch axis rather than a global axis.
|
|||||||
```js
|
```js
|
||||||
mirror2d(
|
mirror2d(
|
||||||
data: Mirror2dData,
|
data: Mirror2dData,
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
): [Sketch]
|
): [Sketch]
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ mirror2d(
|
|||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `data` | [`Mirror2dData`](/docs/kcl/types/Mirror2dData) | Data for a mirror. | Yes |
|
| `data` | [`Mirror2dData`](/docs/kcl/types/Mirror2dData) | Data for a mirror. | Yes |
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Repeat a 2-dimensional sketch some number of times along a partial or complete c
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternCircular2d(
|
patternCircular2d(
|
||||||
sketchSet: SketchSet,
|
sketchSet: [Sketch],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
center: [number],
|
center: [number],
|
||||||
arcDegrees: number,
|
arcDegrees: number,
|
||||||
@ -24,7 +24,7 @@ patternCircular2d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | Which sketch(es) to pattern | Yes |
|
| `sketchSet` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketch(es) to pattern | 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 |
|
| `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]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 2D vector. | Yes |
|
| `center` | [`[number]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 2D vector. | Yes |
|
||||||
| `arcDegrees` | [`number`](/docs/kcl/types/number) | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
| `arcDegrees` | [`number`](/docs/kcl/types/number) | The arc angle (in degrees) to place the repetitions. Must be greater than 0. | Yes |
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Repeat a 3-dimensional solid some number of times along a partial or complete ci
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternCircular3d(
|
patternCircular3d(
|
||||||
solidSet: SolidSet,
|
solids: [Solid],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
axis: [number],
|
axis: [number],
|
||||||
center: [number],
|
center: [number],
|
||||||
@ -25,7 +25,7 @@ patternCircular3d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid(s) to pattern | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | Which solid(s) to pattern | 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 |
|
| `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 |
|
||||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis around which to make the pattern. This is a 3D vector | Yes |
|
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis around which to make the pattern. This is a 3D vector | Yes |
|
||||||
| `center` | [`[number]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 3D vector. | Yes |
|
| `center` | [`[number]`](/docs/kcl/types/number) | The center about which to make the pattern. This is a 3D vector. | Yes |
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Repeat a 2-dimensional sketch along some dimension, with a dynamic amount of dis
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternLinear2d(
|
patternLinear2d(
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
distance: number,
|
distance: number,
|
||||||
axis: [number],
|
axis: [number],
|
||||||
@ -23,7 +23,7 @@ patternLinear2d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | The sketch(es) to duplicate | 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 |
|
| `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 |
|
||||||
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Repeat a 3-dimensional solid along a linear path, with a dynamic amount of dista
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternLinear3d(
|
patternLinear3d(
|
||||||
solidSet: SolidSet,
|
solids: [Solid],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
distance: number,
|
distance: number,
|
||||||
axis: [number],
|
axis: [number],
|
||||||
@ -23,7 +23,7 @@ patternLinear3d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | The solid(s) to duplicate | 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 |
|
| `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 |
|
||||||
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
| `distance` | [`number`](/docs/kcl/types/number) | Distance between each repetition. Also known as 'spacing'. | Yes |
|
||||||
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
| `axis` | [`[number]`](/docs/kcl/types/number) | The axis of the pattern. A 2D vector. | Yes |
|
||||||
|
|||||||
@ -36,7 +36,7 @@ The transform function returns a transform object. All properties of the object
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternTransform(
|
patternTransform(
|
||||||
solidSet: SolidSet,
|
solids: [Solid],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
transform: FunctionSource,
|
transform: FunctionSource,
|
||||||
useOriginal?: bool,
|
useOriginal?: bool,
|
||||||
@ -48,7 +48,7 @@ patternTransform(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | The solid(s) to duplicate | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | The solid(s) to duplicate | 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 |
|
| `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 |
|
||||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||||
| `useOriginal` | [`bool`](/docs/kcl/types/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 |
|
| `useOriginal` | [`bool`](/docs/kcl/types/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 |
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
patternTransform2d(
|
patternTransform2d(
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
instances: integer,
|
instances: integer,
|
||||||
transform: FunctionSource,
|
transform: FunctionSource,
|
||||||
useOriginal?: bool,
|
useOriginal?: bool,
|
||||||
@ -22,7 +22,7 @@ patternTransform2d(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch(es) to duplicate | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | The sketch(es) to duplicate | 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 |
|
| `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 |
|
||||||
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
| `transform` | `FunctionSource` | How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples. | Yes |
|
||||||
| `useOriginal` | [`bool`](/docs/kcl/types/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 |
|
| `useOriginal` | [`bool`](/docs/kcl/types/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 |
|
||||||
|
|||||||
@ -15,8 +15,8 @@ You can provide more than one sketch to revolve, and they will all be revolved a
|
|||||||
```js
|
```js
|
||||||
revolve(
|
revolve(
|
||||||
data: RevolveData,
|
data: RevolveData,
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
): SolidSet
|
): [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -25,11 +25,11 @@ revolve(
|
|||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `data` | [`RevolveData`](/docs/kcl/types/RevolveData) | Data for revolution surfaces. | Yes |
|
| `data` | [`RevolveData`](/docs/kcl/types/RevolveData) | Data for revolution surfaces. | Yes |
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
[`[Solid]`](/docs/kcl/types/Solid)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@ -24,7 +24,7 @@ When rotating a part around an axis, you specify the axis of rotation and the an
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
rotate(
|
rotate(
|
||||||
solidSet: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
roll?: number,
|
roll?: number,
|
||||||
pitch?: number,
|
pitch?: number,
|
||||||
yaw?: number,
|
yaw?: number,
|
||||||
@ -39,7 +39,7 @@ rotate(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to rotate. | Yes |
|
| `solids` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to rotate. | Yes |
|
||||||
| `roll` | [`number`](/docs/kcl/types/number) | The roll angle in degrees. Must be used with `pitch` and `yaw`. Must be between -360 and 360. | No |
|
| `roll` | [`number`](/docs/kcl/types/number) | The roll angle in degrees. Must be used with `pitch` and `yaw`. Must be between -360 and 360. | No |
|
||||||
| `pitch` | [`number`](/docs/kcl/types/number) | The pitch angle in degrees. Must be used with `roll` and `yaw`. Must be between -360 and 360. | No |
|
| `pitch` | [`number`](/docs/kcl/types/number) | The pitch angle in degrees. Must be used with `roll` and `yaw`. Must be between -360 and 360. | No |
|
||||||
| `yaw` | [`number`](/docs/kcl/types/number) | The yaw angle in degrees. Must be used with `roll` and `pitch`. Must be between -360 and 360. | No |
|
| `yaw` | [`number`](/docs/kcl/types/number) | The yaw angle in degrees. Must be used with `roll` and `pitch`. Must be between -360 and 360. | No |
|
||||||
|
|||||||
@ -12,7 +12,7 @@ If you want to apply the transform in global space, set `global` to `true`. The
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
scale(
|
scale(
|
||||||
solidSet: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
scale: [number],
|
scale: [number],
|
||||||
global?: bool,
|
global?: bool,
|
||||||
): SolidOrImportedGeometry
|
): SolidOrImportedGeometry
|
||||||
@ -23,7 +23,7 @@ scale(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to scale. | Yes |
|
| `solids` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to scale. | Yes |
|
||||||
| `scale` | [`[number]`](/docs/kcl/types/number) | The scale factor for the x, y, and z axes. | Yes |
|
| `scale` | [`[number]`](/docs/kcl/types/number) | The scale factor for the x, y, and z axes. | Yes |
|
||||||
| `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No |
|
| `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No |
|
||||||
|
|
||||||
|
|||||||
@ -10,10 +10,10 @@ Remove volume from a 3-dimensional shape such that a wall of the provided thickn
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
shell(
|
shell(
|
||||||
solidSet: SolidSet,
|
solids: [Solid],
|
||||||
thickness: number,
|
thickness: number,
|
||||||
faces: [FaceTag],
|
faces: [FaceTag],
|
||||||
): SolidSet
|
): [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -21,13 +21,13 @@ shell(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidSet`](/docs/kcl/types/SolidSet) | Which solid (or solids) to shell out | Yes |
|
| `solids` | [`[Solid]`](/docs/kcl/types/Solid) | Which solid (or solids) to shell out | Yes |
|
||||||
| `thickness` | [`number`](/docs/kcl/types/number) | The thickness of the shell | Yes |
|
| `thickness` | [`number`](/docs/kcl/types/number) | The thickness of the shell | Yes |
|
||||||
| `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes |
|
| `faces` | [`[FaceTag]`](/docs/kcl/types/FaceTag) | The faces you want removed | Yes |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
[`[Solid]`](/docs/kcl/types/Solid)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
63634
docs/kcl/std.json
63634
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
@ -12,11 +12,11 @@ You can provide more than one sketch to sweep, and they will all be swept along
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
sweep(
|
sweep(
|
||||||
sketchSet: SketchSet,
|
sketches: [Sketch],
|
||||||
path: SweepPath,
|
path: SweepPath,
|
||||||
sectional?: bool,
|
sectional?: bool,
|
||||||
tolerance?: number,
|
tolerance?: number,
|
||||||
): SolidSet
|
): [Solid]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
@ -24,14 +24,14 @@ sweep(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `sketchSet` | [`SketchSet`](/docs/kcl/types/SketchSet) | The sketch or set of sketches that should be swept in space | Yes |
|
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | The sketch or set of sketches that should be swept in space | Yes |
|
||||||
| `path` | [`SweepPath`](/docs/kcl/types/SweepPath) | The path to sweep the sketch along | Yes |
|
| `path` | [`SweepPath`](/docs/kcl/types/SweepPath) | The path to sweep the sketch along | Yes |
|
||||||
| `sectional` | [`bool`](/docs/kcl/types/bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
| `sectional` | [`bool`](/docs/kcl/types/bool) | If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
| `tolerance` | [`number`](/docs/kcl/types/number) | Tolerance for this operation | No |
|
| `tolerance` | [`number`](/docs/kcl/types/number) | Tolerance for this operation | No |
|
||||||
|
|
||||||
### Returns
|
### Returns
|
||||||
|
|
||||||
[`SolidSet`](/docs/kcl/types/SolidSet) - A solid or a group of solids.
|
[`[Solid]`](/docs/kcl/types/Solid)
|
||||||
|
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Move a solid.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
translate(
|
translate(
|
||||||
solidSet: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
translate: [number],
|
translate: [number],
|
||||||
global?: bool,
|
global?: bool,
|
||||||
): SolidOrImportedGeometry
|
): SolidOrImportedGeometry
|
||||||
@ -21,7 +21,7 @@ translate(
|
|||||||
|
|
||||||
| Name | Type | Description | Required |
|
| Name | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `solidSet` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to move. | Yes |
|
| `solids` | [`SolidOrImportedGeometry`](/docs/kcl/types/SolidOrImportedGeometry) | The solid or set of solids to move. | Yes |
|
||||||
| `translate` | [`[number]`](/docs/kcl/types/number) | The amount to move the solid in all three axes. | Yes |
|
| `translate` | [`[number]`](/docs/kcl/types/number) | The amount to move the solid in all three axes. | Yes |
|
||||||
| `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No |
|
| `global` | [`bool`](/docs/kcl/types/bool) | If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move. | No |
|
||||||
|
|
||||||
|
|||||||
@ -100,6 +100,22 @@ Any KCL value.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `HomArray`| | No |
|
||||||
|
| `value` |`[` [`KclValue`](/docs/kcl/types/KclValue) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
@ -122,7 +138,6 @@ Any KCL value.
|
|||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `type` |enum: [`TagIdentifier`](/docs/kcl/types#tag-identifier)| | No |
|
| `type` |enum: [`TagIdentifier`](/docs/kcl/types#tag-identifier)| | No |
|
||||||
| `value` |[`string`](/docs/kcl/types/string)| | No |
|
| `value` |[`string`](/docs/kcl/types/string)| | No |
|
||||||
| `info` |[`TagEngineInfo`](/docs/kcl/types/TagEngineInfo)| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
@ -200,22 +215,6 @@ Any KCL value.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Sketches`| | No |
|
|
||||||
| `value` |`[` [`Sketch`](/docs/kcl/types/Sketch) `]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
@ -232,22 +231,6 @@ Any KCL value.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Solids`| | No |
|
|
||||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
@ -338,22 +321,6 @@ Data for an imported geometry.
|
|||||||
|
|
||||||
----
|
----
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `Tombstone`| | No |
|
|
||||||
| `value` |`null`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,56 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SketchSet"
|
|
||||||
excerpt: "A sketch or a group of sketches."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A sketch or a group of sketches.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `sketch`| | No |
|
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the sketch (this will change when the engine's reference to it changes). | No |
|
|
||||||
| `paths` |`[` [`Path`](/docs/kcl/types/Path) `]`| The paths in the sketch. | No |
|
|
||||||
| `on` |[`SketchSurface`](/docs/kcl/types/SketchSurface)| What the sketch is on (can be a plane or a face). | No |
|
|
||||||
| `start` |[`BasePath`](/docs/kcl/types/BasePath)| The starting path. | No |
|
|
||||||
| `tags` |`object`| Tag identifiers that have been declared in this sketch. | No |
|
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The original id of the sketch. This stays the same even if the sketch is is sketched on face etc. | No |
|
|
||||||
| `originalId` |[`string`](/docs/kcl/types/string)| | No |
|
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `[object, array]`
|
|
||||||
|
|
||||||
`[` [`Sketch`](/docs/kcl/types/Sketch) `]`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `sketches`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -12,30 +12,6 @@ Data for a solid or an imported geometry.
|
|||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `solid`| | No |
|
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the solid. | No |
|
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
|
||||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
|
||||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
|
||||||
| `height` |[`number`](/docs/kcl/types/number)| The height of the solid. | No |
|
|
||||||
| `startCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion start cap | No |
|
|
||||||
| `endCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion end cap | No |
|
|
||||||
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
|
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
Data for an imported geometry.
|
Data for an imported geometry.
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** `object`
|
||||||
|
|||||||
@ -1,57 +0,0 @@
|
|||||||
---
|
|
||||||
title: "SolidSet"
|
|
||||||
excerpt: "A solid or a group of solids."
|
|
||||||
layout: manual
|
|
||||||
---
|
|
||||||
|
|
||||||
A solid or a group of solids.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts exactly one of the following:**
|
|
||||||
|
|
||||||
|
|
||||||
**Type:** `object`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `solid`| | No |
|
|
||||||
| `id` |[`string`](/docs/kcl/types/string)| The id of the solid. | No |
|
|
||||||
| `artifactId` |[`ArtifactId`](/docs/kcl/types/ArtifactId)| The artifact ID of the solid. Unlike `id`, this doesn't change. | No |
|
|
||||||
| `value` |`[` [`ExtrudeSurface`](/docs/kcl/types/ExtrudeSurface) `]`| The extrude surfaces. | No |
|
|
||||||
| `sketch` |[`Sketch`](/docs/kcl/types/Sketch)| The sketch. | No |
|
|
||||||
| `height` |[`number`](/docs/kcl/types/number)| The height of the solid. | No |
|
|
||||||
| `startCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion start cap | No |
|
|
||||||
| `endCapId` |[`string`](/docs/kcl/types/string)| The id of the extrusion end cap | No |
|
|
||||||
| `edgeCuts` |`[` [`EdgeCut`](/docs/kcl/types/EdgeCut) `]`| Chamfers or fillets on this solid. | No |
|
|
||||||
| `units` |[`UnitLen`](/docs/kcl/types/UnitLen)| A unit of length. | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
**Type:** `[object, array]`
|
|
||||||
|
|
||||||
`[` [`Solid`](/docs/kcl/types/Solid) `]`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
|
||||||
|----------|------|-------------|----------|
|
|
||||||
| `type` |enum: `solids`| | No |
|
|
||||||
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1024,7 +1024,7 @@ openSketch = startSketchOn('XY')
|
|||||||
await page.waitForTimeout(15000)
|
await page.waitForTimeout(15000)
|
||||||
|
|
||||||
await test.step(`Look for the blue of the XZ plane`, async () => {
|
await test.step(`Look for the blue of the XZ plane`, async () => {
|
||||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
||||||
})
|
})
|
||||||
await test.step(`Go through the command bar flow`, async () => {
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
await toolbar.offsetPlaneButton.click()
|
await toolbar.offsetPlaneButton.click()
|
||||||
@ -1066,7 +1066,7 @@ openSketch = startSketchOn('XY')
|
|||||||
)
|
)
|
||||||
await operationButton.click({ button: 'left' })
|
await operationButton.click({ button: 'left' })
|
||||||
await page.keyboard.press('Delete')
|
await page.keyboard.press('Delete')
|
||||||
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
//await scene.expectPixelColor([50, 51, 96], testPoint, 15) // FIXME
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -2321,8 +2321,8 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
|||||||
cmdBar,
|
cmdBar,
|
||||||
}) => {
|
}) => {
|
||||||
const initialCode = `sketch001 = startSketchOn('XZ')
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> circle(center = [0, 0], radius = 30)
|
|> circle(center = [0, 0], radius = 30)
|
||||||
extrude001 = extrude(sketch001, length = 30)
|
extrude001 = extrude(sketch001, length = 30)
|
||||||
`
|
`
|
||||||
await context.addInitScript((initialCode) => {
|
await context.addInitScript((initialCode) => {
|
||||||
localStorage.setItem('persistCode', initialCode)
|
localStorage.setItem('persistCode', initialCode)
|
||||||
@ -2336,6 +2336,8 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
|||||||
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell(extrude001, faces = ['end'], thickness = 5)"
|
"shell001 = shell(extrude001, faces = ['end'], thickness = 5)"
|
||||||
|
const editedShellDeclaration =
|
||||||
|
"shell001 = shell(extrude001, faces = ['end'], thickness = 2)"
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
await scene.expectPixelColor([127, 127, 127], testPoint, 15)
|
||||||
@ -2402,6 +2404,45 @@ chamfer04 = chamfer(extrude001, length = 5, tags = [getOppositeEdge(seg02)])
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
await scene.expectPixelColor([146, 146, 146], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Edit shell via feature tree selection works', async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Shell',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.dblclick()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'thickness',
|
||||||
|
currentArgValue: '5',
|
||||||
|
headerArguments: {
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'thickness',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('2')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Thickness: '2',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await scene.expectPixelColor([150, 150, 150], testPoint, 15)
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(editedShellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [editedShellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -2437,6 +2478,8 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
const mutatedCode = 'xLine(length = -40, tag = $seg01)'
|
const mutatedCode = 'xLine(length = -40, tag = $seg01)'
|
||||||
const shellDeclaration =
|
const shellDeclaration =
|
||||||
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 5)"
|
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 5)"
|
||||||
|
const editedShellDeclaration =
|
||||||
|
"shell001 = shell(extrude001, faces = ['end', seg01], thickness = 1)"
|
||||||
|
|
||||||
await test.step(`Look for the grey of the shape`, async () => {
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
@ -2485,6 +2528,41 @@ extrude001 = extrude(sketch001, length = 40)
|
|||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Edit shell via feature tree selection works', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||||
|
await operationButton.dblclick({ button: 'left' })
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'thickness',
|
||||||
|
currentArgValue: '5',
|
||||||
|
headerArguments: {
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'thickness',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await page.keyboard.insertText('1')
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Thickness: '1',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await scene.expectPixelColor([150, 150, 150], testPoint, 15)
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(editedShellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [editedShellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
await test.step('Delete shell via feature tree selection', async () => {
|
await test.step('Delete shell via feature tree selection', async () => {
|
||||||
await editor.closePane()
|
await editor.closePane()
|
||||||
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||||
@ -2579,7 +2657,7 @@ extrude002 = extrude(sketch002, length = 50)
|
|||||||
highlightedCode: '',
|
highlightedCode: '',
|
||||||
})
|
})
|
||||||
await toolbar.closePane('code')
|
await toolbar.closePane('code')
|
||||||
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
|
await scene.expectPixelColor([80, 80, 80], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1174,7 +1174,7 @@ profile001 = startProfileAt([${roundOff(scale * 69.6)}, ${roundOff(
|
|||||||
|> line(endAbsolute = [
|
|> line(endAbsolute = [
|
||||||
railWideWidth / 2,
|
railWideWidth / 2,
|
||||||
railClampable / 2 + railBaseLength
|
railClampable / 2 + railBaseLength
|
||||||
], $seg01)
|
], tag = $seg01)
|
||||||
|> line(endAbsolute = [railTop / 2, railBaseLength])
|
|> line(endAbsolute = [railTop / 2, railBaseLength])
|
||||||
|> line(endAbsolute = [railBaseWidth / 2, railBaseLength])
|
|> line(endAbsolute = [railBaseWidth / 2, railBaseLength])
|
||||||
|> line(endAbsolute = [railBaseWidth / 2, 0])
|
|> line(endAbsolute = [railBaseWidth / 2, 0])
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 74 KiB |
File diff suppressed because one or more lines are too long
@ -3073,7 +3073,7 @@ DATA;
|
|||||||
#3057 = CARTESIAN_POINT('NONE', (0.051104890518972546, -0.039940414856583686, -0.0635));
|
#3057 = CARTESIAN_POINT('NONE', (0.051104890518972546, -0.039940414856583686, -0.0635));
|
||||||
#3058 = CARTESIAN_POINT('NONE', (0.052242074077479335, -0.038876903045998674, -0.0635));
|
#3058 = CARTESIAN_POINT('NONE', (0.052242074077479335, -0.038876903045998674, -0.0635));
|
||||||
#3059 = CARTESIAN_POINT('NONE', (0.05224392753122875, -0.03887516966712757, -0.0635));
|
#3059 = CARTESIAN_POINT('NONE', (0.05224392753122875, -0.03887516966712757, -0.0635));
|
||||||
#3060 = CARTESIAN_POINT('NONE', (0.05311532463588208, -0.03767579444673182, -0.0635));
|
#3060 = CARTESIAN_POINT('NONE', (0.05311532463588208, -0.03767579444673181, -0.0635));
|
||||||
#3061 = CARTESIAN_POINT('NONE', (0.05311674489404425, -0.03767383962907501, -0.0635));
|
#3061 = CARTESIAN_POINT('NONE', (0.05311674489404425, -0.03767383962907501, -0.0635));
|
||||||
#3062 = CARTESIAN_POINT('NONE', (0.053776795686355607, -0.03626367057234418, -0.0635));
|
#3062 = CARTESIAN_POINT('NONE', (0.053776795686355607, -0.03626367057234418, -0.0635));
|
||||||
#3063 = CARTESIAN_POINT('NONE', (0.05377787147891932, -0.036261372189549286, -0.0635));
|
#3063 = CARTESIAN_POINT('NONE', (0.05377787147891932, -0.036261372189549286, -0.0635));
|
||||||
@ -3087,7 +3087,7 @@ DATA;
|
|||||||
#3071 = CARTESIAN_POINT('NONE', (0.053252818350252196, -0.029748655756475863, -0.0635));
|
#3071 = CARTESIAN_POINT('NONE', (0.053252818350252196, -0.029748655756475863, -0.0635));
|
||||||
#3072 = CARTESIAN_POINT('NONE', (0.05233460363130192, -0.028414043632913145, -0.0635));
|
#3072 = CARTESIAN_POINT('NONE', (0.05233460363130192, -0.028414043632913145, -0.0635));
|
||||||
#3073 = CARTESIAN_POINT('NONE', (0.05233310706682834, -0.028411868397590818, -0.0635));
|
#3073 = CARTESIAN_POINT('NONE', (0.05233310706682834, -0.028411868397590818, -0.0635));
|
||||||
#3074 = CARTESIAN_POINT('NONE', (0.051232952266167, -0.02734405921816657, -0.0635));
|
#3074 = CARTESIAN_POINT('NONE', (0.05123295226616701, -0.02734405921816657, -0.0635));
|
||||||
#3075 = CARTESIAN_POINT('NONE', (0.05123115916423111, -0.027342318835171704, -0.0635));
|
#3075 = CARTESIAN_POINT('NONE', (0.05123115916423111, -0.027342318835171704, -0.0635));
|
||||||
#3076 = CARTESIAN_POINT('NONE', (0.0499865731843106, -0.02652506813979786, -0.0635));
|
#3076 = CARTESIAN_POINT('NONE', (0.0499865731843106, -0.02652506813979786, -0.0635));
|
||||||
#3077 = CARTESIAN_POINT('NONE', (0.049984544679296, -0.026523736132881105, -0.0635));
|
#3077 = CARTESIAN_POINT('NONE', (0.049984544679296, -0.026523736132881105, -0.0635));
|
||||||
@ -3105,7 +3105,7 @@ DATA;
|
|||||||
#3089 = CARTESIAN_POINT('NONE', (0.0407616757108459, -0.02775624333996861, -0.0635));
|
#3089 = CARTESIAN_POINT('NONE', (0.0407616757108459, -0.02775624333996861, -0.0635));
|
||||||
#3090 = CARTESIAN_POINT('NONE', (0.03976400232776854, -0.0288872140372878, -0.0635));
|
#3090 = CARTESIAN_POINT('NONE', (0.03976400232776854, -0.0288872140372878, -0.0635));
|
||||||
#3091 = CARTESIAN_POINT('NONE', (0.03976237625653429, -0.028889057364922765, -0.0635));
|
#3091 = CARTESIAN_POINT('NONE', (0.03976237625653429, -0.028889057364922765, -0.0635));
|
||||||
#3092 = B_SPLINE_CURVE_WITH_KNOTS('NONE', 2, (#3029, #3030, #3031, #3032, #3033, #3034, #3035, #3036, #3037, #3038, #3039, #3040, #3041, #3042, #3043, #3044, #3045, #3046, #3047, #3048, #3049, #3050, #3051, #3052, #3053, #3054, #3055, #3056, #3057, #3058, #3059, #3060, #3061, #3062, #3063, #3064, #3065, #3066, #3067, #3068, #3069, #3070, #3071, #3072, #3073, #3074, #3075, #3076, #3077, #3078, #3079, #3080, #3081, #3082, #3083, #3084, #3085, #3086, #3087, #3088, #3089, #3090, #3091), .UNSPECIFIED., .F., .F., (3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3), (0, 0.01639344262295082, 0.03278688524590164, 0.04918032786885246, 0.06557377049180328, 0.0819672131147541, 0.09836065573770492, 0.11475409836065574, 0.13114754098360656, 0.14754098360655737, 0.1639344262295082, 0.18032786885245902, 0.19672131147540983, 0.21311475409836067, 0.22950819672131148, 0.24590163934426232, 0.26229508196721313, 0.27868852459016397, 0.29508196721311475, 0.3114754098360656, 0.3278688524590164, 0.3442622950819672, 0.36065573770491804, 0.3770491803278689, 0.39344262295081966, 0.4098360655737705, 0.42622950819672134, 0.4426229508196722, 0.45901639344262296, 0.4754098360655738, 0.49180327868852464, 0.5081967213114753, 0.5245901639344261, 0.540983606557377, 0.5573770491803278, 0.5737704918032787, 0.5901639344262295, 0.6065573770491803, 0.6229508196721312, 0.639344262295082, 0.6557377049180328, 0.6721311475409836, 0.6885245901639344, 0.7049180327868853, 0.721311475409836, 0.7377049180327868, 0.7540983606557377, 0.7704918032786885, 0.7868852459016393, 0.8032786885245902, 0.819672131147541, 0.8360655737704918, 0.8524590163934427, 0.8688524590163934, 0.8852459016393442, 0.9016393442622951, 0.9180327868852459, 0.9344262295081968, 0.9508196721311475, 0.9672131147540983, 0.9836065573770492, 1), .UNSPECIFIED.);
|
#3092 = B_SPLINE_CURVE_WITH_KNOTS('NONE', 2, (#3029, #3030, #3031, #3032, #3033, #3034, #3035, #3036, #3037, #3038, #3039, #3040, #3041, #3042, #3043, #3044, #3045, #3046, #3047, #3048, #3049, #3050, #3051, #3052, #3053, #3054, #3055, #3056, #3057, #3058, #3059, #3060, #3061, #3062, #3063, #3064, #3065, #3066, #3067, #3068, #3069, #3070, #3071, #3072, #3073, #3074, #3075, #3076, #3077, #3078, #3079, #3080, #3081, #3082, #3083, #3084, #3085, #3086, #3087, #3088, #3089, #3090, #3091), .UNSPECIFIED., .F., .F., (3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3), (-1, -0.9836065573770492, -0.9672131147540983, -0.9508196721311475, -0.9344262295081968, -0.9180327868852459, -0.9016393442622951, -0.8852459016393442, -0.8688524590163934, -0.8524590163934427, -0.8360655737704918, -0.819672131147541, -0.8032786885245902, -0.7868852459016393, -0.7704918032786885, -0.7540983606557377, -0.7377049180327868, -0.721311475409836, -0.7049180327868853, -0.6885245901639344, -0.6721311475409836, -0.6557377049180328, -0.639344262295082, -0.6229508196721312, -0.6065573770491803, -0.5901639344262295, -0.5737704918032787, -0.5573770491803278, -0.540983606557377, -0.5245901639344261, -0.5081967213114753, -0.49180327868852464, -0.4754098360655738, -0.45901639344262296, -0.4426229508196722, -0.42622950819672134, -0.4098360655737705, -0.39344262295081966, -0.3770491803278689, -0.36065573770491804, -0.3442622950819672, -0.3278688524590164, -0.3114754098360656, -0.29508196721311475, -0.27868852459016397, -0.26229508196721313, -0.24590163934426232, -0.22950819672131148, -0.21311475409836067, -0.19672131147540983, -0.18032786885245902, -0.1639344262295082, -0.14754098360655737, -0.13114754098360656, -0.11475409836065574, -0.09836065573770492, -0.0819672131147541, -0.06557377049180328, -0.04918032786885246, -0.03278688524590164, -0.01639344262295082, -0), .UNSPECIFIED.);
|
||||||
#3093 = DIRECTION('NONE', (0, 0, 1));
|
#3093 = DIRECTION('NONE', (0, 0, 1));
|
||||||
#3094 = VECTOR('NONE', #3093, 1);
|
#3094 = VECTOR('NONE', #3093, 1);
|
||||||
#3095 = CARTESIAN_POINT('NONE', (0.03976237625653429, -0.028889057364922765, -0.063501));
|
#3095 = CARTESIAN_POINT('NONE', (0.03976237625653429, -0.028889057364922765, -0.063501));
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -6,7 +6,7 @@ uses-engine = { max-threads = 4 }
|
|||||||
after-engine = { max-threads = 12 }
|
after-engine = { max-threads = 12 }
|
||||||
|
|
||||||
[profile.default]
|
[profile.default]
|
||||||
slow-timeout = { period = "30s", terminate-after = 1 }
|
slow-timeout = { period = "90s", terminate-after = 1 }
|
||||||
|
|
||||||
[profile.ci]
|
[profile.ci]
|
||||||
slow-timeout = { period = "50s", terminate-after = 5 }
|
slow-timeout = { period = "50s", terminate-after = 5 }
|
||||||
|
|||||||
7
rust/Cargo.lock
generated
7
rust/Cargo.lock
generated
@ -3463,6 +3463,12 @@ dependencies = [
|
|||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha1_smol"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bbfa15b3dddfee50a0fff136974b3e1bde555604ba463834a7eb7deb6417705d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha2"
|
name = "sha2"
|
||||||
version = "0.10.8"
|
version = "0.10.8"
|
||||||
@ -4378,6 +4384,7 @@ dependencies = [
|
|||||||
"getrandom 0.3.1",
|
"getrandom 0.3.1",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"serde",
|
"serde",
|
||||||
|
"sha1_smol",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@ -802,7 +802,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = ctx.run(&program, &mut crate::execution::ExecState::new(&ctx.settings)).await {
|
if let Err(e) = ctx.run(&program, &mut crate::execution::ExecState::new(&ctx)).await {
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
error: e.error,
|
error: e.error,
|
||||||
filename: format!("{}{}", #fn_name, #index),
|
filename: format!("{}{}", #fn_name, #index),
|
||||||
|
|||||||
@ -15,10 +15,7 @@ mod test_examples_someFn {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -15,10 +15,7 @@ mod test_examples_someFn {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_show {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_show {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -17,10 +17,7 @@ mod test_examples_my_func {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -17,10 +17,7 @@ mod test_examples_line_to {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_min {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_show {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_import {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_import {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_import {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -16,10 +16,7 @@ mod test_examples_show {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -15,10 +15,7 @@ mod test_examples_some_function {
|
|||||||
context_type: crate::execution::ContextType::Mock,
|
context_type: crate::execution::ContextType::Mock,
|
||||||
};
|
};
|
||||||
if let Err(e) = ctx
|
if let Err(e) = ctx
|
||||||
.run(
|
.run(&program, &mut crate::execution::ExecState::new(&ctx))
|
||||||
&program,
|
|
||||||
&mut crate::execution::ExecState::new(&ctx.settings),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
return Err(miette::Report::new(crate::errors::Report {
|
return Err(miette::Report::new(crate::errors::Report {
|
||||||
|
|||||||
@ -80,7 +80,7 @@ ts-rs = { version = "10.1.0", features = [
|
|||||||
] }
|
] }
|
||||||
tynm = "0.1.10"
|
tynm = "0.1.10"
|
||||||
url = { version = "2.5.4", features = ["serde"] }
|
url = { version = "2.5.4", features = ["serde"] }
|
||||||
uuid = { workspace = true, features = ["v4", "js", "serde"] }
|
uuid = { workspace = true, features = ["v4", "v5", "js", "serde"] }
|
||||||
validator = { version = "0.20.0", features = ["derive"] }
|
validator = { version = "0.20.0", features = ["derive"] }
|
||||||
web-time = "1.1"
|
web-time = "1.1"
|
||||||
winnow = "=0.6.24"
|
winnow = "=0.6.24"
|
||||||
|
|||||||
@ -77,7 +77,7 @@ fn run_benchmarks(c: &mut Criterion) {
|
|||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
if let Err(err) = rt.block_on(async {
|
if let Err(err) = rt.block_on(async {
|
||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
||||||
let mut exec_state = kcl_lib::ExecState::new(&ctx.settings);
|
let mut exec_state = kcl_lib::ExecState::new(&ctx);
|
||||||
ctx.run(black_box(&program), &mut exec_state).await?;
|
ctx.run(black_box(&program), &mut exec_state).await?;
|
||||||
ctx.close().await;
|
ctx.close().await;
|
||||||
Ok::<(), anyhow::Error>(())
|
Ok::<(), anyhow::Error>(())
|
||||||
|
|||||||
@ -2053,7 +2053,7 @@ sketch000 = startSketchOn('XY')
|
|||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default())
|
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut exec_state = kcl_lib::ExecState::new(&ctx.settings);
|
let mut exec_state = kcl_lib::ExecState::new(&ctx);
|
||||||
let program = kcl_lib::Program::parse_no_errs(code).unwrap();
|
let program = kcl_lib::Program::parse_no_errs(code).unwrap();
|
||||||
ctx.run(&program, &mut exec_state).await.unwrap();
|
ctx.run(&program, &mut exec_state).await.unwrap();
|
||||||
|
|
||||||
@ -2078,7 +2078,7 @@ async fn kcl_test_ensure_nothing_left_in_batch_multi_file() {
|
|||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default())
|
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut exec_state = kcl_lib::ExecState::new(&ctx.settings);
|
let mut exec_state = kcl_lib::ExecState::new(&ctx);
|
||||||
let program = kcl_lib::Program::parse_no_errs(&code).unwrap();
|
let program = kcl_lib::Program::parse_no_errs(&code).unwrap();
|
||||||
ctx.run(&program, &mut exec_state).await.unwrap();
|
ctx.run(&program, &mut exec_state).await.unwrap();
|
||||||
|
|
||||||
@ -2106,7 +2106,7 @@ async fn kcl_test_better_type_names() {
|
|||||||
},
|
},
|
||||||
None => todo!(),
|
None => todo!(),
|
||||||
};
|
};
|
||||||
assert_eq!(err, "This function expected the input argument to be of type SolidSet but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
assert_eq!(err, "This function expected the input argument to be one or more Solids but it's actually of type Sketch. You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
|||||||
@ -10,9 +10,9 @@ use pretty_assertions::assert_eq;
|
|||||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, ModuleId, uuid::Uuid)> {
|
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, ModuleId, uuid::Uuid)> {
|
||||||
let program = Program::parse_no_errs(code)?;
|
let program = Program::parse_no_errs(code)?;
|
||||||
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
let ctx = kcl_lib::ExecutorContext::new_with_default_client(Default::default()).await?;
|
||||||
let mut exec_state = ExecState::new(&ctx.settings);
|
let mut exec_state = ExecState::new(&ctx);
|
||||||
let result = ctx.run(&program, &mut exec_state).await?;
|
let result = ctx.run(&program, &mut exec_state).await?;
|
||||||
let outcome = exec_state.to_wasm_outcome(result.0);
|
let outcome = exec_state.to_wasm_outcome(result.0).await;
|
||||||
|
|
||||||
// We need to get the sketch ID.
|
// We need to get the sketch ID.
|
||||||
let KclValue::Sketch { value: sketch } = outcome.variables.get(name).unwrap() else {
|
let KclValue::Sketch { value: sketch } = outcome.variables.get(name).unwrap() else {
|
||||||
|
|||||||
@ -1153,7 +1153,7 @@ fn find_examples(text: &str, filename: &str) -> Vec<(String, String)> {
|
|||||||
async fn run_example(text: &str) -> Result<()> {
|
async fn run_example(text: &str) -> Result<()> {
|
||||||
let program = crate::Program::parse_no_errs(text)?;
|
let program = crate::Program::parse_no_errs(text)?;
|
||||||
let ctx = ExecutorContext::new_with_default_client(crate::UnitLength::Mm).await?;
|
let ctx = ExecutorContext::new_with_default_client(crate::UnitLength::Mm).await?;
|
||||||
let mut exec_state = crate::execution::ExecState::new(&ctx.settings);
|
let mut exec_state = crate::execution::ExecState::new(&ctx);
|
||||||
ctx.run(&program, &mut exec_state).await?;
|
ctx.run(&program, &mut exec_state).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@ -128,9 +128,9 @@ impl StdLibFnArg {
|
|||||||
""
|
""
|
||||||
};
|
};
|
||||||
if self.type_ == "Sketch"
|
if self.type_ == "Sketch"
|
||||||
|| self.type_ == "SketchSet"
|
|| self.type_ == "[Sketch]"
|
||||||
|| self.type_ == "Solid"
|
|| self.type_ == "Solid"
|
||||||
|| self.type_ == "SolidSet"
|
|| self.type_ == "[Solid]"
|
||||||
|| self.type_ == "SketchSurface"
|
|| self.type_ == "SketchSurface"
|
||||||
|| self.type_ == "SketchOrSurface"
|
|| self.type_ == "SketchOrSurface"
|
||||||
|| self.type_ == "SolidOrImportedGeometry"
|
|| self.type_ == "SolidOrImportedGeometry"
|
||||||
|
|||||||
@ -378,22 +378,8 @@ impl EngineManager for EngineConnection {
|
|||||||
original
|
original
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
|
||||||
&self,
|
self.default_planes.clone()
|
||||||
id_generator: &mut IdGenerator,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<DefaultPlanes, KclError> {
|
|
||||||
{
|
|
||||||
let opt = self.default_planes.read().await.as_ref().cloned();
|
|
||||||
if let Some(planes) = opt {
|
|
||||||
return Ok(planes);
|
|
||||||
}
|
|
||||||
} // drop the read lock
|
|
||||||
|
|
||||||
let new_planes = self.new_default_planes(id_generator, source_range).await?;
|
|
||||||
*self.default_planes.write().await = Some(new_planes.clone());
|
|
||||||
|
|
||||||
Ok(new_planes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn clear_scene_post_hook(
|
async fn clear_scene_post_hook(
|
||||||
|
|||||||
@ -30,6 +30,8 @@ pub struct EngineConnection {
|
|||||||
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
batch_end: Arc<RwLock<IndexMap<uuid::Uuid, (WebSocketRequest, SourceRange)>>>,
|
||||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
|
/// The default planes for the scene.
|
||||||
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
@ -39,6 +41,7 @@ impl EngineConnection {
|
|||||||
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
batch_end: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
|
default_planes: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,12 +76,8 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
original
|
original
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
|
||||||
&self,
|
self.default_planes.clone()
|
||||||
_id_generator: &mut IdGenerator,
|
|
||||||
_source_range: SourceRange,
|
|
||||||
) -> Result<DefaultPlanes, KclError> {
|
|
||||||
Ok(DefaultPlanes::default())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn clear_scene_post_hook(
|
async fn clear_scene_post_hook(
|
||||||
|
|||||||
@ -31,12 +31,6 @@ extern "C" {
|
|||||||
idToRangeStr: String,
|
idToRangeStr: String,
|
||||||
) -> Result<js_sys::Promise, js_sys::Error>;
|
) -> Result<js_sys::Promise, js_sys::Error>;
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = wasmGetDefaultPlanes, catch)]
|
|
||||||
fn get_default_planes(this: &EngineCommandManager) -> Result<js_sys::Promise, js_sys::Error>;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = clearDefaultPlanes, catch)]
|
|
||||||
fn clear_default_planes(this: &EngineCommandManager) -> Result<(), js_sys::Error>;
|
|
||||||
|
|
||||||
#[wasm_bindgen(method, js_name = startNewSession, catch)]
|
#[wasm_bindgen(method, js_name = startNewSession, catch)]
|
||||||
fn start_new_session(this: &EngineCommandManager) -> Result<js_sys::Promise, js_sys::Error>;
|
fn start_new_session(this: &EngineCommandManager) -> Result<js_sys::Promise, js_sys::Error>;
|
||||||
}
|
}
|
||||||
@ -49,6 +43,8 @@ pub struct EngineConnection {
|
|||||||
responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
|
responses: Arc<RwLock<IndexMap<Uuid, WebSocketResponse>>>,
|
||||||
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
artifact_commands: Arc<RwLock<Vec<ArtifactCommand>>>,
|
||||||
execution_kind: Arc<RwLock<ExecutionKind>>,
|
execution_kind: Arc<RwLock<ExecutionKind>>,
|
||||||
|
/// The default planes for the scene.
|
||||||
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: WebAssembly will only ever run in a single-threaded context.
|
// Safety: WebAssembly will only ever run in a single-threaded context.
|
||||||
@ -65,6 +61,7 @@ impl EngineConnection {
|
|||||||
responses: Arc::new(RwLock::new(IndexMap::new())),
|
responses: Arc::new(RwLock::new(IndexMap::new())),
|
||||||
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
artifact_commands: Arc::new(RwLock::new(Vec::new())),
|
||||||
execution_kind: Default::default(),
|
execution_kind: Default::default(),
|
||||||
|
default_planes: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,59 +157,18 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
original
|
original
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>> {
|
||||||
&self,
|
self.default_planes.clone()
|
||||||
_id_generator: &mut IdGenerator,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<DefaultPlanes, KclError> {
|
|
||||||
// Get the default planes.
|
|
||||||
let promise = self.manager.get_default_planes().map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: e.to_string().into(),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let value = crate::wasm::JsFuture::from(promise).await.map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to wait for promise from get default planes: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Parse the value as a string.
|
|
||||||
let s = value.as_string().ok_or_else(|| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!(
|
|
||||||
"Failed to get string from response from get default planes: `{:?}`",
|
|
||||||
value
|
|
||||||
),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Deserialize the response.
|
|
||||||
let default_planes: DefaultPlanes = serde_json::from_str(&s).map_err(|e| {
|
|
||||||
KclError::Engine(KclErrorDetails {
|
|
||||||
message: format!("Failed to deserialize default planes: {:?}", e),
|
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(default_planes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn clear_scene_post_hook(
|
async fn clear_scene_post_hook(
|
||||||
&self,
|
&self,
|
||||||
_id_generator: &mut IdGenerator,
|
id_generator: &mut IdGenerator,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<(), KclError> {
|
) -> Result<(), KclError> {
|
||||||
self.manager.clear_default_planes().map_err(|e| {
|
// Remake the default planes, since they would have been removed after the scene was cleared.
|
||||||
KclError::Engine(KclErrorDetails {
|
let new_planes = self.new_default_planes(id_generator, source_range).await?;
|
||||||
message: e.to_string().into(),
|
*self.default_planes.write().await = Some(new_planes);
|
||||||
source_ranges: vec![source_range],
|
|
||||||
})
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Start a new session.
|
// Start a new session.
|
||||||
let promise = self.manager.start_new_session().map_err(|e| {
|
let promise = self.manager.start_new_session().map_err(|e| {
|
||||||
|
|||||||
@ -95,11 +95,26 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
async fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
||||||
|
|
||||||
/// Get the default planes.
|
/// Get the default planes.
|
||||||
|
fn get_default_planes(&self) -> Arc<RwLock<Option<DefaultPlanes>>>;
|
||||||
|
|
||||||
|
/// Get the default planes, creating them if they don't exist.
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
id_generator: &mut IdGenerator,
|
id_generator: &mut IdGenerator,
|
||||||
_source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<DefaultPlanes, crate::errors::KclError>;
|
) -> Result<DefaultPlanes, KclError> {
|
||||||
|
{
|
||||||
|
let opt = self.get_default_planes().read().await.as_ref().cloned();
|
||||||
|
if let Some(planes) = opt {
|
||||||
|
return Ok(planes);
|
||||||
|
}
|
||||||
|
} // drop the read lock
|
||||||
|
|
||||||
|
let new_planes = self.new_default_planes(id_generator, source_range).await?;
|
||||||
|
*self.get_default_planes().write().await = Some(new_planes.clone());
|
||||||
|
|
||||||
|
Ok(new_planes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Helpers to be called after clearing a scene.
|
/// Helpers to be called after clearing a scene.
|
||||||
/// (These really only apply to wasm for now).
|
/// (These really only apply to wasm for now).
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use thiserror::Error;
|
|||||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
|
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
execution::{ArtifactCommand, ArtifactGraph, Operation},
|
execution::{ArtifactCommand, ArtifactGraph, DefaultPlanes, Operation},
|
||||||
lsp::IntoDiagnostic,
|
lsp::IntoDiagnostic,
|
||||||
modules::{ModulePath, ModuleSource},
|
modules::{ModulePath, ModuleSource},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
@ -131,6 +131,7 @@ pub struct KclErrorWithOutputs {
|
|||||||
pub artifact_graph: ArtifactGraph,
|
pub artifact_graph: ArtifactGraph,
|
||||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||||
pub source_files: IndexMap<ModuleId, ModuleSource>,
|
pub source_files: IndexMap<ModuleId, ModuleSource>,
|
||||||
|
pub default_planes: Option<DefaultPlanes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KclErrorWithOutputs {
|
impl KclErrorWithOutputs {
|
||||||
@ -141,6 +142,7 @@ impl KclErrorWithOutputs {
|
|||||||
artifact_graph: ArtifactGraph,
|
artifact_graph: ArtifactGraph,
|
||||||
filenames: IndexMap<ModuleId, ModulePath>,
|
filenames: IndexMap<ModuleId, ModulePath>,
|
||||||
source_files: IndexMap<ModuleId, ModuleSource>,
|
source_files: IndexMap<ModuleId, ModuleSource>,
|
||||||
|
default_planes: Option<DefaultPlanes>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
error,
|
error,
|
||||||
@ -149,6 +151,7 @@ impl KclErrorWithOutputs {
|
|||||||
artifact_graph,
|
artifact_graph,
|
||||||
filenames,
|
filenames,
|
||||||
source_files,
|
source_files,
|
||||||
|
default_planes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn no_outputs(error: KclError) -> Self {
|
pub fn no_outputs(error: KclError) -> Self {
|
||||||
@ -159,6 +162,7 @@ impl KclErrorWithOutputs {
|
|||||||
artifact_graph: Default::default(),
|
artifact_graph: Default::default(),
|
||||||
filenames: Default::default(),
|
filenames: Default::default(),
|
||||||
source_files: Default::default(),
|
source_files: Default::default(),
|
||||||
|
default_planes: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
|
pub fn into_miette_report_with_outputs(self, code: &str) -> anyhow::Result<ReportWithOutputs> {
|
||||||
|
|||||||
@ -180,15 +180,9 @@ pub enum OpKclValue {
|
|||||||
Sketch {
|
Sketch {
|
||||||
value: Box<OpSketch>,
|
value: Box<OpSketch>,
|
||||||
},
|
},
|
||||||
Sketches {
|
|
||||||
value: Vec<OpSketch>,
|
|
||||||
},
|
|
||||||
Solid {
|
Solid {
|
||||||
value: Box<OpSolid>,
|
value: Box<OpSolid>,
|
||||||
},
|
},
|
||||||
Solids {
|
|
||||||
value: Vec<OpSolid>,
|
|
||||||
},
|
|
||||||
Helix {
|
Helix {
|
||||||
value: Box<OpHelix>,
|
value: Box<OpHelix>,
|
||||||
},
|
},
|
||||||
@ -234,7 +228,7 @@ impl From<&KclValue> for OpKclValue {
|
|||||||
ty: ty.clone(),
|
ty: ty.clone(),
|
||||||
},
|
},
|
||||||
KclValue::String { value, .. } => Self::String { value: value.clone() },
|
KclValue::String { value, .. } => Self::String { value: value.clone() },
|
||||||
KclValue::MixedArray { value, .. } => {
|
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
|
||||||
let value = value.iter().map(Self::from).collect();
|
let value = value.iter().map(Self::from).collect();
|
||||||
Self::Array { value }
|
Self::Array { value }
|
||||||
}
|
}
|
||||||
@ -244,7 +238,7 @@ impl From<&KclValue> for OpKclValue {
|
|||||||
}
|
}
|
||||||
KclValue::TagIdentifier(tag_identifier) => Self::TagIdentifier {
|
KclValue::TagIdentifier(tag_identifier) => Self::TagIdentifier {
|
||||||
value: tag_identifier.value.clone(),
|
value: tag_identifier.value.clone(),
|
||||||
artifact_id: tag_identifier.info.as_ref().map(|info| ArtifactId::new(info.id)),
|
artifact_id: tag_identifier.get_cur_info().map(|info| ArtifactId::new(info.id)),
|
||||||
},
|
},
|
||||||
KclValue::TagDeclarator(node) => Self::TagDeclarator {
|
KclValue::TagDeclarator(node) => Self::TagDeclarator {
|
||||||
name: node.name.clone(),
|
name: node.name.clone(),
|
||||||
@ -260,29 +254,11 @@ impl From<&KclValue> for OpKclValue {
|
|||||||
artifact_id: value.artifact_id,
|
artifact_id: value.artifact_id,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
KclValue::Sketches { value } => {
|
|
||||||
let value = value
|
|
||||||
.iter()
|
|
||||||
.map(|sketch| OpSketch {
|
|
||||||
artifact_id: sketch.artifact_id,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self::Sketches { value }
|
|
||||||
}
|
|
||||||
KclValue::Solid { value } => Self::Solid {
|
KclValue::Solid { value } => Self::Solid {
|
||||||
value: Box::new(OpSolid {
|
value: Box::new(OpSolid {
|
||||||
artifact_id: value.artifact_id,
|
artifact_id: value.artifact_id,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
KclValue::Solids { value } => {
|
|
||||||
let value = value
|
|
||||||
.iter()
|
|
||||||
.map(|solid| OpSolid {
|
|
||||||
artifact_id: solid.artifact_id,
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
Self::Solids { value }
|
|
||||||
}
|
|
||||||
KclValue::Helix { value } => Self::Helix {
|
KclValue::Helix { value } => Self::Helix {
|
||||||
value: Box::new(OpHelix {
|
value: Box::new(OpHelix {
|
||||||
artifact_id: value.artifact_id,
|
artifact_id: value.artifact_id,
|
||||||
@ -295,7 +271,6 @@ impl From<&KclValue> for OpKclValue {
|
|||||||
KclValue::Module { .. } => Self::Module {},
|
KclValue::Module { .. } => Self::Module {},
|
||||||
KclValue::KclNone { .. } => Self::KclNone {},
|
KclValue::KclNone { .. } => Self::KclNone {},
|
||||||
KclValue::Type { .. } => Self::Type {},
|
KclValue::Type { .. } => Self::Type {},
|
||||||
KclValue::Tombstone { .. } => unreachable!("Tombstone OpKclValue"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,11 @@ use crate::{
|
|||||||
execution::{
|
execution::{
|
||||||
annotations,
|
annotations,
|
||||||
cad_op::{OpArg, OpKclValue, Operation},
|
cad_op::{OpArg, OpKclValue, Operation},
|
||||||
kcl_value::{FunctionSource, NumericType, PrimitiveType, RuntimeType},
|
kcl_value::{FunctionSource, NumericType, RuntimeType},
|
||||||
memory,
|
memory,
|
||||||
state::ModuleState,
|
state::ModuleState,
|
||||||
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, Plane, PlaneType, Point3d,
|
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, Metadata, PlaneType, TagEngineInfo,
|
||||||
TagEngineInfo, TagIdentifier,
|
TagIdentifier,
|
||||||
},
|
},
|
||||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||||
parsing::ast::types::{
|
parsing::ast::types::{
|
||||||
@ -23,7 +23,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
std::{
|
std::{
|
||||||
args::{Arg, FromKclValue, KwArgs},
|
args::{Arg, KwArgs},
|
||||||
FunctionKind,
|
FunctionKind,
|
||||||
},
|
},
|
||||||
CompilationError,
|
CompilationError,
|
||||||
@ -55,10 +55,9 @@ impl ExecutorContext {
|
|||||||
for annotation in annotations {
|
for annotation in annotations {
|
||||||
if annotation.name() == Some(annotations::SETTINGS) {
|
if annotation.name() == Some(annotations::SETTINGS) {
|
||||||
if matches!(body_type, BodyType::Root) {
|
if matches!(body_type, BodyType::Root) {
|
||||||
let old_units = exec_state.length_unit();
|
|
||||||
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
exec_state.mod_local.settings.update_from_annotation(annotation)?;
|
||||||
let new_units = exec_state.length_unit();
|
let new_units = exec_state.length_unit();
|
||||||
if !self.engine.execution_kind().await.is_isolated() && old_units != new_units {
|
if !self.engine.execution_kind().await.is_isolated() {
|
||||||
self.engine
|
self.engine
|
||||||
.set_units(new_units.into(), annotation.as_source_range())
|
.set_units(new_units.into(), annotation.as_source_range())
|
||||||
.await?;
|
.await?;
|
||||||
@ -94,6 +93,7 @@ impl ExecutorContext {
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
exec_kind: ExecutionKind,
|
exec_kind: ExecutionKind,
|
||||||
preserve_mem: bool,
|
preserve_mem: bool,
|
||||||
|
module_id: ModuleId,
|
||||||
path: &ModulePath,
|
path: &ModulePath,
|
||||||
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
||||||
crate::log::log(format!("enter module {path} {}", exec_state.stack()));
|
crate::log::log(format!("enter module {path} {}", exec_state.stack()));
|
||||||
@ -101,7 +101,12 @@ impl ExecutorContext {
|
|||||||
let old_units = exec_state.length_unit();
|
let old_units = exec_state.length_unit();
|
||||||
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
|
let original_execution = self.engine.replace_execution_kind(exec_kind).await;
|
||||||
|
|
||||||
let mut local_state = ModuleState::new(&self.settings, path.std_path(), exec_state.stack().memory.clone());
|
let mut local_state = ModuleState::new(
|
||||||
|
&self.settings,
|
||||||
|
path.std_path(),
|
||||||
|
exec_state.stack().memory.clone(),
|
||||||
|
Some(module_id),
|
||||||
|
);
|
||||||
if !preserve_mem {
|
if !preserve_mem {
|
||||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||||
}
|
}
|
||||||
@ -452,7 +457,7 @@ impl ExecutorContext {
|
|||||||
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
||||||
ModuleRepr::Kcl(_, Some((env_ref, items))) => Ok((*env_ref, items.clone())),
|
ModuleRepr::Kcl(_, Some((env_ref, items))) => Ok((*env_ref, items.clone())),
|
||||||
ModuleRepr::Kcl(program, cache) => self
|
ModuleRepr::Kcl(program, cache) => self
|
||||||
.exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
|
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
|
||||||
.await
|
.await
|
||||||
.map(|(_, er, items)| {
|
.map(|(_, er, items)| {
|
||||||
*cache = Some((er, items.clone()));
|
*cache = Some((er, items.clone()));
|
||||||
@ -483,7 +488,7 @@ impl ExecutorContext {
|
|||||||
let result = match &repr {
|
let result = match &repr {
|
||||||
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
||||||
ModuleRepr::Kcl(program, _) => self
|
ModuleRepr::Kcl(program, _) => self
|
||||||
.exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
|
.exec_module_from_ast(program, module_id, &path, exec_state, exec_kind, source_range)
|
||||||
.await
|
.await
|
||||||
.map(|(val, _, _)| val),
|
.map(|(val, _, _)| val),
|
||||||
ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
|
ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
|
||||||
@ -499,13 +504,16 @@ impl ExecutorContext {
|
|||||||
async fn exec_module_from_ast(
|
async fn exec_module_from_ast(
|
||||||
&self,
|
&self,
|
||||||
program: &Node<Program>,
|
program: &Node<Program>,
|
||||||
|
module_id: ModuleId,
|
||||||
path: &ModulePath,
|
path: &ModulePath,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
exec_kind: ExecutionKind,
|
exec_kind: ExecutionKind,
|
||||||
source_range: SourceRange,
|
source_range: SourceRange,
|
||||||
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
||||||
exec_state.global.mod_loader.enter_module(path);
|
exec_state.global.mod_loader.enter_module(path);
|
||||||
let result = self.exec_module_body(program, exec_state, exec_kind, false, path).await;
|
let result = self
|
||||||
|
.exec_module_body(program, exec_state, exec_kind, false, module_id, path)
|
||||||
|
.await;
|
||||||
exec_state.global.mod_loader.leave_module(path);
|
exec_state.global.mod_loader.leave_module(path);
|
||||||
|
|
||||||
result.map_err(|err| {
|
result.map_err(|err| {
|
||||||
@ -638,11 +646,11 @@ impl ExecutorContext {
|
|||||||
let result = self
|
let result = self
|
||||||
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
|
.execute_expr(&expr.expr, exec_state, metadata, &[], statement_kind)
|
||||||
.await?;
|
.await?;
|
||||||
coerce(result, &expr.ty, exec_state).map_err(|value| {
|
coerce(&result, &expr.ty, exec_state).ok_or_else(|| {
|
||||||
KclError::Semantic(KclErrorDetails {
|
KclError::Semantic(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
"could not coerce {} value to type {}",
|
"could not coerce {} value to type {}",
|
||||||
value.human_friendly_type(),
|
result.human_friendly_type(),
|
||||||
expr.ty
|
expr.ty
|
||||||
),
|
),
|
||||||
source_ranges: vec![expr.into()],
|
source_ranges: vec![expr.into()],
|
||||||
@ -654,72 +662,14 @@ impl ExecutorContext {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn coerce(value: KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Result<KclValue, KclValue> {
|
fn coerce(value: &KclValue, ty: &Node<Type>, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, (&value).into())
|
let ty = RuntimeType::from_parsed(ty.inner.clone(), exec_state, value.into())
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
exec_state.err(e);
|
exec_state.err(e);
|
||||||
value.clone()
|
})
|
||||||
})?
|
.ok()??;
|
||||||
.ok_or_else(|| value.clone())?;
|
|
||||||
if value.has_type(&ty) {
|
|
||||||
return Ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO coerce numeric types
|
value.coerce(&ty, exec_state)
|
||||||
|
|
||||||
if let KclValue::Object { value, meta } = value {
|
|
||||||
return match ty {
|
|
||||||
RuntimeType::Primitive(PrimitiveType::Plane) => {
|
|
||||||
let origin = value
|
|
||||||
.get("origin")
|
|
||||||
.and_then(Point3d::from_kcl_val)
|
|
||||||
.ok_or_else(|| KclValue::Object {
|
|
||||||
value: value.clone(),
|
|
||||||
meta: meta.clone(),
|
|
||||||
})?;
|
|
||||||
let x_axis = value
|
|
||||||
.get("xAxis")
|
|
||||||
.and_then(Point3d::from_kcl_val)
|
|
||||||
.ok_or_else(|| KclValue::Object {
|
|
||||||
value: value.clone(),
|
|
||||||
meta: meta.clone(),
|
|
||||||
})?;
|
|
||||||
let y_axis = value
|
|
||||||
.get("yAxis")
|
|
||||||
.and_then(Point3d::from_kcl_val)
|
|
||||||
.ok_or_else(|| KclValue::Object {
|
|
||||||
value: value.clone(),
|
|
||||||
meta: meta.clone(),
|
|
||||||
})?;
|
|
||||||
let z_axis = value
|
|
||||||
.get("zAxis")
|
|
||||||
.and_then(Point3d::from_kcl_val)
|
|
||||||
.ok_or_else(|| KclValue::Object {
|
|
||||||
value: value.clone(),
|
|
||||||
meta: meta.clone(),
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let id = exec_state.global.id_generator.next_uuid();
|
|
||||||
let plane = Plane {
|
|
||||||
id,
|
|
||||||
artifact_id: id.into(),
|
|
||||||
origin,
|
|
||||||
x_axis,
|
|
||||||
y_axis,
|
|
||||||
z_axis,
|
|
||||||
value: PlaneType::Uninit,
|
|
||||||
// TODO use length unit from origin
|
|
||||||
units: exec_state.length_unit(),
|
|
||||||
meta,
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(KclValue::Plane { value: Box::new(plane) })
|
|
||||||
}
|
|
||||||
_ => Err(KclValue::Object { value, meta }),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(value)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinaryPart {
|
impl BinaryPart {
|
||||||
@ -745,33 +695,7 @@ impl BinaryPart {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Node<MemberExpression> {
|
impl Node<MemberExpression> {
|
||||||
pub fn get_result_array(&self, exec_state: &mut ExecState, index: usize) -> Result<KclValue, KclError> {
|
fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
||||||
let array = match &self.object {
|
|
||||||
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
|
|
||||||
MemberObject::Identifier(identifier) => {
|
|
||||||
let value = exec_state.stack().get(&identifier.name, identifier.into())?;
|
|
||||||
value.clone()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let KclValue::MixedArray { value: array, meta: _ } = array else {
|
|
||||||
return Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: format!("MemberExpression array is not an array: {:?}", array),
|
|
||||||
source_ranges: vec![self.clone().into()],
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(value) = array.get(index) {
|
|
||||||
Ok(value.to_owned())
|
|
||||||
} else {
|
|
||||||
Err(KclError::UndefinedValue(KclErrorDetails {
|
|
||||||
message: format!("index {} not found in array", index),
|
|
||||||
source_ranges: vec![self.clone().into()],
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_result(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
|
||||||
let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
|
let property = Property::try_from(self.computed, self.property.clone(), exec_state, self.into())?;
|
||||||
let object = match &self.object {
|
let object = match &self.object {
|
||||||
// TODO: Don't use recursion here, use a loop.
|
// TODO: Don't use recursion here, use a loop.
|
||||||
@ -1372,11 +1296,22 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
|||||||
// TODO: This could probably be done in a better way, but as of now this was my only idea
|
// TODO: This could probably be done in a better way, but as of now this was my only idea
|
||||||
// and it works.
|
// and it works.
|
||||||
match result {
|
match result {
|
||||||
KclValue::Sketch { value: ref mut sketch } => {
|
KclValue::Sketch { value } => {
|
||||||
for (_, tag) in sketch.tags.iter() {
|
for (name, tag) in value.tags.iter() {
|
||||||
exec_state
|
if exec_state.stack().cur_frame_contains(name) {
|
||||||
.mut_stack()
|
exec_state.mut_stack().update(name, |v, _| {
|
||||||
.insert_or_update(tag.value.clone(), KclValue::TagIdentifier(Box::new(tag.clone())));
|
v.as_mut_tag().unwrap().merge_info(tag);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
exec_state
|
||||||
|
.mut_stack()
|
||||||
|
.add(
|
||||||
|
name.to_owned(),
|
||||||
|
KclValue::TagIdentifier(Box::new(tag.clone())),
|
||||||
|
SourceRange::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
KclValue::Solid { ref mut value } => {
|
KclValue::Solid { ref mut value } => {
|
||||||
@ -1385,7 +1320,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
|||||||
// Get the past tag and update it.
|
// Get the past tag and update it.
|
||||||
let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
|
let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
|
||||||
let mut t = t.clone();
|
let mut t = t.clone();
|
||||||
let Some(ref info) = t.info else {
|
let Some(info) = t.get_cur_info() else {
|
||||||
return Err(KclError::Internal(KclErrorDetails {
|
return Err(KclError::Internal(KclErrorDetails {
|
||||||
message: format!("Tag {} does not have path info", tag.name),
|
message: format!("Tag {} does not have path info", tag.name),
|
||||||
source_ranges: vec![tag.into()],
|
source_ranges: vec![tag.into()],
|
||||||
@ -1395,59 +1330,70 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
|||||||
let mut info = info.clone();
|
let mut info = info.clone();
|
||||||
info.surface = Some(v.clone());
|
info.surface = Some(v.clone());
|
||||||
info.sketch = value.id;
|
info.sketch = value.id;
|
||||||
t.info = Some(info);
|
t.info.push((exec_state.stack().current_epoch(), info));
|
||||||
t
|
t
|
||||||
} else {
|
} else {
|
||||||
// It's probably a fillet or a chamfer.
|
// It's probably a fillet or a chamfer.
|
||||||
// Initialize it.
|
// Initialize it.
|
||||||
TagIdentifier {
|
TagIdentifier {
|
||||||
value: tag.name.clone(),
|
value: tag.name.clone(),
|
||||||
info: Some(TagEngineInfo {
|
info: vec![(
|
||||||
id: v.get_id(),
|
exec_state.stack().current_epoch(),
|
||||||
surface: Some(v.clone()),
|
TagEngineInfo {
|
||||||
path: None,
|
id: v.get_id(),
|
||||||
sketch: value.id,
|
surface: Some(v.clone()),
|
||||||
}),
|
path: None,
|
||||||
|
sketch: value.id,
|
||||||
|
},
|
||||||
|
)],
|
||||||
meta: vec![Metadata {
|
meta: vec![Metadata {
|
||||||
source_range: tag.clone().into(),
|
source_range: tag.clone().into(),
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
exec_state
|
|
||||||
.mut_stack()
|
|
||||||
.insert_or_update(tag.name.clone(), KclValue::TagIdentifier(Box::new(tag_id.clone())));
|
|
||||||
|
|
||||||
// update the sketch tags.
|
// update the sketch tags.
|
||||||
value.sketch.tags.insert(tag.name.clone(), tag_id);
|
value.sketch.merge_tags(Some(&tag_id).into_iter());
|
||||||
|
|
||||||
|
if exec_state.stack().cur_frame_contains(&tag.name) {
|
||||||
|
exec_state.mut_stack().update(&tag.name, |v, _| {
|
||||||
|
v.as_mut_tag().unwrap().merge_info(&tag_id);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
exec_state
|
||||||
|
.mut_stack()
|
||||||
|
.add(
|
||||||
|
tag.name.clone(),
|
||||||
|
KclValue::TagIdentifier(Box::new(tag_id)),
|
||||||
|
SourceRange::default(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find the stale sketch in memory and update it.
|
// Find the stale sketch in memory and update it.
|
||||||
if !value.sketch.tags.is_empty() {
|
if !value.sketch.tags.is_empty() {
|
||||||
let updates: Vec<_> = exec_state
|
let sketches_to_update: Vec<_> = exec_state
|
||||||
.stack()
|
.stack()
|
||||||
.find_all_in_current_env(|v| match v {
|
.find_keys_in_current_env(|v| match v {
|
||||||
KclValue::Sketch { value: sk } => sk.artifact_id == value.sketch.artifact_id,
|
KclValue::Sketch { value: sk } => sk.artifact_id == value.sketch.artifact_id,
|
||||||
_ => false,
|
_ => false,
|
||||||
})
|
})
|
||||||
.map(|(k, v)| {
|
.cloned()
|
||||||
let mut sketch = v.as_sketch().unwrap().clone();
|
|
||||||
for (tag_name, tag_id) in value.sketch.tags.iter() {
|
|
||||||
sketch.tags.insert(tag_name.clone(), tag_id.clone());
|
|
||||||
}
|
|
||||||
(
|
|
||||||
k.clone(),
|
|
||||||
KclValue::Sketch {
|
|
||||||
value: Box::new(sketch),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
updates
|
for k in sketches_to_update {
|
||||||
.into_iter()
|
exec_state.mut_stack().update(&k, |v, _| {
|
||||||
.for_each(|(k, v)| exec_state.mut_stack().insert_or_update(k, v))
|
let sketch = v.as_mut_sketch().unwrap();
|
||||||
|
sketch.merge_tags(value.sketch.tags.values());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
|
||||||
|
for v in value {
|
||||||
|
update_memory_for_tags_of_geometry(v, exec_state)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -1459,7 +1405,7 @@ impl Node<TagDeclarator> {
|
|||||||
pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
pub async fn execute(&self, exec_state: &mut ExecState) -> Result<KclValue, KclError> {
|
||||||
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
|
let memory_item = KclValue::TagIdentifier(Box::new(TagIdentifier {
|
||||||
value: self.name.clone(),
|
value: self.name.clone(),
|
||||||
info: None,
|
info: Vec::new(),
|
||||||
meta: vec![Metadata {
|
meta: vec![Metadata {
|
||||||
source_range: self.into(),
|
source_range: self.into(),
|
||||||
}],
|
}],
|
||||||
@ -1966,14 +1912,16 @@ impl FunctionSource {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
execution::{memory::Stack, parse_execute},
|
execution::{memory::Stack, parse_execute, ContextType},
|
||||||
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
fn test_assign_args_to_params() {
|
async fn test_assign_args_to_params() {
|
||||||
// Set up a little framework for this test.
|
// Set up a little framework for this test.
|
||||||
fn mem(number: usize) -> KclValue {
|
fn mem(number: usize) -> KclValue {
|
||||||
KclValue::Number {
|
KclValue::Number {
|
||||||
@ -2084,7 +2032,16 @@ mod test {
|
|||||||
digest: None,
|
digest: None,
|
||||||
});
|
});
|
||||||
let args = args.into_iter().map(Arg::synthetic).collect();
|
let args = args.into_iter().map(Arg::synthetic).collect();
|
||||||
let mut exec_state = ExecState::new(&Default::default());
|
let exec_ctxt = ExecutorContext {
|
||||||
|
engine: Arc::new(Box::new(
|
||||||
|
crate::engine::conn_mock::EngineConnection::new().await.unwrap(),
|
||||||
|
)),
|
||||||
|
fs: Arc::new(crate::fs::FileManager::new()),
|
||||||
|
stdlib: Arc::new(crate::std::StdLib::new()),
|
||||||
|
settings: Default::default(),
|
||||||
|
context_type: ContextType::Mock,
|
||||||
|
};
|
||||||
|
let mut exec_state = ExecState::new(&exec_ctxt);
|
||||||
exec_state.mod_local.stack = Stack::new_for_tests();
|
exec_state.mod_local.stack = Stack::new_for_tests();
|
||||||
let actual = assign_args_to_params(func_expr, args, &mut exec_state).map(|_| exec_state.mod_local.stack);
|
let actual = assign_args_to_params(func_expr, args, &mut exec_state).map(|_| exec_state.mod_local.stack);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@ -23,8 +23,8 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum Geometry {
|
pub enum Geometry {
|
||||||
Sketch(Box<Sketch>),
|
Sketch(Sketch),
|
||||||
Solid(Box<Solid>),
|
Solid(Solid),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Geometry {
|
impl Geometry {
|
||||||
@ -52,8 +52,8 @@ impl Geometry {
|
|||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
#[allow(clippy::vec_box)]
|
#[allow(clippy::vec_box)]
|
||||||
pub enum Geometries {
|
pub enum Geometries {
|
||||||
Sketches(Vec<Box<Sketch>>),
|
Sketches(Vec<Sketch>),
|
||||||
Solids(Vec<Box<Solid>>),
|
Solids(Vec<Solid>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Geometry> for Geometries {
|
impl From<Geometry> for Geometries {
|
||||||
@ -65,150 +65,6 @@ impl From<Geometry> for Geometries {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A sketch or a group of sketches.
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
||||||
#[ts(export)]
|
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
|
||||||
#[allow(clippy::vec_box)]
|
|
||||||
pub enum SketchSet {
|
|
||||||
Sketch(Box<Sketch>),
|
|
||||||
Sketches(Vec<Box<Sketch>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SketchSet {
|
|
||||||
pub fn meta(&self) -> Vec<Metadata> {
|
|
||||||
match self {
|
|
||||||
SketchSet::Sketch(sg) => sg.meta.clone(),
|
|
||||||
SketchSet::Sketches(sg) => sg.iter().flat_map(|sg| sg.meta.clone()).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SketchSet> for Vec<Sketch> {
|
|
||||||
fn from(value: SketchSet) -> Self {
|
|
||||||
match value {
|
|
||||||
SketchSet::Sketch(sg) => vec![*sg],
|
|
||||||
SketchSet::Sketches(sgs) => sgs.into_iter().map(|sg| *sg).collect(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Sketch> for SketchSet {
|
|
||||||
fn from(sg: Sketch) -> Self {
|
|
||||||
SketchSet::Sketch(Box::new(sg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<Sketch>> for SketchSet {
|
|
||||||
fn from(sg: Box<Sketch>) -> Self {
|
|
||||||
SketchSet::Sketch(sg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Sketch>> for SketchSet {
|
|
||||||
fn from(sg: Vec<Sketch>) -> Self {
|
|
||||||
if sg.len() == 1 {
|
|
||||||
SketchSet::Sketch(Box::new(sg[0].clone()))
|
|
||||||
} else {
|
|
||||||
SketchSet::Sketches(sg.into_iter().map(Box::new).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Box<Sketch>>> for SketchSet {
|
|
||||||
fn from(sg: Vec<Box<Sketch>>) -> Self {
|
|
||||||
if sg.len() == 1 {
|
|
||||||
SketchSet::Sketch(sg[0].clone())
|
|
||||||
} else {
|
|
||||||
SketchSet::Sketches(sg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SketchSet> for Vec<Box<Sketch>> {
|
|
||||||
fn from(sg: SketchSet) -> Self {
|
|
||||||
match sg {
|
|
||||||
SketchSet::Sketch(sg) => vec![sg],
|
|
||||||
SketchSet::Sketches(sgs) => sgs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Sketch> for Vec<Box<Sketch>> {
|
|
||||||
fn from(sg: &Sketch) -> Self {
|
|
||||||
vec![Box::new(sg.clone())]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<Sketch>> for Vec<Box<Sketch>> {
|
|
||||||
fn from(sg: Box<Sketch>) -> Self {
|
|
||||||
vec![sg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A solid or a group of solids.
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
|
||||||
#[ts(export)]
|
|
||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
|
||||||
#[allow(clippy::vec_box)]
|
|
||||||
pub enum SolidSet {
|
|
||||||
Solid(Box<Solid>),
|
|
||||||
Solids(Vec<Box<Solid>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Solid> for SolidSet {
|
|
||||||
fn from(eg: Solid) -> Self {
|
|
||||||
SolidSet::Solid(Box::new(eg))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<Solid>> for SolidSet {
|
|
||||||
fn from(eg: Box<Solid>) -> Self {
|
|
||||||
SolidSet::Solid(eg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Solid>> for SolidSet {
|
|
||||||
fn from(eg: Vec<Solid>) -> Self {
|
|
||||||
if eg.len() == 1 {
|
|
||||||
SolidSet::Solid(Box::new(eg[0].clone()))
|
|
||||||
} else {
|
|
||||||
SolidSet::Solids(eg.into_iter().map(Box::new).collect())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Box<Solid>>> for SolidSet {
|
|
||||||
fn from(eg: Vec<Box<Solid>>) -> Self {
|
|
||||||
if eg.len() == 1 {
|
|
||||||
SolidSet::Solid(eg[0].clone())
|
|
||||||
} else {
|
|
||||||
SolidSet::Solids(eg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SolidSet> for Vec<Box<Solid>> {
|
|
||||||
fn from(eg: SolidSet) -> Self {
|
|
||||||
match eg {
|
|
||||||
SolidSet::Solid(eg) => vec![eg],
|
|
||||||
SolidSet::Solids(egs) => egs,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&Solid> for Vec<Box<Solid>> {
|
|
||||||
fn from(eg: &Solid) -> Self {
|
|
||||||
vec![Box::new(eg.clone())]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Box<Solid>> for Vec<Box<Solid>> {
|
|
||||||
fn from(eg: Box<Solid>) -> Self {
|
|
||||||
vec![eg]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Data for an imported geometry.
|
/// Data for an imported geometry.
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -228,17 +84,29 @@ pub struct ImportedGeometry {
|
|||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
#[allow(clippy::vec_box)]
|
#[allow(clippy::vec_box)]
|
||||||
pub enum SolidOrImportedGeometry {
|
pub enum SolidOrImportedGeometry {
|
||||||
Solid(Box<Solid>),
|
|
||||||
ImportedGeometry(Box<ImportedGeometry>),
|
ImportedGeometry(Box<ImportedGeometry>),
|
||||||
SolidSet(Vec<Box<Solid>>),
|
SolidSet(Vec<Solid>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SolidOrImportedGeometry> for crate::execution::KclValue {
|
impl From<SolidOrImportedGeometry> for crate::execution::KclValue {
|
||||||
fn from(value: SolidOrImportedGeometry) -> Self {
|
fn from(value: SolidOrImportedGeometry) -> Self {
|
||||||
match value {
|
match value {
|
||||||
SolidOrImportedGeometry::Solid(s) => crate::execution::KclValue::Solid { value: s },
|
|
||||||
SolidOrImportedGeometry::ImportedGeometry(s) => crate::execution::KclValue::ImportedGeometry(*s),
|
SolidOrImportedGeometry::ImportedGeometry(s) => crate::execution::KclValue::ImportedGeometry(*s),
|
||||||
SolidOrImportedGeometry::SolidSet(s) => crate::execution::KclValue::Solids { value: s },
|
SolidOrImportedGeometry::SolidSet(mut s) => {
|
||||||
|
if s.len() == 1 {
|
||||||
|
crate::execution::KclValue::Solid {
|
||||||
|
value: Box::new(s.pop().unwrap()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
crate::execution::KclValue::HomArray {
|
||||||
|
value: s
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| crate::execution::KclValue::Solid { value: Box::new(s) })
|
||||||
|
.collect(),
|
||||||
|
ty: crate::execution::PrimitiveType::Solid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +114,6 @@ impl From<SolidOrImportedGeometry> for crate::execution::KclValue {
|
|||||||
impl SolidOrImportedGeometry {
|
impl SolidOrImportedGeometry {
|
||||||
pub(crate) fn ids(&self) -> Vec<uuid::Uuid> {
|
pub(crate) fn ids(&self) -> Vec<uuid::Uuid> {
|
||||||
match self {
|
match self {
|
||||||
SolidOrImportedGeometry::Solid(s) => vec![s.id],
|
|
||||||
SolidOrImportedGeometry::ImportedGeometry(s) => vec![s.id],
|
SolidOrImportedGeometry::ImportedGeometry(s) => vec![s.id],
|
||||||
SolidOrImportedGeometry::SolidSet(s) => s.iter().map(|s| s.id).collect(),
|
SolidOrImportedGeometry::SolidSet(s) => s.iter().map(|s| s.id).collect(),
|
||||||
}
|
}
|
||||||
@ -370,7 +237,7 @@ impl Plane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_plane_data(value: PlaneData, exec_state: &mut ExecState) -> Self {
|
pub(crate) fn from_plane_data(value: PlaneData, exec_state: &mut ExecState) -> Self {
|
||||||
let id = exec_state.global.id_generator.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
match value {
|
match value {
|
||||||
PlaneData::XY => Plane {
|
PlaneData::XY => Plane {
|
||||||
id,
|
id,
|
||||||
@ -443,17 +310,20 @@ impl Plane {
|
|||||||
x_axis,
|
x_axis,
|
||||||
y_axis,
|
y_axis,
|
||||||
z_axis,
|
z_axis,
|
||||||
} => Plane {
|
} => {
|
||||||
id,
|
let id = exec_state.next_uuid();
|
||||||
artifact_id: id.into(),
|
Plane {
|
||||||
origin,
|
id,
|
||||||
x_axis,
|
artifact_id: id.into(),
|
||||||
y_axis,
|
origin,
|
||||||
z_axis,
|
x_axis,
|
||||||
value: PlaneType::Custom,
|
y_axis,
|
||||||
units: exec_state.length_unit(),
|
z_axis,
|
||||||
meta: vec![],
|
value: PlaneType::Custom,
|
||||||
},
|
units: exec_state.length_unit(),
|
||||||
|
meta: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -636,19 +506,35 @@ impl GetTangentialInfoFromPathsResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Sketch {
|
impl Sketch {
|
||||||
pub(crate) fn add_tag(&mut self, tag: NodeRef<'_, TagDeclarator>, current_path: &Path) {
|
pub(crate) fn add_tag(&mut self, tag: NodeRef<'_, TagDeclarator>, current_path: &Path, exec_state: &ExecState) {
|
||||||
let mut tag_identifier: TagIdentifier = tag.into();
|
let mut tag_identifier: TagIdentifier = tag.into();
|
||||||
let base = current_path.get_base();
|
let base = current_path.get_base();
|
||||||
tag_identifier.info = Some(TagEngineInfo {
|
tag_identifier.info.push((
|
||||||
id: base.geo_meta.id,
|
exec_state.stack().current_epoch(),
|
||||||
sketch: self.id,
|
TagEngineInfo {
|
||||||
path: Some(current_path.clone()),
|
id: base.geo_meta.id,
|
||||||
surface: None,
|
sketch: self.id,
|
||||||
});
|
path: Some(current_path.clone()),
|
||||||
|
surface: None,
|
||||||
|
},
|
||||||
|
));
|
||||||
|
|
||||||
self.tags.insert(tag.name.to_string(), tag_identifier);
|
self.tags.insert(tag.name.to_string(), tag_identifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn merge_tags<'a>(&mut self, tags: impl Iterator<Item = &'a TagIdentifier>) {
|
||||||
|
for t in tags {
|
||||||
|
match self.tags.get_mut(&t.value) {
|
||||||
|
Some(id) => {
|
||||||
|
id.merge_info(t);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.tags.insert(t.value.clone(), t.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the path most recently sketched.
|
/// Get the path most recently sketched.
|
||||||
pub(crate) fn latest_path(&self) -> Option<&Path> {
|
pub(crate) fn latest_path(&self) -> Option<&Path> {
|
||||||
self.paths.last()
|
self.paths.last()
|
||||||
|
|||||||
83
rust/kcl-lib/src/execution/id_generator.rs
Normal file
83
rust/kcl-lib/src/execution/id_generator.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//! A generator for ArtifactIds that can be stable across executions.
|
||||||
|
|
||||||
|
use crate::execution::ModuleId;
|
||||||
|
|
||||||
|
const NAMESPACE_KCL: uuid::Uuid = uuid::uuid!("efcd6508-4ce6-4a09-8317-e6a6994a3cd7");
|
||||||
|
|
||||||
|
/// A generator for ArtifactIds that can be stable across executions.
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq)]
|
||||||
|
pub struct IdGenerator {
|
||||||
|
module_id: Option<ModuleId>,
|
||||||
|
next_id: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IdGenerator {
|
||||||
|
pub fn new(module_id: Option<ModuleId>) -> Self {
|
||||||
|
Self { module_id, next_id: 0 }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next_uuid(&mut self) -> uuid::Uuid {
|
||||||
|
let next_id = self.next_id;
|
||||||
|
|
||||||
|
let next = format!(
|
||||||
|
"{} {}",
|
||||||
|
self.module_id.map(|id| id.to_string()).unwrap_or("none".to_string()),
|
||||||
|
next_id
|
||||||
|
);
|
||||||
|
let next_uuid = uuid::Uuid::new_v5(&NAMESPACE_KCL, next.as_bytes());
|
||||||
|
|
||||||
|
self.next_id += 1;
|
||||||
|
|
||||||
|
next_uuid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_id_generator() {
|
||||||
|
let mut generator = IdGenerator::new(Some(ModuleId::default()));
|
||||||
|
|
||||||
|
let uuid1 = generator.next_uuid();
|
||||||
|
let uuid2 = generator.next_uuid();
|
||||||
|
|
||||||
|
assert_ne!(uuid1, uuid2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Test that the same generator produces the same UUIDs.
|
||||||
|
fn test_id_generator_stable() {
|
||||||
|
let mut generator = IdGenerator::new(Some(ModuleId::default()));
|
||||||
|
|
||||||
|
let uuid1 = generator.next_uuid();
|
||||||
|
let uuid2 = generator.next_uuid();
|
||||||
|
|
||||||
|
let mut generator = IdGenerator::new(Some(ModuleId::default()));
|
||||||
|
|
||||||
|
let uuid3 = generator.next_uuid();
|
||||||
|
let uuid4 = generator.next_uuid();
|
||||||
|
|
||||||
|
assert_eq!(uuid1, uuid3);
|
||||||
|
assert_eq!(uuid2, uuid4);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
// Generate 20 uuids and make sure all are unique.
|
||||||
|
fn test_id_generator_unique() {
|
||||||
|
let mut generator = IdGenerator::new(Some(ModuleId::default()));
|
||||||
|
|
||||||
|
let mut uuids = Vec::new();
|
||||||
|
|
||||||
|
for _ in 0..20 {
|
||||||
|
uuids.push(generator.next_uuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..uuids.len() {
|
||||||
|
for j in i + 1..uuids.len() {
|
||||||
|
assert_ne!(uuids[i], uuids[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,13 +6,12 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
memory::{self, EnvironmentRef},
|
memory::{self, EnvironmentRef},
|
||||||
MetaSettings,
|
MetaSettings, Point3d,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclErrorDetails,
|
errors::KclErrorDetails,
|
||||||
execution::{
|
execution::{
|
||||||
ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, SketchSet, Solid, SolidSet,
|
ExecState, ExecutorContext, Face, Helix, ImportedGeometry, Metadata, Plane, Sketch, Solid, TagIdentifier,
|
||||||
TagIdentifier,
|
|
||||||
},
|
},
|
||||||
parsing::{
|
parsing::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
@ -21,7 +20,10 @@ use crate::{
|
|||||||
},
|
},
|
||||||
token::NumericSuffix,
|
token::NumericSuffix,
|
||||||
},
|
},
|
||||||
std::{args::Arg, StdFnProps},
|
std::{
|
||||||
|
args::{Arg, FromKclValue},
|
||||||
|
StdFnProps,
|
||||||
|
},
|
||||||
CompilationError, KclError, ModuleId, SourceRange,
|
CompilationError, KclError, ModuleId, SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -58,6 +60,13 @@ pub enum KclValue {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
},
|
},
|
||||||
|
// An array where all values have a shared type (not necessarily the same principal type).
|
||||||
|
HomArray {
|
||||||
|
value: Vec<KclValue>,
|
||||||
|
// The type of values, not the array type.
|
||||||
|
#[serde(skip)]
|
||||||
|
ty: PrimitiveType,
|
||||||
|
},
|
||||||
Object {
|
Object {
|
||||||
value: KclObjectFields,
|
value: KclObjectFields,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
@ -74,15 +83,9 @@ pub enum KclValue {
|
|||||||
Sketch {
|
Sketch {
|
||||||
value: Box<Sketch>,
|
value: Box<Sketch>,
|
||||||
},
|
},
|
||||||
Sketches {
|
|
||||||
value: Vec<Box<Sketch>>,
|
|
||||||
},
|
|
||||||
Solid {
|
Solid {
|
||||||
value: Box<Solid>,
|
value: Box<Solid>,
|
||||||
},
|
},
|
||||||
Solids {
|
|
||||||
value: Vec<Box<Solid>>,
|
|
||||||
},
|
|
||||||
Helix {
|
Helix {
|
||||||
value: Box<Helix>,
|
value: Box<Helix>,
|
||||||
},
|
},
|
||||||
@ -111,12 +114,6 @@ pub enum KclValue {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
},
|
},
|
||||||
// Only used for memory management. Should never be visible outside of the memory module.
|
|
||||||
Tombstone {
|
|
||||||
value: (),
|
|
||||||
#[serde(skip)]
|
|
||||||
meta: Vec<Metadata>,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Default)]
|
#[derive(Debug, Clone, PartialEq, Default)]
|
||||||
@ -145,48 +142,46 @@ impl JsonSchema for FunctionSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SketchSet> for KclValue {
|
impl From<Vec<Sketch>> for KclValue {
|
||||||
fn from(sg: SketchSet) -> Self {
|
fn from(mut eg: Vec<Sketch>) -> Self {
|
||||||
match sg {
|
|
||||||
SketchSet::Sketch(value) => KclValue::Sketch { value },
|
|
||||||
SketchSet::Sketches(value) => KclValue::Sketches { value },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Box<Sketch>>> for KclValue {
|
|
||||||
fn from(sg: Vec<Box<Sketch>>) -> Self {
|
|
||||||
KclValue::Sketches { value: sg }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<SolidSet> for KclValue {
|
|
||||||
fn from(eg: SolidSet) -> Self {
|
|
||||||
match eg {
|
|
||||||
SolidSet::Solid(eg) => KclValue::Solid { value: eg },
|
|
||||||
SolidSet::Solids(egs) => KclValue::Solids { value: egs },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Vec<Box<Solid>>> for KclValue {
|
|
||||||
fn from(eg: Vec<Box<Solid>>) -> Self {
|
|
||||||
if eg.len() == 1 {
|
if eg.len() == 1 {
|
||||||
KclValue::Solid { value: eg[0].clone() }
|
KclValue::Sketch {
|
||||||
|
value: Box::new(eg.pop().unwrap()),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
KclValue::Solids { value: eg }
|
KclValue::HomArray {
|
||||||
|
value: eg
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| KclValue::Sketch { value: Box::new(s) })
|
||||||
|
.collect(),
|
||||||
|
ty: crate::execution::PrimitiveType::Sketch,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<Vec<Solid>> for KclValue {
|
||||||
|
fn from(mut eg: Vec<Solid>) -> Self {
|
||||||
|
if eg.len() == 1 {
|
||||||
|
KclValue::Solid {
|
||||||
|
value: Box::new(eg.pop().unwrap()),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
KclValue::HomArray {
|
||||||
|
value: eg.into_iter().map(|s| KclValue::Solid { value: Box::new(s) }).collect(),
|
||||||
|
ty: crate::execution::PrimitiveType::Solid,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<KclValue> for Vec<SourceRange> {
|
impl From<KclValue> for Vec<SourceRange> {
|
||||||
fn from(item: KclValue) -> Self {
|
fn from(item: KclValue) -> Self {
|
||||||
match item {
|
match item {
|
||||||
KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
|
KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
|
||||||
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
||||||
KclValue::Solid { value } => to_vec_sr(&value.meta),
|
KclValue::Solid { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
|
||||||
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
|
||||||
KclValue::Helix { value } => to_vec_sr(&value.meta),
|
KclValue::Helix { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
||||||
KclValue::Function { meta, .. } => to_vec_sr(&meta),
|
KclValue::Function { meta, .. } => to_vec_sr(&meta),
|
||||||
@ -196,12 +191,12 @@ impl From<KclValue> for Vec<SourceRange> {
|
|||||||
KclValue::Number { meta, .. } => to_vec_sr(&meta),
|
KclValue::Number { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
|
KclValue::MixedArray { meta, .. } => to_vec_sr(&meta),
|
||||||
|
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
||||||
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
|
KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Type { meta, .. } => to_vec_sr(&meta),
|
KclValue::Type { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
|
KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
|
||||||
KclValue::Tombstone { .. } => unreachable!("Tombstone SourceRange"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,9 +211,7 @@ impl From<&KclValue> for Vec<SourceRange> {
|
|||||||
KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
|
KclValue::TagDeclarator(t) => vec![SourceRange::new(t.start, t.end, t.module_id)],
|
||||||
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
KclValue::TagIdentifier(t) => to_vec_sr(&t.meta),
|
||||||
KclValue::Solid { value } => to_vec_sr(&value.meta),
|
KclValue::Solid { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::Solids { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
|
||||||
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
KclValue::Sketch { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::Sketches { value } => value.iter().flat_map(|eg| to_vec_sr(&eg.meta)).collect(),
|
|
||||||
KclValue::Helix { value } => to_vec_sr(&value.meta),
|
KclValue::Helix { value } => to_vec_sr(&value.meta),
|
||||||
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
KclValue::ImportedGeometry(i) => to_vec_sr(&i.meta),
|
||||||
KclValue::Function { meta, .. } => to_vec_sr(meta),
|
KclValue::Function { meta, .. } => to_vec_sr(meta),
|
||||||
@ -229,11 +222,11 @@ impl From<&KclValue> for Vec<SourceRange> {
|
|||||||
KclValue::String { meta, .. } => to_vec_sr(meta),
|
KclValue::String { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
|
KclValue::MixedArray { meta, .. } => to_vec_sr(meta),
|
||||||
|
KclValue::HomArray { value, .. } => value.iter().flat_map(Into::<Vec<SourceRange>>::into).collect(),
|
||||||
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::KclNone { meta, .. } => to_vec_sr(meta),
|
KclValue::KclNone { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Type { meta, .. } => to_vec_sr(meta),
|
KclValue::Type { meta, .. } => to_vec_sr(meta),
|
||||||
KclValue::Tombstone { .. } => unreachable!("Tombstone &SourceRange"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -253,22 +246,20 @@ impl KclValue {
|
|||||||
KclValue::Number { meta, .. } => meta.clone(),
|
KclValue::Number { meta, .. } => meta.clone(),
|
||||||
KclValue::String { value: _, meta } => meta.clone(),
|
KclValue::String { value: _, meta } => meta.clone(),
|
||||||
KclValue::MixedArray { value: _, meta } => meta.clone(),
|
KclValue::MixedArray { value: _, meta } => meta.clone(),
|
||||||
|
KclValue::HomArray { value, .. } => value.iter().flat_map(|v| v.metadata()).collect(),
|
||||||
KclValue::Object { value: _, meta } => meta.clone(),
|
KclValue::Object { value: _, meta } => meta.clone(),
|
||||||
KclValue::TagIdentifier(x) => x.meta.clone(),
|
KclValue::TagIdentifier(x) => x.meta.clone(),
|
||||||
KclValue::TagDeclarator(x) => vec![x.metadata()],
|
KclValue::TagDeclarator(x) => vec![x.metadata()],
|
||||||
KclValue::Plane { value } => value.meta.clone(),
|
KclValue::Plane { value } => value.meta.clone(),
|
||||||
KclValue::Face { value } => value.meta.clone(),
|
KclValue::Face { value } => value.meta.clone(),
|
||||||
KclValue::Sketch { value } => value.meta.clone(),
|
KclValue::Sketch { value } => value.meta.clone(),
|
||||||
KclValue::Sketches { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
|
||||||
KclValue::Solid { value } => value.meta.clone(),
|
KclValue::Solid { value } => value.meta.clone(),
|
||||||
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
|
||||||
KclValue::Helix { value } => value.meta.clone(),
|
KclValue::Helix { value } => value.meta.clone(),
|
||||||
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
||||||
KclValue::Function { meta, .. } => meta.clone(),
|
KclValue::Function { meta, .. } => meta.clone(),
|
||||||
KclValue::Module { meta, .. } => meta.clone(),
|
KclValue::Module { meta, .. } => meta.clone(),
|
||||||
KclValue::KclNone { meta, .. } => meta.clone(),
|
KclValue::KclNone { meta, .. } => meta.clone(),
|
||||||
KclValue::Type { meta, .. } => meta.clone(),
|
KclValue::Type { meta, .. } => meta.clone(),
|
||||||
KclValue::Tombstone { .. } => unreachable!("Tombstone Metadata"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,29 +276,6 @@ impl KclValue {
|
|||||||
Some(ast.as_source_range())
|
Some(ast.as_source_range())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_solid_set(&self) -> Result<SolidSet> {
|
|
||||||
match self {
|
|
||||||
KclValue::Solid { value } => Ok(SolidSet::Solid(value.clone())),
|
|
||||||
KclValue::Solids { value } => Ok(SolidSet::Solids(value.clone())),
|
|
||||||
KclValue::MixedArray { value, .. } => {
|
|
||||||
let solids: Vec<_> = value
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, v)| {
|
|
||||||
v.as_solid().map(|v| v.to_owned()).map(Box::new).ok_or_else(|| {
|
|
||||||
anyhow::anyhow!(
|
|
||||||
"expected this array to only contain solids, but element {i} was actually {}",
|
|
||||||
v.human_friendly_type()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect::<Result<_, _>>()?;
|
|
||||||
Ok(SolidSet::Solids(solids))
|
|
||||||
}
|
|
||||||
_ => anyhow::bail!("Not a solid or solids: {:?}", self),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn none() -> Self {
|
pub(crate) fn none() -> Self {
|
||||||
Self::KclNone {
|
Self::KclNone {
|
||||||
@ -324,9 +292,7 @@ impl KclValue {
|
|||||||
KclValue::TagDeclarator(_) => "TagDeclarator",
|
KclValue::TagDeclarator(_) => "TagDeclarator",
|
||||||
KclValue::TagIdentifier(_) => "TagIdentifier",
|
KclValue::TagIdentifier(_) => "TagIdentifier",
|
||||||
KclValue::Solid { .. } => "Solid",
|
KclValue::Solid { .. } => "Solid",
|
||||||
KclValue::Solids { .. } => "Solids",
|
|
||||||
KclValue::Sketch { .. } => "Sketch",
|
KclValue::Sketch { .. } => "Sketch",
|
||||||
KclValue::Sketches { .. } => "Sketches",
|
|
||||||
KclValue::Helix { .. } => "Helix",
|
KclValue::Helix { .. } => "Helix",
|
||||||
KclValue::ImportedGeometry(_) => "ImportedGeometry",
|
KclValue::ImportedGeometry(_) => "ImportedGeometry",
|
||||||
KclValue::Function { .. } => "Function",
|
KclValue::Function { .. } => "Function",
|
||||||
@ -336,11 +302,11 @@ impl KclValue {
|
|||||||
KclValue::Number { .. } => "number",
|
KclValue::Number { .. } => "number",
|
||||||
KclValue::String { .. } => "string (text)",
|
KclValue::String { .. } => "string (text)",
|
||||||
KclValue::MixedArray { .. } => "array (list)",
|
KclValue::MixedArray { .. } => "array (list)",
|
||||||
|
KclValue::HomArray { .. } => "array (list)",
|
||||||
KclValue::Object { .. } => "object",
|
KclValue::Object { .. } => "object",
|
||||||
KclValue::Module { .. } => "module",
|
KclValue::Module { .. } => "module",
|
||||||
KclValue::Type { .. } => "type",
|
KclValue::Type { .. } => "type",
|
||||||
KclValue::KclNone { .. } => "None",
|
KclValue::KclNone { .. } => "None",
|
||||||
KclValue::Tombstone { .. } => "TOMBSTONE",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,16 +333,14 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn map_env_ref(&self, env_map: &HashMap<EnvironmentRef, EnvironmentRef>) -> Self {
|
pub(crate) fn map_env_ref(&self, old_env: usize, new_env: usize) -> Self {
|
||||||
let mut result = self.clone();
|
let mut result = self.clone();
|
||||||
if let KclValue::Function {
|
if let KclValue::Function {
|
||||||
value: FunctionSource::User { ref mut memory, .. },
|
value: FunctionSource::User { ref mut memory, .. },
|
||||||
..
|
..
|
||||||
} = result
|
} = result
|
||||||
{
|
{
|
||||||
if let Some(new) = env_map.get(memory) {
|
memory.replace_env(old_env, new_env);
|
||||||
*memory = *new;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
@ -501,6 +465,21 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_sketch(&mut self) -> Option<&mut Sketch> {
|
||||||
|
if let KclValue::Sketch { value } = self {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_mut_tag(&mut self) -> Option<&mut TagIdentifier> {
|
||||||
|
if let KclValue::TagIdentifier(value) = self {
|
||||||
|
Some(value)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
pub fn as_f64(&self) -> Option<f64> {
|
pub fn as_f64(&self) -> Option<f64> {
|
||||||
if let KclValue::Number { value, .. } = &self {
|
if let KclValue::Number { value, .. } = &self {
|
||||||
Some(*value)
|
Some(*value)
|
||||||
@ -563,17 +542,6 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an optional tag from a memory item.
|
|
||||||
pub fn get_tag_declarator_opt(&self) -> Result<Option<TagNode>, KclError> {
|
|
||||||
match self {
|
|
||||||
KclValue::TagDeclarator(t) => Ok(Some((**t).clone())),
|
|
||||||
_ => Err(KclError::Semantic(KclErrorDetails {
|
|
||||||
message: format!("Not a tag declarator: {:?}", self),
|
|
||||||
source_ranges: self.clone().into(),
|
|
||||||
})),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If this KCL value is a bool, retrieve it.
|
/// If this KCL value is a bool, retrieve it.
|
||||||
pub fn get_bool(&self) -> Result<bool, KclError> {
|
pub fn get_bool(&self) -> Result<bool, KclError> {
|
||||||
let Self::Bool { value: b, .. } = self else {
|
let Self::Bool { value: b, .. } = self else {
|
||||||
@ -594,6 +562,215 @@ impl KclValue {
|
|||||||
self_ty.subtype(ty)
|
self_ty.subtype(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Coerce `self` to a new value which has `ty` as it's closest supertype.
|
||||||
|
///
|
||||||
|
/// If the result is Some, then:
|
||||||
|
/// - result.principal_type().unwrap().subtype(ty)
|
||||||
|
///
|
||||||
|
/// If self.principal_type() == ty then result == self
|
||||||
|
pub fn coerce(&self, ty: &RuntimeType, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
match ty {
|
||||||
|
RuntimeType::Primitive(ty) => self.coerce_to_primitive_type(ty, exec_state),
|
||||||
|
RuntimeType::Array(ty, len) => self.coerce_to_array_type(ty, *len, exec_state),
|
||||||
|
RuntimeType::Tuple(tys) => self.coerce_to_tuple_type(tys, exec_state),
|
||||||
|
RuntimeType::Union(tys) => self.coerce_to_union_type(tys, exec_state),
|
||||||
|
RuntimeType::Object(tys) => self.coerce_to_object_type(tys, exec_state),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_to_primitive_type(&self, ty: &PrimitiveType, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
let value = match self {
|
||||||
|
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } if value.len() == 1 => &value[0],
|
||||||
|
_ => self,
|
||||||
|
};
|
||||||
|
match ty {
|
||||||
|
// TODO numeric type coercions
|
||||||
|
PrimitiveType::Number(_ty) => match value {
|
||||||
|
KclValue::Number { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::String => match value {
|
||||||
|
KclValue::String { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::Boolean => match value {
|
||||||
|
KclValue::Bool { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::Sketch => match value {
|
||||||
|
KclValue::Sketch { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::Solid => match value {
|
||||||
|
KclValue::Solid { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::Plane => match value {
|
||||||
|
KclValue::Plane { .. } => Some(value.clone()),
|
||||||
|
KclValue::Object { value, meta } => {
|
||||||
|
let origin = value.get("origin").and_then(Point3d::from_kcl_val)?;
|
||||||
|
let x_axis = value.get("xAxis").and_then(Point3d::from_kcl_val)?;
|
||||||
|
let y_axis = value.get("yAxis").and_then(Point3d::from_kcl_val)?;
|
||||||
|
let z_axis = value.get("zAxis").and_then(Point3d::from_kcl_val)?;
|
||||||
|
|
||||||
|
let id = exec_state.mod_local.id_generator.next_uuid();
|
||||||
|
let plane = Plane {
|
||||||
|
id,
|
||||||
|
artifact_id: id.into(),
|
||||||
|
origin,
|
||||||
|
x_axis,
|
||||||
|
y_axis,
|
||||||
|
z_axis,
|
||||||
|
value: super::PlaneType::Uninit,
|
||||||
|
// TODO use length unit from origin
|
||||||
|
units: exec_state.length_unit(),
|
||||||
|
meta: meta.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(KclValue::Plane { value: Box::new(plane) })
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
PrimitiveType::ImportedGeometry => match value {
|
||||||
|
KclValue::ImportedGeometry { .. } => Some(value.clone()),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_to_array_type(&self, ty: &PrimitiveType, len: ArrayLen, exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
match self {
|
||||||
|
KclValue::HomArray { value, ty: aty } => {
|
||||||
|
// TODO could check types of values individually
|
||||||
|
if aty != ty {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = match len {
|
||||||
|
ArrayLen::None => value.clone(),
|
||||||
|
ArrayLen::NonEmpty => {
|
||||||
|
if value.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.clone()
|
||||||
|
}
|
||||||
|
ArrayLen::Known(n) => {
|
||||||
|
if n != value.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
value[..n].to_vec()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(KclValue::HomArray { value, ty: ty.clone() })
|
||||||
|
}
|
||||||
|
KclValue::MixedArray { value, .. } => {
|
||||||
|
let value = match len {
|
||||||
|
ArrayLen::None => value.clone(),
|
||||||
|
ArrayLen::NonEmpty => {
|
||||||
|
if value.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
value.clone()
|
||||||
|
}
|
||||||
|
ArrayLen::Known(n) => {
|
||||||
|
if n != value.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
value[..n].to_vec()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let rt = RuntimeType::Primitive(ty.clone());
|
||||||
|
let value = value
|
||||||
|
.iter()
|
||||||
|
.map(|v| v.coerce(&rt, exec_state))
|
||||||
|
.collect::<Option<Vec<_>>>()?;
|
||||||
|
|
||||||
|
Some(KclValue::HomArray { value, ty: ty.clone() })
|
||||||
|
}
|
||||||
|
KclValue::KclNone { .. } if len.satisfied(0) => Some(KclValue::HomArray {
|
||||||
|
value: Vec::new(),
|
||||||
|
ty: ty.clone(),
|
||||||
|
}),
|
||||||
|
value if len.satisfied(1) => {
|
||||||
|
if value.has_type(&RuntimeType::Primitive(ty.clone())) {
|
||||||
|
Some(KclValue::HomArray {
|
||||||
|
value: vec![value.clone()],
|
||||||
|
ty: ty.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_to_tuple_type(&self, tys: &[PrimitiveType], exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
match self {
|
||||||
|
KclValue::MixedArray { value, .. } | KclValue::HomArray { value, .. } => {
|
||||||
|
if value.len() < tys.len() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for (i, t) in tys.iter().enumerate() {
|
||||||
|
result.push(value[i].coerce_to_primitive_type(t, exec_state)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(KclValue::MixedArray {
|
||||||
|
value: result,
|
||||||
|
meta: Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
KclValue::KclNone { meta, .. } if tys.is_empty() => Some(KclValue::MixedArray {
|
||||||
|
value: Vec::new(),
|
||||||
|
meta: meta.clone(),
|
||||||
|
}),
|
||||||
|
value if tys.len() == 1 => {
|
||||||
|
if value.has_type(&RuntimeType::Primitive(tys[0].clone())) {
|
||||||
|
Some(KclValue::MixedArray {
|
||||||
|
value: vec![value.clone()],
|
||||||
|
meta: Vec::new(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_to_union_type(&self, tys: &[RuntimeType], exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
for t in tys {
|
||||||
|
if let Some(v) = self.coerce(t, exec_state) {
|
||||||
|
return Some(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn coerce_to_object_type(&self, tys: &[(String, RuntimeType)], _exec_state: &mut ExecState) -> Option<KclValue> {
|
||||||
|
match self {
|
||||||
|
KclValue::Object { value, .. } => {
|
||||||
|
for (s, t) in tys {
|
||||||
|
// TODO coerce fields
|
||||||
|
if !value.get(s)?.has_type(t) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO remove non-required fields
|
||||||
|
Some(self.clone())
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn principal_type(&self) -> Option<RuntimeType> {
|
pub fn principal_type(&self) -> Option<RuntimeType> {
|
||||||
match self {
|
match self {
|
||||||
KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
|
KclValue::Bool { .. } => Some(RuntimeType::Primitive(PrimitiveType::Boolean)),
|
||||||
@ -608,26 +785,24 @@ impl KclValue {
|
|||||||
}
|
}
|
||||||
KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
|
KclValue::Plane { .. } => Some(RuntimeType::Primitive(PrimitiveType::Plane)),
|
||||||
KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
|
KclValue::Sketch { .. } => Some(RuntimeType::Primitive(PrimitiveType::Sketch)),
|
||||||
KclValue::Sketches { .. } => Some(RuntimeType::Array(PrimitiveType::Sketch)),
|
|
||||||
KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
|
KclValue::Solid { .. } => Some(RuntimeType::Primitive(PrimitiveType::Solid)),
|
||||||
KclValue::Solids { .. } => Some(RuntimeType::Array(PrimitiveType::Solid)),
|
KclValue::ImportedGeometry(..) => Some(RuntimeType::Primitive(PrimitiveType::ImportedGeometry)),
|
||||||
KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
|
KclValue::MixedArray { value, .. } => Some(RuntimeType::Tuple(
|
||||||
value
|
value
|
||||||
.iter()
|
.iter()
|
||||||
.map(|v| v.principal_type().and_then(RuntimeType::primitive))
|
.map(|v| v.principal_type().and_then(RuntimeType::primitive))
|
||||||
.collect::<Option<Vec<_>>>()?,
|
.collect::<Option<Vec<_>>>()?,
|
||||||
)),
|
)),
|
||||||
|
KclValue::HomArray { ty, value, .. } => Some(RuntimeType::Array(ty.clone(), ArrayLen::Known(value.len()))),
|
||||||
KclValue::Face { .. } => None,
|
KclValue::Face { .. } => None,
|
||||||
KclValue::Helix { .. }
|
KclValue::Helix { .. }
|
||||||
| KclValue::ImportedGeometry(..)
|
|
||||||
| KclValue::Function { .. }
|
| KclValue::Function { .. }
|
||||||
| KclValue::Module { .. }
|
| KclValue::Module { .. }
|
||||||
| KclValue::TagIdentifier(_)
|
| KclValue::TagIdentifier(_)
|
||||||
| KclValue::TagDeclarator(_)
|
| KclValue::TagDeclarator(_)
|
||||||
| KclValue::KclNone { .. }
|
| KclValue::KclNone { .. }
|
||||||
| KclValue::Type { .. }
|
| KclValue::Type { .. }
|
||||||
| KclValue::Uuid { .. }
|
| KclValue::Uuid { .. } => None,
|
||||||
| KclValue::Tombstone { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,20 +904,18 @@ impl KclValue {
|
|||||||
KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
|
KclValue::TagIdentifier(tag) => Some(format!("${}", tag.value)),
|
||||||
// TODO better Array and Object stringification
|
// TODO better Array and Object stringification
|
||||||
KclValue::MixedArray { .. } => Some("[...]".to_owned()),
|
KclValue::MixedArray { .. } => Some("[...]".to_owned()),
|
||||||
|
KclValue::HomArray { .. } => Some("[...]".to_owned()),
|
||||||
KclValue::Object { .. } => Some("{ ... }".to_owned()),
|
KclValue::Object { .. } => Some("{ ... }".to_owned()),
|
||||||
KclValue::Module { .. }
|
KclValue::Module { .. }
|
||||||
| KclValue::Solid { .. }
|
| KclValue::Solid { .. }
|
||||||
| KclValue::Solids { .. }
|
|
||||||
| KclValue::Sketch { .. }
|
| KclValue::Sketch { .. }
|
||||||
| KclValue::Sketches { .. }
|
|
||||||
| KclValue::Helix { .. }
|
| KclValue::Helix { .. }
|
||||||
| KclValue::ImportedGeometry(_)
|
| KclValue::ImportedGeometry(_)
|
||||||
| KclValue::Function { .. }
|
| KclValue::Function { .. }
|
||||||
| KclValue::Plane { .. }
|
| KclValue::Plane { .. }
|
||||||
| KclValue::Face { .. }
|
| KclValue::Face { .. }
|
||||||
| KclValue::KclNone { .. }
|
| KclValue::KclNone { .. }
|
||||||
| KclValue::Type { .. }
|
| KclValue::Type { .. } => None,
|
||||||
| KclValue::Tombstone { .. } => None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -750,7 +923,8 @@ impl KclValue {
|
|||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum RuntimeType {
|
pub enum RuntimeType {
|
||||||
Primitive(PrimitiveType),
|
Primitive(PrimitiveType),
|
||||||
Array(PrimitiveType),
|
Array(PrimitiveType, ArrayLen),
|
||||||
|
Union(Vec<RuntimeType>),
|
||||||
Tuple(Vec<PrimitiveType>),
|
Tuple(Vec<PrimitiveType>),
|
||||||
Object(Vec<(String, RuntimeType)>),
|
Object(Vec<(String, RuntimeType)>),
|
||||||
}
|
}
|
||||||
@ -765,7 +939,9 @@ impl RuntimeType {
|
|||||||
Type::Primitive(pt) => {
|
Type::Primitive(pt) => {
|
||||||
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
|
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Primitive)
|
||||||
}
|
}
|
||||||
Type::Array(pt) => PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(RuntimeType::Array),
|
Type::Array(pt) => {
|
||||||
|
PrimitiveType::from_parsed(pt, exec_state, source_range)?.map(|t| RuntimeType::Array(t, ArrayLen::None))
|
||||||
|
}
|
||||||
Type::Object { properties } => properties
|
Type::Object { properties } => properties
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
@ -781,15 +957,37 @@ impl RuntimeType {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn human_friendly_type(&self) -> String {
|
||||||
|
match self {
|
||||||
|
RuntimeType::Primitive(ty) => ty.to_string(),
|
||||||
|
RuntimeType::Array(ty, ArrayLen::None) => format!("an array of {}", ty.display_multiple()),
|
||||||
|
RuntimeType::Array(ty, ArrayLen::NonEmpty) => format!("one or more {}", ty.display_multiple()),
|
||||||
|
RuntimeType::Array(ty, ArrayLen::Known(n)) => format!("an array of {n} {}", ty.display_multiple()),
|
||||||
|
RuntimeType::Union(tys) => tys
|
||||||
|
.iter()
|
||||||
|
.map(Self::human_friendly_type)
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" or "),
|
||||||
|
RuntimeType::Tuple(tys) => format!(
|
||||||
|
"an array with values of types ({})",
|
||||||
|
tys.iter().map(PrimitiveType::to_string).collect::<Vec<_>>().join(", ")
|
||||||
|
),
|
||||||
|
RuntimeType::Object(_) => format!("an object with fields {}", self),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Subtype with no coercion, including refining numeric types.
|
// Subtype with no coercion, including refining numeric types.
|
||||||
fn subtype(&self, sup: &RuntimeType) -> bool {
|
fn subtype(&self, sup: &RuntimeType) -> bool {
|
||||||
use RuntimeType::*;
|
use RuntimeType::*;
|
||||||
|
|
||||||
match (self, sup) {
|
match (self, sup) {
|
||||||
|
(Primitive(t1), Primitive(t2)) => t1 == t2,
|
||||||
// TODO arrays could be covariant
|
// TODO arrays could be covariant
|
||||||
(Primitive(t1), Primitive(t2)) | (Array(t1), Array(t2)) => t1 == t2,
|
(Array(t1, l1), Array(t2, l2)) => t1 == t2 && l1.subtype(*l2),
|
||||||
(Tuple(t1), Tuple(t2)) => t1 == t2,
|
(Tuple(t1), Tuple(t2)) => t1 == t2,
|
||||||
(Tuple(t1), Array(t2)) => t1.iter().all(|t| t == t2),
|
(Tuple(t1), Array(t2, l2)) => (l2.satisfied(t1.len())) && t1.iter().all(|t| t == t2),
|
||||||
|
(Union(ts1), Union(ts2)) => ts1.iter().all(|t| ts2.contains(t)),
|
||||||
|
(t1, Union(ts2)) => ts2.contains(t1),
|
||||||
// TODO record subtyping - subtype can be larger, fields can be covariant.
|
// TODO record subtyping - subtype can be larger, fields can be covariant.
|
||||||
(Object(t1), Object(t2)) => t1 == t2,
|
(Object(t1), Object(t2)) => t1 == t2,
|
||||||
_ => false,
|
_ => false,
|
||||||
@ -808,12 +1006,21 @@ impl fmt::Display for RuntimeType {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
RuntimeType::Primitive(t) => t.fmt(f),
|
RuntimeType::Primitive(t) => t.fmt(f),
|
||||||
RuntimeType::Array(t) => write!(f, "[{t}]"),
|
RuntimeType::Array(t, l) => match l {
|
||||||
|
ArrayLen::None => write!(f, "[{t}]"),
|
||||||
|
ArrayLen::NonEmpty => write!(f, "[{t}; 1+]"),
|
||||||
|
ArrayLen::Known(n) => write!(f, "[{t}; {n}]"),
|
||||||
|
},
|
||||||
RuntimeType::Tuple(ts) => write!(
|
RuntimeType::Tuple(ts) => write!(
|
||||||
f,
|
f,
|
||||||
"[{}]",
|
"[{}]",
|
||||||
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
|
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(", ")
|
||||||
),
|
),
|
||||||
|
RuntimeType::Union(ts) => write!(
|
||||||
|
f,
|
||||||
|
"{}",
|
||||||
|
ts.iter().map(|t| t.to_string()).collect::<Vec<_>>().join(" | ")
|
||||||
|
),
|
||||||
RuntimeType::Object(items) => write!(
|
RuntimeType::Object(items) => write!(
|
||||||
f,
|
f,
|
||||||
"{{ {} }}",
|
"{{ {} }}",
|
||||||
@ -827,6 +1034,34 @@ impl fmt::Display for RuntimeType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum ArrayLen {
|
||||||
|
None,
|
||||||
|
NonEmpty,
|
||||||
|
Known(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArrayLen {
|
||||||
|
pub fn subtype(self, other: ArrayLen) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(_, ArrayLen::None) => true,
|
||||||
|
(ArrayLen::NonEmpty, ArrayLen::NonEmpty) => true,
|
||||||
|
(ArrayLen::Known(size), ArrayLen::NonEmpty) if size > 0 => true,
|
||||||
|
(ArrayLen::Known(s1), ArrayLen::Known(s2)) if s1 == s2 => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// True if the length constraint is satisfied by the supplied length.
|
||||||
|
fn satisfied(self, len: usize) -> bool {
|
||||||
|
match self {
|
||||||
|
ArrayLen::None => true,
|
||||||
|
ArrayLen::NonEmpty => len > 0,
|
||||||
|
ArrayLen::Known(s) => len == s,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum PrimitiveType {
|
pub enum PrimitiveType {
|
||||||
Number(NumericType),
|
Number(NumericType),
|
||||||
@ -835,6 +1070,7 @@ pub enum PrimitiveType {
|
|||||||
Sketch,
|
Sketch,
|
||||||
Solid,
|
Solid,
|
||||||
Plane,
|
Plane,
|
||||||
|
ImportedGeometry,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrimitiveType {
|
impl PrimitiveType {
|
||||||
@ -866,6 +1102,19 @@ impl PrimitiveType {
|
|||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn display_multiple(&self) -> String {
|
||||||
|
match self {
|
||||||
|
PrimitiveType::Number(NumericType::Known(unit)) => format!("numbers({unit})"),
|
||||||
|
PrimitiveType::Number(_) => "numbers".to_owned(),
|
||||||
|
PrimitiveType::String => "strings".to_owned(),
|
||||||
|
PrimitiveType::Boolean => "bools".to_owned(),
|
||||||
|
PrimitiveType::Sketch => "Sketches".to_owned(),
|
||||||
|
PrimitiveType::Solid => "Solids".to_owned(),
|
||||||
|
PrimitiveType::Plane => "Planes".to_owned(),
|
||||||
|
PrimitiveType::ImportedGeometry => "imported geometries".to_owned(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for PrimitiveType {
|
impl fmt::Display for PrimitiveType {
|
||||||
@ -878,6 +1127,7 @@ impl fmt::Display for PrimitiveType {
|
|||||||
PrimitiveType::Sketch => write!(f, "Sketch"),
|
PrimitiveType::Sketch => write!(f, "Sketch"),
|
||||||
PrimitiveType::Solid => write!(f, "Solid"),
|
PrimitiveType::Solid => write!(f, "Solid"),
|
||||||
PrimitiveType::Plane => write!(f, "Plane"),
|
PrimitiveType::Plane => write!(f, "Plane"),
|
||||||
|
PrimitiveType::ImportedGeometry => write!(f, "imported geometry"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,18 +6,18 @@
|
|||||||
//! one per execution. It has no explicit support for caching between executions.
|
//! one per execution. It has no explicit support for caching between executions.
|
||||||
//!
|
//!
|
||||||
//! Memory is mostly immutable (since KCL does not support mutation or reassignment). However, tags
|
//! Memory is mostly immutable (since KCL does not support mutation or reassignment). However, tags
|
||||||
//! may change as code is executed and that mutates memory. Therefore,
|
//! may change as code is executed and that mutates memory. Therefore to some extent,
|
||||||
//! ProgramMemory supports mutability and does not rely on KCL's (mostly) immutable nature.
|
//! ProgramMemory supports mutability and does not rely on KCL's (mostly) immutable nature.
|
||||||
//!
|
//!
|
||||||
//! ProgramMemory is observably monotonic, i.e., it only grows and even when we pop a stack frame,
|
//! ProgramMemory is observably monotonic, i.e., it only grows and even when we pop a stack frame,
|
||||||
//! the frame is retained unless we can prove it is unreferenced. We remove some values which we
|
//! the frame is retained unless we can prove it is unreferenced. We remove some values which we
|
||||||
//! know cannot be referenced, but we should in the future do better garbage collection (of values
|
//! know cannot be referenced, but we should in the future do better garbage collection (of values
|
||||||
//! and envs).
|
//! and envs).
|
||||||
//!
|
//!
|
||||||
//! ## Concepts
|
//! ## Concepts
|
||||||
//!
|
//!
|
||||||
//! There are three main moving parts for ProgramMemory: environments, snapshots, and stacks. I'll
|
//! There are three main moving parts for ProgramMemory: environments, epochs, and stacks. I'll
|
||||||
//! cover environments (and the call stack) first as if snapshots didn't exist, then describe snapshots.
|
//! cover environments (and the call stack) first as if epochs didn't exist, then describe epochs.
|
||||||
//!
|
//!
|
||||||
//! An environment is a set of bindings (i.e., a map from names to values). Environments handle
|
//! An environment is a set of bindings (i.e., a map from names to values). Environments handle
|
||||||
//! both scoping and context switching. A new lexical scope means a new environment. Nesting of scopes
|
//! both scoping and context switching. A new lexical scope means a new environment. Nesting of scopes
|
||||||
@ -81,12 +81,25 @@
|
|||||||
//! temporally) the definition of `c`. (Note that although KCL does not permit mutation, objects
|
//! temporally) the definition of `c`. (Note that although KCL does not permit mutation, objects
|
||||||
//! can change due to the way tags are implemented).
|
//! can change due to the way tags are implemented).
|
||||||
//!
|
//!
|
||||||
//! To make this work, when we save a reference to an enclosing scope we take a snapshot of memory at
|
//! To make this work, we have the concept of an epoch. An epoch is a simple, global, monotonic counter
|
||||||
//! that point and save a reference to that snapshot. When we call a function, the parent of the new
|
//! which is incremented at any significant moment in execution (we use the term snapshot). When a
|
||||||
//! callee env is that snapshot, not the current version of the enclosing scope.
|
//! value is saved in memory we also save the epoch at which it was stored.
|
||||||
//!
|
//!
|
||||||
//! Entering an inline scope (e.g., the body of an `if` statement) means pushing an env whose parent
|
//! When we save a reference to an enclosing scope we take a snapshot and save that epoch as part of
|
||||||
//! is the current env. We don't need to snapshot in this case.
|
//! the reference. When we call a function, we use the epoch when it was defined to look up variables,
|
||||||
|
//! ignoring any variables which have a creation time later than the saved epoch.
|
||||||
|
//!
|
||||||
|
//! Because the callee could create new variables (with a creation time of the current epoch) which
|
||||||
|
//! the callee should be able to read, we can't simply check the epoch with the callees (and we'd need
|
||||||
|
//! to maintain a stack of callee epochs for further calls, etc.). Instead a stack frame consists of
|
||||||
|
//! a reference to an environment and an epoch at which reads should take place. When we call a function
|
||||||
|
//! this creates a new env using the current epoch, and it's parent env (which is the enclosing scope
|
||||||
|
//! of the function declaration) includes the epoch at which the function was declared.
|
||||||
|
//!
|
||||||
|
//! So far, this handles variables created after a function is declared, but does not handle mutation.
|
||||||
|
//! Mutation must be handled internally in values, see for example `TagIdentifier`. It is suggested
|
||||||
|
//! that objects rely on epochs for this. Since epochs are linked to the stack frame, only objects in
|
||||||
|
//! the current stack frame should be mutated.
|
||||||
//!
|
//!
|
||||||
//! ### Std
|
//! ### Std
|
||||||
//!
|
//!
|
||||||
@ -107,53 +120,17 @@
|
|||||||
//! Pushing and popping stack frames is straightforward. Most get/set/update operations don't touch
|
//! Pushing and popping stack frames is straightforward. Most get/set/update operations don't touch
|
||||||
//! the call stack other than the current env (updating tags on function return is the exception).
|
//! the call stack other than the current env (updating tags on function return is the exception).
|
||||||
//!
|
//!
|
||||||
//! Snapshots are maintained within an environment and are always specific to an environment. Snapshots
|
|
||||||
//! must also have a parent reference (since they are logically a snapshot of all memory). This parent
|
|
||||||
//! refers to a snapshot within the parent env. When a snapshot is created, we must create a snapshot
|
|
||||||
//! object for each parent env. When using a snapshot we must check the parent snapshot whenever
|
|
||||||
//! we check the parent env (and not the current version of the parent env).
|
|
||||||
//!
|
|
||||||
//! An environment will have many snapshots, they are kept in time order, and do not reference each
|
|
||||||
//! other. (The parent of a snapshot is always in another env).
|
|
||||||
//!
|
|
||||||
//! A snapshot is created empty (we don't copy memory) and we use a copy-on-write design: when a
|
|
||||||
//! value in an environment is modified, we copy the old version into the most recent snapshot (note
|
|
||||||
//! that we never overwrite a value in the snapshot, if a value is modified multiple times, we want
|
|
||||||
//! to keep the original version, not an intermediate one). Likewise, if we insert a new variable,
|
|
||||||
//! we put a tombstone value in the snapshot.
|
|
||||||
//!
|
|
||||||
//! When we read from the current version of an environment, we simply read from the bindings in the
|
|
||||||
//! env and ignore the snapshots. When we read from a snapshot, we first check the specific snapshot
|
|
||||||
//! for the key, then check any newer snapshots, then finally check the env bindings.
|
|
||||||
//!
|
|
||||||
//! A minor optimisation is that when creating a snapshot, if the previous one is empty, then
|
|
||||||
//! we can reuse that rather than creating a new one. Since we only create a snapshot when a function
|
|
||||||
//! is declared and the function decl is immediately saved into the new snapshot, the empty snapshot
|
|
||||||
//! optimisation only happens with parent snapshots (though if the env tree is deep this means we
|
|
||||||
//! can save a lot of snapshots).
|
|
||||||
//!
|
|
||||||
//! ## Invariants
|
//! ## Invariants
|
||||||
//!
|
//!
|
||||||
//! There's obviously a bunch of invariants in this design, some are kinda obvious, some are limited
|
//! There's obviously a bunch of invariants in this design, some are kinda obvious, some are limited
|
||||||
//! in scope and are documented inline, here are some others:
|
//! in scope and are documented inline, here are some others:
|
||||||
//!
|
//!
|
||||||
//! - The current env and all envs in the call stack are 'just envs', never a snapshot (we could
|
|
||||||
//! use just a ref to an env, rather than to a snapshot but this is pretty inconvenient, so just
|
|
||||||
//! know that the snapshot ref is always to the current version). Only the parent envs or saved refs
|
|
||||||
//! can be refs to snapshots.
|
|
||||||
//! - We only ever write into the current env, never into any parent envs (though we can read from
|
//! - We only ever write into the current env, never into any parent envs (though we can read from
|
||||||
//! both).
|
//! both).
|
||||||
//! - Therefore, there is no concept of writing into a snapshot, only reading from one.
|
//! - We only ever write (or mutate) at the most recent epoch, never at an older one.
|
||||||
//! - The env ref saved with a function decl is always to a snapshot, never to the current version.
|
//! - The env ref saved with a function decl is always to an historic epoch, never to the current one.
|
||||||
//! - If there are no snapshots in an environment and it is no longer in the call stack, then there
|
|
||||||
//! are no references from function decls to the env (if it is the parent of an env with extant refs
|
|
||||||
//! then there would be snapshots in the child env and that implies there must be a snapshot in the
|
|
||||||
//! parent to be the parent of that snapshot).
|
|
||||||
//! - Since KCL does not have submodules and decls are not visible outside of a nested scope, all
|
//! - Since KCL does not have submodules and decls are not visible outside of a nested scope, all
|
||||||
//! references to variables in other modules must be in the root scope of a module.
|
//! references to variables in other modules must be in the root scope of a module.
|
||||||
//! - Therefore, an active env must either be on the call stack, have snapshots, or be a root env. This
|
|
||||||
//! is however a conservative approximation since snapshots may exist even if there are no live
|
|
||||||
//! references to an env.
|
|
||||||
//!
|
//!
|
||||||
//! ## Concurrency and thread-safety
|
//! ## Concurrency and thread-safety
|
||||||
//!
|
//!
|
||||||
@ -227,7 +204,6 @@
|
|||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
cell::UnsafeCell,
|
cell::UnsafeCell,
|
||||||
collections::HashMap,
|
|
||||||
fmt,
|
fmt,
|
||||||
pin::Pin,
|
pin::Pin,
|
||||||
sync::{
|
sync::{
|
||||||
@ -267,6 +243,7 @@ pub(crate) struct ProgramMemory {
|
|||||||
/// Statistics about the memory, should not be used for anything other than meta-info.
|
/// Statistics about the memory, should not be used for anything other than meta-info.
|
||||||
pub(crate) stats: MemoryStats,
|
pub(crate) stats: MemoryStats,
|
||||||
next_stack_id: AtomicUsize,
|
next_stack_id: AtomicUsize,
|
||||||
|
epoch: AtomicUsize,
|
||||||
write_lock: AtomicBool,
|
write_lock: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -307,7 +284,7 @@ impl fmt::Display for Stack {
|
|||||||
.call_stack
|
.call_stack
|
||||||
.iter()
|
.iter()
|
||||||
.chain(Some(&self.current_env))
|
.chain(Some(&self.current_env))
|
||||||
.map(|e| format!("EnvRef({}, {})", e.0, e.1 .0))
|
.map(|e| format!("EnvRef({}, {})", e.0, e.1))
|
||||||
.collect();
|
.collect();
|
||||||
write!(f, "Stack {}\nstack frames:\n{}", self.id, stack.join("\n"))
|
write!(f, "Stack {}\nstack frames:\n{}", self.id, stack.join("\n"))
|
||||||
}
|
}
|
||||||
@ -322,6 +299,7 @@ impl ProgramMemory {
|
|||||||
std: None,
|
std: None,
|
||||||
stats: MemoryStats::default(),
|
stats: MemoryStats::default(),
|
||||||
next_stack_id: AtomicUsize::new(1),
|
next_stack_id: AtomicUsize::new(1),
|
||||||
|
epoch: AtomicUsize::new(1),
|
||||||
write_lock: AtomicBool::new(false),
|
write_lock: AtomicBool::new(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -340,10 +318,12 @@ impl ProgramMemory {
|
|||||||
std: self.std,
|
std: self.std,
|
||||||
stats: MemoryStats::default(),
|
stats: MemoryStats::default(),
|
||||||
next_stack_id: AtomicUsize::new(self.next_stack_id.load(Ordering::Relaxed)),
|
next_stack_id: AtomicUsize::new(self.next_stack_id.load(Ordering::Relaxed)),
|
||||||
|
epoch: AtomicUsize::new(self.epoch.load(Ordering::Relaxed)),
|
||||||
write_lock: AtomicBool::new(false),
|
write_lock: AtomicBool::new(false),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new stack object referencing this `ProgramMemory`.
|
||||||
pub fn new_stack(self: Arc<Self>) -> Stack {
|
pub fn new_stack(self: Arc<Self>) -> Stack {
|
||||||
let id = self.next_stack_id.fetch_add(1, Ordering::Relaxed);
|
let id = self.next_stack_id.fetch_add(1, Ordering::Relaxed);
|
||||||
assert!(id > 0);
|
assert!(id > 0);
|
||||||
@ -367,7 +347,7 @@ impl ProgramMemory {
|
|||||||
self.std.is_none()
|
self.std.is_none()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a value from a specific snapshot of the memory.
|
/// Get a value from a specific environment of the memory at a specific point in time.
|
||||||
pub fn get_from(
|
pub fn get_from(
|
||||||
&self,
|
&self,
|
||||||
var: &str,
|
var: &str,
|
||||||
@ -438,7 +418,7 @@ impl ProgramMemory {
|
|||||||
|
|
||||||
let new_env = Environment::new(parent, is_root_env, owner);
|
let new_env = Environment::new(parent, is_root_env, owner);
|
||||||
self.with_envs(|envs| {
|
self.with_envs(|envs| {
|
||||||
let result = EnvironmentRef(envs.len(), SnapshotRef::none());
|
let result = EnvironmentRef(envs.len(), usize::MAX);
|
||||||
// Note this might reallocate, which would hold the `with_envs` spin lock for way too long
|
// Note this might reallocate, which would hold the `with_envs` spin lock for way too long
|
||||||
// so somehow we should make sure we don't do that (though honestly the chance of that
|
// so somehow we should make sure we don't do that (though honestly the chance of that
|
||||||
// happening while another thread is waiting for the lock is pretty small).
|
// happening while another thread is waiting for the lock is pretty small).
|
||||||
@ -490,23 +470,12 @@ impl ProgramMemory {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn update_with_env(&self, key: &str, value: KclValue, env: usize, owner: usize) {
|
|
||||||
self.stats.mutation_count.fetch_add(1, Ordering::Relaxed);
|
|
||||||
self.get_env(env).insert_or_update(key.to_owned(), value, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get a value from memory without checking for ownership of the env.
|
/// Get a value from memory without checking for ownership of the env.
|
||||||
///
|
///
|
||||||
/// This is not safe to use in general and should only be used if you have unique access to
|
/// This is not safe to use in general and should only be used if you have unique access to
|
||||||
/// the `self` which is generally only true during testing.
|
/// the `self` which is generally only true during testing.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn get_from_unchecked(
|
pub fn get_from_unchecked(&self, var: &str, mut env_ref: EnvironmentRef) -> Result<&KclValue, KclError> {
|
||||||
&self,
|
|
||||||
var: &str,
|
|
||||||
mut env_ref: EnvironmentRef,
|
|
||||||
source_range: SourceRange,
|
|
||||||
) -> Result<&KclValue, KclError> {
|
|
||||||
loop {
|
loop {
|
||||||
let env = self.get_env(env_ref.index());
|
let env = self.get_env(env_ref.index());
|
||||||
env_ref = match env.get_unchecked(var, env_ref.1) {
|
env_ref = match env.get_unchecked(var, env_ref.1) {
|
||||||
@ -518,7 +487,7 @@ impl ProgramMemory {
|
|||||||
|
|
||||||
Err(KclError::UndefinedValue(KclErrorDetails {
|
Err(KclError::UndefinedValue(KclErrorDetails {
|
||||||
message: format!("memory item key `{}` is not defined", var),
|
message: format!("memory item key `{}` is not defined", var),
|
||||||
source_ranges: vec![source_range],
|
source_ranges: vec![],
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -544,6 +513,11 @@ impl Stack {
|
|||||||
stack
|
stack
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current (globally most recent) epoch.
|
||||||
|
pub fn current_epoch(&self) -> usize {
|
||||||
|
self.memory.epoch.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
/// Push a new (standard KCL) stack frame on to the call stack.
|
/// Push a new (standard KCL) stack frame on to the call stack.
|
||||||
///
|
///
|
||||||
/// `parent` is the environment where the function being called is declared (not the caller's
|
/// `parent` is the environment where the function being called is declared (not the caller's
|
||||||
@ -577,7 +551,7 @@ impl Stack {
|
|||||||
// Rust functions shouldn't try to set or access anything in their environment, so don't
|
// Rust functions shouldn't try to set or access anything in their environment, so don't
|
||||||
// waste time and space on a new env. Using usize::MAX means we'll get an overflow if we
|
// waste time and space on a new env. Using usize::MAX means we'll get an overflow if we
|
||||||
// try to access anything rather than a silent error.
|
// try to access anything rather than a silent error.
|
||||||
self.current_env = EnvironmentRef(usize::MAX, SnapshotRef::none());
|
self.current_env = EnvironmentRef(usize::MAX, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push a new stack frame on to the call stack with no connection to a parent environment.
|
/// Push a new stack frame on to the call stack with no connection to a parent environment.
|
||||||
@ -596,7 +570,6 @@ impl Stack {
|
|||||||
/// SAFETY: the env must not be being used by another `Stack` since we'll move the env from
|
/// SAFETY: the env must not be being used by another `Stack` since we'll move the env from
|
||||||
/// read-only to owned.
|
/// read-only to owned.
|
||||||
pub fn restore_env(&mut self, env: EnvironmentRef) {
|
pub fn restore_env(&mut self, env: EnvironmentRef) {
|
||||||
assert!(env.1.is_none());
|
|
||||||
self.call_stack.push(self.current_env);
|
self.call_stack.push(self.current_env);
|
||||||
self.memory.get_env(env.index()).restore_owner(self.id);
|
self.memory.get_env(env.index()).restore_owner(self.id);
|
||||||
self.current_env = env;
|
self.current_env = env;
|
||||||
@ -642,25 +615,28 @@ impl Stack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut old_env = self.memory.take_env(old);
|
let mut old_env = self.memory.take_env(old);
|
||||||
|
if old_env.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Map of any old env refs to the current env.
|
// Make a new scope so we override variables properly.
|
||||||
let snapshot_map: HashMap<_, _> = old_env
|
self.push_new_env_for_scope();
|
||||||
.snapshot_parents()
|
|
||||||
.map(|(s, p)| (EnvironmentRef(old.0, s), (EnvironmentRef(self.current_env.0, p))))
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Move the variables in the popped env into the current env.
|
// Move the variables in the popped env into the current env.
|
||||||
let env = self.memory.get_env(self.current_env.index());
|
let env = self.memory.get_env(self.current_env.index());
|
||||||
for (k, v) in old_env.as_mut().take_bindings() {
|
for (k, (e, v)) in old_env.as_mut().take_bindings() {
|
||||||
env.insert_or_update(k.clone(), v.map_env_ref(&snapshot_map), self.id);
|
env.insert(k, e, v.map_env_ref(old.0, self.current_env.0), self.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Snapshot the current state of the memory.
|
/// Snapshot the current state of the memory.
|
||||||
pub fn snapshot(&mut self) -> EnvironmentRef {
|
pub fn snapshot(&mut self) -> EnvironmentRef {
|
||||||
self.memory.stats.snapshot_count.fetch_add(1, Ordering::Relaxed);
|
self.memory.stats.epoch_count.fetch_add(1, Ordering::Relaxed);
|
||||||
let snapshot = env::snapshot(&self.memory, self.current_env, self.id);
|
|
||||||
EnvironmentRef(self.current_env.0, snapshot)
|
let env = self.memory.get_env(self.current_env.index());
|
||||||
|
env.mark_as_refed();
|
||||||
|
|
||||||
|
let prev_epoch = self.memory.epoch.fetch_add(1, Ordering::Relaxed);
|
||||||
|
EnvironmentRef(self.current_env.0, prev_epoch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a value to the program memory (in the current scope). The value must not already exist.
|
/// Add a value to the program memory (in the current scope). The value must not already exist.
|
||||||
@ -675,16 +651,21 @@ impl Stack {
|
|||||||
|
|
||||||
self.memory.stats.mutation_count.fetch_add(1, Ordering::Relaxed);
|
self.memory.stats.mutation_count.fetch_add(1, Ordering::Relaxed);
|
||||||
|
|
||||||
env.insert(key, value, self.id);
|
env.insert(key, self.memory.epoch.load(Ordering::Relaxed), value, self.id);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_or_update(&mut self, key: String, value: KclValue) {
|
/// Update a variable in memory. `key` must exist in memory. If it doesn't, this function will panic
|
||||||
|
/// in debug builds and do nothing in release builds.
|
||||||
|
pub fn update(&mut self, key: &str, f: impl Fn(&mut KclValue, usize)) {
|
||||||
self.memory.stats.mutation_count.fetch_add(1, Ordering::Relaxed);
|
self.memory.stats.mutation_count.fetch_add(1, Ordering::Relaxed);
|
||||||
self.memory
|
self.memory.get_env(self.current_env.index()).update(
|
||||||
.get_env(self.current_env.index())
|
key,
|
||||||
.insert_or_update(key, value, self.id);
|
f,
|
||||||
|
self.memory.epoch.load(Ordering::Relaxed),
|
||||||
|
self.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a value from the program memory.
|
/// Get a value from the program memory.
|
||||||
@ -693,38 +674,41 @@ impl Stack {
|
|||||||
self.memory.get_from(var, self.current_env, source_range, self.id)
|
self.memory.get_from(var, self.current_env, source_range, self.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the current frame of the stack contains a variable with the given name.
|
||||||
|
pub fn cur_frame_contains(&self, var: &str) -> bool {
|
||||||
|
let env = self.memory.get_env(self.current_env.index());
|
||||||
|
env.contains_key(var)
|
||||||
|
}
|
||||||
|
|
||||||
/// Get a key from the first KCL (i.e., non-Rust) stack frame on the call stack.
|
/// Get a key from the first KCL (i.e., non-Rust) stack frame on the call stack.
|
||||||
pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<&KclValue, KclError> {
|
pub fn get_from_call_stack(&self, key: &str, source_range: SourceRange) -> Result<(usize, &KclValue), KclError> {
|
||||||
if !self.current_env.skip_env() {
|
if !self.current_env.skip_env() {
|
||||||
return self.get(key, source_range);
|
return Ok((self.current_env.1, self.get(key, source_range)?));
|
||||||
}
|
}
|
||||||
|
|
||||||
for env in self.call_stack.iter().rev() {
|
for env in self.call_stack.iter().rev() {
|
||||||
if !env.skip_env() {
|
if !env.skip_env() {
|
||||||
return self.memory.get_from(key, *env, source_range, self.id);
|
return Ok((env.1, self.memory.get_from(key, *env, source_range, self.id)?));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable!("It can't be Rust frames all the way down");
|
unreachable!("It can't be Rust frames all the way down");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all key/value pairs in the current environment which satisfy the provided
|
/// Iterate over all keys in the current environment which satisfy the provided predicate.
|
||||||
/// predicate.
|
pub fn find_keys_in_current_env<'a>(
|
||||||
pub fn find_all_in_current_env<'a>(
|
|
||||||
&'a self,
|
&'a self,
|
||||||
pred: impl Fn(&KclValue) -> bool + 'a,
|
pred: impl Fn(&KclValue) -> bool + 'a,
|
||||||
) -> impl Iterator<Item = (&'a String, &'a KclValue)> {
|
) -> impl Iterator<Item = &'a String> {
|
||||||
self.memory.find_all_in_env(self.current_env, pred, self.id)
|
self.memory
|
||||||
|
.find_all_in_env(self.current_env, pred, self.id)
|
||||||
|
.map(|(k, _)| k)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all key/value pairs in the specified environment which satisfy the provided
|
/// Iterate over all key/value pairs in the specified environment which satisfy the provided
|
||||||
/// predicate. `env` must either be read-only or owned by `self`.
|
/// predicate. `env` must either be read-only or owned by `self`.
|
||||||
pub fn find_all_in_env<'a>(
|
pub fn find_all_in_env(&self, env: EnvironmentRef) -> impl Iterator<Item = (&String, &KclValue)> {
|
||||||
&'a self,
|
self.memory.find_all_in_env(env, |_| true, self.id)
|
||||||
env: EnvironmentRef,
|
|
||||||
pred: impl Fn(&KclValue) -> bool + 'a,
|
|
||||||
) -> impl Iterator<Item = (&'a String, &'a KclValue)> {
|
|
||||||
self.memory.find_all_in_env(env, pred, self.id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Walk all values accessible from any environment in the call stack.
|
/// Walk all values accessible from any environment in the call stack.
|
||||||
@ -781,7 +765,7 @@ impl<'a> Iterator for CallStackIterator<'a> {
|
|||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(env_ref) = self.stack.memory.get_env(self.cur_env.index()).parent(self.cur_env.1) {
|
if let Some(env_ref) = self.stack.memory.get_env(self.cur_env.index()).parent() {
|
||||||
self.cur_env = env_ref;
|
self.cur_env = env_ref;
|
||||||
self.init_iter();
|
self.init_iter();
|
||||||
} else {
|
} else {
|
||||||
@ -816,23 +800,32 @@ impl<'a> Iterator for CallStackIterator<'a> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
impl PartialEq for Stack {
|
impl PartialEq for Stack {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
let vars: Vec<_> = self.find_all_in_current_env(|_| true).collect();
|
let vars: Vec<_> = self.find_keys_in_current_env(|_| true).collect();
|
||||||
let vars_other: Vec<_> = other.find_all_in_current_env(|_| true).collect();
|
let vars_other: Vec<_> = other.find_keys_in_current_env(|_| true).collect();
|
||||||
vars == vars_other
|
if vars != vars_other {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vars.iter()
|
||||||
|
.all(|k| self.get(k, SourceRange::default()).unwrap() == other.get(k, SourceRange::default()).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An index pointing to an environment at a point in time (either a snapshot or the current version, see the module docs).
|
/// An index pointing to an environment at a point in time.
|
||||||
|
///
|
||||||
|
/// The first field indexes an environment, the second field is an epoch. An epoch of 0 is indicates
|
||||||
|
/// a dummy, error, or placeholder env ref, an epoch of `usize::MAX` represents the current most
|
||||||
|
/// recent epoch.
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Hash, Eq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Hash, Eq, ts_rs::TS, JsonSchema)]
|
||||||
pub struct EnvironmentRef(usize, SnapshotRef);
|
pub struct EnvironmentRef(usize, usize);
|
||||||
|
|
||||||
impl EnvironmentRef {
|
impl EnvironmentRef {
|
||||||
fn dummy() -> Self {
|
fn dummy() -> Self {
|
||||||
Self(usize::MAX, SnapshotRef(usize::MAX))
|
Self(usize::MAX, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_regular(&self) -> bool {
|
fn is_regular(&self) -> bool {
|
||||||
self.0 < usize::MAX && self.1 .0 < usize::MAX
|
self.0 < usize::MAX && self.1 > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn index(&self) -> usize {
|
fn index(&self) -> usize {
|
||||||
@ -842,33 +835,11 @@ impl EnvironmentRef {
|
|||||||
fn skip_env(&self) -> bool {
|
fn skip_env(&self) -> bool {
|
||||||
self.0 == usize::MAX
|
self.0 == usize::MAX
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// An index pointing to a snapshot within a specific (unspecified) environment.
|
pub fn replace_env(&mut self, old: usize, new: usize) {
|
||||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Hash, Eq, ts_rs::TS, JsonSchema)]
|
if self.0 == old {
|
||||||
struct SnapshotRef(usize);
|
self.0 = new;
|
||||||
|
}
|
||||||
impl SnapshotRef {
|
|
||||||
/// Represents no snapshot, use the current version of the environment.
|
|
||||||
fn none() -> Self {
|
|
||||||
Self(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `self` represents a snapshot.
|
|
||||||
fn is_some(self) -> bool {
|
|
||||||
self.0 > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `self` represents the current version.
|
|
||||||
fn is_none(self) -> bool {
|
|
||||||
self.0 == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Precondition: self.is_some()
|
|
||||||
fn index(&self) -> usize {
|
|
||||||
// Note that `0` is a distinguished value meaning 'no snapshot', so the reference value
|
|
||||||
// is one greater than the index into the list of snapshots.
|
|
||||||
self.0 - 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,8 +848,8 @@ impl SnapshotRef {
|
|||||||
pub(crate) struct MemoryStats {
|
pub(crate) struct MemoryStats {
|
||||||
// Total number of environments created.
|
// Total number of environments created.
|
||||||
env_count: AtomicUsize,
|
env_count: AtomicUsize,
|
||||||
// Total number of snapshots created.
|
// Total number of epochs.
|
||||||
snapshot_count: AtomicUsize,
|
epoch_count: AtomicUsize,
|
||||||
// Total number of values inserted or updated.
|
// Total number of values inserted or updated.
|
||||||
mutation_count: AtomicUsize,
|
mutation_count: AtomicUsize,
|
||||||
// The number of envs we delete when popped from the call stack.
|
// The number of envs we delete when popped from the call stack.
|
||||||
@ -900,12 +871,10 @@ mod env {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct Environment {
|
pub(super) struct Environment {
|
||||||
bindings: UnsafeCell<IndexMap<String, KclValue>>,
|
bindings: UnsafeCell<IndexMap<String, (usize, KclValue)>>,
|
||||||
// invariant: self.parent.is_none() => forall s in self.snapshots: s.parent_snapshot.is_none()
|
|
||||||
snapshots: UnsafeCell<Vec<Snapshot>>,
|
|
||||||
// An outer scope, if one exists.
|
// An outer scope, if one exists.
|
||||||
parent: Option<EnvironmentRef>,
|
parent: Option<EnvironmentRef>,
|
||||||
is_root_env: bool,
|
might_be_refed: AtomicBool,
|
||||||
// The id of the `Stack` if this `Environment` is on a call stack. If this is >0 then it may
|
// The id of the `Stack` if this `Environment` is on a call stack. If this is >0 then it may
|
||||||
// only be read or written by that `Stack`; if 0 then the env is read-only.
|
// only be read or written by that `Stack`; if 0 then the env is read-only.
|
||||||
owner: AtomicUsize,
|
owner: AtomicUsize,
|
||||||
@ -918,9 +887,8 @@ mod env {
|
|||||||
assert!(self.owner.load(Ordering::Acquire) == 0);
|
assert!(self.owner.load(Ordering::Acquire) == 0);
|
||||||
Self {
|
Self {
|
||||||
bindings: UnsafeCell::new(self.get_bindings().clone()),
|
bindings: UnsafeCell::new(self.get_bindings().clone()),
|
||||||
snapshots: UnsafeCell::new(self.iter_snapshots().cloned().collect()),
|
|
||||||
parent: self.parent,
|
parent: self.parent,
|
||||||
is_root_env: self.is_root_env,
|
might_be_refed: AtomicBool::new(self.might_be_refed.load(Ordering::Acquire)),
|
||||||
owner: AtomicUsize::new(0),
|
owner: AtomicUsize::new(0),
|
||||||
_unpin: PhantomPinned,
|
_unpin: PhantomPinned,
|
||||||
}
|
}
|
||||||
@ -931,45 +899,19 @@ mod env {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
let parent = self
|
let parent = self
|
||||||
.parent
|
.parent
|
||||||
.map(|e| format!("EnvRef({}, {})", e.0, e.1 .0))
|
.map(|e| format!("EnvRef({}, {})", e.0, e.1))
|
||||||
.unwrap_or("_".to_owned());
|
.unwrap_or("_".to_owned());
|
||||||
let data: Vec<String> = self
|
let data: Vec<String> = self
|
||||||
.get_bindings()
|
.get_bindings()
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| format!("{k}: {}", v.human_friendly_type()))
|
.map(|(k, v)| format!("{k}: {}@{}", v.1.human_friendly_type(), v.0))
|
||||||
.collect();
|
.collect();
|
||||||
let snapshots: Vec<String> = self.iter_snapshots().map(|s| s.to_string()).collect();
|
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Env {{\n parent: {parent},\n owner: {},\n is root: {},\n bindings:\n {},\n snapshots:\n {}\n}}",
|
"Env {{\n parent: {parent},\n owner: {},\n ref'ed?: {},\n bindings:\n {}\n}}",
|
||||||
self.owner.load(Ordering::Relaxed),
|
self.owner.load(Ordering::Relaxed),
|
||||||
self.is_root_env,
|
self.might_be_refed.load(Ordering::Relaxed),
|
||||||
data.join("\n "),
|
data.join("\n "),
|
||||||
snapshots.join("\n ")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
struct Snapshot {
|
|
||||||
/// The version of the owning environment's parent environment corresponding to this snapshot.
|
|
||||||
parent_snapshot: Option<SnapshotRef>,
|
|
||||||
/// CoW'ed data from the environment.
|
|
||||||
data: IndexMap<String, KclValue>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Snapshot {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
let parent = self.parent_snapshot.map(|s| s.0.to_string()).unwrap_or("_".to_owned());
|
|
||||||
let data: Vec<String> = self
|
|
||||||
.data
|
|
||||||
.iter()
|
|
||||||
.map(|(k, v)| format!("{k}: {}", v.human_friendly_type()))
|
|
||||||
.collect();
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"Snapshot {{\n parent: {parent},\n data: {},\n }}",
|
|
||||||
data.join("\n ")
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -977,80 +919,47 @@ mod env {
|
|||||||
impl Environment {
|
impl Environment {
|
||||||
/// Create a new environment, parent points to it's surrounding lexical scope or the std
|
/// Create a new environment, parent points to it's surrounding lexical scope or the std
|
||||||
/// env if it's a root scope.
|
/// env if it's a root scope.
|
||||||
pub(super) fn new(parent: Option<EnvironmentRef>, is_root_env: bool, owner: usize) -> Self {
|
pub(super) fn new(parent: Option<EnvironmentRef>, might_be_refed: bool, owner: usize) -> Self {
|
||||||
assert!(parent.map(|p| p.is_regular()).unwrap_or(true));
|
assert!(parent.map(|p| p.is_regular()).unwrap_or(true));
|
||||||
Self {
|
Self {
|
||||||
bindings: UnsafeCell::new(IndexMap::new()),
|
bindings: UnsafeCell::new(IndexMap::new()),
|
||||||
snapshots: UnsafeCell::new(Vec::new()),
|
|
||||||
parent,
|
parent,
|
||||||
is_root_env,
|
might_be_refed: AtomicBool::new(might_be_refed),
|
||||||
owner: AtomicUsize::new(owner),
|
owner: AtomicUsize::new(owner),
|
||||||
_unpin: PhantomPinned,
|
_unpin: PhantomPinned,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark this env as read-only (see module docs).
|
/// Mark this env as read-only (see module docs).
|
||||||
pub(super) fn read_only(&self) {
|
pub(super) fn read_only(&self) {
|
||||||
self.owner.store(0, Ordering::Release);
|
self.owner.store(0, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark this env as owned (see module docs).
|
/// Mark this env as owned (see module docs).
|
||||||
pub(super) fn restore_owner(&self, owner: usize) {
|
pub(super) fn restore_owner(&self, owner: usize) {
|
||||||
self.owner.store(owner, Ordering::Release);
|
self.owner.store(owner, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: either the owner of the env is on the Rust stack or the env is read-only.
|
/// Mark this environment as possibly having external references.
|
||||||
fn snapshots_len(&self) -> usize {
|
pub(super) fn mark_as_refed(&self) {
|
||||||
unsafe { self.snapshots.get().as_ref().unwrap().len() }
|
self.might_be_refed.store(true, Ordering::Release);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY: either the owner of the env is on the Rust stack or the env is read-only.
|
// SAFETY: either the owner of the env is on the Rust stack or the env is read-only.
|
||||||
fn get_shapshot(&self, index: usize) -> &Snapshot {
|
fn get_bindings(&self) -> &IndexMap<String, (usize, KclValue)> {
|
||||||
unsafe { &self.snapshots.get().as_ref().unwrap()[index] }
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: either the owner of the env is on the Rust stack or the env is read-only.
|
|
||||||
fn iter_snapshots(&self) -> impl Iterator<Item = &Snapshot> {
|
|
||||||
unsafe { self.snapshots.get().as_ref().unwrap().iter() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cur_snapshot(&self, owner: usize) -> Option<&mut Snapshot> {
|
|
||||||
assert!(owner > 0 && self.owner.load(Ordering::Acquire) == owner);
|
|
||||||
unsafe { self.snapshots.get().as_mut().unwrap().last_mut() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// SAFETY: either the owner of the env is on the Rust stack or the env is read-only.
|
|
||||||
fn get_bindings(&self) -> &IndexMap<String, KclValue> {
|
|
||||||
unsafe { self.bindings.get().as_ref().unwrap() }
|
unsafe { self.bindings.get().as_ref().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// SAFETY do not call this function while a previous mutable reference is live
|
// SAFETY do not call this function while a previous mutable reference is live
|
||||||
#[allow(clippy::mut_from_ref)]
|
#[allow(clippy::mut_from_ref)]
|
||||||
fn get_mut_bindings(&self, owner: usize) -> &mut IndexMap<String, KclValue> {
|
fn get_mut_bindings(&self, owner: usize) -> &mut IndexMap<String, (usize, KclValue)> {
|
||||||
assert!(owner > 0 && self.owner.load(Ordering::Acquire) == owner);
|
assert!(owner > 0 && self.owner.load(Ordering::Acquire) == owner);
|
||||||
unsafe { self.bindings.get().as_mut().unwrap() }
|
unsafe { self.bindings.get().as_mut().unwrap() }
|
||||||
}
|
}
|
||||||
|
|
||||||
// True if the env is empty and not a root env.
|
// True if the env is empty and has no external references.
|
||||||
pub(super) fn is_empty(&self) -> bool {
|
pub(super) fn is_empty(&self) -> bool {
|
||||||
self.snapshots_len() == 0 && self.get_bindings().is_empty() && !self.is_root_env
|
self.get_bindings().is_empty() && !self.might_be_refed.load(Ordering::Acquire)
|
||||||
}
|
|
||||||
|
|
||||||
fn push_snapshot(&self, parent: Option<SnapshotRef>, owner: usize) -> SnapshotRef {
|
|
||||||
let env_owner = self.owner.load(Ordering::Acquire);
|
|
||||||
// The env is read-only, no need to snapshot.
|
|
||||||
if env_owner == 0 {
|
|
||||||
return SnapshotRef::none();
|
|
||||||
}
|
|
||||||
assert!(
|
|
||||||
owner > 0 && env_owner == owner,
|
|
||||||
"mutating owner: {owner}, env: {self}({env_owner})"
|
|
||||||
);
|
|
||||||
unsafe {
|
|
||||||
let snapshots = self.snapshots.get().as_mut().unwrap();
|
|
||||||
snapshots.push(Snapshot::new(parent));
|
|
||||||
SnapshotRef(snapshots.len())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Possibly compress this environment by deleting the memory.
|
/// Possibly compress this environment by deleting the memory.
|
||||||
@ -1062,116 +971,61 @@ mod env {
|
|||||||
/// See module docs for more details.
|
/// See module docs for more details.
|
||||||
pub(super) fn compact(&self, owner: usize) {
|
pub(super) fn compact(&self, owner: usize) {
|
||||||
// Don't compress if there might be a closure or import referencing us.
|
// Don't compress if there might be a closure or import referencing us.
|
||||||
if self.snapshots_len() != 0 || self.is_root_env {
|
if self.might_be_refed.load(Ordering::Acquire) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
*self.get_mut_bindings(owner) = IndexMap::new();
|
*self.get_mut_bindings(owner) = IndexMap::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn get(
|
pub(super) fn get(&self, key: &str, epoch: usize, owner: usize) -> Result<&KclValue, Option<EnvironmentRef>> {
|
||||||
&self,
|
|
||||||
key: &str,
|
|
||||||
snapshot: SnapshotRef,
|
|
||||||
owner: usize,
|
|
||||||
) -> Result<&KclValue, Option<EnvironmentRef>> {
|
|
||||||
let env_owner = self.owner.load(Ordering::Acquire);
|
let env_owner = self.owner.load(Ordering::Acquire);
|
||||||
assert!(env_owner == 0 || env_owner == owner);
|
assert!(env_owner == 0 || env_owner == owner);
|
||||||
|
|
||||||
self.get_unchecked(key, snapshot)
|
self.get_unchecked(key, epoch)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a value from memory without checking the env's ownership invariant. Prefer to use `get`.
|
/// Get a value from memory without checking the env's ownership invariant. Prefer to use `get`.
|
||||||
pub(super) fn get_unchecked(
|
pub(super) fn get_unchecked(&self, key: &str, epoch: usize) -> Result<&KclValue, Option<EnvironmentRef>> {
|
||||||
&self,
|
|
||||||
key: &str,
|
|
||||||
snapshot: SnapshotRef,
|
|
||||||
) -> Result<&KclValue, Option<EnvironmentRef>> {
|
|
||||||
if snapshot.is_some() {
|
|
||||||
for i in snapshot.index()..self.snapshots_len() {
|
|
||||||
match self.get_shapshot(i).data.get(key) {
|
|
||||||
Some(KclValue::Tombstone { .. }) => return Err(self.parent(snapshot)),
|
|
||||||
Some(v) => return Ok(v),
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.get_bindings()
|
self.get_bindings()
|
||||||
.get(key)
|
.get(key)
|
||||||
.and_then(|v| match v {
|
.and_then(|(e, v)| if *e <= epoch { Some(v) } else { None })
|
||||||
KclValue::Tombstone { .. } => None,
|
.ok_or(self.parent)
|
||||||
_ => Some(v),
|
|
||||||
})
|
|
||||||
.ok_or(self.parent(snapshot))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the `EnvironmentRef` of the parent of this environment corresponding to the specified snapshot.
|
pub(super) fn update(&self, key: &str, f: impl Fn(&mut KclValue, usize), epoch: usize, owner: usize) {
|
||||||
pub(super) fn parent(&self, snapshot: SnapshotRef) -> Option<EnvironmentRef> {
|
let Some((_, value)) = self.get_mut_bindings(owner).get_mut(key) else {
|
||||||
if snapshot.is_none() {
|
debug_assert!(false, "Missing memory entry for {key}");
|
||||||
return self.parent;
|
return;
|
||||||
}
|
};
|
||||||
|
|
||||||
match self.get_shapshot(snapshot.index()).parent_snapshot {
|
f(value, epoch);
|
||||||
Some(sr) => Some(EnvironmentRef(self.parent.unwrap().0, sr)),
|
|
||||||
None => self.parent,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all values in the environment at the specified snapshot.
|
pub(super) fn parent(&self) -> Option<EnvironmentRef> {
|
||||||
pub(super) fn values<'a>(&'a self, snapshot: SnapshotRef) -> Box<dyn Iterator<Item = &'a KclValue> + 'a> {
|
self.parent
|
||||||
if snapshot.is_none() {
|
}
|
||||||
return Box::new(self.get_bindings().values());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/// Iterate over all values in the environment at the specified epoch.
|
||||||
|
pub(super) fn values<'a>(&'a self, epoch: usize) -> Box<dyn Iterator<Item = &'a KclValue> + 'a> {
|
||||||
Box::new(
|
Box::new(
|
||||||
self.get_bindings()
|
self.get_bindings()
|
||||||
.iter()
|
.values()
|
||||||
.filter_map(move |(k, v)| {
|
.filter_map(move |(e, v)| (*e <= epoch).then_some(v)),
|
||||||
(!self.snapshot_contains_key(k, snapshot) && !matches!(v, KclValue::Tombstone { .. }))
|
|
||||||
.then_some(v)
|
|
||||||
})
|
|
||||||
.chain(
|
|
||||||
self.iter_snapshots()
|
|
||||||
.flat_map(|s| s.data.values().filter(|v| !matches!(v, KclValue::Tombstone { .. }))),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pure insert, panics if `key` is already in this environment.
|
/// Pure insert, panics if `key` is already in this environment.
|
||||||
///
|
///
|
||||||
/// Precondition: !self.contains_key(key)
|
/// Precondition: !self.contains_key(key)
|
||||||
pub(super) fn insert(&self, key: String, value: KclValue, owner: usize) {
|
pub(super) fn insert(&self, key: String, epoch: usize, value: KclValue, owner: usize) {
|
||||||
debug_assert!(!self.get_bindings().contains_key(&key));
|
debug_assert!(!self.get_bindings().contains_key(&key));
|
||||||
if let Some(s) = self.cur_snapshot(owner) {
|
self.get_mut_bindings(owner).insert(key, (epoch, value));
|
||||||
s.data.insert(key.clone(), tombstone());
|
|
||||||
}
|
|
||||||
self.get_mut_bindings(owner).insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn insert_or_update(&self, key: String, value: KclValue, owner: usize) {
|
|
||||||
if let Some(s) = self.cur_snapshot(owner) {
|
|
||||||
if !s.data.contains_key(&key) {
|
|
||||||
let old_value = self.get_bindings().get(&key).cloned().unwrap_or_else(tombstone);
|
|
||||||
s.data.insert(key.clone(), old_value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.get_mut_bindings(owner).insert(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Was the key contained in this environment at the specified point in time.
|
|
||||||
fn snapshot_contains_key(&self, key: &str, snapshot: SnapshotRef) -> bool {
|
|
||||||
for i in snapshot.index()..self.snapshots_len() {
|
|
||||||
if self.get_shapshot(i).data.contains_key(key) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the key currently contained in this environment.
|
/// Is the key currently contained in this environment.
|
||||||
pub(super) fn contains_key(&self, key: &str) -> bool {
|
pub(super) fn contains_key(&self, key: &str) -> bool {
|
||||||
!matches!(self.get_bindings().get(key), Some(KclValue::Tombstone { .. }) | None)
|
self.get_bindings().contains_key(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all key/value pairs currently in this environment where the value satisfies
|
/// Iterate over all key/value pairs currently in this environment where the value satisfies
|
||||||
@ -1186,61 +1040,14 @@ mod env {
|
|||||||
|
|
||||||
self.get_bindings()
|
self.get_bindings()
|
||||||
.iter()
|
.iter()
|
||||||
.filter(move |(_, v)| f(v) && !matches!(v, KclValue::Tombstone { .. }))
|
.filter_map(move |(k, (_, v))| f(v).then_some((k, v)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take all bindings from the environment.
|
/// Take all bindings from the environment.
|
||||||
pub(super) fn take_bindings(self: Pin<&mut Self>) -> impl Iterator<Item = (String, KclValue)> {
|
pub(super) fn take_bindings(self: Pin<&mut Self>) -> impl Iterator<Item = (String, (usize, KclValue))> {
|
||||||
// SAFETY: caller must have unique access since self is mut. We're not moving or invalidating `self`.
|
// SAFETY: caller must have unique access since self is mut. We're not moving or invalidating `self`.
|
||||||
let bindings = std::mem::take(unsafe { self.bindings.get().as_mut().unwrap() });
|
let bindings = std::mem::take(unsafe { self.bindings.get().as_mut().unwrap() });
|
||||||
bindings
|
bindings.into_iter()
|
||||||
.into_iter()
|
|
||||||
.filter(move |(_, v)| !matches!(v, KclValue::Tombstone { .. }))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns an iterator over any snapshots in this environment, returning the ref to the
|
|
||||||
/// snapshot and its parent.
|
|
||||||
pub(super) fn snapshot_parents(&self) -> impl Iterator<Item = (SnapshotRef, SnapshotRef)> + '_ {
|
|
||||||
self.iter_snapshots()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, s)| (SnapshotRef(i + 1), s.parent_snapshot.unwrap()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Snapshot {
|
|
||||||
fn new(parent_snapshot: Option<SnapshotRef>) -> Self {
|
|
||||||
Snapshot {
|
|
||||||
parent_snapshot,
|
|
||||||
data: IndexMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build a new snapshot of the specified environment at the current moment.
|
|
||||||
///
|
|
||||||
/// This is non-trival since we have to build the tree of parent snapshots.
|
|
||||||
pub(super) fn snapshot(mem: &ProgramMemory, env_ref: EnvironmentRef, owner: usize) -> SnapshotRef {
|
|
||||||
let env = mem.get_env(env_ref.index());
|
|
||||||
let parent_snapshot = env.parent.map(|p| snapshot(mem, p, owner));
|
|
||||||
|
|
||||||
let env = mem.get_env(env_ref.index());
|
|
||||||
if env.snapshots_len() == 0 {
|
|
||||||
return env.push_snapshot(parent_snapshot, owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
let prev_snapshot = env.cur_snapshot(owner).unwrap();
|
|
||||||
if prev_snapshot.data.is_empty() && prev_snapshot.parent_snapshot == parent_snapshot {
|
|
||||||
// If the prev snapshot is empty, reuse it.
|
|
||||||
return SnapshotRef(env.snapshots_len());
|
|
||||||
}
|
|
||||||
|
|
||||||
env.push_snapshot(parent_snapshot, owner)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tombstone() -> KclValue {
|
|
||||||
KclValue::Tombstone {
|
|
||||||
value: (),
|
|
||||||
meta: Vec::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1270,16 +1077,9 @@ mod test {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_small_number(value: &KclValue) -> Option<i64> {
|
|
||||||
match value {
|
|
||||||
KclValue::Number { value, .. } if value > &0.0 && value < &10.0 => Some(*value as i64),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_get_from(mem: &Stack, key: &str, n: i64, snapshot: EnvironmentRef) {
|
fn assert_get_from(mem: &Stack, key: &str, n: i64, snapshot: EnvironmentRef) {
|
||||||
match mem.memory.get_from_unchecked(key, snapshot, sr()).unwrap() {
|
match mem.memory.get_from_unchecked(key, snapshot).unwrap() {
|
||||||
KclValue::Number { value, .. } => assert_eq!(*value as i64, n),
|
KclValue::Number { value, .. } => assert_eq!(*value as i64, n),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -1318,7 +1118,7 @@ mod test {
|
|||||||
assert_get(mem, "a", 1);
|
assert_get(mem, "a", 1);
|
||||||
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
||||||
assert_get(mem, "b", 3);
|
assert_get(mem, "b", 3);
|
||||||
mem.memory.get_from_unchecked("b", sn, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("b", sn).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1337,11 +1137,11 @@ mod test {
|
|||||||
assert_get(mem, "b", 3);
|
assert_get(mem, "b", 3);
|
||||||
assert_get(mem, "c", 6);
|
assert_get(mem, "c", 6);
|
||||||
assert_get_from(mem, "a", 1, sn1);
|
assert_get_from(mem, "a", 1, sn1);
|
||||||
mem.memory.get_from_unchecked("b", sn1, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("b", sn1).unwrap_err();
|
||||||
mem.memory.get_from_unchecked("c", sn1, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("c", sn1).unwrap_err();
|
||||||
assert_get_from(mem, "a", 1, sn2);
|
assert_get_from(mem, "a", 1, sn2);
|
||||||
assert_get_from(mem, "b", 3, sn2);
|
assert_get_from(mem, "b", 3, sn2);
|
||||||
mem.memory.get_from_unchecked("c", sn2, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("c", sn2).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1481,7 +1281,7 @@ mod test {
|
|||||||
|
|
||||||
mem.pop_env();
|
mem.pop_env();
|
||||||
// old snapshot still untouched
|
// old snapshot still untouched
|
||||||
mem.memory.get_from_unchecked("b", sn, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("b", sn).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1503,62 +1303,22 @@ mod test {
|
|||||||
|
|
||||||
mem.pop_env();
|
mem.pop_env();
|
||||||
// old snapshots still untouched
|
// old snapshots still untouched
|
||||||
mem.memory.get_from_unchecked("b", sn1, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("b", sn1).unwrap_err();
|
||||||
assert_get_from(mem, "b", 3, sn2);
|
assert_get_from(mem, "b", 3, sn2);
|
||||||
mem.memory.get_from_unchecked("c", sn2, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("c", sn2).unwrap_err();
|
||||||
assert_get_from(mem, "b", 4, sn3);
|
assert_get_from(mem, "b", 4, sn3);
|
||||||
mem.memory.get_from_unchecked("c", sn3, sr()).unwrap_err();
|
mem.memory.get_from_unchecked("c", sn3).unwrap_err();
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn snap_env_two_updates() {
|
|
||||||
let mem = &mut Stack::new_for_tests();
|
|
||||||
mem.add("a".to_owned(), val(1), sr()).unwrap();
|
|
||||||
|
|
||||||
let sn1 = mem.snapshot();
|
|
||||||
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
|
||||||
let sn2 = mem.snapshot();
|
|
||||||
|
|
||||||
let callee_env = mem.current_env.0;
|
|
||||||
mem.push_new_env_for_call(sn2);
|
|
||||||
let sn3 = mem.snapshot();
|
|
||||||
mem.add("b".to_owned(), val(4), sr()).unwrap();
|
|
||||||
let sn4 = mem.snapshot();
|
|
||||||
mem.insert_or_update("b".to_owned(), val(6));
|
|
||||||
mem.memory.update_with_env("b", val(7), callee_env, mem.id);
|
|
||||||
|
|
||||||
assert_get(mem, "b", 6);
|
|
||||||
assert_get_from(mem, "b", 3, sn3);
|
|
||||||
assert_get_from(mem, "b", 4, sn4);
|
|
||||||
|
|
||||||
let vals: Vec<_> = mem.walk_call_stack().filter_map(expect_small_number).collect();
|
|
||||||
let expected = [6, 1, 3, 1, 7];
|
|
||||||
assert_eq!(vals, expected);
|
|
||||||
|
|
||||||
let popped = mem.pop_env();
|
|
||||||
assert_get(mem, "b", 7);
|
|
||||||
mem.memory.get_from_unchecked("b", sn1, sr()).unwrap_err();
|
|
||||||
assert_get_from(mem, "b", 3, sn2);
|
|
||||||
|
|
||||||
let vals: Vec<_> = mem.walk_call_stack().filter_map(expect_small_number).collect();
|
|
||||||
let expected = [1, 7];
|
|
||||||
assert_eq!(vals, expected);
|
|
||||||
|
|
||||||
let popped_env = mem.memory.get_env(popped.index());
|
|
||||||
let sp: Vec<_> = popped_env.snapshot_parents().collect();
|
|
||||||
assert_eq!(
|
|
||||||
sp,
|
|
||||||
vec![(SnapshotRef(1), SnapshotRef(2)), (SnapshotRef(2), SnapshotRef(2))]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn squash_env() {
|
fn squash_env() {
|
||||||
let mem = &mut Stack::new_for_tests();
|
let mem = &mut Stack::new_for_tests();
|
||||||
mem.add("a".to_owned(), val(1), sr()).unwrap();
|
mem.add("a".to_owned(), val(1), sr()).unwrap();
|
||||||
|
mem.add("b".to_owned(), val(3), sr()).unwrap();
|
||||||
let sn1 = mem.snapshot();
|
let sn1 = mem.snapshot();
|
||||||
mem.push_new_env_for_call(sn1);
|
mem.push_new_env_for_call(sn1);
|
||||||
mem.add("b".to_owned(), val(2), sr()).unwrap();
|
mem.add("b".to_owned(), val(2), sr()).unwrap();
|
||||||
|
|
||||||
let sn2 = mem.snapshot();
|
let sn2 = mem.snapshot();
|
||||||
mem.add(
|
mem.add(
|
||||||
"f".to_owned(),
|
"f".to_owned(),
|
||||||
@ -1581,11 +1341,10 @@ mod test {
|
|||||||
KclValue::Function {
|
KclValue::Function {
|
||||||
value: FunctionSource::User { memory, .. },
|
value: FunctionSource::User { memory, .. },
|
||||||
..
|
..
|
||||||
} if memory == &sn1 => {}
|
} if memory.0 == mem.current_env.0 => {}
|
||||||
v => panic!("{v:#?}"),
|
v => panic!("{v:#?}, expected {sn1:?}"),
|
||||||
}
|
}
|
||||||
assert_eq!(mem.memory.envs().len(), 1);
|
assert_eq!(mem.memory.envs().len(), 2);
|
||||||
assert_eq!(mem.current_env, EnvironmentRef(0, SnapshotRef(0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -10,6 +10,7 @@ use cache::OldAstState;
|
|||||||
pub use cache::{bust_cache, clear_mem_cache};
|
pub use cache::{bust_cache, clear_mem_cache};
|
||||||
pub use cad_op::Operation;
|
pub use cad_op::Operation;
|
||||||
pub use geometry::*;
|
pub use geometry::*;
|
||||||
|
pub use id_generator::IdGenerator;
|
||||||
pub(crate) use import::{
|
pub(crate) use import::{
|
||||||
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
||||||
};
|
};
|
||||||
@ -25,7 +26,7 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
pub use memory::EnvironmentRef;
|
pub use memory::EnvironmentRef;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
pub use state::{ExecState, IdGenerator, MetaSettings};
|
pub use state::{ExecState, MetaSettings};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine::EngineManager,
|
engine::EngineManager,
|
||||||
@ -49,6 +50,7 @@ pub(crate) mod cache;
|
|||||||
mod cad_op;
|
mod cad_op;
|
||||||
mod exec_ast;
|
mod exec_ast;
|
||||||
mod geometry;
|
mod geometry;
|
||||||
|
mod id_generator;
|
||||||
mod import;
|
mod import;
|
||||||
pub(crate) mod kcl_value;
|
pub(crate) mod kcl_value;
|
||||||
mod memory;
|
mod memory;
|
||||||
@ -72,6 +74,8 @@ pub struct ExecOutcome {
|
|||||||
pub errors: Vec<CompilationError>,
|
pub errors: Vec<CompilationError>,
|
||||||
/// File Names in module Id array index order
|
/// File Names in module Id array index order
|
||||||
pub filenames: IndexMap<ModuleId, ModulePath>,
|
pub filenames: IndexMap<ModuleId, ModulePath>,
|
||||||
|
/// The default planes.
|
||||||
|
pub default_planes: Option<DefaultPlanes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
@ -91,11 +95,46 @@ pub struct DefaultPlanes {
|
|||||||
#[serde(tag = "type", rename_all = "camelCase")]
|
#[serde(tag = "type", rename_all = "camelCase")]
|
||||||
pub struct TagIdentifier {
|
pub struct TagIdentifier {
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub info: Option<TagEngineInfo>,
|
// Multi-version representation of info about the tag. Kept ordered. The usize is the epoch at which the info
|
||||||
|
// was written. Note that there might be multiple versions of tag info from the same epoch, the version with
|
||||||
|
// the higher index will be the most recent.
|
||||||
|
#[serde(skip)]
|
||||||
|
pub info: Vec<(usize, TagEngineInfo)>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub meta: Vec<Metadata>,
|
pub meta: Vec<Metadata>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TagIdentifier {
|
||||||
|
/// Get the tag info for this tag at a specified epoch.
|
||||||
|
pub fn get_info(&self, at_epoch: usize) -> Option<&TagEngineInfo> {
|
||||||
|
for (e, info) in self.info.iter().rev() {
|
||||||
|
if *e <= at_epoch {
|
||||||
|
return Some(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the most recent tag info for this tag.
|
||||||
|
pub fn get_cur_info(&self) -> Option<&TagEngineInfo> {
|
||||||
|
self.info.last().map(|i| &i.1)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add info from a different instance of this tag.
|
||||||
|
pub fn merge_info(&mut self, other: &TagIdentifier) {
|
||||||
|
assert_eq!(&self.value, &other.value);
|
||||||
|
'new_info: for (oe, ot) in &other.info {
|
||||||
|
for (e, _) in &self.info {
|
||||||
|
if e > oe {
|
||||||
|
continue 'new_info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.info.push((*oe, ot.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Eq for TagIdentifier {}
|
impl Eq for TagIdentifier {}
|
||||||
|
|
||||||
impl std::fmt::Display for TagIdentifier {
|
impl std::fmt::Display for TagIdentifier {
|
||||||
@ -110,7 +149,7 @@ impl std::str::FromStr for TagIdentifier {
|
|||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
value: s.to_string(),
|
value: s.to_string(),
|
||||||
info: None,
|
info: Vec::new(),
|
||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -367,22 +406,14 @@ impl ExecutorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
pub async fn new(
|
pub fn new(engine: Arc<Box<dyn EngineManager>>, fs: Arc<FileManager>, settings: ExecutorSettings) -> Self {
|
||||||
engine_manager: crate::engine::conn_wasm::EngineCommandManager,
|
ExecutorContext {
|
||||||
fs_manager: crate::fs::wasm::FileSystemManager,
|
engine,
|
||||||
settings: ExecutorSettings,
|
fs,
|
||||||
) -> Result<Self, String> {
|
|
||||||
Ok(ExecutorContext {
|
|
||||||
engine: Arc::new(Box::new(
|
|
||||||
crate::engine::conn_wasm::EngineConnection::new(engine_manager)
|
|
||||||
.await
|
|
||||||
.map_err(|e| format!("{:?}", e))?,
|
|
||||||
)),
|
|
||||||
fs: Arc::new(FileManager::new(fs_manager)),
|
|
||||||
stdlib: Arc::new(StdLib::new()),
|
stdlib: Arc::new(StdLib::new()),
|
||||||
settings,
|
settings,
|
||||||
context_type: ContextType::Live,
|
context_type: ContextType::Live,
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -499,7 +530,7 @@ impl ExecutorContext {
|
|||||||
source_range: crate::execution::SourceRange,
|
source_range: crate::execution::SourceRange,
|
||||||
) -> Result<(), KclError> {
|
) -> Result<(), KclError> {
|
||||||
self.engine
|
self.engine
|
||||||
.clear_scene(&mut exec_state.global.id_generator, source_range)
|
.clear_scene(&mut exec_state.mod_local.id_generator, source_range)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,7 +549,7 @@ impl ExecutorContext {
|
|||||||
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||||
assert!(self.is_mock());
|
assert!(self.is_mock());
|
||||||
|
|
||||||
let mut exec_state = ExecState::new(&self.settings);
|
let mut exec_state = ExecState::new(self);
|
||||||
if use_prev_memory {
|
if use_prev_memory {
|
||||||
match cache::read_old_memory().await {
|
match cache::read_old_memory().await {
|
||||||
Some(mem) => *exec_state.mut_stack() = mem,
|
Some(mem) => *exec_state.mut_stack() = mem,
|
||||||
@ -539,7 +570,7 @@ impl ExecutorContext {
|
|||||||
// memory, not to the exec_state which is not cached for mock execution.
|
// memory, not to the exec_state which is not cached for mock execution.
|
||||||
|
|
||||||
let mut mem = exec_state.stack().clone();
|
let mut mem = exec_state.stack().clone();
|
||||||
let outcome = exec_state.to_mock_wasm_outcome(result.0);
|
let outcome = exec_state.to_mock_wasm_outcome(result.0).await;
|
||||||
|
|
||||||
mem.squash_env(result.0);
|
mem.squash_env(result.0);
|
||||||
cache::write_old_memory(mem).await;
|
cache::write_old_memory(mem).await;
|
||||||
@ -607,13 +638,13 @@ impl ExecutorContext {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let outcome = old_state.to_wasm_outcome(result_env);
|
let outcome = old_state.to_wasm_outcome(result_env).await;
|
||||||
return Ok(outcome);
|
return Ok(outcome);
|
||||||
}
|
}
|
||||||
(true, program)
|
(true, program)
|
||||||
}
|
}
|
||||||
CacheResult::NoAction(false) => {
|
CacheResult::NoAction(false) => {
|
||||||
let outcome = old_state.to_wasm_outcome(result_env);
|
let outcome = old_state.to_wasm_outcome(result_env).await;
|
||||||
return Ok(outcome);
|
return Ok(outcome);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -621,7 +652,7 @@ impl ExecutorContext {
|
|||||||
let (exec_state, preserve_mem) = if clear_scene {
|
let (exec_state, preserve_mem) = if clear_scene {
|
||||||
// Pop the execution state, since we are starting fresh.
|
// Pop the execution state, since we are starting fresh.
|
||||||
let mut exec_state = old_state;
|
let mut exec_state = old_state;
|
||||||
exec_state.reset(&self.settings);
|
exec_state.reset(self);
|
||||||
|
|
||||||
// We don't do this in mock mode since there is no engine connection
|
// We don't do this in mock mode since there is no engine connection
|
||||||
// anyways and from the TS side we override memory and don't want to clear it.
|
// anyways and from the TS side we override memory and don't want to clear it.
|
||||||
@ -638,7 +669,7 @@ impl ExecutorContext {
|
|||||||
|
|
||||||
(program, exec_state, preserve_mem)
|
(program, exec_state, preserve_mem)
|
||||||
} else {
|
} else {
|
||||||
let mut exec_state = ExecState::new(&self.settings);
|
let mut exec_state = ExecState::new(self);
|
||||||
self.send_clear_scene(&mut exec_state, Default::default())
|
self.send_clear_scene(&mut exec_state, Default::default())
|
||||||
.await
|
.await
|
||||||
.map_err(KclErrorWithOutputs::no_outputs)?;
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
||||||
@ -663,7 +694,7 @@ impl ExecutorContext {
|
|||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let outcome = exec_state.to_wasm_outcome(result.0);
|
let outcome = exec_state.to_wasm_outcome(result.0).await;
|
||||||
Ok(outcome)
|
Ok(outcome)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,6 +730,7 @@ impl ExecutorContext {
|
|||||||
.await
|
.await
|
||||||
.map_err(KclErrorWithOutputs::no_outputs)?;
|
.map_err(KclErrorWithOutputs::no_outputs)?;
|
||||||
|
|
||||||
|
let default_planes = self.engine.get_default_planes().read().await.clone();
|
||||||
let env_ref = self
|
let env_ref = self
|
||||||
.execute_and_build_graph(&program.ast, exec_state, preserve_mem)
|
.execute_and_build_graph(&program.ast, exec_state, preserve_mem)
|
||||||
.await
|
.await
|
||||||
@ -717,6 +749,7 @@ impl ExecutorContext {
|
|||||||
exec_state.global.artifact_graph.clone(),
|
exec_state.global.artifact_graph.clone(),
|
||||||
module_id_to_module_path,
|
module_id_to_module_path,
|
||||||
exec_state.global.id_to_source.clone(),
|
exec_state.global.id_to_source.clone(),
|
||||||
|
default_planes,
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
@ -754,6 +787,7 @@ impl ExecutorContext {
|
|||||||
exec_state,
|
exec_state,
|
||||||
ExecutionKind::Normal,
|
ExecutionKind::Normal,
|
||||||
preserve_mem,
|
preserve_mem,
|
||||||
|
ModuleId::default(),
|
||||||
&ModulePath::Main,
|
&ModulePath::Main,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@ -933,7 +967,7 @@ pub(crate) async fn parse_execute(code: &str) -> Result<ExecTestResults, KclErro
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: ContextType::Mock,
|
context_type: ContextType::Mock,
|
||||||
};
|
};
|
||||||
let mut exec_state = ExecState::new(&exec_ctxt.settings);
|
let mut exec_state = ExecState::new(&exec_ctxt);
|
||||||
let result = exec_ctxt.run(&program, &mut exec_state).await?;
|
let result = exec_ctxt.run(&program, &mut exec_state).await?;
|
||||||
|
|
||||||
Ok(ExecTestResults {
|
Ok(ExecTestResults {
|
||||||
@ -963,11 +997,7 @@ mod tests {
|
|||||||
/// Convenience function to get a JSON value from memory and unwrap.
|
/// Convenience function to get a JSON value from memory and unwrap.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn mem_get_json(memory: &Stack, env: EnvironmentRef, name: &str) -> KclValue {
|
fn mem_get_json(memory: &Stack, env: EnvironmentRef, name: &str) -> KclValue {
|
||||||
memory
|
memory.memory.get_from_unchecked(name, env).unwrap().to_owned()
|
||||||
.memory
|
|
||||||
.get_from_unchecked(name, env, SourceRange::default())
|
|
||||||
.unwrap()
|
|
||||||
.to_owned()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
@ -1849,15 +1879,6 @@ let w = f() + f()
|
|||||||
parse_execute(ast).await.unwrap();
|
parse_execute(ast).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_serialize_memory_item() {
|
|
||||||
let mem = KclValue::Solids {
|
|
||||||
value: Default::default(),
|
|
||||||
};
|
|
||||||
let json = serde_json::to_string(&mem).unwrap();
|
|
||||||
assert_eq!(json, r#"{"type":"Solids","value":[]}"#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn kcl_test_ids_stable_between_executions() {
|
async fn kcl_test_ids_stable_between_executions() {
|
||||||
let code = r#"sketch001 = startSketchOn(XZ)
|
let code = r#"sketch001 = startSketchOn(XZ)
|
||||||
@ -1880,10 +1901,14 @@ let w = f() + f()
|
|||||||
let old_program = crate::Program::parse_no_errs(code).unwrap();
|
let old_program = crate::Program::parse_no_errs(code).unwrap();
|
||||||
|
|
||||||
// Execute the program.
|
// Execute the program.
|
||||||
ctx.run_with_caching(old_program).await.unwrap();
|
if let Err(err) = ctx.run_with_caching(old_program).await {
|
||||||
|
let report = err.into_miette_report_with_outputs(code).unwrap();
|
||||||
|
let report = miette::Report::new(report);
|
||||||
|
panic!("Error executing program: {:?}", report);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the id_generator from the first execution.
|
// Get the id_generator from the first execution.
|
||||||
let id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
|
let id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
|
||||||
|
|
||||||
let code = r#"sketch001 = startSketchOn(XZ)
|
let code = r#"sketch001 = startSketchOn(XZ)
|
||||||
|> startProfileAt([62.74, 206.13], %)
|
|> startProfileAt([62.74, 206.13], %)
|
||||||
@ -1904,7 +1929,7 @@ let w = f() + f()
|
|||||||
// Execute the program.
|
// Execute the program.
|
||||||
ctx.run_with_caching(program).await.unwrap();
|
ctx.run_with_caching(program).await.unwrap();
|
||||||
|
|
||||||
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
|
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.mod_local.id_generator;
|
||||||
|
|
||||||
assert_eq!(id_generator, new_id_generator);
|
assert_eq!(id_generator, new_id_generator);
|
||||||
}
|
}
|
||||||
@ -1933,7 +1958,6 @@ let w = f() + f()
|
|||||||
// Execute the program.
|
// Execute the program.
|
||||||
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
||||||
|
|
||||||
// Get the id_generator from the first execution.
|
|
||||||
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
||||||
|
|
||||||
// Ensure the settings are as expected.
|
// Ensure the settings are as expected.
|
||||||
@ -1945,7 +1969,6 @@ let w = f() + f()
|
|||||||
// Execute the program.
|
// Execute the program.
|
||||||
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
ctx.run_with_caching(old_program.clone()).await.unwrap();
|
||||||
|
|
||||||
// Get the id_generator from the first execution.
|
|
||||||
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
||||||
|
|
||||||
// Ensure the settings are as expected.
|
// Ensure the settings are as expected.
|
||||||
@ -1957,7 +1980,6 @@ let w = f() + f()
|
|||||||
// Execute the program.
|
// Execute the program.
|
||||||
ctx.run_with_caching(old_program).await.unwrap();
|
ctx.run_with_caching(old_program).await.unwrap();
|
||||||
|
|
||||||
// Get the id_generator from the first execution.
|
|
||||||
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
let settings_state = cache::read_old_ast().await.unwrap().settings;
|
||||||
|
|
||||||
// Ensure the settings are as expected.
|
// Ensure the settings are as expected.
|
||||||
@ -1976,4 +1998,41 @@ let w = f() + f()
|
|||||||
let result = ctx2.run_mock(program2, true).await.unwrap();
|
let result = ctx2.run_mock(program2, true).await.unwrap();
|
||||||
assert_eq!(result.variables.get("z").unwrap().as_f64().unwrap(), 3.0);
|
assert_eq!(result.variables.get("z").unwrap().as_f64().unwrap(), 3.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn read_tag_version() {
|
||||||
|
let ast = r#"fn bar(t) {
|
||||||
|
return startSketchOn(XY)
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> angledLine({
|
||||||
|
angle = -60,
|
||||||
|
length = segLen(t),
|
||||||
|
}, %)
|
||||||
|
|> line(end = [0, 0])
|
||||||
|
|> close()
|
||||||
|
}
|
||||||
|
|
||||||
|
sketch = startSketchOn(XY)
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> line(end = [0, 10])
|
||||||
|
|> line(end = [10, 0], tag = $tag0)
|
||||||
|
|> line(end = [0, 0])
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
// tag0 tags an edge
|
||||||
|
return bar(tag0)
|
||||||
|
}
|
||||||
|
|
||||||
|
solid = sketch |> extrude(length = 10)
|
||||||
|
// tag0 tags a face
|
||||||
|
sketch2 = startSketchOn(solid, tag0)
|
||||||
|
|> startProfileAt([0,0], %)
|
||||||
|
|> line(end = [0, 1])
|
||||||
|
|> line(end = [1, 0])
|
||||||
|
|> line(end = [0, 0])
|
||||||
|
|
||||||
|
foo() |> extrude(length = 1)
|
||||||
|
"#;
|
||||||
|
parse_execute(ast).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,9 @@ use uuid::Uuid;
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails, Severity},
|
errors::{KclError, KclErrorDetails, Severity},
|
||||||
execution::{
|
execution::{
|
||||||
annotations, kcl_value,
|
annotations,
|
||||||
|
id_generator::IdGenerator,
|
||||||
|
kcl_value,
|
||||||
memory::{ProgramMemory, Stack},
|
memory::{ProgramMemory, Stack},
|
||||||
Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue,
|
Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, EnvironmentRef, ExecOutcome, ExecutorSettings, KclValue,
|
||||||
Operation, UnitAngle, UnitLen,
|
Operation, UnitAngle, UnitLen,
|
||||||
@ -26,12 +28,11 @@ use crate::{
|
|||||||
pub struct ExecState {
|
pub struct ExecState {
|
||||||
pub(super) global: GlobalState,
|
pub(super) global: GlobalState,
|
||||||
pub(super) mod_local: ModuleState,
|
pub(super) mod_local: ModuleState,
|
||||||
|
pub(super) exec_context: Option<super::ExecutorContext>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct GlobalState {
|
pub(super) struct GlobalState {
|
||||||
/// The stable artifact ID generator.
|
|
||||||
pub id_generator: IdGenerator,
|
|
||||||
/// Map from source file absolute path to module ID.
|
/// Map from source file absolute path to module ID.
|
||||||
pub path_to_source_id: IndexMap<ModulePath, ModuleId>,
|
pub path_to_source_id: IndexMap<ModulePath, ModuleId>,
|
||||||
/// Map from module ID to source file.
|
/// Map from module ID to source file.
|
||||||
@ -62,6 +63,8 @@ pub(super) struct GlobalState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct ModuleState {
|
pub(super) struct ModuleState {
|
||||||
|
/// The id generator for this module.
|
||||||
|
pub id_generator: IdGenerator,
|
||||||
pub stack: Stack,
|
pub stack: Stack,
|
||||||
/// The current value of the pipe operator returned from the previous
|
/// The current value of the pipe operator returned from the previous
|
||||||
/// expression. If we're not currently in a pipeline, this will be None.
|
/// expression. If we're not currently in a pipeline, this will be None.
|
||||||
@ -73,25 +76,21 @@ pub(super) struct ModuleState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ExecState {
|
impl ExecState {
|
||||||
pub fn new(exec_settings: &ExecutorSettings) -> Self {
|
pub fn new(exec_context: &super::ExecutorContext) -> Self {
|
||||||
ExecState {
|
ExecState {
|
||||||
global: GlobalState::new(exec_settings),
|
global: GlobalState::new(&exec_context.settings),
|
||||||
mod_local: ModuleState::new(exec_settings, None, ProgramMemory::new()),
|
mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
|
||||||
|
exec_context: Some(exec_context.clone()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn reset(&mut self, exec_settings: &ExecutorSettings) {
|
pub(super) fn reset(&mut self, exec_context: &super::ExecutorContext) {
|
||||||
let mut id_generator = self.global.id_generator.clone();
|
let global = GlobalState::new(&exec_context.settings);
|
||||||
// We do not pop the ids, since we want to keep the same id generator.
|
|
||||||
// This is for the front end to keep track of the ids.
|
|
||||||
id_generator.next_id = 0;
|
|
||||||
|
|
||||||
let mut global = GlobalState::new(exec_settings);
|
|
||||||
global.id_generator = id_generator;
|
|
||||||
|
|
||||||
*self = ExecState {
|
*self = ExecState {
|
||||||
global,
|
global,
|
||||||
mod_local: ModuleState::new(exec_settings, None, ProgramMemory::new()),
|
mod_local: ModuleState::new(&exec_context.settings, None, ProgramMemory::new(), Default::default()),
|
||||||
|
exec_context: Some(exec_context.clone()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,13 +112,13 @@ impl ExecState {
|
|||||||
/// Convert to execution outcome when running in WebAssembly. We want to
|
/// Convert to execution outcome when running in WebAssembly. We want to
|
||||||
/// reduce the amount of data that crosses the WASM boundary as much as
|
/// reduce the amount of data that crosses the WASM boundary as much as
|
||||||
/// possible.
|
/// possible.
|
||||||
pub fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
pub async fn to_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||||
// Fields are opt-in so that we don't accidentally leak private internal
|
// Fields are opt-in so that we don't accidentally leak private internal
|
||||||
// state when we add more to ExecState.
|
// state when we add more to ExecState.
|
||||||
ExecOutcome {
|
ExecOutcome {
|
||||||
variables: self
|
variables: self
|
||||||
.stack()
|
.stack()
|
||||||
.find_all_in_env(main_ref, |_| true)
|
.find_all_in_env(main_ref)
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
.collect(),
|
.collect(),
|
||||||
operations: self.global.operations,
|
operations: self.global.operations,
|
||||||
@ -132,16 +131,21 @@ impl ExecState {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|(k, v)| ((*v), k.clone()))
|
.map(|(k, v)| ((*v), k.clone()))
|
||||||
.collect(),
|
.collect(),
|
||||||
|
default_planes: if let Some(ctx) = &self.exec_context {
|
||||||
|
ctx.engine.get_default_planes().read().await.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
pub async fn to_mock_wasm_outcome(self, main_ref: EnvironmentRef) -> ExecOutcome {
|
||||||
// Fields are opt-in so that we don't accidentally leak private internal
|
// Fields are opt-in so that we don't accidentally leak private internal
|
||||||
// state when we add more to ExecState.
|
// state when we add more to ExecState.
|
||||||
ExecOutcome {
|
ExecOutcome {
|
||||||
variables: self
|
variables: self
|
||||||
.stack()
|
.stack()
|
||||||
.find_all_in_env(main_ref, |_| true)
|
.find_all_in_env(main_ref)
|
||||||
.map(|(k, v)| (k.clone(), v.clone()))
|
.map(|(k, v)| (k.clone(), v.clone()))
|
||||||
.collect(),
|
.collect(),
|
||||||
operations: Default::default(),
|
operations: Default::default(),
|
||||||
@ -149,6 +153,11 @@ impl ExecState {
|
|||||||
artifact_graph: Default::default(),
|
artifact_graph: Default::default(),
|
||||||
errors: self.global.errors,
|
errors: self.global.errors,
|
||||||
filenames: Default::default(),
|
filenames: Default::default(),
|
||||||
|
default_planes: if let Some(ctx) = &self.exec_context {
|
||||||
|
ctx.engine.get_default_planes().read().await.clone()
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,8 +169,12 @@ impl ExecState {
|
|||||||
&mut self.mod_local.stack
|
&mut self.mod_local.stack
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn next_uuid(&mut self) -> Uuid {
|
pub fn next_uuid(&mut self) -> Uuid {
|
||||||
self.global.id_generator.next_uuid()
|
self.mod_local.id_generator.next_uuid()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id_generator(&mut self) -> &mut IdGenerator {
|
||||||
|
&mut self.mod_local.id_generator
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add_artifact(&mut self, artifact: Artifact) {
|
pub(crate) fn add_artifact(&mut self, artifact: Artifact) {
|
||||||
@ -241,7 +254,6 @@ impl ExecState {
|
|||||||
impl GlobalState {
|
impl GlobalState {
|
||||||
fn new(settings: &ExecutorSettings) -> Self {
|
fn new(settings: &ExecutorSettings) -> Self {
|
||||||
let mut global = GlobalState {
|
let mut global = GlobalState {
|
||||||
id_generator: Default::default(),
|
|
||||||
path_to_source_id: Default::default(),
|
path_to_source_id: Default::default(),
|
||||||
module_infos: Default::default(),
|
module_infos: Default::default(),
|
||||||
artifacts: Default::default(),
|
artifacts: Default::default(),
|
||||||
@ -274,8 +286,14 @@ impl GlobalState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleState {
|
impl ModuleState {
|
||||||
pub(super) fn new(exec_settings: &ExecutorSettings, std_path: Option<String>, memory: Arc<ProgramMemory>) -> Self {
|
pub(super) fn new(
|
||||||
|
exec_settings: &ExecutorSettings,
|
||||||
|
std_path: Option<String>,
|
||||||
|
memory: Arc<ProgramMemory>,
|
||||||
|
module_id: Option<ModuleId>,
|
||||||
|
) -> Self {
|
||||||
ModuleState {
|
ModuleState {
|
||||||
|
id_generator: IdGenerator::new(module_id),
|
||||||
stack: memory.new_stack(),
|
stack: memory.new_stack(),
|
||||||
pipe_value: Default::default(),
|
pipe_value: Default::default(),
|
||||||
module_exports: Default::default(),
|
module_exports: Default::default(),
|
||||||
@ -332,29 +350,3 @@ impl MetaSettings {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generator for ArtifactIds that can be stable across executions.
|
|
||||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct IdGenerator {
|
|
||||||
pub(super) next_id: usize,
|
|
||||||
ids: Vec<uuid::Uuid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IdGenerator {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_uuid(&mut self) -> uuid::Uuid {
|
|
||||||
if let Some(id) = self.ids.get(self.next_id) {
|
|
||||||
self.next_id += 1;
|
|
||||||
*id
|
|
||||||
} else {
|
|
||||||
let id = uuid::Uuid::new_v4();
|
|
||||||
self.ids.push(id);
|
|
||||||
self.next_id += 1;
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -8,11 +8,11 @@
|
|||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! println {
|
macro_rules! println {
|
||||||
($($rest:tt)*) => {
|
($($rest:tt)*) => {
|
||||||
#[cfg(feature = "disable-println")]
|
#[cfg(all(feature = "disable-println", not(test)))]
|
||||||
{
|
{
|
||||||
let _ = format!($($rest)*);
|
let _ = format!($($rest)*);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "disable-println"))]
|
#[cfg(any(not(feature = "disable-println"), test))]
|
||||||
std::println!($($rest)*)
|
std::println!($($rest)*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -20,11 +20,11 @@ macro_rules! println {
|
|||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! eprintln {
|
macro_rules! eprintln {
|
||||||
($($rest:tt)*) => {
|
($($rest:tt)*) => {
|
||||||
#[cfg(feature = "disable-println")]
|
#[cfg(all(feature = "disable-println", not(test)))]
|
||||||
{
|
{
|
||||||
let _ = format!($($rest)*);
|
let _ = format!($($rest)*);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "disable-println"))]
|
#[cfg(any(not(feature = "disable-println"), test))]
|
||||||
std::eprintln!($($rest)*)
|
std::eprintln!($($rest)*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,11 +32,11 @@ macro_rules! eprintln {
|
|||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
($($rest:tt)*) => {
|
($($rest:tt)*) => {
|
||||||
#[cfg(feature = "disable-println")]
|
#[cfg(all(feature = "disable-println", not(test)))]
|
||||||
{
|
{
|
||||||
let _ = format!($($rest)*);
|
let _ = format!($($rest)*);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "disable-println"))]
|
#[cfg(any(not(feature = "disable-println"), test))]
|
||||||
std::print!($($rest)*)
|
std::print!($($rest)*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -44,11 +44,11 @@ macro_rules! print {
|
|||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
macro_rules! eprint {
|
macro_rules! eprint {
|
||||||
($($rest:tt)*) => {
|
($($rest:tt)*) => {
|
||||||
#[cfg(feature = "disable-println")]
|
#[cfg(all(feature = "disable-println", not(test)))]
|
||||||
{
|
{
|
||||||
let _ = format!($($rest)*);
|
let _ = format!($($rest)*);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "disable-println"))]
|
#[cfg(any(not(feature = "disable-println"), test))]
|
||||||
std::eprint!($($rest)*)
|
std::eprint!($($rest)*)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,7 +108,7 @@ pub mod wasm_engine {
|
|||||||
pub use crate::{
|
pub use crate::{
|
||||||
coredump::wasm::{CoreDumpManager, CoreDumper},
|
coredump::wasm::{CoreDumpManager, CoreDumper},
|
||||||
engine::conn_wasm::{EngineCommandManager, EngineConnection},
|
engine::conn_wasm::{EngineCommandManager, EngineConnection},
|
||||||
fs::wasm::FileSystemManager,
|
fs::wasm::{FileManager, FileSystemManager},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ macro_rules! logln {
|
|||||||
}
|
}
|
||||||
pub(crate) use logln;
|
pub(crate) use logln;
|
||||||
|
|
||||||
#[cfg(all(not(feature = "disable-println"), not(target_arch = "wasm32")))]
|
#[cfg(any(test, all(not(feature = "disable-println"), not(target_arch = "wasm32"))))]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn log_inner(msg: String) {
|
fn log_inner(msg: String) {
|
||||||
eprintln!("{msg}");
|
eprintln!("{msg}");
|
||||||
@ -48,7 +48,7 @@ fn log_inner(msg: String) {
|
|||||||
web_sys::console::log_1(&msg.into());
|
web_sys::console::log_1(&msg.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "disable-println")]
|
#[cfg(all(feature = "disable-println", not(test)))]
|
||||||
#[inline]
|
#[inline]
|
||||||
fn log_inner(_msg: String) {}
|
fn log_inner(_msg: String) {}
|
||||||
|
|
||||||
|
|||||||
@ -1170,7 +1170,7 @@ impl LanguageServer for Backend {
|
|||||||
Hover::Variable { name, ty: None, range } => Ok(with_cached_var(&name, |value| {
|
Hover::Variable { name, ty: None, range } => Ok(with_cached_var(&name, |value| {
|
||||||
let mut text: String = format!("```\n{}", name);
|
let mut text: String = format!("```\n{}", name);
|
||||||
if let Some(ty) = value.principal_type() {
|
if let Some(ty) = value.principal_type() {
|
||||||
text.push_str(&format!(": {}", ty));
|
text.push_str(&format!(": {}", ty.human_friendly_type()));
|
||||||
}
|
}
|
||||||
if let Some(v) = value.value_str() {
|
if let Some(v) = value.value_str() {
|
||||||
text.push_str(&format!(" = {}", v));
|
text.push_str(&format!(" = {}", v));
|
||||||
|
|||||||
@ -36,6 +36,6 @@ async fn main() {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let mut exec_state = ExecState::new(&ctx.settings);
|
let mut exec_state = ExecState::new(&ctx);
|
||||||
ctx.run(&program, &mut exec_state).await.unwrap();
|
ctx.run(&program, &mut exec_state).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,12 @@ impl ModuleId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for ModuleId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
#[derive(Debug, Clone, Default)]
|
||||||
pub(crate) struct ModuleLoader {
|
pub(crate) struct ModuleLoader {
|
||||||
/// The stack of import statements for detecting circular module imports.
|
/// The stack of import statements for detecting circular module imports.
|
||||||
|
|||||||
@ -2149,7 +2149,7 @@ impl From<&Node<TagDeclarator>> for TagIdentifier {
|
|||||||
fn from(tag: &Node<TagDeclarator>) -> Self {
|
fn from(tag: &Node<TagDeclarator>) -> Self {
|
||||||
TagIdentifier {
|
TagIdentifier {
|
||||||
value: tag.name.clone(),
|
value: tag.name.clone(),
|
||||||
info: None,
|
info: Vec::new(),
|
||||||
meta: vec![Metadata {
|
meta: vec![Metadata {
|
||||||
source_range: tag.into(),
|
source_range: tag.into(),
|
||||||
}],
|
}],
|
||||||
@ -2937,7 +2937,7 @@ impl fmt::Display for Type {
|
|||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Type::Primitive(primitive_type) => primitive_type.fmt(f),
|
Type::Primitive(primitive_type) => primitive_type.fmt(f),
|
||||||
Type::Array(primitive_type) => write!(f, "{primitive_type}[]"),
|
Type::Array(primitive_type) => write!(f, "[{primitive_type}]"),
|
||||||
Type::Object { properties } => {
|
Type::Object { properties } => {
|
||||||
write!(f, "{{")?;
|
write!(f, "{{")?;
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
@ -3509,7 +3509,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_parse_type_args_array_on_functions() {
|
async fn test_parse_type_args_array_on_functions() {
|
||||||
let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => {
|
let some_program_string = r#"fn thing = (arg0: [number], arg1: [string], tag?: string) => {
|
||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
@ -3540,7 +3540,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_parse_type_args_object_on_functions() {
|
async fn test_parse_type_args_object_on_functions() {
|
||||||
let some_program_string = r#"fn thing = (arg0: number[], arg1: {thing: number, things: string[], more?: string}, tag?: string) => {
|
let some_program_string = r#"fn thing = (arg0: [number], arg1: {thing: number, things: [string], more?: string}, tag?: string) => {
|
||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
@ -3594,7 +3594,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
56,
|
56,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(Type::Array(PrimitiveType::String), 58, 64, module_id)),
|
type_: Some(Node::new(Type::Array(PrimitiveType::String), 59, 65, module_id)),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
labeled: true,
|
labeled: true,
|
||||||
digest: None
|
digest: None
|
||||||
@ -3625,7 +3625,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_parse_return_type_on_functions() {
|
async fn test_parse_return_type_on_functions() {
|
||||||
let some_program_string = r#"fn thing(): {thing: number, things: string[], more?: string} {
|
let some_program_string = r#"fn thing(): {thing: number, things: [string], more?: string} {
|
||||||
return 1
|
return 1
|
||||||
}"#;
|
}"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
@ -3675,7 +3675,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
34,
|
34,
|
||||||
module_id,
|
module_id,
|
||||||
),
|
),
|
||||||
type_: Some(Node::new(Type::Array(PrimitiveType::String), 36, 42, module_id)),
|
type_: Some(Node::new(Type::Array(PrimitiveType::String), 37, 43, module_id)),
|
||||||
default_value: None,
|
default_value: None,
|
||||||
labeled: true,
|
labeled: true,
|
||||||
digest: None
|
digest: None
|
||||||
|
|||||||
@ -4,17 +4,17 @@
|
|||||||
use std::{cell::RefCell, collections::BTreeMap};
|
use std::{cell::RefCell, collections::BTreeMap};
|
||||||
|
|
||||||
use winnow::{
|
use winnow::{
|
||||||
combinator::{alt, delimited, opt, peek, preceded, repeat, separated, separated_pair, terminated},
|
combinator::{alt, delimited, opt, peek, preceded, repeat, repeat_till, separated, separated_pair, terminated},
|
||||||
dispatch,
|
dispatch,
|
||||||
error::{ErrMode, StrContext, StrContextValue},
|
error::{ErrMode, StrContext, StrContextValue},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
stream::Stream,
|
stream::Stream,
|
||||||
token::{any, one_of, take_till},
|
token::{any, none_of, one_of, take_till},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
ast::types::{Ascription, ImportPath, LabelledExpression},
|
ast::types::{Ascription, ImportPath, LabelledExpression},
|
||||||
token::NumericSuffix,
|
token::{NumericSuffix, RESERVED_WORDS},
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
@ -746,21 +746,58 @@ pub(crate) fn array_elem_by_elem(i: &mut TokenSlice) -> PResult<Node<ArrayExpres
|
|||||||
)
|
)
|
||||||
.context(expected("array contents, a list of elements (like [1, 2, 3])"))
|
.context(expected("array contents, a list of elements (like [1, 2, 3])"))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
|
ignore_trailing_comma(i);
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let end = close_bracket(i)
|
|
||||||
.map_err(|e| {
|
let maybe_end = close_bracket(i).map_err(|e| {
|
||||||
if let Some(mut err) = e.clone().into_inner() {
|
if let Some(mut err) = e.clone().into_inner() {
|
||||||
err.cause = Some(CompilationError::fatal(
|
let start_range = open.as_source_range();
|
||||||
open.as_source_range(),
|
let end_range = i.as_source_range();
|
||||||
"Array is missing a closing bracket(`]`)",
|
err.cause = Some(CompilationError::fatal(
|
||||||
));
|
SourceRange::from([start_range.start(), end_range.start(), end_range.module_id().as_usize()]),
|
||||||
ErrMode::Cut(err)
|
"Encountered an unexpected character(s) before finding a closing bracket(`]`) for the array",
|
||||||
} else {
|
));
|
||||||
// ErrMode::Incomplete, not sure if it's actually possible to end up with this here
|
ErrMode::Cut(err)
|
||||||
e
|
} else {
|
||||||
}
|
// ErrMode::Incomplete, not sure if it's actually possible to end up with this here
|
||||||
})?
|
e
|
||||||
.end;
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if maybe_end.is_err() {
|
||||||
|
// if there is a closing bracket at some point, but it wasn't the next token, it's likely that they forgot a comma between some
|
||||||
|
// of the elements
|
||||||
|
let maybe_closing_bracket: PResult<((), Token)> = peek(repeat_till(
|
||||||
|
0..,
|
||||||
|
none_of(|token: Token| {
|
||||||
|
// bail out early if we encounter something that is for sure not allowed in an
|
||||||
|
// array, otherwise we could seek to find a closing bracket until the end of the
|
||||||
|
// file
|
||||||
|
RESERVED_WORDS
|
||||||
|
.keys()
|
||||||
|
.chain([",,", "{", "}", "["].iter())
|
||||||
|
.any(|word| *word == token.value)
|
||||||
|
})
|
||||||
|
.void(),
|
||||||
|
one_of(|term: Token| term.value == "]"),
|
||||||
|
))
|
||||||
|
.parse_next(i);
|
||||||
|
let has_closing_bracket = maybe_closing_bracket.is_ok();
|
||||||
|
if has_closing_bracket {
|
||||||
|
let start_range = i.as_source_range();
|
||||||
|
// safe to unwrap here because we checked it was Ok above
|
||||||
|
let end_range = maybe_closing_bracket.unwrap().1.as_source_range();
|
||||||
|
let e = ContextError {
|
||||||
|
context: vec![],
|
||||||
|
cause: Some(CompilationError::fatal(
|
||||||
|
SourceRange::from([start_range.start(), end_range.end(), end_range.module_id().as_usize()]),
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between elements.",
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
return Err(ErrMode::Cut(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let end = maybe_end?.end;
|
||||||
|
|
||||||
// Sort the array's elements (i.e. expression nodes) from the noncode nodes.
|
// Sort the array's elements (i.e. expression nodes) from the noncode nodes.
|
||||||
let (elements, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = elements.into_iter().enumerate().fold(
|
let (elements, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = elements.into_iter().enumerate().fold(
|
||||||
@ -819,7 +856,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
||||||
let key = nameable_identifier.context(expected("the property's key (the name or identifier of the property), e.g. in 'height: 4', 'height' is the property key")).parse_next(i)?;
|
let key = nameable_identifier.context(expected("the property's key (the name or identifier of the property), e.g. in 'height = 4', 'height' is the property key")).parse_next(i)?;
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
Ok(Node {
|
Ok(Node {
|
||||||
start: key.start,
|
start: key.start,
|
||||||
@ -846,7 +883,7 @@ fn object_property(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
|||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
let expr = match expression
|
let expr = match expression
|
||||||
.context(expected(
|
.context(expected(
|
||||||
"the value which you're setting the property to, e.g. in 'height: 4', the value is 4",
|
"the value which you're setting the property to, e.g. in 'height = 4', the value is 4",
|
||||||
))
|
))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
{
|
{
|
||||||
@ -892,7 +929,7 @@ fn property_separator(i: &mut TokenSlice) -> PResult<()> {
|
|||||||
alt((
|
alt((
|
||||||
// Normally you need a comma.
|
// Normally you need a comma.
|
||||||
comma_sep,
|
comma_sep,
|
||||||
// But, if the array is ending, no need for a comma.
|
// But, if the object is ending, no need for a comma.
|
||||||
peek(preceded(opt(whitespace), close_brace)).void(),
|
peek(preceded(opt(whitespace), close_brace)).void(),
|
||||||
))
|
))
|
||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
@ -926,10 +963,62 @@ pub(crate) fn object(i: &mut TokenSlice) -> PResult<Node<ObjectExpression>> {
|
|||||||
)),
|
)),
|
||||||
)
|
)
|
||||||
.context(expected(
|
.context(expected(
|
||||||
"a comma-separated list of key-value pairs, e.g. 'height: 4, width: 3'",
|
"a comma-separated list of key-value pairs, e.g. 'height = 4, width = 3'",
|
||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
|
ignore_trailing_comma(i);
|
||||||
|
ignore_whitespace(i);
|
||||||
|
|
||||||
|
let maybe_end = close_brace(i).map_err(|e| {
|
||||||
|
if let Some(mut err) = e.clone().into_inner() {
|
||||||
|
let start_range = open.as_source_range();
|
||||||
|
let end_range = i.as_source_range();
|
||||||
|
err.cause = Some(CompilationError::fatal(
|
||||||
|
SourceRange::from([start_range.start(), end_range.start(), end_range.module_id().as_usize()]),
|
||||||
|
"Encountered an unexpected character(s) before finding a closing brace(`}`) for the object",
|
||||||
|
));
|
||||||
|
ErrMode::Cut(err)
|
||||||
|
} else {
|
||||||
|
// ErrMode::Incomplete, not sure if it's actually possible to end up with this here
|
||||||
|
e
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if maybe_end.is_err() {
|
||||||
|
// if there is a closing brace at some point, but it wasn't the next token, it's likely that they forgot a comma between some
|
||||||
|
// of the properties
|
||||||
|
let maybe_closing_brace: PResult<((), Token)> = peek(repeat_till(
|
||||||
|
0..,
|
||||||
|
none_of(|token: Token| {
|
||||||
|
// bail out early if we encounter something that is for sure not allowed in an
|
||||||
|
// object, otherwise we could seek to find a closing brace until the end of the
|
||||||
|
// file
|
||||||
|
RESERVED_WORDS
|
||||||
|
.keys()
|
||||||
|
.chain([",,", "[", "]", "{"].iter())
|
||||||
|
.any(|word| *word == token.value)
|
||||||
|
})
|
||||||
|
.void(),
|
||||||
|
one_of(|c: Token| c.value == "}"),
|
||||||
|
))
|
||||||
|
.parse_next(i);
|
||||||
|
let has_closing_brace = maybe_closing_brace.is_ok();
|
||||||
|
if has_closing_brace {
|
||||||
|
let start_range = i.as_source_range();
|
||||||
|
// okay to unwrap here because we checked it was Ok above
|
||||||
|
let end_range = maybe_closing_brace.unwrap().1.as_source_range();
|
||||||
|
|
||||||
|
let e = ContextError {
|
||||||
|
context: vec![],
|
||||||
|
cause: Some(CompilationError::fatal(
|
||||||
|
SourceRange::from([start_range.start(), end_range.end(), end_range.module_id().as_usize()]),
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between properties.",
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
return Err(ErrMode::Cut(e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let end = maybe_end?.end;
|
||||||
// Sort the object's properties from the noncode nodes.
|
// Sort the object's properties from the noncode nodes.
|
||||||
let (properties, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = properties.into_iter().enumerate().fold(
|
let (properties, non_code_nodes): (Vec<_>, BTreeMap<usize, _>) = properties.into_iter().enumerate().fold(
|
||||||
(Vec::new(), BTreeMap::new()),
|
(Vec::new(), BTreeMap::new()),
|
||||||
@ -945,9 +1034,7 @@ pub(crate) fn object(i: &mut TokenSlice) -> PResult<Node<ObjectExpression>> {
|
|||||||
(properties, non_code_nodes)
|
(properties, non_code_nodes)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
ignore_trailing_comma(i);
|
|
||||||
ignore_whitespace(i);
|
|
||||||
let end = close_brace(i)?.end;
|
|
||||||
let non_code_meta = NonCodeMeta {
|
let non_code_meta = NonCodeMeta {
|
||||||
non_code_nodes,
|
non_code_nodes,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -2564,7 +2651,7 @@ fn argument_type(i: &mut TokenSlice) -> PResult<Node<Type>> {
|
|||||||
))
|
))
|
||||||
}),
|
}),
|
||||||
// Array types
|
// Array types
|
||||||
(primitive_type, open_bracket, close_bracket).map(|(t, _, _)| Ok(t.map(Type::Array))),
|
(open_bracket, primitive_type, close_bracket).map(|(_, t, _)| Ok(t.map(Type::Array))),
|
||||||
// Primitive types
|
// Primitive types
|
||||||
primitive_type.map(|t| Ok(t.map(Type::Primitive))),
|
primitive_type.map(|t| Ok(t.map(Type::Primitive))),
|
||||||
))
|
))
|
||||||
@ -3869,7 +3956,7 @@ mySk1 = startSketchOn(XY)
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
src_expected,
|
src_expected,
|
||||||
src_actual,
|
src_actual,
|
||||||
"expected error would highlight {} but it actually highlighted {}",
|
"expected error would highlight `{}` but it actually highlighted `{}`",
|
||||||
&p[src_expected[0]..src_expected[1]],
|
&p[src_expected[0]..src_expected[1]],
|
||||||
&p[src_actual[0]..src_actual[1]],
|
&p[src_actual[0]..src_actual[1]],
|
||||||
);
|
);
|
||||||
@ -4060,7 +4147,11 @@ z(-[["#,
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_weird_lots_of_fancy_brackets() {
|
fn test_parse_weird_lots_of_fancy_brackets() {
|
||||||
assert_err(r#"zz({{{{{{{{)iegAng{{{{{{{##"#, "Unexpected token: (", [2, 3]);
|
assert_err(
|
||||||
|
r#"zz({{{{{{{{)iegAng{{{{{{{##"#,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing brace(`}`) for the object",
|
||||||
|
[3, 4],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -4601,10 +4692,123 @@ let myBox = box([0,0], -3, -16, -10)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_missing_closing_bracket() {
|
fn test_parse_array_missing_closing_bracket() {
|
||||||
let some_program_string = r#"
|
let some_program_string = r#"
|
||||||
sketch001 = startSketchOn('XZ') |> startProfileAt([90.45, 119.09, %)"#;
|
sketch001 = startSketchOn('XZ') |> startProfileAt([90.45, 119.09, %)"#;
|
||||||
assert_err(some_program_string, "Array is missing a closing bracket(`]`)", [51, 52]);
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing bracket(`]`) for the array",
|
||||||
|
[51, 67],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_array_missing_comma() {
|
||||||
|
let some_program_string = r#"
|
||||||
|
sketch001 = startSketchOn('XZ') |> startProfileAt([90.45 119.09], %)"#;
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between elements.",
|
||||||
|
[52, 65],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_array_reserved_word_early_exit() {
|
||||||
|
// since there is an early exit if encountering a reserved word, the error should be about
|
||||||
|
// that and not the missing comma
|
||||||
|
let some_program_string = r#"
|
||||||
|
sketch001 = startSketchOn('XZ') |> startProfileAt([90.45 $struct], %)"#;
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing bracket(`]`) for the array",
|
||||||
|
[51, 52],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_array_random_brace() {
|
||||||
|
let some_program_string = r#"
|
||||||
|
sketch001 = startSketchOn('XZ') |> startProfileAt([}], %)"#;
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing bracket(`]`) for the array",
|
||||||
|
[51, 52],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_missing_closing_brace() {
|
||||||
|
let some_program_string = r#"{
|
||||||
|
foo = bar,"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing brace(`}`) for the object",
|
||||||
|
[0, 23],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_reserved_word_early_exit() {
|
||||||
|
// since there is an early exit if encountering a reserved word, the error should be about
|
||||||
|
// that and not the missing comma
|
||||||
|
let some_program_string = r#"{bar = foo struct = man}"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing brace(`}`) for the object",
|
||||||
|
[0, 1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_missing_comma() {
|
||||||
|
let some_program_string = r#"{
|
||||||
|
foo = bar,
|
||||||
|
bar = foo
|
||||||
|
bat = man
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between properties.",
|
||||||
|
[37, 78],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_missing_comma_one_line() {
|
||||||
|
let some_program_string = r#"{bar = foo bat = man}"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between properties.",
|
||||||
|
[1, 21],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_random_bracket() {
|
||||||
|
let some_program_string = r#"{]}"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Encountered an unexpected character(s) before finding a closing brace(`}`) for the object",
|
||||||
|
[0, 1],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_shorthand_missing_comma() {
|
||||||
|
let some_program_string = r#"
|
||||||
|
bar = 1
|
||||||
|
{
|
||||||
|
foo = bar,
|
||||||
|
bar
|
||||||
|
bat = man
|
||||||
|
}"#;
|
||||||
|
|
||||||
|
assert_err(
|
||||||
|
some_program_string,
|
||||||
|
"Unexpected character encountered. You might be missing a comma in between properties.",
|
||||||
|
[54, 89],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@ -24,7 +24,6 @@ use crate::{
|
|||||||
|
|
||||||
mod tokeniser;
|
mod tokeniser;
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(crate) use tokeniser::RESERVED_WORDS;
|
pub(crate) use tokeniser::RESERVED_WORDS;
|
||||||
|
|
||||||
// Note the ordering, it's important that `m` comes after `mm` and `cm`.
|
// Note the ordering, it's important that `m` comes after `mm` and `cm`.
|
||||||
@ -162,7 +161,9 @@ impl IntoIterator for TokenStream {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct TokenSlice<'a> {
|
pub(crate) struct TokenSlice<'a> {
|
||||||
stream: &'a TokenStream,
|
stream: &'a TokenStream,
|
||||||
|
/// Current position of the leading Token in the stream
|
||||||
start: usize,
|
start: usize,
|
||||||
|
/// The number of total Tokens in the stream
|
||||||
end: usize,
|
end: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +191,21 @@ impl<'a> TokenSlice<'a> {
|
|||||||
stream: self.stream,
|
stream: self.stream,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_source_range(&self) -> SourceRange {
|
||||||
|
let stream_len = self.stream.tokens.len();
|
||||||
|
let first_token = if stream_len == self.start {
|
||||||
|
&self.stream.tokens[self.start - 1]
|
||||||
|
} else {
|
||||||
|
self.token(0)
|
||||||
|
};
|
||||||
|
let last_token = if stream_len == self.end {
|
||||||
|
&self.stream.tokens[stream_len - 1]
|
||||||
|
} else {
|
||||||
|
self.token(self.end - self.start)
|
||||||
|
};
|
||||||
|
SourceRange::new(first_token.start, last_token.end, last_token.module_id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for TokenSlice<'a> {
|
impl<'a> IntoIterator for TokenSlice<'a> {
|
||||||
@ -294,6 +310,14 @@ impl<'a> winnow::stream::StreamIsPartial for TokenSlice<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> winnow::stream::FindSlice<&str> for TokenSlice<'a> {
|
||||||
|
fn find_slice(&self, substr: &str) -> Option<std::ops::Range<usize>> {
|
||||||
|
self.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(i, b)| if b.value == substr { Some(i..self.end) } else { None })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Checkpoint(usize, usize);
|
pub struct Checkpoint(usize, usize);
|
||||||
|
|
||||||
|
|||||||
@ -149,7 +149,7 @@ async fn execute_test(test: &Test, render_to_png: bool, export_step: bool) {
|
|||||||
// due to SSI and GPU.
|
// due to SSI and GPU.
|
||||||
std::fs::write(test.output_dir.join(EXPORTED_STEP_NAME), step).unwrap();
|
std::fs::write(test.output_dir.join(EXPORTED_STEP_NAME), step).unwrap();
|
||||||
}
|
}
|
||||||
let outcome = exec_state.to_wasm_outcome(env_ref);
|
let outcome = exec_state.to_wasm_outcome(env_ref).await;
|
||||||
assert_common_snapshots(
|
assert_common_snapshots(
|
||||||
test,
|
test,
|
||||||
outcome.operations,
|
outcome.operations,
|
||||||
@ -2224,3 +2224,24 @@ mod crazy_multi_profile {
|
|||||||
super::execute(TEST_NAME, true).await
|
super::execute(TEST_NAME, true).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mod assembly_mixed_units_cubes {
|
||||||
|
const TEST_NAME: &str = "assembly_mixed_units_cubes";
|
||||||
|
|
||||||
|
/// Test parsing KCL.
|
||||||
|
#[test]
|
||||||
|
fn parse() {
|
||||||
|
super::parse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||||
|
#[test]
|
||||||
|
fn unparse() {
|
||||||
|
super::unparse(TEST_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Test that KCL is executed correctly.
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn kcl_test_execute() {
|
||||||
|
super::execute(TEST_NAME, true).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -12,7 +12,10 @@ use validator::Validate;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, Solid, SolidSet},
|
execution::{
|
||||||
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
|
ExecState, KclValue, PrimitiveType, Solid,
|
||||||
|
},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -38,8 +41,12 @@ struct AppearanceData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
/// Set the appearance of a solid. This only works on solids, not sketches or individual paths.
|
||||||
pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn appearance(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
|
|
||||||
let color: String = args.get_kw_arg("color")?;
|
let color: String = args.get_kw_arg("color")?;
|
||||||
let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?;
|
let metalness: Option<f64> = args.get_kw_arg_opt("metalness")?;
|
||||||
@ -66,7 +73,7 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = inner_appearance(solid_set, data.color, data.metalness, data.roughness, args).await?;
|
let result = inner_appearance(solids, data.color, data.metalness, data.roughness, args).await?;
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,21 +283,19 @@ pub async fn appearance(_exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = { docs = "The solid(s) whose appearance is being set" },
|
solids = { docs = "The solid(s) whose appearance is being set" },
|
||||||
color = { docs = "Color of the new material, a hex string like '#ff0000'"},
|
color = { docs = "Color of the new material, a hex string like '#ff0000'"},
|
||||||
metalness = { docs = "Metalness of the new material, a percentage like 95.7." },
|
metalness = { docs = "Metalness of the new material, a percentage like 95.7." },
|
||||||
roughness = { docs = "Roughness of the new material, a percentage like 95.7." },
|
roughness = { docs = "Roughness of the new material, a percentage like 95.7." },
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_appearance(
|
async fn inner_appearance(
|
||||||
solid_set: SolidSet,
|
solids: Vec<Solid>,
|
||||||
color: String,
|
color: String,
|
||||||
metalness: Option<f64>,
|
metalness: Option<f64>,
|
||||||
roughness: Option<f64>,
|
roughness: Option<f64>,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidSet, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
let solids: Vec<Box<Solid>> = solid_set.into();
|
|
||||||
|
|
||||||
for solid in &solids {
|
for solid in &solids {
|
||||||
// Set the material properties.
|
// Set the material properties.
|
||||||
let rgb = rgba_simple::RGB::<f32>::from_hex(&color).map_err(|err| {
|
let rgb = rgba_simple::RGB::<f32>::from_hex(&color).map_err(|err| {
|
||||||
@ -323,5 +328,5 @@ async fn inner_appearance(
|
|||||||
// I can't think of a use case for it.
|
// I can't think of a use case for it.
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(SolidSet::from(solids))
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,9 +12,9 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
kcl_value::{FunctionSource, NumericType},
|
kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
|
||||||
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, Sketch, SketchSet,
|
ExecState, ExecutorContext, ExtrudeSurface, Helix, KclObjectFields, KclValue, Metadata, PrimitiveType, Sketch,
|
||||||
SketchSurface, Solid, SolidSet, TagIdentifier,
|
SketchSurface, Solid, TagIdentifier,
|
||||||
},
|
},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
@ -233,9 +233,41 @@ impl Args {
|
|||||||
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
T::from_kcl_val(&arg.value).ok_or_else(|| {
|
||||||
let expected_type_name = tynm::type_name::<T>();
|
let expected_type_name = tynm::type_name::<T>();
|
||||||
let actual_type_name = arg.value.human_friendly_type();
|
let actual_type_name = arg.value.human_friendly_type();
|
||||||
let msg_base = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
let message = format!("This function expected the input argument to be of type {expected_type_name} but it's actually of type {actual_type_name}");
|
||||||
let suggestion = match (expected_type_name.as_str(), actual_type_name) {
|
KclError::Semantic(KclErrorDetails {
|
||||||
("SolidSet", "Sketch") => Some(
|
source_ranges: arg.source_ranges(),
|
||||||
|
message,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the unlabeled keyword argument. If not set, returns Err. If it
|
||||||
|
/// can't be converted to the given type, returns Err.
|
||||||
|
pub(crate) fn get_unlabeled_kw_arg_typed<T>(
|
||||||
|
&self,
|
||||||
|
label: &str,
|
||||||
|
ty: &RuntimeType,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> Result<T, KclError>
|
||||||
|
where
|
||||||
|
T: for<'a> FromKclValue<'a>,
|
||||||
|
{
|
||||||
|
let arg = self
|
||||||
|
.unlabeled_kw_arg_unconverted()
|
||||||
|
.ok_or(KclError::Semantic(KclErrorDetails {
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
||||||
|
}))?;
|
||||||
|
|
||||||
|
let arg = arg.value.coerce(ty, exec_state).ok_or_else(|| {
|
||||||
|
let actual_type_name = arg.value.human_friendly_type();
|
||||||
|
let msg_base = format!(
|
||||||
|
"This function expected the input argument to be {} but it's actually of type {actual_type_name}",
|
||||||
|
ty.human_friendly_type(),
|
||||||
|
);
|
||||||
|
let suggestion = match (ty, actual_type_name) {
|
||||||
|
(RuntimeType::Primitive(PrimitiveType::Solid), "Sketch")
|
||||||
|
| (RuntimeType::Array(PrimitiveType::Solid, _), "Sketch") => Some(
|
||||||
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
"You can convert a sketch (2D) into a Solid (3D) by calling a function like `extrude` or `revolve`",
|
||||||
),
|
),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -248,7 +280,10 @@ impl Args {
|
|||||||
source_ranges: arg.source_ranges(),
|
source_ranges: arg.source_ranges(),
|
||||||
message,
|
message,
|
||||||
})
|
})
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
// TODO unnecessary cloning
|
||||||
|
Ok(T::from_kcl_val(&arg).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a modeling command to the batch but don't fire it right away.
|
// Add a modeling command to the batch but don't fire it right away.
|
||||||
@ -286,13 +321,16 @@ impl Args {
|
|||||||
exec_state: &'e mut ExecState,
|
exec_state: &'e mut ExecState,
|
||||||
tag: &'a TagIdentifier,
|
tag: &'a TagIdentifier,
|
||||||
) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
|
) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
|
||||||
if let KclValue::TagIdentifier(t) = exec_state.stack().get_from_call_stack(&tag.value, self.source_range)? {
|
if let (epoch, KclValue::TagIdentifier(t)) =
|
||||||
Ok(t.info.as_ref().ok_or_else(|| {
|
exec_state.stack().get_from_call_stack(&tag.value, self.source_range)?
|
||||||
|
{
|
||||||
|
let info = t.get_info(epoch).ok_or_else(|| {
|
||||||
KclError::Type(KclErrorDetails {
|
KclError::Type(KclErrorDetails {
|
||||||
message: format!("Tag `{}` does not have engine info", tag.value),
|
message: format!("Tag `{}` does not have engine info", tag.value),
|
||||||
source_ranges: vec![self.source_range],
|
source_ranges: vec![self.source_range],
|
||||||
})
|
})
|
||||||
})?)
|
})?;
|
||||||
|
Ok(info)
|
||||||
} else {
|
} else {
|
||||||
Err(KclError::Type(KclErrorDetails {
|
Err(KclError::Type(KclErrorDetails {
|
||||||
message: format!("Tag `{}` does not exist", tag.value),
|
message: format!("Tag `{}` does not exist", tag.value),
|
||||||
@ -309,7 +347,7 @@ impl Args {
|
|||||||
where
|
where
|
||||||
'e: 'a,
|
'e: 'a,
|
||||||
{
|
{
|
||||||
if let Some(info) = &tag.info {
|
if let Some(info) = tag.get_cur_info() {
|
||||||
return Ok(info);
|
return Ok(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +362,7 @@ impl Args {
|
|||||||
where
|
where
|
||||||
'e: 'a,
|
'e: 'a,
|
||||||
{
|
{
|
||||||
if let Some(info) = &tag.info {
|
if let Some(info) = tag.get_cur_info() {
|
||||||
if info.surface.is_some() {
|
if info.surface.is_some() {
|
||||||
return Ok(info);
|
return Ok(info);
|
||||||
}
|
}
|
||||||
@ -335,10 +373,10 @@ impl Args {
|
|||||||
|
|
||||||
/// Flush just the fillets and chamfers for this specific SolidSet.
|
/// Flush just the fillets and chamfers for this specific SolidSet.
|
||||||
#[allow(clippy::vec_box)]
|
#[allow(clippy::vec_box)]
|
||||||
pub(crate) async fn flush_batch_for_solid_set(
|
pub(crate) async fn flush_batch_for_solids(
|
||||||
&self,
|
&self,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
solids: Vec<Box<Solid>>,
|
solids: Vec<Solid>,
|
||||||
) -> Result<(), KclError> {
|
) -> Result<(), KclError> {
|
||||||
// Make sure we don't traverse sketches more than once.
|
// Make sure we don't traverse sketches more than once.
|
||||||
let mut traversed_sketches = Vec::new();
|
let mut traversed_sketches = Vec::new();
|
||||||
@ -507,12 +545,48 @@ impl Args {
|
|||||||
Ok((a.n, b.n, ty))
|
Ok((a.n, b.n, ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_sketches(&self) -> Result<(SketchSet, Sketch), KclError> {
|
pub(crate) fn get_sketches(&self, exec_state: &mut ExecState) -> Result<(Vec<Sketch>, Sketch), KclError> {
|
||||||
FromArgs::from_args(self, 0)
|
let sarg = self.args[0]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected an array of sketches, found {}",
|
||||||
|
self.args[0].value.human_friendly_type()
|
||||||
|
),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
let sketches = match sarg {
|
||||||
|
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let sarg = self.args[1]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!("Expected a sketch, found {}", self.args[1].value.human_friendly_type()),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
let sketch = match sarg {
|
||||||
|
KclValue::Sketch { value } => *value,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok((sketches, sketch))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_sketch(&self) -> Result<Sketch, KclError> {
|
pub(crate) fn get_sketch(&self, exec_state: &mut ExecState) -> Result<Sketch, KclError> {
|
||||||
FromArgs::from_args(self, 0)
|
let sarg = self.args[0]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!("Expected a sketch, found {}", self.args[0].value.human_friendly_type()),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
match sarg {
|
||||||
|
KclValue::Sketch { value } => Ok(*value),
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
|
pub(crate) fn get_data<'a, T>(&'a self) -> Result<T, KclError>
|
||||||
@ -533,18 +607,55 @@ impl Args {
|
|||||||
FromArgs::from_args(self, 0)
|
FromArgs::from_args(self, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_data_and_sketch_set<'a, T>(&'a self) -> Result<(T, SketchSet), KclError>
|
pub(crate) fn get_data_and_sketches<'a, T>(
|
||||||
|
&'a self,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> Result<(T, Vec<Sketch>), KclError>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned + FromArgs<'a>,
|
T: serde::de::DeserializeOwned + FromArgs<'a>,
|
||||||
{
|
{
|
||||||
FromArgs::from_args(self, 0)
|
let data: T = FromArgs::from_args(self, 0)?;
|
||||||
|
let sarg = self.args[1]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::None), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected an array of sketches for second argument, found {}",
|
||||||
|
self.args[1].value.human_friendly_type()
|
||||||
|
),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
let sketches = match sarg {
|
||||||
|
KclValue::HomArray { value, .. } => value.iter().map(|v| v.as_sketch().unwrap().clone()).collect(),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok((data, sketches))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(&'a self) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
pub(crate) fn get_data_and_sketch_and_tag<'a, T>(
|
||||||
|
&'a self,
|
||||||
|
exec_state: &mut ExecState,
|
||||||
|
) -> Result<(T, Sketch, Option<TagNode>), KclError>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||||
{
|
{
|
||||||
FromArgs::from_args(self, 0)
|
let data: T = FromArgs::from_args(self, 0)?;
|
||||||
|
let sarg = self.args[1]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a sketch for second argument, found {}",
|
||||||
|
self.args[1].value.human_friendly_type()
|
||||||
|
),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
let sketch = match sarg {
|
||||||
|
KclValue::Sketch { value } => *value,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
let tag: Option<TagNode> = FromArgs::from_args(self, 2)?;
|
||||||
|
Ok((data, sketch, tag))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
|
pub(crate) fn get_data_and_sketch_surface<'a, T>(&'a self) -> Result<(T, SketchSurface, Option<TagNode>), KclError>
|
||||||
@ -554,11 +665,26 @@ impl Args {
|
|||||||
FromArgs::from_args(self, 0)
|
FromArgs::from_args(self, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_data_and_solid<'a, T>(&'a self) -> Result<(T, Box<Solid>), KclError>
|
pub(crate) fn get_data_and_solid<'a, T>(&'a self, exec_state: &mut ExecState) -> Result<(T, Box<Solid>), KclError>
|
||||||
where
|
where
|
||||||
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
T: serde::de::DeserializeOwned + FromKclValue<'a> + Sized,
|
||||||
{
|
{
|
||||||
FromArgs::from_args(self, 0)
|
let data: T = FromArgs::from_args(self, 0)?;
|
||||||
|
let sarg = self.args[1]
|
||||||
|
.value
|
||||||
|
.coerce(&RuntimeType::Primitive(PrimitiveType::Solid), exec_state)
|
||||||
|
.ok_or(KclError::Type(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Expected a solid for second argument, found {}",
|
||||||
|
self.args[1].value.human_friendly_type()
|
||||||
|
),
|
||||||
|
source_ranges: vec![self.source_range],
|
||||||
|
}))?;
|
||||||
|
let solid = match sarg {
|
||||||
|
KclValue::Solid { value } => value,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
Ok((data, solid))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
|
pub(crate) fn get_tag_to_number_sketch(&self) -> Result<(TagIdentifier, f64, Sketch), KclError> {
|
||||||
@ -1301,7 +1427,6 @@ impl_from_kcl_for_vec!(crate::execution::EdgeCut);
|
|||||||
impl_from_kcl_for_vec!(crate::execution::Metadata);
|
impl_from_kcl_for_vec!(crate::execution::Metadata);
|
||||||
impl_from_kcl_for_vec!(super::fillet::EdgeReference);
|
impl_from_kcl_for_vec!(super::fillet::EdgeReference);
|
||||||
impl_from_kcl_for_vec!(ExtrudeSurface);
|
impl_from_kcl_for_vec!(ExtrudeSurface);
|
||||||
impl_from_kcl_for_vec!(Sketch);
|
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for SourceRange {
|
impl<'a> FromKclValue<'a> for SourceRange {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
@ -1337,8 +1462,10 @@ impl<'a> FromKclValue<'a> for crate::execution::Solid {
|
|||||||
impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
|
impl<'a> FromKclValue<'a> for crate::execution::SolidOrImportedGeometry {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
match arg {
|
match arg {
|
||||||
KclValue::Solid { value } => Some(Self::Solid(value.clone())),
|
KclValue::Solid { value } => Some(Self::SolidSet(vec![(**value).clone()])),
|
||||||
KclValue::Solids { value } => Some(Self::SolidSet(value.clone())),
|
KclValue::HomArray { value, .. } => Some(Self::SolidSet(
|
||||||
|
value.iter().map(|v| v.as_solid().unwrap().clone()).collect(),
|
||||||
|
)),
|
||||||
KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
|
KclValue::ImportedGeometry(value) => Some(Self::ImportedGeometry(Box::new(value.clone()))),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
@ -1351,11 +1478,13 @@ impl<'a> FromKclValue<'a> for super::sketch::SketchData {
|
|||||||
let case1 = crate::execution::Plane::from_kcl_val;
|
let case1 = crate::execution::Plane::from_kcl_val;
|
||||||
let case2 = super::sketch::PlaneData::from_kcl_val;
|
let case2 = super::sketch::PlaneData::from_kcl_val;
|
||||||
let case3 = crate::execution::Solid::from_kcl_val;
|
let case3 = crate::execution::Solid::from_kcl_val;
|
||||||
|
let case4 = <Vec<Solid>>::from_kcl_val;
|
||||||
case1(arg)
|
case1(arg)
|
||||||
.map(Box::new)
|
.map(Box::new)
|
||||||
.map(Self::Plane)
|
.map(Self::Plane)
|
||||||
.or_else(|| case2(arg).map(Self::PlaneOrientation))
|
.or_else(|| case2(arg).map(Self::PlaneOrientation))
|
||||||
.or_else(|| case3(arg).map(Box::new).map(Self::Solid))
|
.or_else(|| case3(arg).map(Box::new).map(Self::Solid))
|
||||||
|
.or_else(|| case4(arg).map(|v| Box::new(v[0].clone())).map(Self::Solid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1528,6 +1657,7 @@ impl<'a> FromKclValue<'a> for TyF64 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for Sketch {
|
impl<'a> FromKclValue<'a> for Sketch {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
let KclValue::Sketch { value } = arg else {
|
let KclValue::Sketch { value } = arg else {
|
||||||
@ -1545,13 +1675,16 @@ impl<'a> FromKclValue<'a> for Helix {
|
|||||||
Some(value.as_ref().to_owned())
|
Some(value.as_ref().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for SweepPath {
|
impl<'a> FromKclValue<'a> for SweepPath {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
let case1 = Sketch::from_kcl_val;
|
let case1 = Sketch::from_kcl_val;
|
||||||
let case2 = Helix::from_kcl_val;
|
let case2 = <Vec<Sketch>>::from_kcl_val;
|
||||||
|
let case3 = Helix::from_kcl_val;
|
||||||
case1(arg)
|
case1(arg)
|
||||||
.map(Self::Sketch)
|
.map(Self::Sketch)
|
||||||
.or_else(|| case2(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
|
.or_else(|| case2(arg).map(|arg0: Vec<Sketch>| Self::Sketch(arg0[0].clone())))
|
||||||
|
.or_else(|| case3(arg).map(|arg0: Helix| Self::Helix(Box::new(arg0))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<'a> FromKclValue<'a> for String {
|
impl<'a> FromKclValue<'a> for String {
|
||||||
@ -1579,20 +1712,6 @@ impl<'a> FromKclValue<'a> for bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for SketchSet {
|
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
|
||||||
match arg {
|
|
||||||
KclValue::Sketch { value: sketch } => Some(SketchSet::from(sketch.to_owned())),
|
|
||||||
KclValue::Sketches { value } => Some(SketchSet::from(value.to_owned())),
|
|
||||||
KclValue::MixedArray { .. } => {
|
|
||||||
let v: Option<Vec<Sketch>> = FromKclValue::from_kcl_val(arg);
|
|
||||||
Some(SketchSet::Sketches(v?.iter().cloned().map(Box::new).collect()))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for Box<Solid> {
|
impl<'a> FromKclValue<'a> for Box<Solid> {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
let KclValue::Solid { value } = arg else {
|
let KclValue::Solid { value } = arg else {
|
||||||
@ -1602,15 +1721,27 @@ impl<'a> FromKclValue<'a> for Box<Solid> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for &'a FunctionSource {
|
impl<'a> FromKclValue<'a> for Vec<Solid> {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
arg.get_function()
|
let KclValue::HomArray { value, .. } = arg else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
value.iter().map(Solid::from_kcl_val).collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> FromKclValue<'a> for SolidSet {
|
impl<'a> FromKclValue<'a> for Vec<Sketch> {
|
||||||
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
arg.get_solid_set().ok()
|
let KclValue::HomArray { value, .. } = arg else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
value.iter().map(Sketch::from_kcl_val).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FromKclValue<'a> for &'a FunctionSource {
|
||||||
|
fn from_kcl_val(arg: &'a KclValue) -> Option<Self> {
|
||||||
|
arg.get_function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,10 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, Solid},
|
execution::{
|
||||||
|
kcl_value::RuntimeType, ChamferSurface, EdgeCut, ExecState, ExtrudeSurface, GeoMeta, KclValue, PrimitiveType,
|
||||||
|
Solid,
|
||||||
|
},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
std::{fillet::EdgeReference, Args},
|
std::{fillet::EdgeReference, Args},
|
||||||
};
|
};
|
||||||
@ -16,7 +19,7 @@ pub(crate) const DEFAULT_TOLERANCE: f64 = 0.0000001;
|
|||||||
|
|
||||||
/// Create chamfers on tagged paths.
|
/// Create chamfers on tagged paths.
|
||||||
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn chamfer(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||||
let length = args.get_kw_arg("length")?;
|
let length = args.get_kw_arg("length")?;
|
||||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||||
let tag = args.get_kw_arg_opt("tag")?;
|
let tag = args.get_kw_arg_opt("tag")?;
|
||||||
|
|||||||
@ -19,18 +19,22 @@ use uuid::Uuid;
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, Sketch, SketchSet, SketchSurface, Solid,
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
SolidSet,
|
ArtifactId, ExecState, ExtrudeSurface, GeoMeta, KclValue, Path, PrimitiveType, Sketch, SketchSurface, Solid,
|
||||||
},
|
},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Extrudes by a given amount.
|
/// Extrudes by a given amount.
|
||||||
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch_set = args.get_unlabeled_kw_arg("sketch_set")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let length = args.get_kw_arg("length")?;
|
let length = args.get_kw_arg("length")?;
|
||||||
|
|
||||||
let result = inner_extrude(sketch_set, length, exec_state, args).await?;
|
let result = inner_extrude(sketches, length, exec_state, args).await?;
|
||||||
|
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
@ -90,18 +94,17 @@ pub async fn extrude(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_set = { docs = "Which sketch or set of sketches should be extruded"},
|
sketches = { docs = "Which sketch or sketches should be extruded"},
|
||||||
length = { docs = "How far to extrude the given sketches"},
|
length = { docs = "How far to extrude the given sketches"},
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_extrude(
|
async fn inner_extrude(
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
length: f64,
|
length: f64,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidSet, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
// Extrude the element(s).
|
// Extrude the element(s).
|
||||||
let sketches: Vec<Sketch> = sketch_set.into();
|
|
||||||
let mut solids = Vec::new();
|
let mut solids = Vec::new();
|
||||||
for sketch in &sketches {
|
for sketch in &sketches {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
@ -121,7 +124,7 @@ async fn inner_extrude(
|
|||||||
solids.push(do_post_extrude(sketch.clone(), id.into(), length, exec_state, args.clone()).await?);
|
solids.push(do_post_extrude(sketch.clone(), id.into(), length, exec_state, args.clone()).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solids.into())
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn do_post_extrude(
|
pub(crate) async fn do_post_extrude(
|
||||||
@ -130,7 +133,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
length: f64,
|
length: f64,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Box<Solid>, KclError> {
|
) -> Result<Solid, KclError> {
|
||||||
// Bring the object to the front of the scene.
|
// Bring the object to the front of the scene.
|
||||||
// See: https://github.com/KittyCAD/modeling-app/issues/806
|
// See: https://github.com/KittyCAD/modeling-app/issues/806
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
@ -282,7 +285,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Box::new(Solid {
|
Ok(Solid {
|
||||||
// Ok so you would think that the id would be the id of the solid,
|
// Ok so you would think that the id would be the id of the solid,
|
||||||
// that we passed in to the function, but it's actually the id of the
|
// that we passed in to the function, but it's actually the id of the
|
||||||
// sketch.
|
// sketch.
|
||||||
@ -296,7 +299,7 @@ pub(crate) async fn do_post_extrude(
|
|||||||
start_cap_id,
|
start_cap_id,
|
||||||
end_cap_id,
|
end_cap_id,
|
||||||
edge_cuts: vec![],
|
edge_cuts: vec![],
|
||||||
}))
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
|
|||||||
@ -14,7 +14,10 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
execution::{
|
||||||
|
kcl_value::RuntimeType, EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, PrimitiveType,
|
||||||
|
Solid, TagIdentifier,
|
||||||
|
},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
std::Args,
|
std::Args,
|
||||||
@ -64,8 +67,7 @@ pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]
|
|||||||
|
|
||||||
/// Create fillets on tagged paths.
|
/// Create fillets on tagged paths.
|
||||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
// Get all args:
|
let solid = args.get_unlabeled_kw_arg_typed("solid", &RuntimeType::Primitive(PrimitiveType::Solid), exec_state)?;
|
||||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
|
||||||
let radius = args.get_kw_arg("radius")?;
|
let radius = args.get_kw_arg("radius")?;
|
||||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||||
|
|||||||
@ -196,7 +196,7 @@ pub struct HelixRevolutionsData {
|
|||||||
|
|
||||||
/// Create a helix on a cylinder.
|
/// Create a helix on a cylinder.
|
||||||
pub async fn helix_revolutions(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn helix_revolutions(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, solid): (HelixRevolutionsData, Box<Solid>) = args.get_data_and_solid()?;
|
let (data, solid): (HelixRevolutionsData, Box<Solid>) = args.get_data_and_solid(exec_state)?;
|
||||||
|
|
||||||
let value = inner_helix_revolutions(data, solid, exec_state, args).await?;
|
let value = inner_helix_revolutions(data, solid, exec_state, args).await?;
|
||||||
Ok(KclValue::Solid { value })
|
Ok(KclValue::Solid { value })
|
||||||
|
|||||||
@ -9,7 +9,10 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, Sketch, Solid},
|
execution::{
|
||||||
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
|
ExecState, KclValue, PrimitiveType, Sketch, Solid,
|
||||||
|
},
|
||||||
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -17,7 +20,11 @@ const DEFAULT_V_DEGREE: u32 = 2;
|
|||||||
|
|
||||||
/// Create a 3D surface or solid by interpolating between two or more sketches.
|
/// Create a 3D surface or solid by interpolating between two or more sketches.
|
||||||
pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn loft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketches = args.get_unlabeled_kw_arg("sketches")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let v_degree: NonZeroU32 = args
|
let v_degree: NonZeroU32 = args
|
||||||
.get_kw_arg_opt("vDegree")?
|
.get_kw_arg_opt("vDegree")?
|
||||||
.unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap());
|
.unwrap_or(NonZeroU32::new(DEFAULT_V_DEGREE).unwrap());
|
||||||
@ -159,5 +166,7 @@ async fn inner_loft(
|
|||||||
let mut sketch = sketches[0].clone();
|
let mut sketch = sketches[0].clone();
|
||||||
// Override its id with the loft id so we can get its faces later
|
// Override its id with the loft id so we can get its faces later
|
||||||
sketch.id = id;
|
sketch.id = id;
|
||||||
do_post_extrude(sketch, id.into(), 0.0, exec_state, args).await
|
Ok(Box::new(
|
||||||
|
do_post_extrude(sketch, id.into(), 0.0, exec_state, args).await?,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{ExecState, KclValue, Sketch, SketchSet},
|
execution::{ExecState, KclValue, Sketch},
|
||||||
std::{axis_or_reference::Axis2dOrEdgeReference, Args},
|
std::{axis_or_reference::Axis2dOrEdgeReference, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ pub struct Mirror2dData {
|
|||||||
///
|
///
|
||||||
/// Only works on unclosed sketches for now.
|
/// Only works on unclosed sketches for now.
|
||||||
pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch_set): (Mirror2dData, SketchSet) = args.get_data_and_sketch_set()?;
|
let (data, sketch_set): (Mirror2dData, Vec<Sketch>) = args.get_data_and_sketches(exec_state)?;
|
||||||
|
|
||||||
let sketches = inner_mirror_2d(data, sketch_set, exec_state, args).await?;
|
let sketches = inner_mirror_2d(data, sketch_set, exec_state, args).await?;
|
||||||
Ok(sketches.into())
|
Ok(sketches.into())
|
||||||
@ -103,14 +103,11 @@ pub async fn mirror_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
|||||||
}]
|
}]
|
||||||
async fn inner_mirror_2d(
|
async fn inner_mirror_2d(
|
||||||
data: Mirror2dData,
|
data: Mirror2dData,
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
) -> Result<Vec<Sketch>, KclError> {
|
||||||
let starting_sketches = match sketch_set {
|
let starting_sketches = sketches;
|
||||||
SketchSet::Sketch(sketch) => vec![sketch],
|
|
||||||
SketchSet::Sketches(sketches) => sketches,
|
|
||||||
};
|
|
||||||
|
|
||||||
if args.ctx.no_engine_commands().await {
|
if args.ctx.no_engine_commands().await {
|
||||||
return Ok(starting_sketches);
|
return Ok(starting_sketches);
|
||||||
|
|||||||
@ -20,9 +20,8 @@ use super::args::Arg;
|
|||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
kcl_value::{FunctionSource, NumericType},
|
kcl_value::{ArrayLen, FunctionSource, NumericType, RuntimeType},
|
||||||
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, Sketch, SketchSet, Solid,
|
ExecState, Geometries, Geometry, KclObjectFields, KclValue, Point2d, Point3d, PrimitiveType, Sketch, Solid,
|
||||||
SolidSet,
|
|
||||||
},
|
},
|
||||||
std::Args,
|
std::Args,
|
||||||
ExecutorContext, SourceRange,
|
ExecutorContext, SourceRange,
|
||||||
@ -48,25 +47,32 @@ pub struct LinearPattern3dData {
|
|||||||
|
|
||||||
/// Repeat some 3D solid, changing each repetition slightly.
|
/// Repeat some 3D solid, changing each repetition slightly.
|
||||||
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set = args.get_unlabeled_kw_arg("solidSet")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let instances: u32 = args.get_kw_arg("instances")?;
|
let instances: u32 = args.get_kw_arg("instances")?;
|
||||||
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
||||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||||
|
|
||||||
let solids = inner_pattern_transform(solid_set, instances, transform, use_original, exec_state, &args).await?;
|
let solids = inner_pattern_transform(solids, instances, transform, use_original, exec_state, &args).await?;
|
||||||
Ok(KclValue::Solids { value: solids })
|
Ok(solids.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repeat some 2D sketch, changing each repetition slightly.
|
/// Repeat some 2D sketch, changing each repetition slightly.
|
||||||
pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch_set = args.get_unlabeled_kw_arg("sketchSet")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let instances: u32 = args.get_kw_arg("instances")?;
|
let instances: u32 = args.get_kw_arg("instances")?;
|
||||||
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
let transform: &FunctionSource = args.get_kw_arg("transform")?;
|
||||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||||
|
|
||||||
let sketches =
|
let sketches = inner_pattern_transform_2d(sketches, instances, transform, use_original, exec_state, &args).await?;
|
||||||
inner_pattern_transform_2d(sketch_set, instances, transform, use_original, exec_state, &args).await?;
|
Ok(sketches.into())
|
||||||
Ok(KclValue::Sketches { value: sketches })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repeat a 3-dimensional solid, changing it each time.
|
/// Repeat a 3-dimensional solid, changing it each time.
|
||||||
@ -258,20 +264,20 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = { docs = "The solid(s) to duplicate" },
|
solids = { docs = "The solid(s) to duplicate" },
|
||||||
instances = { docs = "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." },
|
instances = { docs = "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." },
|
||||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
||||||
use_original = { docs = "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." },
|
use_original = { docs = "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." },
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_pattern_transform<'a>(
|
async fn inner_pattern_transform<'a>(
|
||||||
solid_set: SolidSet,
|
solids: Vec<Solid>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
transform: &'a FunctionSource,
|
transform: &'a FunctionSource,
|
||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: &'a Args,
|
args: &'a Args,
|
||||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
// Build the vec of transforms, one for each repetition.
|
// Build the vec of transforms, one for each repetition.
|
||||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||||
if instances < 1 {
|
if instances < 1 {
|
||||||
@ -281,12 +287,12 @@ async fn inner_pattern_transform<'a>(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
for i in 1..instances {
|
for i in 1..instances {
|
||||||
let t = make_transform::<Box<Solid>>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
let t = make_transform::<Solid>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||||
transform_vec.push(t);
|
transform_vec.push(t);
|
||||||
}
|
}
|
||||||
execute_pattern_transform(
|
execute_pattern_transform(
|
||||||
transform_vec,
|
transform_vec,
|
||||||
solid_set,
|
solids,
|
||||||
use_original.unwrap_or_default(),
|
use_original.unwrap_or_default(),
|
||||||
exec_state,
|
exec_state,
|
||||||
args,
|
args,
|
||||||
@ -311,20 +317,20 @@ async fn inner_pattern_transform<'a>(
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_set = { docs = "The sketch(es) to duplicate" },
|
sketches = { docs = "The sketch(es) to duplicate" },
|
||||||
instances = { docs = "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." },
|
instances = { docs = "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." },
|
||||||
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
transform = { docs = "How each replica should be transformed. The transform function takes a single parameter: an integer representing which number replication the transform is for. E.g. the first replica to be transformed will be passed the argument `1`. This simplifies your math: the transform function can rely on id `0` being the original instance passed into the `patternTransform`. See the examples." },
|
||||||
use_original = { docs = "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." },
|
use_original = { docs = "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." },
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_pattern_transform_2d<'a>(
|
async fn inner_pattern_transform_2d<'a>(
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
transform: &'a FunctionSource,
|
transform: &'a FunctionSource,
|
||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: &'a Args,
|
args: &'a Args,
|
||||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
) -> Result<Vec<Sketch>, KclError> {
|
||||||
// Build the vec of transforms, one for each repetition.
|
// Build the vec of transforms, one for each repetition.
|
||||||
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
let mut transform_vec = Vec::with_capacity(usize::try_from(instances).unwrap());
|
||||||
if instances < 1 {
|
if instances < 1 {
|
||||||
@ -334,12 +340,12 @@ async fn inner_pattern_transform_2d<'a>(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
for i in 1..instances {
|
for i in 1..instances {
|
||||||
let t = make_transform::<Box<Sketch>>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
let t = make_transform::<Sketch>(i, transform, args.source_range, exec_state, &args.ctx).await?;
|
||||||
transform_vec.push(t);
|
transform_vec.push(t);
|
||||||
}
|
}
|
||||||
execute_pattern_transform(
|
execute_pattern_transform(
|
||||||
transform_vec,
|
transform_vec,
|
||||||
sketch_set,
|
sketches,
|
||||||
use_original.unwrap_or_default(),
|
use_original.unwrap_or_default(),
|
||||||
exec_state,
|
exec_state,
|
||||||
args,
|
args,
|
||||||
@ -611,8 +617,8 @@ trait GeometryTrait: Clone {
|
|||||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: Self::Set) -> Result<(), KclError>;
|
async fn flush_batch(args: &Args, exec_state: &mut ExecState, set: Self::Set) -> Result<(), KclError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeometryTrait for Box<Sketch> {
|
impl GeometryTrait for Sketch {
|
||||||
type Set = SketchSet;
|
type Set = Vec<Sketch>;
|
||||||
fn set_id(&mut self, id: Uuid) {
|
fn set_id(&mut self, id: Uuid) {
|
||||||
self.id = id;
|
self.id = id;
|
||||||
}
|
}
|
||||||
@ -632,8 +638,8 @@ impl GeometryTrait for Box<Sketch> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GeometryTrait for Box<Solid> {
|
impl GeometryTrait for Solid {
|
||||||
type Set = SolidSet;
|
type Set = Vec<Solid>;
|
||||||
fn set_id(&mut self, id: Uuid) {
|
fn set_id(&mut self, id: Uuid) {
|
||||||
self.id = id;
|
self.id = id;
|
||||||
}
|
}
|
||||||
@ -651,7 +657,7 @@ impl GeometryTrait for Box<Solid> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn flush_batch(args: &Args, exec_state: &mut ExecState, solid_set: Self::Set) -> Result<(), KclError> {
|
async fn flush_batch(args: &Args, exec_state: &mut ExecState, solid_set: Self::Set) -> Result<(), KclError> {
|
||||||
args.flush_batch_for_solid_set(exec_state, solid_set.into()).await
|
args.flush_batch_for_solids(exec_state, solid_set).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -690,7 +696,11 @@ mod tests {
|
|||||||
|
|
||||||
/// A linear pattern on a 2D sketch.
|
/// A linear pattern on a 2D sketch.
|
||||||
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch_set: SketchSet = args.get_unlabeled_kw_arg("sketchSet")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let instances: u32 = args.get_kw_arg("instances")?;
|
let instances: u32 = args.get_kw_arg("instances")?;
|
||||||
let distance: f64 = args.get_kw_arg("distance")?;
|
let distance: f64 = args.get_kw_arg("distance")?;
|
||||||
let axis: [f64; 2] = args.get_kw_arg("axis")?;
|
let axis: [f64; 2] = args.get_kw_arg("axis")?;
|
||||||
@ -705,8 +715,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let sketches =
|
let sketches = inner_pattern_linear_2d(sketches, instances, distance, axis, use_original, exec_state, args).await?;
|
||||||
inner_pattern_linear_2d(sketch_set, instances, distance, axis, use_original, exec_state, args).await?;
|
|
||||||
Ok(sketches.into())
|
Ok(sketches.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -729,7 +738,7 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_set = { docs = "The sketch(es) to duplicate" },
|
sketches = { docs = "The sketch(es) to duplicate" },
|
||||||
instances = { docs = "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." },
|
instances = { docs = "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." },
|
||||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||||
axis = { docs = "The axis of the pattern. A 2D vector." },
|
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||||
@ -737,14 +746,14 @@ pub async fn pattern_linear_2d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_pattern_linear_2d(
|
async fn inner_pattern_linear_2d(
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
distance: f64,
|
distance: f64,
|
||||||
axis: [f64; 2],
|
axis: [f64; 2],
|
||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
) -> Result<Vec<Sketch>, KclError> {
|
||||||
let [x, y] = axis;
|
let [x, y] = axis;
|
||||||
let axis_len = f64::sqrt(x * x + y * y);
|
let axis_len = f64::sqrt(x * x + y * y);
|
||||||
let normalized_axis = kcmc::shared::Point2d::from([x / axis_len, y / axis_len]);
|
let normalized_axis = kcmc::shared::Point2d::from([x / axis_len, y / axis_len]);
|
||||||
@ -760,7 +769,7 @@ async fn inner_pattern_linear_2d(
|
|||||||
.collect();
|
.collect();
|
||||||
execute_pattern_transform(
|
execute_pattern_transform(
|
||||||
transforms,
|
transforms,
|
||||||
sketch_set,
|
sketches,
|
||||||
use_original.unwrap_or_default(),
|
use_original.unwrap_or_default(),
|
||||||
exec_state,
|
exec_state,
|
||||||
&args,
|
&args,
|
||||||
@ -770,7 +779,11 @@ async fn inner_pattern_linear_2d(
|
|||||||
|
|
||||||
/// A linear pattern on a 3D model.
|
/// A linear pattern on a 3D model.
|
||||||
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let instances: u32 = args.get_kw_arg("instances")?;
|
let instances: u32 = args.get_kw_arg("instances")?;
|
||||||
let distance: f64 = args.get_kw_arg("distance")?;
|
let distance: f64 = args.get_kw_arg("distance")?;
|
||||||
let axis: [f64; 3] = args.get_kw_arg("axis")?;
|
let axis: [f64; 3] = args.get_kw_arg("axis")?;
|
||||||
@ -785,7 +798,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let solids = inner_pattern_linear_3d(solid_set, instances, distance, axis, use_original, exec_state, args).await?;
|
let solids = inner_pattern_linear_3d(solids, instances, distance, axis, use_original, exec_state, args).await?;
|
||||||
Ok(solids.into())
|
Ok(solids.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -866,7 +879,7 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = { docs = "The solid(s) to duplicate" },
|
solids = { docs = "The solid(s) to duplicate" },
|
||||||
instances = { docs = "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." },
|
instances = { docs = "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." },
|
||||||
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
distance = { docs = "Distance between each repetition. Also known as 'spacing'."},
|
||||||
axis = { docs = "The axis of the pattern. A 2D vector." },
|
axis = { docs = "The axis of the pattern. A 2D vector." },
|
||||||
@ -874,14 +887,14 @@ pub async fn pattern_linear_3d(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_pattern_linear_3d(
|
async fn inner_pattern_linear_3d(
|
||||||
solid_set: SolidSet,
|
solids: Vec<Solid>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
distance: f64,
|
distance: f64,
|
||||||
axis: [f64; 3],
|
axis: [f64; 3],
|
||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
let [x, y, z] = axis;
|
let [x, y, z] = axis;
|
||||||
let axis_len = f64::sqrt(x * x + y * y + z * z);
|
let axis_len = f64::sqrt(x * x + y * y + z * z);
|
||||||
let normalized_axis = kcmc::shared::Point3d::from([x / axis_len, y / axis_len, z / axis_len]);
|
let normalized_axis = kcmc::shared::Point3d::from([x / axis_len, y / axis_len, z / axis_len]);
|
||||||
@ -895,14 +908,7 @@ async fn inner_pattern_linear_3d(
|
|||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
execute_pattern_transform(
|
execute_pattern_transform(transforms, solids, use_original.unwrap_or_default(), exec_state, &args).await
|
||||||
transforms,
|
|
||||||
solid_set,
|
|
||||||
use_original.unwrap_or_default(),
|
|
||||||
exec_state,
|
|
||||||
&args,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Data for a circular pattern on a 2D sketch.
|
/// Data for a circular pattern on a 2D sketch.
|
||||||
@ -1022,7 +1028,11 @@ impl CircularPattern {
|
|||||||
|
|
||||||
/// A circular pattern on a 2D sketch.
|
/// A circular pattern on a 2D sketch.
|
||||||
pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch_set: SketchSet = args.get_unlabeled_kw_arg("sketchSet")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let instances: u32 = args.get_kw_arg("instances")?;
|
let instances: u32 = args.get_kw_arg("instances")?;
|
||||||
let center: [f64; 2] = args.get_kw_arg("center")?;
|
let center: [f64; 2] = args.get_kw_arg("center")?;
|
||||||
let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
|
let arc_degrees: f64 = args.get_kw_arg("arcDegrees")?;
|
||||||
@ -1030,7 +1040,7 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||||
|
|
||||||
let sketches = inner_pattern_circular_2d(
|
let sketches = inner_pattern_circular_2d(
|
||||||
sketch_set,
|
sketches,
|
||||||
instances,
|
instances,
|
||||||
center,
|
center,
|
||||||
arc_degrees,
|
arc_degrees,
|
||||||
@ -1079,7 +1089,7 @@ pub async fn pattern_circular_2d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
}]
|
}]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn inner_pattern_circular_2d(
|
async fn inner_pattern_circular_2d(
|
||||||
sketch_set: SketchSet,
|
sketch_set: Vec<Sketch>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
center: [f64; 2],
|
center: [f64; 2],
|
||||||
arc_degrees: f64,
|
arc_degrees: f64,
|
||||||
@ -1087,8 +1097,8 @@ async fn inner_pattern_circular_2d(
|
|||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Box<Sketch>>, KclError> {
|
) -> Result<Vec<Sketch>, KclError> {
|
||||||
let starting_sketches: Vec<Box<Sketch>> = sketch_set.into();
|
let starting_sketches = sketch_set;
|
||||||
|
|
||||||
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
||||||
return Ok(starting_sketches);
|
return Ok(starting_sketches);
|
||||||
@ -1126,7 +1136,11 @@ async fn inner_pattern_circular_2d(
|
|||||||
|
|
||||||
/// A circular pattern on a 3D model.
|
/// A circular pattern on a 3D model.
|
||||||
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set: SolidSet = args.get_unlabeled_kw_arg("solidSet")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
// The number of total instances. Must be greater than or equal to 1.
|
// The number of total instances. Must be greater than or equal to 1.
|
||||||
// This includes the original entity. For example, if instances is 2,
|
// This includes the original entity. For example, if instances is 2,
|
||||||
// there will be two copies -- the original, and one new copy.
|
// there will be two copies -- the original, and one new copy.
|
||||||
@ -1145,7 +1159,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
let use_original: Option<bool> = args.get_kw_arg_opt("useOriginal")?;
|
||||||
|
|
||||||
let solids = inner_pattern_circular_3d(
|
let solids = inner_pattern_circular_3d(
|
||||||
solid_set,
|
solids,
|
||||||
instances,
|
instances,
|
||||||
axis,
|
axis,
|
||||||
center,
|
center,
|
||||||
@ -1183,7 +1197,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = { docs = "Which solid(s) to pattern" },
|
solids = { docs = "Which solid(s) to pattern" },
|
||||||
instances = { docs = "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."},
|
instances = { docs = "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."},
|
||||||
axis = { docs = "The axis around which to make the pattern. This is a 3D vector"},
|
axis = { docs = "The axis around which to make the pattern. This is a 3D vector"},
|
||||||
center = { docs = "The center about which to make the pattern. This is a 3D vector."},
|
center = { docs = "The center about which to make the pattern. This is a 3D vector."},
|
||||||
@ -1194,7 +1208,7 @@ pub async fn pattern_circular_3d(exec_state: &mut ExecState, args: Args) -> Resu
|
|||||||
}]
|
}]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn inner_pattern_circular_3d(
|
async fn inner_pattern_circular_3d(
|
||||||
solid_set: SolidSet,
|
solids: Vec<Solid>,
|
||||||
instances: u32,
|
instances: u32,
|
||||||
axis: [f64; 3],
|
axis: [f64; 3],
|
||||||
center: [f64; 3],
|
center: [f64; 3],
|
||||||
@ -1203,14 +1217,13 @@ async fn inner_pattern_circular_3d(
|
|||||||
use_original: Option<bool>,
|
use_original: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Vec<Box<Solid>>, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
// Flush the batch for our fillets/chamfers if there are any.
|
// Flush the batch for our fillets/chamfers if there are any.
|
||||||
// If we do not flush these, then you won't be able to pattern something with fillets.
|
// If we do not flush these, then you won't be able to pattern something with fillets.
|
||||||
// Flush just the fillets/chamfers that apply to these solids.
|
// Flush just the fillets/chamfers that apply to these solids.
|
||||||
args.flush_batch_for_solid_set(exec_state, solid_set.clone().into())
|
args.flush_batch_for_solids(exec_state, solids.clone()).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
let starting_solids: Vec<Box<Solid>> = solid_set.into();
|
let starting_solids = solids;
|
||||||
|
|
||||||
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
if args.ctx.context_type == crate::execution::ContextType::Mock {
|
||||||
return Ok(starting_solids);
|
return Ok(starting_solids);
|
||||||
|
|||||||
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, Sketch, SketchSet, SolidSet},
|
execution::{ExecState, KclValue, Sketch, Solid},
|
||||||
std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, fillet::default_tolerance, Args},
|
std::{axis_or_reference::Axis2dOrEdgeReference, extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ pub struct RevolveData {
|
|||||||
|
|
||||||
/// Revolve a sketch or set of sketches around an axis.
|
/// Revolve a sketch or set of sketches around an axis.
|
||||||
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch_set): (RevolveData, SketchSet) = args.get_data_and_sketch_set()?;
|
let (data, sketches): (RevolveData, _) = args.get_data_and_sketches(exec_state)?;
|
||||||
|
|
||||||
let value = inner_revolve(data, sketch_set, exec_state, args).await?;
|
let value = inner_revolve(data, sketches, exec_state, args).await?;
|
||||||
Ok(value.into())
|
Ok(value.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,10 +206,10 @@ pub async fn revolve(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
}]
|
}]
|
||||||
async fn inner_revolve(
|
async fn inner_revolve(
|
||||||
data: RevolveData,
|
data: RevolveData,
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidSet, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
if let Some(angle) = data.angle {
|
if let Some(angle) = data.angle {
|
||||||
// Return an error if the angle is zero.
|
// Return an error if the angle is zero.
|
||||||
// We don't use validate() here because we want to return a specific error message that is
|
// We don't use validate() here because we want to return a specific error message that is
|
||||||
@ -224,7 +224,6 @@ async fn inner_revolve(
|
|||||||
|
|
||||||
let angle = Angle::from_degrees(data.angle.unwrap_or(360.0));
|
let angle = Angle::from_degrees(data.angle.unwrap_or(360.0));
|
||||||
|
|
||||||
let sketches: Vec<Sketch> = sketch_set.into();
|
|
||||||
let mut solids = Vec::new();
|
let mut solids = Vec::new();
|
||||||
for sketch in &sketches {
|
for sketch in &sketches {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
@ -263,5 +262,5 @@ async fn inner_revolve(
|
|||||||
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solids.into())
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use kittycad_modeling_cmds::shared::Angle;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, Point2d, Sketch, TagIdentifier},
|
execution::{kcl_value::RuntimeType, ExecState, KclValue, Point2d, PrimitiveType, Sketch, TagIdentifier},
|
||||||
std::{utils::between, Args},
|
std::{utils::between, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -282,8 +282,9 @@ fn inner_segment_start_y(tag: &TagIdentifier, exec_state: &mut ExecState, args:
|
|||||||
Ok(path.get_from()[1])
|
Ok(path.get_from()[1])
|
||||||
}
|
}
|
||||||
/// Returns the last segment of x.
|
/// Returns the last segment of x.
|
||||||
pub async fn last_segment_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn last_segment_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let result = inner_last_segment_x(sketch, args.clone())?;
|
let result = inner_last_segment_x(sketch, args.clone())?;
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64(result))
|
Ok(args.make_user_val_from_f64(result))
|
||||||
@ -327,8 +328,9 @@ fn inner_last_segment_x(sketch: Sketch, args: Args) -> Result<f64, KclError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the last segment of y.
|
/// Returns the last segment of y.
|
||||||
pub async fn last_segment_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn last_segment_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let result = inner_last_segment_y(sketch, args.clone())?;
|
let result = inner_last_segment_y(sketch, args.clone())?;
|
||||||
|
|
||||||
Ok(args.make_user_val_from_f64(result))
|
Ok(args.make_user_val_from_f64(result))
|
||||||
|
|||||||
@ -139,7 +139,7 @@ async fn inner_circle(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -251,7 +251,7 @@ async fn inner_circle_three_point(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -414,7 +414,7 @@ async fn inner_polygon(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
sketch.add_tag(tag, ¤t_path);
|
sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
sketch.paths.push(current_path);
|
sketch.paths.push(current_path);
|
||||||
@ -450,7 +450,7 @@ async fn inner_polygon(
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
sketch.add_tag(tag, ¤t_path);
|
sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
sketch.paths.push(current_path);
|
sketch.paths.push(current_path);
|
||||||
|
|||||||
@ -7,17 +7,24 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, Solid, SolidSet},
|
execution::{
|
||||||
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
|
ExecState, KclValue, PrimitiveType, Solid,
|
||||||
|
},
|
||||||
std::{sketch::FaceTag, Args},
|
std::{sketch::FaceTag, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Create a shell.
|
/// Create a shell.
|
||||||
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set = args.get_unlabeled_kw_arg("solidSet")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let thickness = args.get_kw_arg("thickness")?;
|
let thickness = args.get_kw_arg("thickness")?;
|
||||||
let faces = args.get_kw_arg("faces")?;
|
let faces = args.get_kw_arg("faces")?;
|
||||||
|
|
||||||
let result = inner_shell(solid_set, thickness, faces, exec_state, args).await?;
|
let result = inner_shell(solids, thickness, faces, exec_state, args).await?;
|
||||||
Ok(result.into())
|
Ok(result.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,18 +180,18 @@ pub async fn shell(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = { docs = "Which solid (or solids) to shell out"},
|
solids = { docs = "Which solid (or solids) to shell out"},
|
||||||
thickness = {docs = "The thickness of the shell"},
|
thickness = {docs = "The thickness of the shell"},
|
||||||
faces = {docs = "The faces you want removed"},
|
faces = {docs = "The faces you want removed"},
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_shell(
|
async fn inner_shell(
|
||||||
solid_set: SolidSet,
|
solids: Vec<Solid>,
|
||||||
thickness: f64,
|
thickness: f64,
|
||||||
faces: Vec<FaceTag>,
|
faces: Vec<FaceTag>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidSet, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
if faces.is_empty() {
|
if faces.is_empty() {
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
message: "You must shell at least one face".to_string(),
|
message: "You must shell at least one face".to_string(),
|
||||||
@ -192,7 +199,6 @@ async fn inner_shell(
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let solids: Vec<Box<Solid>> = solid_set.clone().into();
|
|
||||||
if solids.is_empty() {
|
if solids.is_empty() {
|
||||||
return Err(KclError::Type(KclErrorDetails {
|
return Err(KclError::Type(KclErrorDetails {
|
||||||
message: "You must shell at least one solid".to_string(),
|
message: "You must shell at least one solid".to_string(),
|
||||||
@ -204,7 +210,7 @@ async fn inner_shell(
|
|||||||
for solid in &solids {
|
for solid in &solids {
|
||||||
// Flush the batch for our fillets/chamfers if there are any.
|
// Flush the batch for our fillets/chamfers if there are any.
|
||||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
args.flush_batch_for_solids(exec_state, vec![solid.clone()]).await?;
|
||||||
|
|
||||||
for tag in &faces {
|
for tag in &faces {
|
||||||
let extrude_plane_id = tag.get_face_id(solid, exec_state, &args, false).await?;
|
let extrude_plane_id = tag.get_face_id(solid, exec_state, &args, false).await?;
|
||||||
@ -241,12 +247,12 @@ async fn inner_shell(
|
|||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
Ok(solid_set)
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make the inside of a 3D object hollow.
|
/// Make the inside of a 3D object hollow.
|
||||||
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn hollow(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (thickness, solid): (f64, Box<Solid>) = args.get_data_and_solid()?;
|
let (thickness, solid): (f64, Box<Solid>) = args.get_data_and_solid(exec_state)?;
|
||||||
|
|
||||||
let value = inner_hollow(thickness, solid, exec_state, args).await?;
|
let value = inner_hollow(thickness, solid, exec_state, args).await?;
|
||||||
Ok(KclValue::Solid { value })
|
Ok(KclValue::Solid { value })
|
||||||
@ -314,7 +320,7 @@ async fn inner_hollow(
|
|||||||
) -> Result<Box<Solid>, KclError> {
|
) -> Result<Box<Solid>, KclError> {
|
||||||
// Flush the batch for our fillets/chamfers if there are any.
|
// Flush the batch for our fillets/chamfers if there are any.
|
||||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
args.flush_batch_for_solids(exec_state, vec![(*solid).clone()]).await?;
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
exec_state.next_uuid(),
|
exec_state.next_uuid(),
|
||||||
|
|||||||
@ -11,11 +11,13 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::execution::kcl_value::RuntimeType;
|
||||||
|
use crate::execution::PrimitiveType;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{
|
execution::{
|
||||||
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
|
Artifact, ArtifactId, BasePath, CodeRef, ExecState, Face, GeoMeta, KclValue, Path, Plane, Point2d, Point3d,
|
||||||
Sketch, SketchSet, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
Sketch, SketchSurface, Solid, StartSketchOnFace, StartSketchOnPlane, TagEngineInfo, TagIdentifier,
|
||||||
},
|
},
|
||||||
parsing::ast::types::TagNode,
|
parsing::ast::types::TagNode,
|
||||||
std::{
|
std::{
|
||||||
@ -96,7 +98,8 @@ pub const NEW_TAG_KW: &str = "tag";
|
|||||||
/// Draw a line to a point.
|
/// Draw a line to a point.
|
||||||
pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
// let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
// let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let end = args.get_kw_arg_opt("end")?;
|
let end = args.get_kw_arg_opt("end")?;
|
||||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||||
@ -253,7 +256,7 @@ async fn straight_line(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -263,7 +266,8 @@ async fn straight_line(
|
|||||||
|
|
||||||
/// Draw a line on the x-axis.
|
/// Draw a line on the x-axis.
|
||||||
pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn x_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let length = args.get_kw_arg_opt("length")?;
|
let length = args.get_kw_arg_opt("length")?;
|
||||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||||
@ -331,7 +335,8 @@ async fn inner_x_line(
|
|||||||
|
|
||||||
/// Draw a line on the y-axis.
|
/// Draw a line on the y-axis.
|
||||||
pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn y_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let length = args.get_kw_arg_opt("length")?;
|
let length = args.get_kw_arg_opt("length")?;
|
||||||
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
let end_absolute = args.get_kw_arg_opt("endAbsolute")?;
|
||||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||||
@ -410,7 +415,8 @@ pub enum AngledLineData {
|
|||||||
|
|
||||||
/// Draw an angled line.
|
/// Draw an angled line.
|
||||||
pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_angled_line(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -489,7 +495,7 @@ async fn inner_angled_line(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -498,7 +504,8 @@ async fn inner_angled_line(
|
|||||||
|
|
||||||
/// Draw an angled line of a given x length.
|
/// Draw an angled line of a given x length.
|
||||||
pub async fn angled_line_of_x_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line_of_x_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_angled_line_of_x_length(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line_of_x_length(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -568,7 +575,8 @@ pub struct AngledLineToData {
|
|||||||
|
|
||||||
/// Draw an angled line to a given x coordinate.
|
/// Draw an angled line to a given x coordinate.
|
||||||
pub async fn angled_line_to_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line_to_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_angled_line_to_x(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line_to_x(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -632,7 +640,8 @@ async fn inner_angled_line_to_x(
|
|||||||
|
|
||||||
/// Draw an angled line of a given y length.
|
/// Draw an angled line of a given y length.
|
||||||
pub async fn angled_line_of_y_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line_of_y_length(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (AngledLineData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_angled_line_of_y_length(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line_of_y_length(data, sketch, tag, exec_state, args).await?;
|
||||||
|
|
||||||
@ -694,7 +703,8 @@ async fn inner_angled_line_of_y_length(
|
|||||||
|
|
||||||
/// Draw an angled line to a given y coordinate.
|
/// Draw an angled line to a given y coordinate.
|
||||||
pub async fn angled_line_to_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line_to_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (AngledLineToData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_angled_line_to_y(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line_to_y(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -773,7 +783,7 @@ pub struct AngledLineThatIntersectsData {
|
|||||||
/// Draw an angled line that intersects with a given line.
|
/// Draw an angled line that intersects with a given line.
|
||||||
pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn angled_line_that_intersects(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (AngledLineThatIntersectsData, Sketch, Option<TagNode>) =
|
let (data, sketch, tag): (AngledLineThatIntersectsData, Sketch, Option<TagNode>) =
|
||||||
args.get_data_and_sketch_and_tag()?;
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
let new_sketch = inner_angled_line_that_intersects(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_angled_line_that_intersects(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
value: Box::new(new_sketch),
|
value: Box::new(new_sketch),
|
||||||
@ -1098,6 +1108,8 @@ async fn make_sketch_plane_from_orientation(
|
|||||||
let hide = Some(true);
|
let hide = Some(true);
|
||||||
match data {
|
match data {
|
||||||
PlaneData::XY | PlaneData::NegXY | PlaneData::XZ | PlaneData::NegXZ | PlaneData::YZ | PlaneData::NegYZ => {
|
PlaneData::XY | PlaneData::NegXY | PlaneData::XZ | PlaneData::NegXZ | PlaneData::YZ | PlaneData::NegYZ => {
|
||||||
|
// TODO: ignoring the default planes here since we already created them, breaks the
|
||||||
|
// front end for the feature tree which is stupid and we should fix it.
|
||||||
let x_axis = match data {
|
let x_axis = match data {
|
||||||
PlaneData::NegXY => Point3d::new(-1.0, 0.0, 0.0),
|
PlaneData::NegXY => Point3d::new(-1.0, 0.0, 0.0),
|
||||||
PlaneData::NegXZ => Point3d::new(-1.0, 0.0, 0.0),
|
PlaneData::NegXZ => Point3d::new(-1.0, 0.0, 0.0),
|
||||||
@ -1200,7 +1212,7 @@ pub(crate) async fn inner_start_profile_at(
|
|||||||
SketchSurface::Face(face) => {
|
SketchSurface::Face(face) => {
|
||||||
// Flush the batch for our fillets/chamfers if there are any.
|
// Flush the batch for our fillets/chamfers if there are any.
|
||||||
// If we do not do these for sketch on face, things will fail with face does not exist.
|
// If we do not do these for sketch on face, things will fail with face does not exist.
|
||||||
args.flush_batch_for_solid_set(exec_state, face.solid.clone().into())
|
args.flush_batch_for_solids(exec_state, vec![(*face.solid).clone()])
|
||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
SketchSurface::Plane(plane) if !plane.is_standard() => {
|
SketchSurface::Plane(plane) if !plane.is_standard() => {
|
||||||
@ -1278,14 +1290,17 @@ pub(crate) async fn inner_start_profile_at(
|
|||||||
meta: vec![args.source_range.into()],
|
meta: vec![args.source_range.into()],
|
||||||
tags: if let Some(tag) = &tag {
|
tags: if let Some(tag) = &tag {
|
||||||
let mut tag_identifier: TagIdentifier = tag.into();
|
let mut tag_identifier: TagIdentifier = tag.into();
|
||||||
tag_identifier.info = Some(TagEngineInfo {
|
tag_identifier.info = vec![(
|
||||||
id: current_path.geo_meta.id,
|
exec_state.stack().current_epoch(),
|
||||||
sketch: path_id,
|
TagEngineInfo {
|
||||||
path: Some(Path::Base {
|
id: current_path.geo_meta.id,
|
||||||
base: current_path.clone(),
|
sketch: path_id,
|
||||||
}),
|
path: Some(Path::Base {
|
||||||
surface: None,
|
base: current_path.clone(),
|
||||||
});
|
}),
|
||||||
|
surface: None,
|
||||||
|
},
|
||||||
|
)];
|
||||||
IndexMap::from([(tag.name.to_string(), tag_identifier)])
|
IndexMap::from([(tag.name.to_string(), tag_identifier)])
|
||||||
} else {
|
} else {
|
||||||
Default::default()
|
Default::default()
|
||||||
@ -1296,8 +1311,8 @@ pub(crate) async fn inner_start_profile_at(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the X component of the sketch profile start point.
|
/// Returns the X component of the sketch profile start point.
|
||||||
pub async fn profile_start_x(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn profile_start_x(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch: Sketch = args.get_sketch()?;
|
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||||
let ty = sketch.units.into();
|
let ty = sketch.units.into();
|
||||||
let x = inner_profile_start_x(sketch)?;
|
let x = inner_profile_start_x(sketch)?;
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
||||||
@ -1321,8 +1336,8 @@ pub(crate) fn inner_profile_start_x(sketch: Sketch) -> Result<f64, KclError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Y component of the sketch profile start point.
|
/// Returns the Y component of the sketch profile start point.
|
||||||
pub async fn profile_start_y(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn profile_start_y(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch: Sketch = args.get_sketch()?;
|
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||||
let ty = sketch.units.into();
|
let ty = sketch.units.into();
|
||||||
let x = inner_profile_start_y(sketch)?;
|
let x = inner_profile_start_y(sketch)?;
|
||||||
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
Ok(args.make_user_val_from_f64_with_type(TyF64::new(x, ty)))
|
||||||
@ -1345,8 +1360,8 @@ pub(crate) fn inner_profile_start_y(sketch: Sketch) -> Result<f64, KclError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the sketch profile start point.
|
/// Returns the sketch profile start point.
|
||||||
pub async fn profile_start(_exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn profile_start(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch: Sketch = args.get_sketch()?;
|
let sketch: Sketch = args.get_sketch(exec_state)?;
|
||||||
let ty = sketch.units.into();
|
let ty = sketch.units.into();
|
||||||
let point = inner_profile_start(sketch)?;
|
let point = inner_profile_start(sketch)?;
|
||||||
Ok(KclValue::from_point2d(point, ty, args.into()))
|
Ok(KclValue::from_point2d(point, ty, args.into()))
|
||||||
@ -1373,7 +1388,8 @@ pub(crate) fn inner_profile_start(sketch: Sketch) -> Result<[f64; 2], KclError>
|
|||||||
|
|
||||||
/// Close the current sketch.
|
/// Close the current sketch.
|
||||||
pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn close(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch = args.get_unlabeled_kw_arg("sketch")?;
|
let sketch =
|
||||||
|
args.get_unlabeled_kw_arg_typed("sketch", &RuntimeType::Primitive(PrimitiveType::Sketch), exec_state)?;
|
||||||
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
let tag = args.get_kw_arg_opt(NEW_TAG_KW)?;
|
||||||
let new_sketch = inner_close(sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_close(sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1440,7 +1456,7 @@ pub(crate) async fn inner_close(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -1490,7 +1506,7 @@ pub struct ArcToData {
|
|||||||
|
|
||||||
/// Draw an arc.
|
/// Draw an arc.
|
||||||
pub async fn arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (ArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (ArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_arc(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_arc(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1593,7 +1609,7 @@ pub(crate) async fn inner_arc(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -1603,7 +1619,7 @@ pub(crate) async fn inner_arc(
|
|||||||
|
|
||||||
/// Draw a three point arc.
|
/// Draw a three point arc.
|
||||||
pub async fn arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (ArcToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (ArcToData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_arc_to(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_arc_to(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1695,7 +1711,7 @@ pub(crate) async fn inner_arc_to(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -1739,7 +1755,8 @@ pub enum TangentialArcData {
|
|||||||
|
|
||||||
/// Draw a tangential arc.
|
/// Draw a tangential arc.
|
||||||
pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn tangential_arc(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (TangentialArcData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (TangentialArcData, Sketch, Option<TagNode>) =
|
||||||
|
args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_tangential_arc(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_tangential_arc(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1846,7 +1863,7 @@ async fn inner_tangential_arc(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -1866,7 +1883,7 @@ fn tan_arc_to(sketch: &Sketch, to: &[f64; 2]) -> ModelingCmd {
|
|||||||
|
|
||||||
/// Draw a tangential arc to a specific point.
|
/// Draw a tangential arc to a specific point.
|
||||||
pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
let (to, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_tangential_arc_to(to, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_tangential_arc_to(to, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1876,7 +1893,7 @@ pub async fn tangential_arc_to(exec_state: &mut ExecState, args: Args) -> Result
|
|||||||
|
|
||||||
/// Draw a tangential arc to point some distance away..
|
/// Draw a tangential arc to point some distance away..
|
||||||
pub async fn tangential_arc_to_relative(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn tangential_arc_to_relative(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = super::args::FromArgs::from_args(&args, 0)?;
|
let (delta, sketch, tag): ([f64; 2], Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_tangential_arc_to_relative(delta, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_tangential_arc_to_relative(delta, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -1943,7 +1960,7 @@ async fn inner_tangential_arc_to(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -2027,7 +2044,7 @@ async fn inner_tangential_arc_to_relative(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -2050,7 +2067,7 @@ pub struct BezierData {
|
|||||||
|
|
||||||
/// Draw a bezier curve.
|
/// Draw a bezier curve.
|
||||||
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn bezier_curve(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (data, sketch, tag): (BezierData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag()?;
|
let (data, sketch, tag): (BezierData, Sketch, Option<TagNode>) = args.get_data_and_sketch_and_tag(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_bezier_curve(data, sketch, tag, exec_state, args).await?;
|
let new_sketch = inner_bezier_curve(data, sketch, tag, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -2123,7 +2140,7 @@ async fn inner_bezier_curve(
|
|||||||
|
|
||||||
let mut new_sketch = sketch.clone();
|
let mut new_sketch = sketch.clone();
|
||||||
if let Some(tag) = &tag {
|
if let Some(tag) = &tag {
|
||||||
new_sketch.add_tag(tag, ¤t_path);
|
new_sketch.add_tag(tag, ¤t_path, exec_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_sketch.paths.push(current_path);
|
new_sketch.paths.push(current_path);
|
||||||
@ -2133,7 +2150,7 @@ async fn inner_bezier_curve(
|
|||||||
|
|
||||||
/// Use a sketch to cut a hole in another sketch.
|
/// Use a sketch to cut a hole in another sketch.
|
||||||
pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let (hole_sketch, sketch): (SketchSet, Sketch) = args.get_sketches()?;
|
let (hole_sketch, sketch): (Vec<Sketch>, Sketch) = args.get_sketches(exec_state)?;
|
||||||
|
|
||||||
let new_sketch = inner_hole(hole_sketch, sketch, exec_state, args).await?;
|
let new_sketch = inner_hole(hole_sketch, sketch, exec_state, args).await?;
|
||||||
Ok(KclValue::Sketch {
|
Ok(KclValue::Sketch {
|
||||||
@ -2177,13 +2194,12 @@ pub async fn hole(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kc
|
|||||||
feature_tree_operation = true,
|
feature_tree_operation = true,
|
||||||
}]
|
}]
|
||||||
async fn inner_hole(
|
async fn inner_hole(
|
||||||
hole_sketch: SketchSet,
|
hole_sketch: Vec<Sketch>,
|
||||||
sketch: Sketch,
|
sketch: Sketch,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<Sketch, KclError> {
|
) -> Result<Sketch, KclError> {
|
||||||
let hole_sketches: Vec<Sketch> = hole_sketch.into();
|
for hole_sketch in hole_sketch {
|
||||||
for hole_sketch in hole_sketches {
|
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
exec_state.next_uuid(),
|
exec_state.next_uuid(),
|
||||||
ModelingCmd::from(mcmd::Solid2dAddHole {
|
ModelingCmd::from(mcmd::Solid2dAddHole {
|
||||||
@ -2252,7 +2268,7 @@ mod tests {
|
|||||||
|
|
||||||
str_json = serde_json::to_string(&TagIdentifier {
|
str_json = serde_json::to_string(&TagIdentifier {
|
||||||
value: "thing".to_string(),
|
value: "thing".to_string(),
|
||||||
info: None,
|
info: Vec::new(),
|
||||||
meta: Default::default(),
|
meta: Default::default(),
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -2261,7 +2277,7 @@ mod tests {
|
|||||||
data,
|
data,
|
||||||
crate::std::sketch::FaceTag::Tag(Box::new(TagIdentifier {
|
crate::std::sketch::FaceTag::Tag(Box::new(TagIdentifier {
|
||||||
value: "thing".to_string(),
|
value: "thing".to_string(),
|
||||||
info: None,
|
info: Vec::new(),
|
||||||
meta: Default::default()
|
meta: Default::default()
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|||||||
@ -9,7 +9,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
execution::{ExecState, Helix, KclValue, Sketch, SketchSet, SolidSet},
|
execution::{
|
||||||
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
|
ExecState, Helix, KclValue, PrimitiveType, Sketch, Solid,
|
||||||
|
},
|
||||||
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
std::{extrude::do_post_extrude, fillet::default_tolerance, Args},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -24,12 +27,16 @@ pub enum SweepPath {
|
|||||||
|
|
||||||
/// Extrude a sketch along a path.
|
/// Extrude a sketch along a path.
|
||||||
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let sketch_set = args.get_unlabeled_kw_arg("sketch_set")?;
|
let sketches = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"sketches",
|
||||||
|
&RuntimeType::Array(PrimitiveType::Sketch, ArrayLen::NonEmpty),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let path: SweepPath = args.get_kw_arg("path")?;
|
let path: SweepPath = args.get_kw_arg("path")?;
|
||||||
let sectional = args.get_kw_arg_opt("sectional")?;
|
let sectional = args.get_kw_arg_opt("sectional")?;
|
||||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||||
|
|
||||||
let value = inner_sweep(sketch_set, path, sectional, tolerance, exec_state, args).await?;
|
let value = inner_sweep(sketches, path, sectional, tolerance, exec_state, args).await?;
|
||||||
Ok(value.into())
|
Ok(value.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,26 +141,25 @@ pub async fn sweep(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
sketch_set = { docs = "The sketch or set of sketches that should be swept in space" },
|
sketches = { docs = "The sketch or set of sketches that should be swept in space" },
|
||||||
path = { docs = "The path to sweep the sketch along" },
|
path = { docs = "The path to sweep the sketch along" },
|
||||||
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
|
sectional = { docs = "If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components." },
|
||||||
tolerance = { docs = "Tolerance for this operation" },
|
tolerance = { docs = "Tolerance for this operation" },
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_sweep(
|
async fn inner_sweep(
|
||||||
sketch_set: SketchSet,
|
sketches: Vec<Sketch>,
|
||||||
path: SweepPath,
|
path: SweepPath,
|
||||||
sectional: Option<bool>,
|
sectional: Option<bool>,
|
||||||
tolerance: Option<f64>,
|
tolerance: Option<f64>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidSet, KclError> {
|
) -> Result<Vec<Solid>, KclError> {
|
||||||
let trajectory = match path {
|
let trajectory = match path {
|
||||||
SweepPath::Sketch(sketch) => sketch.id.into(),
|
SweepPath::Sketch(sketch) => sketch.id.into(),
|
||||||
SweepPath::Helix(helix) => helix.value.into(),
|
SweepPath::Helix(helix) => helix.value.into(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sketches: Vec<Sketch> = sketch_set.into();
|
|
||||||
let mut solids = Vec::new();
|
let mut solids = Vec::new();
|
||||||
for sketch in &sketches {
|
for sketch in &sketches {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
@ -171,5 +177,5 @@ async fn inner_sweep(
|
|||||||
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
solids.push(do_post_extrude(sketch.clone(), id.into(), 0.0, exec_state, args.clone()).await?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solids.into())
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,18 +13,28 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{ExecState, KclValue, SolidOrImportedGeometry},
|
execution::{
|
||||||
|
kcl_value::{ArrayLen, RuntimeType},
|
||||||
|
ExecState, KclValue, PrimitiveType, SolidOrImportedGeometry,
|
||||||
|
},
|
||||||
std::Args,
|
std::Args,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Scale a solid.
|
/// Scale a solid.
|
||||||
pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Union(vec![
|
||||||
|
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||||
|
]),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let scale = args.get_kw_arg("scale")?;
|
let scale = args.get_kw_arg("scale")?;
|
||||||
let global = args.get_kw_arg_opt("global")?;
|
let global = args.get_kw_arg_opt("global")?;
|
||||||
|
|
||||||
let solid = inner_scale(solid_set, scale, global, exec_state, args).await?;
|
let solids = inner_scale(solids, scale, global, exec_state, args).await?;
|
||||||
Ok(solid.into())
|
Ok(solids.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Scale a solid.
|
/// Scale a solid.
|
||||||
@ -124,19 +134,19 @@ pub async fn scale(exec_state: &mut ExecState, args: Args) -> Result<KclValue, K
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = {docs = "The solid or set of solids to scale."},
|
solids = {docs = "The solid or set of solids to scale."},
|
||||||
scale = {docs = "The scale factor for the x, y, and z axes."},
|
scale = {docs = "The scale factor for the x, y, and z axes."},
|
||||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_scale(
|
async fn inner_scale(
|
||||||
solid_set: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
scale: [f64; 3],
|
scale: [f64; 3],
|
||||||
global: Option<bool>,
|
global: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||||
for solid_id in solid_set.ids() {
|
for solid_id in solids.ids() {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
@ -162,17 +172,24 @@ async fn inner_scale(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solid_set)
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move a solid.
|
/// Move a solid.
|
||||||
pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solids",
|
||||||
|
&RuntimeType::Union(vec![
|
||||||
|
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||||
|
]),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let translate = args.get_kw_arg("translate")?;
|
let translate = args.get_kw_arg("translate")?;
|
||||||
let global = args.get_kw_arg_opt("global")?;
|
let global = args.get_kw_arg_opt("global")?;
|
||||||
|
|
||||||
let solid = inner_translate(solid_set, translate, global, exec_state, args).await?;
|
let solids = inner_translate(solids, translate, global, exec_state, args).await?;
|
||||||
Ok(solid.into())
|
Ok(solids.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Move a solid.
|
/// Move a solid.
|
||||||
@ -264,19 +281,19 @@ pub async fn translate(exec_state: &mut ExecState, args: Args) -> Result<KclValu
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = {docs = "The solid or set of solids to move."},
|
solids = {docs = "The solid or set of solids to move."},
|
||||||
translate = {docs = "The amount to move the solid in all three axes."},
|
translate = {docs = "The amount to move the solid in all three axes."},
|
||||||
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
global = {docs = "If true, the transform is applied in global space. The origin of the model will move. By default, the transform is applied in local sketch axis, therefore the origin will not move."}
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
async fn inner_translate(
|
async fn inner_translate(
|
||||||
solid_set: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
translate: [f64; 3],
|
translate: [f64; 3],
|
||||||
global: Option<bool>,
|
global: Option<bool>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||||
for solid_id in solid_set.ids() {
|
for solid_id in solids.ids() {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
args.batch_modeling_cmd(
|
args.batch_modeling_cmd(
|
||||||
@ -302,12 +319,19 @@ async fn inner_translate(
|
|||||||
.await?;
|
.await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solid_set)
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate a solid.
|
/// Rotate a solid.
|
||||||
pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||||
let solid_set = args.get_unlabeled_kw_arg("solid_set")?;
|
let solids = args.get_unlabeled_kw_arg_typed(
|
||||||
|
"solid",
|
||||||
|
&RuntimeType::Union(vec![
|
||||||
|
RuntimeType::Array(PrimitiveType::Solid, ArrayLen::NonEmpty),
|
||||||
|
RuntimeType::Primitive(PrimitiveType::ImportedGeometry),
|
||||||
|
]),
|
||||||
|
exec_state,
|
||||||
|
)?;
|
||||||
let roll = args.get_kw_arg_opt("roll")?;
|
let roll = args.get_kw_arg_opt("roll")?;
|
||||||
let pitch = args.get_kw_arg_opt("pitch")?;
|
let pitch = args.get_kw_arg_opt("pitch")?;
|
||||||
let yaw = args.get_kw_arg_opt("yaw")?;
|
let yaw = args.get_kw_arg_opt("yaw")?;
|
||||||
@ -415,8 +439,8 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let solid = inner_rotate(solid_set, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
|
let solids = inner_rotate(solids, roll, pitch, yaw, axis, angle, global, exec_state, args).await?;
|
||||||
Ok(solid.into())
|
Ok(solids.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rotate a solid.
|
/// Rotate a solid.
|
||||||
@ -571,7 +595,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
keywords = true,
|
keywords = true,
|
||||||
unlabeled_first = true,
|
unlabeled_first = true,
|
||||||
args = {
|
args = {
|
||||||
solid_set = {docs = "The solid or set of solids to rotate."},
|
solids = {docs = "The solid or set of solids to rotate."},
|
||||||
roll = {docs = "The roll angle in degrees. Must be used with `pitch` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
roll = {docs = "The roll angle in degrees. Must be used with `pitch` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
||||||
pitch = {docs = "The pitch angle in degrees. Must be used with `roll` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
pitch = {docs = "The pitch angle in degrees. Must be used with `roll` and `yaw`. Must be between -360 and 360.", include_in_snippet = true},
|
||||||
yaw = {docs = "The yaw angle in degrees. Must be used with `roll` and `pitch`. Must be between -360 and 360.", include_in_snippet = true},
|
yaw = {docs = "The yaw angle in degrees. Must be used with `roll` and `pitch`. Must be between -360 and 360.", include_in_snippet = true},
|
||||||
@ -582,7 +606,7 @@ pub async fn rotate(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
|||||||
}]
|
}]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
async fn inner_rotate(
|
async fn inner_rotate(
|
||||||
solid_set: SolidOrImportedGeometry,
|
solids: SolidOrImportedGeometry,
|
||||||
roll: Option<f64>,
|
roll: Option<f64>,
|
||||||
pitch: Option<f64>,
|
pitch: Option<f64>,
|
||||||
yaw: Option<f64>,
|
yaw: Option<f64>,
|
||||||
@ -592,7 +616,7 @@ async fn inner_rotate(
|
|||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
args: Args,
|
args: Args,
|
||||||
) -> Result<SolidOrImportedGeometry, KclError> {
|
) -> Result<SolidOrImportedGeometry, KclError> {
|
||||||
for solid_id in solid_set.ids() {
|
for solid_id in solids.ids() {
|
||||||
let id = exec_state.next_uuid();
|
let id = exec_state.next_uuid();
|
||||||
|
|
||||||
if let (Some(roll), Some(pitch), Some(yaw)) = (roll, pitch, yaw) {
|
if let (Some(roll), Some(pitch), Some(yaw)) = (roll, pitch, yaw) {
|
||||||
@ -645,7 +669,7 @@ async fn inner_rotate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(solid_set)
|
Ok(solids)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@ -81,7 +81,7 @@ async fn do_execute_and_snapshot(
|
|||||||
ctx: &ExecutorContext,
|
ctx: &ExecutorContext,
|
||||||
program: Program,
|
program: Program,
|
||||||
) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
|
) -> Result<(ExecState, EnvironmentRef, image::DynamicImage), ExecErrorWithState> {
|
||||||
let mut exec_state = ExecState::new(&ctx.settings);
|
let mut exec_state = ExecState::new(ctx);
|
||||||
let result = ctx
|
let result = ctx
|
||||||
.run(&program, &mut exec_state)
|
.run(&program, &mut exec_state)
|
||||||
.await
|
.await
|
||||||
@ -156,7 +156,7 @@ pub async fn execute_and_export_step(
|
|||||||
ExecErrorWithState,
|
ExecErrorWithState,
|
||||||
> {
|
> {
|
||||||
let ctx = new_context(units, true, current_file).await?;
|
let ctx = new_context(units, true, current_file).await?;
|
||||||
let mut exec_state = ExecState::new(&ctx.settings);
|
let mut exec_state = ExecState::new(&ctx);
|
||||||
let program = Program::parse_no_errs(code)
|
let program = Program::parse_no_errs(code)
|
||||||
.map_err(|err| ExecErrorWithState::new(KclErrorWithOutputs::no_outputs(err).into(), exec_state.clone()))?;
|
.map_err(|err| ExecErrorWithState::new(KclErrorWithOutputs::no_outputs(err).into(), exec_state.clone()))?;
|
||||||
let result = ctx
|
let result = ctx
|
||||||
|
|||||||
@ -821,7 +821,7 @@ impl Type {
|
|||||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||||
match self {
|
match self {
|
||||||
Type::Primitive(t) => t.to_string(),
|
Type::Primitive(t) => t.to_string(),
|
||||||
Type::Array(t) => format!("{t}[]"),
|
Type::Array(t) => format!("[{t}]"),
|
||||||
Type::Object { properties } => {
|
Type::Object { properties } => {
|
||||||
let mut result = "{".to_owned();
|
let mut result = "{".to_owned();
|
||||||
for p in properties {
|
for p in properties {
|
||||||
@ -1268,7 +1268,7 @@ thing(1)
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recast_typed_fn() {
|
fn test_recast_typed_fn() {
|
||||||
let some_program_string = r#"fn thing(x: string, y: bool[]): number {
|
let some_program_string = r#"fn thing(x: string, y: [bool]): number {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|||||||
@ -284,56 +284,7 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
103,
|
|
||||||
142,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
19.93,
|
|
||||||
15.04
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 141,
|
|
||||||
"start": 135,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
23.08,
|
|
||||||
5.19
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
103,
|
|
||||||
142,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 141,
|
|
||||||
"start": 135,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -353,55 +304,6 @@ description: Variables in memory after executing angled_line.kcl
|
|||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
103,
|
|
||||||
142,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
19.93,
|
|
||||||
15.04
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 141,
|
|
||||||
"start": 135,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
23.08,
|
|
||||||
5.19
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
103,
|
|
||||||
142,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 141,
|
|
||||||
"start": 135,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -249,109 +249,11 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.0,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
-5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -779,109 +681,11 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.0,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
-5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -949,110 +753,12 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
|
|||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.0,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
-5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sketch001": {
|
"sketch001": {
|
||||||
"type": "Sketch",
|
"type": "Sketch",
|
||||||
@ -1240,109 +946,11 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.0,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
-5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -1715,109 +1323,11 @@ description: Variables in memory after executing artifact_graph_example_code1.kc
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.0,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
95,
|
|
||||||
131,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 130,
|
|
||||||
"start": 124,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.55,
|
|
||||||
5.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.55,
|
|
||||||
-5.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
137,
|
|
||||||
171,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 170,
|
|
||||||
"start": 164,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Artifact graph flowchart artifact_graph_example_code_no_3d.kcl
|
description: Artifact graph flowchart artifact_graph_example_code_no_3d.kcl
|
||||||
extension: md
|
extension: md
|
||||||
snapshot_kind: binary
|
snapshot_kind: binary
|
||||||
|
|||||||
@ -6,119 +6,17 @@ description: Variables in memory after executing artifact_graph_example_code_no_
|
|||||||
"rectangleSegmentA001": {
|
"rectangleSegmentA001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentA001",
|
"value": "rectangleSegmentA001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
71,
|
|
||||||
121,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.82,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 120,
|
|
||||||
"start": 99,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentA001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
-5.72,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"rectangleSegmentB001": {
|
"rectangleSegmentB001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentB001",
|
"value": "rectangleSegmentB001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
127,
|
|
||||||
227,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.72,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 226,
|
|
||||||
"start": 205,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentB001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
-5.72,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"rectangleSegmentC001": {
|
"rectangleSegmentC001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentC001",
|
"value": "rectangleSegmentC001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
233,
|
|
||||||
353,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.72,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 352,
|
|
||||||
"start": 331,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentC001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.82,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sketch003": {
|
"sketch003": {
|
||||||
"type": "Sketch",
|
"type": "Sketch",
|
||||||
@ -311,117 +209,15 @@ description: Variables in memory after executing artifact_graph_example_code_no_
|
|||||||
"tags": {
|
"tags": {
|
||||||
"rectangleSegmentA001": {
|
"rectangleSegmentA001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentA001",
|
"value": "rectangleSegmentA001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
71,
|
|
||||||
121,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
5.82,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 120,
|
|
||||||
"start": 99,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentA001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
-5.72,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"rectangleSegmentB001": {
|
"rectangleSegmentB001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentB001",
|
"value": "rectangleSegmentB001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
127,
|
|
||||||
227,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.72,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 226,
|
|
||||||
"start": 205,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentB001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
-5.72,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"rectangleSegmentC001": {
|
"rectangleSegmentC001": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "rectangleSegmentC001",
|
"value": "rectangleSegmentC001"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
233,
|
|
||||||
353,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
-5.72,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 352,
|
|
||||||
"start": 331,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "rectangleSegmentC001"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
5.82,
|
|
||||||
8.21
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/simulation_tests.rs
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
description: Artifact graph flowchart artifact_graph_example_code_offset_planes.kcl
|
description: Artifact graph flowchart artifact_graph_example_code_offset_planes.kcl
|
||||||
extension: md
|
extension: md
|
||||||
snapshot_kind: binary
|
snapshot_kind: binary
|
||||||
|
|||||||
@ -205,56 +205,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -629,56 +580,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -1258,56 +1160,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -1391,56 +1244,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
1.0,
|
|
||||||
1.5
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
1.5,
|
|
||||||
3.5
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -2137,56 +1941,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -2270,56 +2025,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
1.0,
|
|
||||||
1.5
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
1.5,
|
|
||||||
3.5
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -2378,110 +2084,12 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
1.0,
|
|
||||||
1.5
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
1.5,
|
|
||||||
3.5
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"sketch001": {
|
"sketch001": {
|
||||||
"type": "Sketch",
|
"type": "Sketch",
|
||||||
@ -2641,56 +2249,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -3019,56 +2578,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -3597,56 +3107,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -3730,56 +3191,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
1.0,
|
|
||||||
1.5
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
1.5,
|
|
||||||
3.5
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -4430,56 +3842,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg01": {
|
"seg01": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg01",
|
"value": "seg01"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
4.0,
|
|
||||||
8.0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
9.0,
|
|
||||||
0.0
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
92,
|
|
||||||
125,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 124,
|
|
||||||
"start": 118,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg01"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
@ -4563,56 +3926,7 @@ description: Variables in memory after executing artifact_graph_sketch_on_face_e
|
|||||||
"tags": {
|
"tags": {
|
||||||
"seg02": {
|
"seg02": {
|
||||||
"type": "TagIdentifier",
|
"type": "TagIdentifier",
|
||||||
"value": "seg02",
|
"value": "seg02"
|
||||||
"info": {
|
|
||||||
"type": "TagEngineInfo",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sketch": "[uuid]",
|
|
||||||
"path": {
|
|
||||||
"__geoMeta": {
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"from": [
|
|
||||||
1.0,
|
|
||||||
1.5
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"to": [
|
|
||||||
1.5,
|
|
||||||
3.5
|
|
||||||
],
|
|
||||||
"type": "ToPoint",
|
|
||||||
"units": {
|
|
||||||
"type": "Mm"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"surface": {
|
|
||||||
"faceId": "[uuid]",
|
|
||||||
"id": "[uuid]",
|
|
||||||
"sourceRange": [
|
|
||||||
577,
|
|
||||||
611,
|
|
||||||
0
|
|
||||||
],
|
|
||||||
"tag": {
|
|
||||||
"end": 610,
|
|
||||||
"start": 604,
|
|
||||||
"type": "TagDeclarator",
|
|
||||||
"value": "seg02"
|
|
||||||
},
|
|
||||||
"type": "extrudePlane"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"artifactId": "[uuid]",
|
"artifactId": "[uuid]",
|
||||||
|
|||||||
@ -0,0 +1,836 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Artifact commands assembly_mixed_units_cubes.kcl
|
||||||
|
---
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "edge_lines_visible",
|
||||||
|
"hidden": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "mm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_visible",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"hidden": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
33,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "in"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
33,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "in"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
47,
|
||||||
|
66,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 60.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
113,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
113,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "start_path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
113,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "move_path_pen",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"to": {
|
||||||
|
"x": -10.0,
|
||||||
|
"y": -10.0,
|
||||||
|
"z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
113,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
119,
|
||||||
|
136,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 5.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
142,
|
||||||
|
160,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": -5.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
166,
|
||||||
|
184,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -5.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
190,
|
||||||
|
246,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -10.0,
|
||||||
|
"y": -10.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
252,
|
||||||
|
259,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "close_path",
|
||||||
|
"path_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extrude",
|
||||||
|
"target": "[uuid]",
|
||||||
|
"distance": 5.0,
|
||||||
|
"faces": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_bring_to_front",
|
||||||
|
"object_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_extrusion_face_info",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
265,
|
||||||
|
287,
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
33,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "mm"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
47,
|
||||||
|
66,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "make_plane",
|
||||||
|
"origin": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"x_axis": {
|
||||||
|
"x": 1.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"y_axis": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 1.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"size": 60.0,
|
||||||
|
"clobber": false,
|
||||||
|
"hide": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
111,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
111,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "start_path"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
111,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "move_path_pen",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"to": {
|
||||||
|
"x": 10.0,
|
||||||
|
"y": 10.0,
|
||||||
|
"z": 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
76,
|
||||||
|
111,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
117,
|
||||||
|
134,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 5.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
140,
|
||||||
|
158,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": -5.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
164,
|
||||||
|
182,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": -5.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
188,
|
||||||
|
244,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extend_path",
|
||||||
|
"path": "[uuid]",
|
||||||
|
"segment": {
|
||||||
|
"type": "line",
|
||||||
|
"end": {
|
||||||
|
"x": 10.0,
|
||||||
|
"y": 10.0,
|
||||||
|
"z": 0.0
|
||||||
|
},
|
||||||
|
"relative": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
250,
|
||||||
|
257,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "close_path",
|
||||||
|
"path_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "enable_sketch_mode",
|
||||||
|
"entity_id": "[uuid]",
|
||||||
|
"ortho": false,
|
||||||
|
"animated": false,
|
||||||
|
"adjust_camera": false,
|
||||||
|
"planar_normal": {
|
||||||
|
"x": 0.0,
|
||||||
|
"y": 0.0,
|
||||||
|
"z": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "extrude",
|
||||||
|
"target": "[uuid]",
|
||||||
|
"distance": 5.0,
|
||||||
|
"faces": null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "sketch_mode_disable"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "object_bring_to_front",
|
||||||
|
"object_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_extrusion_face_info",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_opposite_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
263,
|
||||||
|
285,
|
||||||
|
4
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "solid3d_get_next_adjacent_edge",
|
||||||
|
"object_id": "[uuid]",
|
||||||
|
"edge_id": "[uuid]",
|
||||||
|
"face_id": "[uuid]"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cmdId": "[uuid]",
|
||||||
|
"range": [
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"command": {
|
||||||
|
"type": "set_scene_units",
|
||||||
|
"unit": "in"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Artifact graph flowchart assembly_mixed_units_cubes.kcl
|
||||||
|
extension: md
|
||||||
|
snapshot_kind: binary
|
||||||
|
---
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
subgraph path2 [Path]
|
||||||
|
2["Path<br>[76, 113, 3]"]
|
||||||
|
3["Segment<br>[119, 136, 3]"]
|
||||||
|
4["Segment<br>[142, 160, 3]"]
|
||||||
|
5["Segment<br>[166, 184, 3]"]
|
||||||
|
6["Segment<br>[190, 246, 3]"]
|
||||||
|
7["Segment<br>[252, 259, 3]"]
|
||||||
|
8[Solid2d]
|
||||||
|
end
|
||||||
|
subgraph path25 [Path]
|
||||||
|
25["Path<br>[76, 111, 4]"]
|
||||||
|
26["Segment<br>[117, 134, 4]"]
|
||||||
|
27["Segment<br>[140, 158, 4]"]
|
||||||
|
28["Segment<br>[164, 182, 4]"]
|
||||||
|
29["Segment<br>[188, 244, 4]"]
|
||||||
|
30["Segment<br>[250, 257, 4]"]
|
||||||
|
31[Solid2d]
|
||||||
|
end
|
||||||
|
1["Plane<br>[47, 66, 3]"]
|
||||||
|
9["Sweep Extrusion<br>[265, 287, 3]"]
|
||||||
|
10[Wall]
|
||||||
|
11[Wall]
|
||||||
|
12[Wall]
|
||||||
|
13[Wall]
|
||||||
|
14["Cap Start"]
|
||||||
|
15["Cap End"]
|
||||||
|
16["SweepEdge Opposite"]
|
||||||
|
17["SweepEdge Adjacent"]
|
||||||
|
18["SweepEdge Opposite"]
|
||||||
|
19["SweepEdge Adjacent"]
|
||||||
|
20["SweepEdge Opposite"]
|
||||||
|
21["SweepEdge Adjacent"]
|
||||||
|
22["SweepEdge Opposite"]
|
||||||
|
23["SweepEdge Adjacent"]
|
||||||
|
24["Plane<br>[47, 66, 4]"]
|
||||||
|
32["Sweep Extrusion<br>[263, 285, 4]"]
|
||||||
|
33[Wall]
|
||||||
|
34[Wall]
|
||||||
|
35[Wall]
|
||||||
|
36[Wall]
|
||||||
|
37["Cap Start"]
|
||||||
|
38["Cap End"]
|
||||||
|
39["SweepEdge Opposite"]
|
||||||
|
40["SweepEdge Adjacent"]
|
||||||
|
41["SweepEdge Opposite"]
|
||||||
|
42["SweepEdge Adjacent"]
|
||||||
|
43["SweepEdge Opposite"]
|
||||||
|
44["SweepEdge Adjacent"]
|
||||||
|
45["SweepEdge Opposite"]
|
||||||
|
46["SweepEdge Adjacent"]
|
||||||
|
1 --- 2
|
||||||
|
2 --- 3
|
||||||
|
2 --- 4
|
||||||
|
2 --- 5
|
||||||
|
2 --- 6
|
||||||
|
2 --- 7
|
||||||
|
2 ---- 9
|
||||||
|
2 --- 8
|
||||||
|
3 --- 13
|
||||||
|
3 --- 22
|
||||||
|
3 --- 23
|
||||||
|
4 --- 12
|
||||||
|
4 --- 20
|
||||||
|
4 --- 21
|
||||||
|
5 --- 11
|
||||||
|
5 --- 18
|
||||||
|
5 --- 19
|
||||||
|
6 --- 10
|
||||||
|
6 --- 16
|
||||||
|
6 --- 17
|
||||||
|
9 --- 10
|
||||||
|
9 --- 11
|
||||||
|
9 --- 12
|
||||||
|
9 --- 13
|
||||||
|
9 --- 14
|
||||||
|
9 --- 15
|
||||||
|
9 --- 16
|
||||||
|
9 --- 17
|
||||||
|
9 --- 18
|
||||||
|
9 --- 19
|
||||||
|
9 --- 20
|
||||||
|
9 --- 21
|
||||||
|
9 --- 22
|
||||||
|
9 --- 23
|
||||||
|
24 --- 25
|
||||||
|
25 --- 26
|
||||||
|
25 --- 27
|
||||||
|
25 --- 28
|
||||||
|
25 --- 29
|
||||||
|
25 --- 30
|
||||||
|
25 ---- 32
|
||||||
|
25 --- 31
|
||||||
|
26 --- 36
|
||||||
|
26 --- 45
|
||||||
|
26 --- 46
|
||||||
|
27 --- 35
|
||||||
|
27 --- 43
|
||||||
|
27 --- 44
|
||||||
|
28 --- 34
|
||||||
|
28 --- 41
|
||||||
|
28 --- 42
|
||||||
|
29 --- 33
|
||||||
|
29 --- 39
|
||||||
|
29 --- 40
|
||||||
|
32 --- 33
|
||||||
|
32 --- 34
|
||||||
|
32 --- 35
|
||||||
|
32 --- 36
|
||||||
|
32 --- 37
|
||||||
|
32 --- 38
|
||||||
|
32 --- 39
|
||||||
|
32 --- 40
|
||||||
|
32 --- 41
|
||||||
|
32 --- 42
|
||||||
|
32 --- 43
|
||||||
|
32 --- 44
|
||||||
|
32 --- 45
|
||||||
|
32 --- 46
|
||||||
|
```
|
||||||
133
rust/kcl-lib/tests/assembly_mixed_units_cubes/ast.snap
Normal file
133
rust/kcl-lib/tests/assembly_mixed_units_cubes/ast.snap
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
source: kcl-lib/src/simulation_tests.rs
|
||||||
|
description: Result of parsing assembly_mixed_units_cubes.kcl
|
||||||
|
---
|
||||||
|
{
|
||||||
|
"Ok": {
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"end": 70,
|
||||||
|
"path": {
|
||||||
|
"type": "Kcl",
|
||||||
|
"filename": "cube-inches.kcl"
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"type": "None",
|
||||||
|
"alias": {
|
||||||
|
"end": 70,
|
||||||
|
"name": "cubeIn",
|
||||||
|
"start": 64,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 36,
|
||||||
|
"type": "ImportStatement",
|
||||||
|
"type": "ImportStatement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 101,
|
||||||
|
"path": {
|
||||||
|
"type": "Kcl",
|
||||||
|
"filename": "cube-mm.kcl"
|
||||||
|
},
|
||||||
|
"selector": {
|
||||||
|
"type": "None",
|
||||||
|
"alias": {
|
||||||
|
"end": 101,
|
||||||
|
"name": "cubeMm",
|
||||||
|
"start": 95,
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"start": 71,
|
||||||
|
"type": "ImportStatement",
|
||||||
|
"type": "ImportStatement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 109,
|
||||||
|
"expression": {
|
||||||
|
"end": 109,
|
||||||
|
"name": "cubeIn",
|
||||||
|
"start": 103,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 103,
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"end": 116,
|
||||||
|
"expression": {
|
||||||
|
"end": 116,
|
||||||
|
"name": "cubeMm",
|
||||||
|
"start": 110,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 110,
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"type": "ExpressionStatement"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"end": 117,
|
||||||
|
"innerAttrs": [
|
||||||
|
{
|
||||||
|
"end": 33,
|
||||||
|
"name": {
|
||||||
|
"end": 9,
|
||||||
|
"name": "settings",
|
||||||
|
"start": 1,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"end": 32,
|
||||||
|
"key": {
|
||||||
|
"end": 27,
|
||||||
|
"name": "defaultLengthUnit",
|
||||||
|
"start": 10,
|
||||||
|
"type": "Identifier"
|
||||||
|
},
|
||||||
|
"start": 10,
|
||||||
|
"type": "ObjectProperty",
|
||||||
|
"value": {
|
||||||
|
"end": 32,
|
||||||
|
"name": "in",
|
||||||
|
"start": 30,
|
||||||
|
"type": "Identifier",
|
||||||
|
"type": "Identifier"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start": 0,
|
||||||
|
"type": "Annotation"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nonCodeMeta": {
|
||||||
|
"nonCodeNodes": {
|
||||||
|
"1": [
|
||||||
|
{
|
||||||
|
"end": 103,
|
||||||
|
"start": 101,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"startNodes": [
|
||||||
|
{
|
||||||
|
"end": 36,
|
||||||
|
"start": 33,
|
||||||
|
"type": "NonCodeNode",
|
||||||
|
"value": {
|
||||||
|
"type": "newLine"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user