Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
b17e61d963 | |||
d31d07d9c8 | |||
7aa2d63c21 | |||
e1081b0ee6 | |||
59223279b7 | |||
8a4e717565 | |||
80b542ca18 | |||
e4bfc863ea | |||
77ef255de4 | |||
64c3841079 | |||
c7bb6bc845 |
3
.github/workflows/cargo-build.yml
vendored
@ -15,6 +15,9 @@ on:
|
||||
- '**/Cargo.lock'
|
||||
- '**/rust-toolchain.toml'
|
||||
- .github/workflows/cargo-build.yml
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo build
|
||||
jobs:
|
||||
cargobuild:
|
||||
|
3
.github/workflows/cargo-clippy.yml
vendored
@ -15,6 +15,9 @@ on:
|
||||
- '**/rust-toolchain.toml'
|
||||
- '**.rs'
|
||||
- .github/workflows/cargo-build.yml
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo clippy
|
||||
jobs:
|
||||
cargoclippy:
|
||||
|
3
.github/workflows/cargo-criterion.yml
vendored
@ -17,6 +17,9 @@ on:
|
||||
- .github/workflows/cargo-criterion.yml
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo criterion
|
||||
jobs:
|
||||
cargocriterion:
|
||||
|
3
.github/workflows/cargo-fmt.yml
vendored
@ -18,6 +18,9 @@ on:
|
||||
permissions:
|
||||
packages: read
|
||||
contents: read
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo fmt
|
||||
jobs:
|
||||
cargofmt:
|
||||
|
3
.github/workflows/cargo-test.yml
vendored
@ -17,6 +17,9 @@ on:
|
||||
- .github/workflows/cargo-test.yml
|
||||
workflow_dispatch:
|
||||
permissions: read-all
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
name: cargo test
|
||||
jobs:
|
||||
cargotest:
|
||||
|
3
.github/workflows/ci.yml
vendored
@ -8,6 +8,9 @@ on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
check-format:
|
||||
runs-on: 'ubuntu-20.04'
|
||||
|
1262
docs/kcl/std.json
314
docs/kcl/std.md
@ -48,7 +48,9 @@
|
||||
* [`show`](#show)
|
||||
* [`sin`](#sin)
|
||||
* [`sqrt`](#sqrt)
|
||||
* [`startProfileAt`](#startProfileAt)
|
||||
* [`startSketchAt`](#startSketchAt)
|
||||
* [`startSketchOn`](#startSketchOn)
|
||||
* [`tan`](#tan)
|
||||
* [`tangentalArc`](#tangentalArc)
|
||||
* [`tangentalArcTo`](#tangentalArcTo)
|
||||
@ -120,6 +122,8 @@ angleToMatchLengthX(segment_name: string, to: number, sketch_group: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -204,6 +208,8 @@ angleToMatchLengthY(segment_name: string, to: number, sketch_group: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -298,6 +304,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -364,6 +372,8 @@ angledLine(data: AngledLineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -454,6 +464,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -520,6 +532,8 @@ angledLineOfXLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -610,6 +624,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -676,6 +692,8 @@ angledLineOfYLength(data: AngledLineData, sketch_group: SketchGroup) -> SketchGr
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -767,6 +785,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -833,6 +853,8 @@ angledLineThatIntersects(data: AngeledLineThatIntersectsData, sketch_group: Sket
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -923,6 +945,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -989,6 +1013,8 @@ angledLineToX(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1079,6 +1105,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1145,6 +1173,8 @@ angledLineToY(data: AngledLineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1262,6 +1292,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1328,6 +1360,8 @@ arc(data: ArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1467,6 +1501,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1533,6 +1569,8 @@ bezierCurve(data: BezierData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1631,6 +1669,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1697,6 +1737,8 @@ close(sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1815,6 +1857,8 @@ extrude(length: number, sketch_group: SketchGroup) -> ExtrudeGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -1996,6 +2040,8 @@ lastSegX(sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2078,6 +2124,8 @@ lastSegY(sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2233,6 +2281,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2299,6 +2349,8 @@ line(data: LineData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2387,6 +2439,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2453,6 +2507,8 @@ lineTo(data: LineToData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2693,6 +2749,8 @@ segAng(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2776,6 +2834,8 @@ segEndX(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2859,6 +2919,8 @@ segEndY(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -2942,6 +3004,8 @@ segLen(segment_name: string, sketch_group: SketchGroup) -> number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3024,6 +3088,8 @@ show(sketch: SketchGroup)
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3125,14 +3191,14 @@ sqrt(num: number) -> number
|
||||
|
||||
|
||||
|
||||
### startSketchAt
|
||||
### startProfileAt
|
||||
|
||||
Start a sketch at a given point.
|
||||
Start a profile at a given point.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchAt(data: LineData) -> SketchGroup
|
||||
startProfileAt(data: LineData, plane: Plane) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
@ -3147,6 +3213,40 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
* `plane`: `Plane` - A plane.
|
||||
```
|
||||
{
|
||||
// The id of the plane.
|
||||
id: uuid,
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// Type for a plane.
|
||||
value: string |
|
||||
string,
|
||||
// What should the plane’s X axis be?
|
||||
xAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
yAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
zAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
@ -3155,6 +3255,8 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3216,6 +3318,188 @@ startSketchAt(data: LineData) -> SketchGroup
|
||||
|
||||
|
||||
|
||||
### startSketchAt
|
||||
|
||||
Start a sketch at a given point on the 'XY' plane.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchAt(data: LineData) -> SketchGroup
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `LineData` - Data to draw a line.
|
||||
```
|
||||
{
|
||||
// The tag.
|
||||
tag: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
} |
|
||||
[number]
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `SketchGroup` - A sketch group is a collection of paths.
|
||||
```
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
rotation: [number],
|
||||
// The starting path.
|
||||
start: {
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
},
|
||||
// The paths in the sketch group.
|
||||
value: [{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
// The x coordinate.
|
||||
x: number,
|
||||
// The y coordinate.
|
||||
y: number,
|
||||
} |
|
||||
{
|
||||
// The from point.
|
||||
from: [number],
|
||||
// The name of the path.
|
||||
name: string,
|
||||
// The to point.
|
||||
to: [number],
|
||||
type: string,
|
||||
}],
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### startSketchOn
|
||||
|
||||
Start a sketch at a given point.
|
||||
|
||||
|
||||
|
||||
```
|
||||
startSketchOn(data: PlaneData) -> Plane
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
* `data`: `PlaneData` - Data for a plane.
|
||||
```
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
string |
|
||||
{
|
||||
plane: {
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s X axis be?
|
||||
x_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
y_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
z_axis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
* `Plane` - A plane.
|
||||
```
|
||||
{
|
||||
// The id of the plane.
|
||||
id: uuid,
|
||||
// Origin of the plane.
|
||||
origin: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// Type for a plane.
|
||||
value: string |
|
||||
string,
|
||||
// What should the plane’s X axis be?
|
||||
xAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// What should the plane’s Y axis be?
|
||||
yAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
// The z-axis (normal).
|
||||
zAxis: {
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
### tan
|
||||
|
||||
Computes the tangent of a number (in radians).
|
||||
@ -3269,6 +3553,8 @@ tangentalArc(data: TangentalArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3335,6 +3621,8 @@ tangentalArc(data: TangentalArcData, sketch_group: SketchGroup) -> SketchGroup
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3423,6 +3711,8 @@ tangentalArcTo(data: TangentalArcToData, sketch_group: SketchGroup) -> SketchGro
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3489,6 +3779,8 @@ tangentalArcTo(data: TangentalArcToData, sketch_group: SketchGroup) -> SketchGro
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3596,6 +3888,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3662,6 +3956,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3750,6 +4046,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3816,6 +4114,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3904,6 +4204,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -3970,6 +4272,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -4058,6 +4362,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
@ -4124,6 +4430,8 @@ number
|
||||
{
|
||||
// The id of the sketch group.
|
||||
id: uuid,
|
||||
// The plane id of the sketch group.
|
||||
planeId: uuid,
|
||||
// The position of the sketch group.
|
||||
position: [number],
|
||||
// The rotation of the sketch group.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "untitled-app",
|
||||
"version": "0.9.5",
|
||||
"version": "0.10.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.9.0",
|
||||
@ -10,7 +10,7 @@
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@headlessui/react": "^1.7.13",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@kittycad/lib": "^0.0.40",
|
||||
"@kittycad/lib": "^0.0.43",
|
||||
"@lezer/javascript": "^1.4.7",
|
||||
"@open-rpc/client-js": "^1.8.1",
|
||||
"@react-hook/resize-observer": "^1.2.6",
|
||||
@ -32,7 +32,7 @@
|
||||
"fuse.js": "^6.6.2",
|
||||
"http-server": "^14.1.1",
|
||||
"json-rpc-2.0": "^1.6.0",
|
||||
"re-resizable": "^6.9.9",
|
||||
"re-resizable": "^6.9.11",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
|
6
src-tauri/Cargo.lock
generated
@ -3712,9 +3712,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "1.5.0"
|
||||
version = "1.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72aee3277d0a0df01472cc704ab5934a51a1f25348838df17bfb3c5cb727880c"
|
||||
checksum = "0238c5063bf9613054149a1b6bce4935922e532b7d8211f36989a490a79806be"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64 0.21.2",
|
||||
@ -3828,7 +3828,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs-extra"
|
||||
version = "0.0.0"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#9b96996b5a90a6a57d587ce4312975f13a4d8bc2"
|
||||
source = "git+https://github.com/tauri-apps/plugins-workspace?branch=v1#fa32d1afa97f52f74d814c5619b8d95da3268e3e"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
|
@ -20,7 +20,7 @@ kittycad = "0.2.28"
|
||||
oauth2 = "4.4.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tauri = { version = "1.5.0", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
|
||||
tauri = { version = "1.5.1", features = [ "os-all", "dialog-all", "fs-all", "http-request", "path-all", "shell-open", "shell-open-api", "updater", "devtools"] }
|
||||
tauri-plugin-fs-extra = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
|
||||
tokio = { version = "1.32.0", features = ["time"] }
|
||||
toml = "0.8.2"
|
||||
|
@ -8,7 +8,7 @@
|
||||
},
|
||||
"package": {
|
||||
"productName": "kittycad-modeling",
|
||||
"version": "0.9.5"
|
||||
"version": "0.10.0"
|
||||
},
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
|
@ -1,9 +1,15 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { App } from './App'
|
||||
import { describe, test, vi } from 'vitest'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import {
|
||||
Route,
|
||||
RouterProvider,
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom'
|
||||
import { GlobalStateProvider } from './components/GlobalStateProvider'
|
||||
import CommandBarProvider from 'components/CommandBar'
|
||||
import { BROWSER_FILE_NAME } from 'Router'
|
||||
|
||||
let listener: ((rect: any) => void) | undefined = undefined
|
||||
;(global as any).ResizeObserver = class ResizeObserver {
|
||||
@ -24,7 +30,7 @@ describe('App tests', () => {
|
||||
>
|
||||
return {
|
||||
...actual,
|
||||
useParams: () => ({ id: 'new' }),
|
||||
useParams: () => ({ id: BROWSER_FILE_NAME }),
|
||||
useLoaderData: () => ({ code: null }),
|
||||
}
|
||||
})
|
||||
@ -41,12 +47,24 @@ describe('App tests', () => {
|
||||
})
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// wrap in router and xState context
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
// We have to use a memory router in the testing environment,
|
||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
||||
const router = createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="/file/:id"
|
||||
element={
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
initialEntries: ['/file/new'],
|
||||
initialIndex: 0,
|
||||
}
|
||||
)
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
@ -94,6 +94,8 @@ export const paths = {
|
||||
) as typeof onboardingPaths,
|
||||
}
|
||||
|
||||
export const BROWSER_FILE_NAME = 'new'
|
||||
|
||||
export type IndexLoaderData = {
|
||||
code: string | null
|
||||
project?: ProjectWithEntryPointMetadata
|
||||
@ -129,7 +131,9 @@ const router = createBrowserRouter(
|
||||
{
|
||||
path: paths.INDEX,
|
||||
loader: () =>
|
||||
isTauri() ? redirect(paths.HOME) : redirect(paths.FILE + '/new'),
|
||||
isTauri()
|
||||
? redirect(paths.HOME)
|
||||
: redirect(paths.FILE + '/' + BROWSER_FILE_NAME),
|
||||
errorElement: <ErrorPage />,
|
||||
},
|
||||
{
|
||||
@ -167,7 +171,7 @@ const router = createBrowserRouter(
|
||||
)
|
||||
}
|
||||
|
||||
if (params.id && params.id !== 'new') {
|
||||
if (params.id && params.id !== BROWSER_FILE_NAME) {
|
||||
// Note that PROJECT_ENTRYPOINT is hardcoded until we support multiple files
|
||||
const code = await readTextFile(params.id + '/' + PROJECT_ENTRYPOINT)
|
||||
const entrypoint_metadata = await metadata(
|
||||
@ -212,7 +216,7 @@ const router = createBrowserRouter(
|
||||
),
|
||||
loader: async () => {
|
||||
if (!isTauri()) {
|
||||
return redirect(paths.FILE + '/new')
|
||||
return redirect(paths.FILE + '/' + BROWSER_FILE_NAME)
|
||||
}
|
||||
const fetchedStorage = localStorage?.getItem(SETTINGS_PERSIST_KEY)
|
||||
const persistedSettings = JSON.parse(fetchedStorage || '{}') as Partial<
|
||||
|
@ -10,7 +10,7 @@ import { SetHorzVertDistance } from './components/Toolbar/SetHorzVertDistance'
|
||||
import { SetAngleLength } from './components/Toolbar/setAngleLength'
|
||||
import { SetAbsDistance } from './components/Toolbar/SetAbsDistance'
|
||||
import { SetAngleBetween } from './components/Toolbar/SetAngleBetween'
|
||||
import { Fragment, useEffect } from 'react'
|
||||
import { Fragment, WheelEvent, useRef } from 'react'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faSearch, faX } from '@fortawesome/free-solid-svg-icons'
|
||||
import { Popover, Transition } from '@headlessui/react'
|
||||
@ -62,10 +62,24 @@ export const Toolbar = () => {
|
||||
executeAst: s.executeAst,
|
||||
}))
|
||||
useAppMode()
|
||||
const toolbarButtonsRef = useRef<HTMLSpanElement>(null)
|
||||
|
||||
function handleToolbarButtonsWheelEvent(ev: WheelEvent<HTMLSpanElement>) {
|
||||
const span = toolbarButtonsRef.current
|
||||
if (!span) {
|
||||
return
|
||||
}
|
||||
|
||||
span.scrollLeft = span.scrollLeft += ev.deltaY
|
||||
}
|
||||
|
||||
function ToolbarButtons({ className }: React.HTMLAttributes<HTMLElement>) {
|
||||
return (
|
||||
<span className={styles.toolbarButtons + ' ' + className}>
|
||||
<span
|
||||
ref={toolbarButtonsRef}
|
||||
onWheel={handleToolbarButtonsWheelEvent}
|
||||
className={styles.toolbarButtons + ' ' + className}
|
||||
>
|
||||
{guiMode.mode === 'default' && (
|
||||
<button
|
||||
onClick={() => {
|
||||
|
@ -91,11 +91,14 @@ export function useCalc({
|
||||
newVariableInsertIndex: number
|
||||
setNewVariableName: (a: string) => void
|
||||
} {
|
||||
const { ast, programMemory, selectionRange } = useStore((s) => ({
|
||||
ast: s.ast,
|
||||
programMemory: s.programMemory,
|
||||
selectionRange: s.selectionRanges.codeBasedSelections[0].range,
|
||||
}))
|
||||
const { ast, programMemory, selectionRange, defaultPlanes } = useStore(
|
||||
(s) => ({
|
||||
ast: s.ast,
|
||||
programMemory: s.programMemory,
|
||||
selectionRange: s.selectionRanges.codeBasedSelections[0].range,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
})
|
||||
)
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const [availableVarInfo, setAvailableVarInfo] = useState<
|
||||
ReturnType<typeof findAllPreviousVariables>
|
||||
@ -143,19 +146,22 @@ export function useCalc({
|
||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||
})
|
||||
executor(ast, _programMem, engineCommandManager).then((programMemory) => {
|
||||
const resultDeclaration = ast.body.find(
|
||||
(a) =>
|
||||
a.type === 'VariableDeclaration' &&
|
||||
a.declarations?.[0]?.id?.name === '__result__'
|
||||
)
|
||||
const init =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declarations?.[0]?.init
|
||||
const result = programMemory?.root?.__result__?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
})
|
||||
if (!defaultPlanes) return
|
||||
executor(ast, _programMem, engineCommandManager, defaultPlanes!).then(
|
||||
(programMemory) => {
|
||||
const resultDeclaration = ast.body.find(
|
||||
(a) =>
|
||||
a.type === 'VariableDeclaration' &&
|
||||
a.declarations?.[0]?.id?.name === '__result__'
|
||||
)
|
||||
const init =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declarations?.[0]?.init
|
||||
const result = programMemory?.root?.__result__?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
}
|
||||
)
|
||||
} catch (e) {
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
|
@ -47,11 +47,9 @@ export const ErrorPage = () => {
|
||||
Clear storage
|
||||
</ActionButton>
|
||||
<ActionButton
|
||||
Element="link"
|
||||
Element="externalLink"
|
||||
icon={{ icon: faBug }}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
to="https://discord.com/channels/915388055236509727/1138967922614743060"
|
||||
to="https://github.com/KittyCAD/modeling-app/issues/new"
|
||||
>
|
||||
Report Bug
|
||||
</ActionButton>
|
||||
|
@ -14,12 +14,14 @@ describe('processMemory', () => {
|
||||
}
|
||||
const otherVar = myFn(5)
|
||||
|
||||
const theExtrude = startSketchAt([0, 0])
|
||||
const theExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.4, myVar], %)
|
||||
|> lineTo([-0.76, otherVar], %)
|
||||
|> extrude(4, %)
|
||||
|
||||
const theSketch = startSketchAt([0, 0])
|
||||
const theSketch = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-3.35, 0.17], %)
|
||||
|> lineTo([0.98, 5.16], %)
|
||||
|> lineTo([2.15, 4.32], %)
|
||||
|
@ -46,6 +46,8 @@ export const Stream = ({ className = '' }) => {
|
||||
updateAst,
|
||||
setGuiMode,
|
||||
programMemory,
|
||||
defaultPlanes,
|
||||
currentPlane,
|
||||
} = useStore((s) => ({
|
||||
mediaStream: s.mediaStream,
|
||||
setButtonDownInStream: s.setButtonDownInStream,
|
||||
@ -59,6 +61,8 @@ export const Stream = ({ className = '' }) => {
|
||||
updateAst: s.updateAst,
|
||||
setGuiMode: s.setGuiMode,
|
||||
programMemory: s.programMemory,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
currentPlane: s.currentPlane,
|
||||
}))
|
||||
const {
|
||||
settings: {
|
||||
@ -252,10 +256,26 @@ export const Stream = ({ className = '' }) => {
|
||||
|
||||
let engineId = guiMode.pathId
|
||||
|
||||
// Get the current plane string for plane we are on.
|
||||
let currentPlaneString = ''
|
||||
if (currentPlane === defaultPlanes?.xy) {
|
||||
currentPlaneString = 'XY'
|
||||
} else if (currentPlane === defaultPlanes?.yz) {
|
||||
currentPlaneString = 'YZ'
|
||||
} else if (currentPlane === defaultPlanes?.xz) {
|
||||
currentPlaneString = 'XZ'
|
||||
}
|
||||
|
||||
// Do not supporting editing/moving lines on a non-default plane.
|
||||
// Eventually we can support this but for now we will just throw an
|
||||
// error.
|
||||
if (currentPlaneString === '') return
|
||||
|
||||
const updatedAst: Program = await modifyAstForSketch(
|
||||
engineCommandManager,
|
||||
ast,
|
||||
variableName,
|
||||
currentPlaneString,
|
||||
engineId
|
||||
)
|
||||
|
||||
@ -282,8 +302,46 @@ export const Stream = ({ className = '' }) => {
|
||||
})
|
||||
const coords: { x: number; y: number }[] =
|
||||
curve.data.data.control_points
|
||||
|
||||
// We need the normal for the plane we are on.
|
||||
const plane = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'get_sketch_mode_plane',
|
||||
},
|
||||
})
|
||||
const z_axis = plane.data.data.z_axis
|
||||
|
||||
// Get the current axis.
|
||||
let currentAxis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz' | null =
|
||||
null
|
||||
if (currentPlane === defaultPlanes?.xy) {
|
||||
if (z_axis.z === -1) {
|
||||
currentAxis = '-xy'
|
||||
} else {
|
||||
currentAxis = 'xy'
|
||||
}
|
||||
} else if (currentPlane === defaultPlanes?.yz) {
|
||||
if (z_axis.x === -1) {
|
||||
currentAxis = '-yz'
|
||||
} else {
|
||||
currentAxis = 'yz'
|
||||
}
|
||||
} else if (currentPlane === defaultPlanes?.xz) {
|
||||
if (z_axis.y === -1) {
|
||||
currentAxis = '-xz'
|
||||
} else {
|
||||
currentAxis = 'xz'
|
||||
}
|
||||
}
|
||||
|
||||
// Do not support starting a new sketch on a non-default plane.
|
||||
if (!currentAxis) return
|
||||
|
||||
const _addStartSketch = addStartSketch(
|
||||
ast,
|
||||
currentAxis,
|
||||
[roundOff(coords[0].x), roundOff(coords[0].y)],
|
||||
[
|
||||
roundOff(coords[1].x - coords[0].x),
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { fireEvent, render, screen } from '@testing-library/react'
|
||||
import UserSidebarMenu from './UserSidebarMenu'
|
||||
import { BrowserRouter } from 'react-router-dom'
|
||||
import {
|
||||
Route,
|
||||
RouterProvider,
|
||||
createMemoryRouter,
|
||||
createRoutesFromElements,
|
||||
} from 'react-router-dom'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { GlobalStateProvider } from './GlobalStateProvider'
|
||||
import CommandBarProvider from './CommandBar'
|
||||
@ -93,11 +98,24 @@ describe('UserSidebarMenu tests', () => {
|
||||
|
||||
function TestWrap({ children }: { children: React.ReactNode }) {
|
||||
// wrap in router and xState context
|
||||
return (
|
||||
<BrowserRouter>
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
</BrowserRouter>
|
||||
// We have to use a memory router in the testing environment,
|
||||
// and we have to use the createMemoryRouter function instead of <MemoryRouter /> as of react-router v6.4:
|
||||
// https://reactrouter.com/en/6.16.0/routers/picking-a-router#using-v64-data-apis
|
||||
const router = createMemoryRouter(
|
||||
createRoutesFromElements(
|
||||
<Route
|
||||
path="/file/:id"
|
||||
element={
|
||||
<CommandBarProvider>
|
||||
<GlobalStateProvider>{children}</GlobalStateProvider>
|
||||
</CommandBarProvider>
|
||||
}
|
||||
/>
|
||||
),
|
||||
{
|
||||
initialEntries: ['/file/new'],
|
||||
initialIndex: 0,
|
||||
}
|
||||
)
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
@ -10,14 +10,15 @@ import { faGithub } from '@fortawesome/free-brands-svg-icons'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { paths } from '../Router'
|
||||
import makeUrlPathRelative from '../lib/makeUrlPathRelative'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
|
||||
type User = Models['User_type']
|
||||
|
||||
const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
const location = useLocation()
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const displayedName = getDisplayName(user)
|
||||
const [imageLoadFailed, setImageLoadFailed] = useState(false)
|
||||
const navigate = useNavigate()
|
||||
@ -132,11 +133,10 @@ const UserSidebarMenu = ({ user }: { user?: User }) => {
|
||||
// since /settings is a nested route the sidebar doesn't close
|
||||
// automatically when navigating to it
|
||||
close()
|
||||
navigate(
|
||||
(location.pathname.endsWith('/')
|
||||
? location.pathname.slice(0, -1)
|
||||
: location.pathname) + paths.SETTINGS
|
||||
)
|
||||
const targetPath = location.pathname.includes(paths.FILE)
|
||||
? filePath + paths.SETTINGS
|
||||
: paths.HOME + paths.SETTINGS
|
||||
navigate(targetPath)
|
||||
}}
|
||||
>
|
||||
Settings
|
||||
|
12
src/hooks/useAbsoluteFilePath.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { BROWSER_FILE_NAME, IndexLoaderData, paths } from 'Router'
|
||||
import { useRouteLoaderData } from 'react-router-dom'
|
||||
|
||||
export function useAbsoluteFilePath() {
|
||||
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
|
||||
return (
|
||||
paths.FILE +
|
||||
'/' +
|
||||
encodeURIComponent(routeData?.project?.path || BROWSER_FILE_NAME)
|
||||
)
|
||||
}
|
@ -2,30 +2,37 @@
|
||||
// Once we have xState this should be removed
|
||||
|
||||
import { useStore, Selections } from 'useStore'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import { ArtifactMap, EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { Models } from '@kittycad/lib/dist/types/src'
|
||||
import { isReducedMotion } from 'lang/util'
|
||||
import { isOverlap } from 'lib/utils'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
|
||||
interface DefaultPlanes {
|
||||
xy: string
|
||||
// TODO re-enable
|
||||
// yz: string
|
||||
// xz: string
|
||||
}
|
||||
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { getNodeFromPath } from '../lang/queryAst'
|
||||
import { CallExpression, PipeExpression } from '../lang/wasm'
|
||||
|
||||
export function useAppMode() {
|
||||
const { guiMode, setGuiMode, selectionRanges, selectionRangeTypeMap } =
|
||||
useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
selectionRanges: s.selectionRanges,
|
||||
selectionRangeTypeMap: s.selectionRangeTypeMap,
|
||||
}))
|
||||
const [defaultPlanes, setDefaultPlanes] = useState<DefaultPlanes | null>(null)
|
||||
const {
|
||||
guiMode,
|
||||
setGuiMode,
|
||||
selectionRanges,
|
||||
selectionRangeTypeMap,
|
||||
defaultPlanes,
|
||||
setDefaultPlanes,
|
||||
setCurrentPlane,
|
||||
ast,
|
||||
} = useStore((s) => ({
|
||||
guiMode: s.guiMode,
|
||||
setGuiMode: s.setGuiMode,
|
||||
selectionRanges: s.selectionRanges,
|
||||
selectionRangeTypeMap: s.selectionRangeTypeMap,
|
||||
defaultPlanes: s.defaultPlanes,
|
||||
setDefaultPlanes: s.setDefaultPlanes,
|
||||
setCurrentPlane: s.setCurrentPlane,
|
||||
ast: s.ast,
|
||||
}))
|
||||
useEffect(() => {
|
||||
if (
|
||||
guiMode.mode === 'sketch' &&
|
||||
@ -35,8 +42,10 @@ export function useAppMode() {
|
||||
const createAndShowPlanes = async () => {
|
||||
let localDefaultPlanes: DefaultPlanes
|
||||
if (!defaultPlanes) {
|
||||
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
setDefaultPlanes(localDefaultPlanes)
|
||||
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
if (!newDefaultPlanes) return
|
||||
setDefaultPlanes(newDefaultPlanes)
|
||||
localDefaultPlanes = newDefaultPlanes
|
||||
} else {
|
||||
localDefaultPlanes = defaultPlanes
|
||||
}
|
||||
@ -52,20 +61,52 @@ export function useAppMode() {
|
||||
const enableSketchMode = async () => {
|
||||
let localDefaultPlanes: DefaultPlanes
|
||||
if (!defaultPlanes) {
|
||||
localDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
setDefaultPlanes(localDefaultPlanes)
|
||||
const newDefaultPlanes = await initDefaultPlanes(engineCommandManager)
|
||||
if (!newDefaultPlanes) return
|
||||
setDefaultPlanes(newDefaultPlanes)
|
||||
localDefaultPlanes = newDefaultPlanes
|
||||
} else {
|
||||
localDefaultPlanes = defaultPlanes
|
||||
}
|
||||
setDefaultPlanesHidden(engineCommandManager, localDefaultPlanes, true)
|
||||
// TODO figure out the plane to use based on the sketch
|
||||
// maybe it's easier to make a new plane than rely on the defaults
|
||||
|
||||
const pipeExpression = getNodeFromPath<PipeExpression>(
|
||||
ast,
|
||||
guiMode.pathToNode,
|
||||
'PipeExpression'
|
||||
).node
|
||||
if (pipeExpression.type !== 'PipeExpression') return /// bad bad bad
|
||||
const sketchCallExpression = pipeExpression.body.find(
|
||||
(e) =>
|
||||
e.type === 'CallExpression' && e.callee.name === 'startSketchOn'
|
||||
) as CallExpression
|
||||
if (!sketchCallExpression) return // also bad bad bad
|
||||
const firstArg = sketchCallExpression.arguments[0]
|
||||
let planeId = ''
|
||||
if (firstArg.type === 'Literal' && firstArg.value) {
|
||||
const planeStrCleaned = firstArg.value
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.replace('-', '')
|
||||
if (
|
||||
planeStrCleaned === 'xy' ||
|
||||
planeStrCleaned === 'xz' ||
|
||||
planeStrCleaned === 'yz'
|
||||
) {
|
||||
planeId = localDefaultPlanes[planeStrCleaned]
|
||||
}
|
||||
}
|
||||
|
||||
if (!planeId) return // they are on some non default plane, which we don't support yet
|
||||
|
||||
setCurrentPlane(planeId)
|
||||
|
||||
await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'sketch_mode_enable',
|
||||
plane_id: localDefaultPlanes.xy,
|
||||
plane_id: planeId,
|
||||
ortho: true,
|
||||
animated: !isReducedMotion(),
|
||||
},
|
||||
@ -139,6 +180,7 @@ export function useAppMode() {
|
||||
// user clicked something else in the scene
|
||||
return
|
||||
}
|
||||
setCurrentPlane(data.entity_id)
|
||||
const sketchModeResponse = await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
@ -194,10 +236,12 @@ async function createPlane(
|
||||
x_axis,
|
||||
y_axis,
|
||||
color,
|
||||
hidden,
|
||||
}: {
|
||||
x_axis: Models['Point3d_type']
|
||||
y_axis: Models['Point3d_type']
|
||||
color: Models['Color_type']
|
||||
hidden: boolean
|
||||
}
|
||||
) {
|
||||
const planeId = uuidv4()
|
||||
@ -210,6 +254,7 @@ async function createPlane(
|
||||
x_axis,
|
||||
y_axis,
|
||||
clobber: false,
|
||||
hide: hidden,
|
||||
},
|
||||
cmd_id: planeId,
|
||||
})
|
||||
@ -225,61 +270,82 @@ async function createPlane(
|
||||
return planeId
|
||||
}
|
||||
|
||||
function setDefaultPlanesHidden(
|
||||
export function setDefaultPlanesHidden(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
defaultPlanes: DefaultPlanes,
|
||||
hidden: boolean
|
||||
) {
|
||||
Object.values(defaultPlanes).forEach((planeId) => {
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'object_visible',
|
||||
object_id: planeId,
|
||||
hidden: hidden,
|
||||
},
|
||||
})
|
||||
hidePlane(engineCommandManager, planeId, hidden)
|
||||
})
|
||||
}
|
||||
|
||||
async function initDefaultPlanes(
|
||||
engineCommandManager: EngineCommandManager
|
||||
): Promise<DefaultPlanes> {
|
||||
function hidePlane(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planeId: string,
|
||||
hidden: boolean
|
||||
) {
|
||||
engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
type: 'object_visible',
|
||||
object_id: planeId,
|
||||
hidden: hidden,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function initDefaultPlanes(
|
||||
engineCommandManager: EngineCommandManager,
|
||||
hidePlanes?: boolean
|
||||
): Promise<DefaultPlanes | null> {
|
||||
if (!engineCommandManager.engineConnection?.isReady()) {
|
||||
return null
|
||||
}
|
||||
const xy = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 1, y: 0, z: 0 },
|
||||
y_axis: { x: 0, y: 1, z: 0 },
|
||||
color: { r: 0.7, g: 0.28, b: 0.28, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
// TODO re-enable
|
||||
// const yz = createPlane(engineCommandManager, {
|
||||
// x_axis: { x: 0, y: 1, z: 0 },
|
||||
// y_axis: { x: 0, y: 0, z: 1 },
|
||||
// color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
||||
// })
|
||||
// const xz = createPlane(engineCommandManager, {
|
||||
// x_axis: { x: 1, y: 0, z: 0 },
|
||||
// y_axis: { x: 0, y: 0, z: 1 },
|
||||
// color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
||||
// })
|
||||
return { xy }
|
||||
if (hidePlanes) {
|
||||
hidePlane(engineCommandManager, xy, true)
|
||||
}
|
||||
const yz = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 0, y: 1, z: 0 },
|
||||
y_axis: { x: 0, y: 0, z: 1 },
|
||||
color: { r: 0.28, g: 0.7, b: 0.28, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
if (hidePlanes) {
|
||||
hidePlane(engineCommandManager, yz, true)
|
||||
}
|
||||
const xz = await createPlane(engineCommandManager, {
|
||||
x_axis: { x: 1, y: 0, z: 0 },
|
||||
y_axis: { x: 0, y: 0, z: 1 },
|
||||
color: { r: 0.28, g: 0.28, b: 0.7, a: 0.4 },
|
||||
hidden: hidePlanes ? true : false,
|
||||
})
|
||||
return { xy, yz, xz }
|
||||
}
|
||||
|
||||
function isCursorInSketchCommandRange(
|
||||
artifactMap: ArtifactMap,
|
||||
selectionRanges: Selections
|
||||
): string | false {
|
||||
const overlapingEntries = Object.entries(artifactMap || {}).filter(
|
||||
([id, artifact]) =>
|
||||
selectionRanges.codeBasedSelections.some(
|
||||
(selection) =>
|
||||
Array.isArray(selection?.range) &&
|
||||
Array.isArray(artifact?.range) &&
|
||||
isOverlap(selection.range, artifact.range) &&
|
||||
(artifact.commandType === 'start_path' ||
|
||||
artifact.commandType === 'extend_path' ||
|
||||
artifact.commandType === 'close_path')
|
||||
)
|
||||
const overlapingEntries: [string, ArtifactMap[string]][] = Object.entries(
|
||||
artifactMap
|
||||
).filter(([id, artifact]: [string, ArtifactMap[string]]) =>
|
||||
selectionRanges.codeBasedSelections.some(
|
||||
(selection) =>
|
||||
Array.isArray(selection?.range) &&
|
||||
Array.isArray(artifact?.range) &&
|
||||
isOverlap(selection.range, artifact.range) &&
|
||||
(artifact.commandType === 'start_path' ||
|
||||
artifact.commandType === 'extend_path' ||
|
||||
artifact.commandType === 'close_path')
|
||||
)
|
||||
)
|
||||
return overlapingEntries.length && overlapingEntries[0][1].parentId
|
||||
? overlapingEntries[0][1].parentId
|
||||
|
@ -3,7 +3,6 @@ import { _executor } from '../lang/wasm'
|
||||
import { useStore } from '../useStore'
|
||||
import { engineCommandManager } from '../lang/std/engineConnection'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export function useSetupEngineManager(
|
||||
streamRef: React.RefObject<HTMLDivElement>,
|
||||
|
@ -7,7 +7,8 @@ describe('testing artifacts', () => {
|
||||
// Enable rotations #152
|
||||
test('sketch artifacts', async () => {
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
@ -27,7 +28,7 @@ show(mySketch001)`
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [21, 42],
|
||||
sourceRange: [46, 71],
|
||||
},
|
||||
},
|
||||
value: [
|
||||
@ -37,7 +38,7 @@ show(mySketch001)`
|
||||
to: [-1.59, -1.54],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [48, 73],
|
||||
sourceRange: [77, 102],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -47,7 +48,7 @@ show(mySketch001)`
|
||||
from: [-1.59, -1.54],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [79, 103],
|
||||
sourceRange: [108, 132],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -55,14 +56,16 @@ show(mySketch001)`
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [21, 42] }],
|
||||
planeId: expect.any(String),
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
test('extrude artifacts', async () => {
|
||||
// Enable rotations #152
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
@ -82,7 +85,7 @@ show(mySketch001)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [21, 42] }],
|
||||
__meta: [{ sourceRange: [46, 71] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
@ -90,7 +93,8 @@ show(mySketch001)`
|
||||
// Enable rotations #152
|
||||
// TODO #153 in order for getExtrudeWallTransform to work we need to query the engine for the location of a face.
|
||||
const code = `
|
||||
const sk1 = startSketchAt([0, 0])
|
||||
const sk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.5, 0], %)
|
||||
|> lineTo({ to: [0, 10], tag: "p" }, %)
|
||||
|> lineTo([2.5, 0], %)
|
||||
@ -99,7 +103,8 @@ const sk1 = startSketchAt([0, 0])
|
||||
// |> ry(5, %)
|
||||
const theExtrude = extrude(2, sk1)
|
||||
// const theTransf = getExtrudeWallTransform('p', theExtrude)
|
||||
const sk2 = startSketchAt([0, 0])
|
||||
const sk2 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([-2.5, 0], %)
|
||||
|> lineTo({ to: [0, 3], tag: "p" }, %)
|
||||
|> lineTo([2.5, 0], %)
|
||||
@ -122,7 +127,7 @@ show(theExtrude, sk2)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [13, 34] }],
|
||||
__meta: [{ sourceRange: [38, 63] }],
|
||||
},
|
||||
{
|
||||
type: 'ExtrudeGroup',
|
||||
@ -131,7 +136,7 @@ show(theExtrude, sk2)`
|
||||
height: 2,
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
__meta: [{ sourceRange: [302, 323] }],
|
||||
__meta: [{ sourceRange: [356, 381] }],
|
||||
},
|
||||
])
|
||||
})
|
||||
|
@ -41,7 +41,8 @@ const newVar = myVar + 1`
|
||||
expect(root.magicNum.value).toBe(69)
|
||||
})
|
||||
it('sketch declaration', async () => {
|
||||
let code = `const mySketch = startSketchAt([0,0])
|
||||
let code = `const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({to: [0,2], tag: "myPath"}, %)
|
||||
|> lineTo([2,3], %)
|
||||
|> lineTo({ to: [5,-1], tag: "rightPath" }, %)
|
||||
@ -57,7 +58,7 @@ show(mySketch)
|
||||
to: [0, 2],
|
||||
from: [0, 0],
|
||||
__geoMeta: {
|
||||
sourceRange: [43, 80],
|
||||
sourceRange: [72, 109],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'myPath',
|
||||
@ -68,7 +69,7 @@ show(mySketch)
|
||||
from: [0, 2],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [86, 102],
|
||||
sourceRange: [115, 131],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -77,7 +78,7 @@ show(mySketch)
|
||||
to: [5, -1],
|
||||
from: [2, 3],
|
||||
__geoMeta: {
|
||||
sourceRange: [108, 151],
|
||||
sourceRange: [137, 180],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'rightPath',
|
||||
@ -87,8 +88,8 @@ show(mySketch)
|
||||
expect(_return).toEqual([
|
||||
{
|
||||
type: 'Identifier',
|
||||
start: 174,
|
||||
end: 182,
|
||||
start: 203,
|
||||
end: 211,
|
||||
name: 'mySketch',
|
||||
},
|
||||
])
|
||||
@ -132,7 +133,8 @@ show(mySketch)
|
||||
it('execute pipe sketch into call expression', async () => {
|
||||
// Enable rotations #152
|
||||
const code = [
|
||||
'const mySk1 = startSketchAt([0,0])',
|
||||
"const mySk1 = startSketchOn('XY')",
|
||||
' |> startProfileAt([0,0], %)',
|
||||
' |> lineTo([1,1], %)',
|
||||
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
||||
' |> lineTo([1,1], %)',
|
||||
@ -147,7 +149,7 @@ show(mySketch)
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
id: expect.any(String),
|
||||
sourceRange: [14, 34],
|
||||
sourceRange: [39, 63],
|
||||
},
|
||||
},
|
||||
value: [
|
||||
@ -157,7 +159,7 @@ show(mySketch)
|
||||
from: [0, 0],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [40, 56],
|
||||
sourceRange: [69, 85],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -166,7 +168,7 @@ show(mySketch)
|
||||
to: [0, 1],
|
||||
from: [1, 1],
|
||||
__geoMeta: {
|
||||
sourceRange: [62, 100],
|
||||
sourceRange: [91, 129],
|
||||
id: expect.any(String),
|
||||
},
|
||||
name: 'myPath',
|
||||
@ -177,7 +179,7 @@ show(mySketch)
|
||||
from: [0, 1],
|
||||
name: '',
|
||||
__geoMeta: {
|
||||
sourceRange: [106, 122],
|
||||
sourceRange: [135, 151],
|
||||
id: expect.any(String),
|
||||
},
|
||||
},
|
||||
@ -185,7 +187,8 @@ show(mySketch)
|
||||
position: [0, 0, 0],
|
||||
rotation: [0, 0, 0, 1],
|
||||
id: expect.any(String),
|
||||
__meta: [{ sourceRange: [14, 34] }],
|
||||
planeId: expect.any(String),
|
||||
__meta: [{ sourceRange: [39, 63] }],
|
||||
})
|
||||
})
|
||||
it('execute array expression', async () => {
|
||||
@ -329,7 +332,8 @@ describe('testing math operators', () => {
|
||||
})
|
||||
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
|
||||
const code = [
|
||||
'const part001 = startSketchAt([0, 0])',
|
||||
"const part001 = startSketchOn('XY')",
|
||||
' |> startProfileAt([0, 0], %)',
|
||||
'|> line([-2.21, -legLen(5, min(3, 999))], %)',
|
||||
].join('\n')
|
||||
const { root } = await exe(code)
|
||||
@ -341,7 +345,8 @@ describe('testing math operators', () => {
|
||||
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
|
||||
const code = [
|
||||
`const myVar = 3`,
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
` |> line({ to: [3, 4], tag: 'seg01' }, %)`,
|
||||
` |> line([`,
|
||||
` min(segLen('seg01', %), myVar),`,
|
||||
@ -377,7 +382,8 @@ describe('testing math operators', () => {
|
||||
describe('Testing Errors', () => {
|
||||
it('should throw an error when a variable is not defined', async () => {
|
||||
const code = `const myVar = 5
|
||||
const theExtrude = startSketchAt([0, 0])
|
||||
const theExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([-2.4, 5], %)
|
||||
|> line([-0.76], myVarZ, %)
|
||||
|> line([5,5], %)
|
||||
@ -388,7 +394,7 @@ show(theExtrude)`
|
||||
new KCLError(
|
||||
'undefined_value',
|
||||
'memory item key `myVarZ` is not defined',
|
||||
[[100, 106]]
|
||||
[[129, 135]]
|
||||
)
|
||||
)
|
||||
})
|
||||
|
@ -109,8 +109,8 @@ describe('Testing addSketchTo', () => {
|
||||
'yz'
|
||||
)
|
||||
const str = recast(result.modifiedAst)
|
||||
expect(str).toBe(`const part001 = startSketchAt('default')
|
||||
|> ry(90, %)
|
||||
expect(str).toBe(`const part001 = startSketchOn('YZ')
|
||||
|> startProfileAt('default', %)
|
||||
|> line('default', %)
|
||||
show(part001)
|
||||
`)
|
||||
@ -133,7 +133,8 @@ function giveSketchFnCallTagTestHelper(
|
||||
}
|
||||
|
||||
describe('Testing giveSketchFnCallTag', () => {
|
||||
const code = `const part001 = startSketchAt([0, 0])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([-2.57, -0.13], %)
|
||||
|> line([0, 0.83], %)
|
||||
|> line([0.82, 0.34], %)
|
||||
@ -185,7 +186,8 @@ fn ghi = (x) => {
|
||||
const abc = 3
|
||||
const identifierGuy = 5
|
||||
const yo = 5 + 6
|
||||
const part001 = startSketchAt([-1.2, 4.83])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.09], %)
|
||||
|> angledLine([abc, 3.09], %)
|
||||
|
@ -32,21 +32,26 @@ import { isLiteralArrayOrStatic } from './std/sketchcombos'
|
||||
|
||||
export function addStartSketch(
|
||||
node: Program,
|
||||
axis: 'xy' | 'xz' | 'yz' | '-xy' | '-xz' | '-yz',
|
||||
start: [number, number],
|
||||
end: [number, number]
|
||||
): { modifiedAst: Program; id: string; pathToNode: PathToNode } {
|
||||
const _node = { ...node }
|
||||
const _name = findUniqueName(node, 'part')
|
||||
|
||||
const startSketchAt = createCallExpression('startSketchAt', [
|
||||
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
||||
createLiteral(axis.toUpperCase()),
|
||||
])
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
createArrayExpression([createLiteral(start[0]), createLiteral(start[1])]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
const initialLineTo = createCallExpression('line', [
|
||||
createArrayExpression([createLiteral(end[0]), createLiteral(end[1])]),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
const pipeBody = [startSketchAt, initialLineTo]
|
||||
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
|
||||
|
||||
const variableDeclaration = createVariableDeclaration(
|
||||
_name,
|
||||
@ -79,11 +84,11 @@ export function addSketchTo(
|
||||
const _node = { ...node }
|
||||
const _name = name || findUniqueName(node, 'part')
|
||||
|
||||
const startSketchAt = createCallExpressionStdLib('startSketchAt', [
|
||||
createLiteral('default'),
|
||||
const startSketchOn = createCallExpressionStdLib('startSketchOn', [
|
||||
createLiteral(axis.toUpperCase()),
|
||||
])
|
||||
const rotate = createCallExpression(axis === 'xz' ? 'rx' : 'ry', [
|
||||
createLiteral(90),
|
||||
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||
createLiteral('default'),
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
const initialLineTo = createCallExpressionStdLib('line', [
|
||||
@ -91,10 +96,7 @@ export function addSketchTo(
|
||||
createPipeSubstitution(),
|
||||
])
|
||||
|
||||
const pipeBody =
|
||||
axis !== 'xy'
|
||||
? [startSketchAt, rotate, initialLineTo]
|
||||
: [startSketchAt, initialLineTo]
|
||||
const pipeBody = [startSketchOn, startProfileAt, initialLineTo]
|
||||
|
||||
const variableDeclaration = createVariableDeclaration(
|
||||
_name,
|
||||
|
@ -26,7 +26,8 @@ const halfArmAngle = armAngle / 2
|
||||
const arrExpShouldNotBeIncluded = [1, 2, 3]
|
||||
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
|
||||
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLineTo(1, %)
|
||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||
|
||||
@ -57,7 +58,8 @@ show(part001)`
|
||||
})
|
||||
|
||||
describe('testing argIsNotIdentifier', () => {
|
||||
const code = `const part001 = startSketchAt([-1.2, 4.83])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.09], %)
|
||||
|> angledLine([abc, 3.09], %)
|
||||
@ -194,7 +196,8 @@ show(part001)`
|
||||
})
|
||||
|
||||
describe('testing getNodePathFromSourceRange', () => {
|
||||
const code = `const part001 = startSketchAt([0.39, -0.05])
|
||||
const code = `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.39, -0.05], %)
|
||||
|> line([0.94, 2.61], %)
|
||||
|> line([-0.21, -1.4], %)
|
||||
show(part001)`
|
||||
@ -210,7 +213,7 @@ show(part001)`
|
||||
[0, 'index'],
|
||||
['init', ''],
|
||||
['body', 'PipeExpression'],
|
||||
[1, 'index'],
|
||||
[2, 'index'],
|
||||
])
|
||||
})
|
||||
it('finds the last line when cursor is put at the end', () => {
|
||||
@ -225,7 +228,7 @@ show(part001)`
|
||||
[0, 'index'],
|
||||
['init', ''],
|
||||
['body', 'PipeExpression'],
|
||||
[2, 'index'],
|
||||
[3, 'index'],
|
||||
]
|
||||
expect(result).toEqual(expected)
|
||||
// expect similar result for start of line
|
||||
|
@ -725,7 +725,11 @@ export class EngineCommandManager {
|
||||
message.request_id
|
||||
) {
|
||||
this.handleModelingCommand(message.resp, message.request_id)
|
||||
} else if (!message.success && message.request_id) {
|
||||
} else if (
|
||||
!message.success &&
|
||||
message.request_id &&
|
||||
this.artifactMap[message.request_id]
|
||||
) {
|
||||
this.handleFailedModelingCommand(message)
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,8 @@ describe('testing changeSketchArguments', () => {
|
||||
const lineAfterChange = 'lineTo([2, 3], %)'
|
||||
test('changeSketchArguments', async () => {
|
||||
// Enable rotations #152
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> ${line}
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
// |> rx(45, %)
|
||||
@ -137,7 +138,8 @@ describe('testing addNewSketchLn', () => {
|
||||
test('addNewSketchLn', async () => {
|
||||
// Enable rotations #152
|
||||
const code = `
|
||||
const mySketch001 = startSketchAt([0, 0])
|
||||
const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
@ -145,7 +147,7 @@ show(mySketch001)`
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
expect(sourceStart).toBe(66)
|
||||
expect(sourceStart).toBe(95)
|
||||
let { modifiedAst } = addNewSketchLn({
|
||||
node: ast,
|
||||
programMemory,
|
||||
@ -160,7 +162,8 @@ show(mySketch001)`
|
||||
],
|
||||
})
|
||||
// Enable rotations #152
|
||||
let expectedCode = `const mySketch001 = startSketchAt([0, 0])
|
||||
let expectedCode = `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
@ -181,7 +184,8 @@ show(mySketch001)
|
||||
],
|
||||
})
|
||||
|
||||
expectedCode = `const mySketch001 = startSketchAt([0, 0])
|
||||
expectedCode = `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> lineTo([-1.59, -1.54], %)
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
@ -196,7 +200,8 @@ describe('testing addTagForSketchOnFace', () => {
|
||||
it('needs to be in it', async () => {
|
||||
const originalLine = 'lineTo([-1.59, -1.54], %)'
|
||||
// Enable rotations #152
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchAt([0, 0])
|
||||
const genCode = (line: string) => `const mySketch001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
// |> rx(45, %)
|
||||
|> ${line}
|
||||
|> lineTo([0.46, -5.82], %)
|
||||
|
@ -52,7 +52,8 @@ async function testingSwapSketchFnCall({
|
||||
|
||||
describe('testing swaping out sketch calls with xLine/xLineTo', () => {
|
||||
const bigExampleArr = [
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
` |> lineTo({ to: [1, 1], tag: 'abc1' }, %)`,
|
||||
` |> line({ to: [-2.04, -0.7], tag: 'abc2' }, %)`,
|
||||
` |> angledLine({`,
|
||||
@ -277,7 +278,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
|
||||
`const angledLineOfYLengthY = 0.89`,
|
||||
`const angledLineToXx = -1.86`,
|
||||
`const angledLineToYy = -0.76`,
|
||||
`const part001 = startSketchAt([0, 0])`,
|
||||
`const part001 = startSketchOn('XY')`,
|
||||
` |> startProfileAt([0, 0], %)`,
|
||||
// ` |> rx(90, %)`,
|
||||
` |> lineTo([1, 1], %)`,
|
||||
` |> line([lineX, 2.13], %)`,
|
||||
@ -371,7 +373,8 @@ describe('testing swaping out sketch calls with xLine/xLineTo while keeping vari
|
||||
|
||||
describe('testing getSketchSegmentIndexFromSourceRange', () => {
|
||||
const code = `
|
||||
const part001 = startSketchAt([0, 0.04]) // segment-in-start
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0.04], %) // segment-in-start
|
||||
|> line([0, 0.4], %)
|
||||
|> xLine(3.48, %)
|
||||
|> line([2.14, 1.35], %) // normal-segment
|
||||
|
@ -92,7 +92,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([1, 3.82], %) // ln-should-get-tag
|
||||
|> lineTo([myVar, 1], %) // ln-lineTo-xAbsolute should use angleToMatchLengthX helper
|
||||
|> lineTo([1, myVar], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
|
||||
@ -128,7 +129,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -231,7 +233,8 @@ describe('testing transformAstForSketchLines for vertical and horizontal constra
|
||||
const inputScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
||||
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
||||
@ -259,7 +262,8 @@ show(part001)
|
||||
const expectModifiedScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> xLine(-6.28, %) // select for horizontal constraint 1
|
||||
|> line([-1.07, myVar], %) // select for vertical constraint 1
|
||||
@ -317,7 +321,8 @@ show(part001)
|
||||
const expectModifiedScript = `const myVar = 2
|
||||
const myVar2 = 12
|
||||
const myVar3 = -10
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
|> line([-6.28, 1.4], %) // select for horizontal constraint 1
|
||||
|> yLine(myVar, %) // select for vertical constraint 1
|
||||
@ -376,7 +381,8 @@ show(part001)
|
||||
describe('testing transformAstForSketchLines for vertical and horizontal distance constraints', () => {
|
||||
describe('testing setHorzDistance for line', () => {
|
||||
const inputScript = `const myVar = 1
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0.31, 1.67], %) // base selection
|
||||
|> line([0.45, 1.46], %)
|
||||
|> line([0.45, 1.46], %) // free
|
||||
@ -477,7 +483,8 @@ const baseThickHalf = baseThick / 2
|
||||
const halfHeight = totalHeight / 2
|
||||
const halfArmAngle = armAngle / 2
|
||||
|
||||
const part001 = startSketchAt([-0.01, -0.05])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-0.01, -0.05], %)
|
||||
|> line([0.01, 0.94 + 0], %) // partial
|
||||
|> xLine(3.03, %) // partial
|
||||
|> angledLine({
|
||||
|
@ -5,7 +5,8 @@ beforeAll(() => initPromise)
|
||||
|
||||
describe('testing angledLineThatIntersects', () => {
|
||||
it('angledLineThatIntersects should intersect with another line', async () => {
|
||||
const code = (offset: string) => `const part001 = startSketchAt([0, 0])
|
||||
const code = (offset: string) => `const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo({to:[2, 2], tag: "yo"}, %)
|
||||
|> lineTo([3, 1], %)
|
||||
|> angledLineThatIntersects({
|
||||
|
@ -16,6 +16,7 @@ import { ProgramReturn } from '../wasm-lib/kcl/bindings/ProgramReturn'
|
||||
import { MemoryItem } from '../wasm-lib/kcl/bindings/MemoryItem'
|
||||
import type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
import type { Token } from '../wasm-lib/kcl/bindings/Token'
|
||||
import { DefaultPlanes } from '../wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Value } from '../wasm-lib/kcl/bindings/Value'
|
||||
@ -118,6 +119,7 @@ export const executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planes: DefaultPlanes,
|
||||
// work around while the gemotry is still be stored on the frontend
|
||||
// will be removed when the stream UI is added.
|
||||
tempMapCallback: (a: {
|
||||
@ -129,7 +131,8 @@ export const executor = async (
|
||||
const _programMemory = await _executor(
|
||||
node,
|
||||
programMemory,
|
||||
engineCommandManager
|
||||
engineCommandManager,
|
||||
planes
|
||||
)
|
||||
const { artifactMap, sourceRangeMap } =
|
||||
await engineCommandManager.waitForAllCommands(node, _programMemory)
|
||||
@ -142,13 +145,15 @@ export const executor = async (
|
||||
export const _executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory = { root: {}, return: null },
|
||||
engineCommandManager: EngineCommandManager
|
||||
engineCommandManager: EngineCommandManager,
|
||||
planes: DefaultPlanes
|
||||
): Promise<ProgramMemory> => {
|
||||
try {
|
||||
const memory: ProgramMemory = await execute_wasm(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemory),
|
||||
engineCommandManager
|
||||
engineCommandManager,
|
||||
JSON.stringify(planes)
|
||||
)
|
||||
return memory
|
||||
} catch (e: any) {
|
||||
@ -190,6 +195,7 @@ export const modifyAstForSketch = async (
|
||||
engineCommandManager: EngineCommandManager,
|
||||
ast: Program,
|
||||
variableName: string,
|
||||
currentPlane: string,
|
||||
engineId: string
|
||||
): Promise<Program> => {
|
||||
try {
|
||||
@ -197,6 +203,7 @@ export const modifyAstForSketch = async (
|
||||
engineCommandManager,
|
||||
JSON.stringify(ast),
|
||||
variableName,
|
||||
JSON.stringify(currentPlane),
|
||||
engineId
|
||||
)
|
||||
|
||||
|
@ -8,7 +8,8 @@ const filletR = thickness * 2
|
||||
const shelfMountL = 9
|
||||
const wallMountL = 8
|
||||
|
||||
const bracket = startSketchAt([0, 0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, wallMountL], %)
|
||||
|> tangentalArc({
|
||||
radius: filletR,
|
||||
|
@ -4,6 +4,7 @@ import {
|
||||
EngineCommand,
|
||||
} from '../lang/std/engineConnection'
|
||||
import { Models } from '@kittycad/lib'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
type WebSocketResponse = Models['OkWebSocketResponseData_type']
|
||||
|
||||
@ -64,7 +65,11 @@ export async function enginelessExecutor(
|
||||
}) as any as EngineCommandManager
|
||||
await mockEngineCommandManager.waitForReady
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager)
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, {
|
||||
xy: uuidv4(),
|
||||
yz: uuidv4(),
|
||||
xz: uuidv4(),
|
||||
})
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
return programMemory
|
||||
}
|
||||
@ -83,7 +88,11 @@ export async function executor(
|
||||
})
|
||||
await engineCommandManager.waitForReady
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, engineCommandManager)
|
||||
const programMemory = await _executor(ast, pm, engineCommandManager, {
|
||||
xy: uuidv4(),
|
||||
yz: uuidv4(),
|
||||
xz: uuidv4(),
|
||||
})
|
||||
await engineCommandManager.waitForAllCommands(ast, programMemory)
|
||||
return programMemory
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
import { faArrowRight, faXmark } from '@fortawesome/free-solid-svg-icons'
|
||||
import { ActionButton } from '../../components/ActionButton'
|
||||
import { onboardingPaths, useDismiss, useNextClick } from '.'
|
||||
import {
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
onboardingPaths,
|
||||
useDismiss,
|
||||
useNextClick,
|
||||
} from '.'
|
||||
import { useGlobalStateContext } from 'hooks/useGlobalStateContext'
|
||||
import { Themes, getSystemTheme } from 'lib/theme'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
@ -25,14 +30,20 @@ function OnboardingWithNewFile() {
|
||||
}))
|
||||
const {
|
||||
settings: {
|
||||
context: { defaultDirectory, defaultProjectName },
|
||||
context: { defaultDirectory },
|
||||
},
|
||||
} = useGlobalStateContext()
|
||||
|
||||
async function createAndOpenNewProject() {
|
||||
const projects = await getProjectsInDir(defaultDirectory)
|
||||
const nextIndex = await getNextProjectIndex(defaultProjectName, projects)
|
||||
const name = interpolateProjectNameWithIndex(defaultProjectName, nextIndex)
|
||||
const nextIndex = await getNextProjectIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
projects
|
||||
)
|
||||
const name = interpolateProjectNameWithIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
nextIndex
|
||||
)
|
||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useHotkeys } from 'react-hotkeys-hook'
|
||||
import { Outlet, useRouteLoaderData, useNavigate } from 'react-router-dom'
|
||||
import { Outlet, useNavigate } from 'react-router-dom'
|
||||
import Introduction from './Introduction'
|
||||
import Camera from './Camera'
|
||||
import Sketching from './Sketching'
|
||||
@ -15,7 +15,10 @@ import UserMenu from './UserMenu'
|
||||
import ProjectMenu from './ProjectMenu'
|
||||
import Export from './Export'
|
||||
import FutureWork from './FutureWork'
|
||||
import { IndexLoaderData, paths } from 'Router'
|
||||
import { paths } from 'Router'
|
||||
import { useAbsoluteFilePath } from 'hooks/useAbsoluteFilePath'
|
||||
|
||||
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||
|
||||
export const onboardingPaths = {
|
||||
INDEX: '/',
|
||||
@ -86,29 +89,23 @@ export const onboardingRoutes = [
|
||||
]
|
||||
|
||||
export function useNextClick(newStatus: string) {
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const {
|
||||
settings: { send },
|
||||
} = useGlobalStateContext()
|
||||
const navigate = useNavigate()
|
||||
const { project } = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
|
||||
return useCallback(() => {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: newStatus },
|
||||
})
|
||||
navigate(
|
||||
paths.FILE +
|
||||
'/' +
|
||||
encodeURIComponent(project?.path || 'new') +
|
||||
paths.ONBOARDING.INDEX.slice(0, -1) +
|
||||
newStatus
|
||||
)
|
||||
}, [project, newStatus, send, navigate])
|
||||
navigate(filePath + paths.ONBOARDING.INDEX.slice(0, -1) + newStatus)
|
||||
}, [filePath, newStatus, send, navigate])
|
||||
}
|
||||
|
||||
export function useDismiss() {
|
||||
const routeData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const filePath = useAbsoluteFilePath()
|
||||
const {
|
||||
settings: { send },
|
||||
} = useGlobalStateContext()
|
||||
@ -119,10 +116,8 @@ export function useDismiss() {
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: 'dismissed' },
|
||||
})
|
||||
navigate(
|
||||
paths.FILE + '/' + encodeURIComponent(routeData?.project?.path || 'new')
|
||||
)
|
||||
}, [send, navigate, routeData])
|
||||
navigate(filePath)
|
||||
}, [send, navigate, filePath])
|
||||
}
|
||||
|
||||
const Onboarding = () => {
|
||||
|
@ -24,11 +24,19 @@ import {
|
||||
} from 'lib/cameraControls'
|
||||
import { UnitSystem } from 'machines/settingsMachine'
|
||||
import { useDotDotSlash } from 'hooks/useDotDotSlash'
|
||||
import {
|
||||
createNewProject,
|
||||
getNextProjectIndex,
|
||||
getProjectsInDir,
|
||||
interpolateProjectNameWithIndex,
|
||||
} from 'lib/tauriFS'
|
||||
import { ONBOARDING_PROJECT_NAME } from './Onboarding'
|
||||
|
||||
export const Settings = () => {
|
||||
const loaderData = useRouteLoaderData(paths.FILE) as IndexLoaderData
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const isFileSettings = location.pathname.includes(paths.FILE)
|
||||
const dotDotSlash = useDotDotSlash()
|
||||
useHotkeys('esc', () => navigate(dotDotSlash()))
|
||||
const {
|
||||
@ -63,6 +71,33 @@ export const Settings = () => {
|
||||
}
|
||||
}
|
||||
|
||||
function restartOnboarding() {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: '' },
|
||||
})
|
||||
|
||||
if (isFileSettings) {
|
||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||
} else {
|
||||
createAndOpenNewProject()
|
||||
}
|
||||
}
|
||||
|
||||
async function createAndOpenNewProject() {
|
||||
const projects = await getProjectsInDir(defaultDirectory)
|
||||
const nextIndex = await getNextProjectIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
projects
|
||||
)
|
||||
const name = interpolateProjectNameWithIndex(
|
||||
ONBOARDING_PROJECT_NAME,
|
||||
nextIndex
|
||||
)
|
||||
const newFile = await createNewProject(defaultDirectory + '/' + name)
|
||||
navigate(`${paths.FILE}/${encodeURIComponent(newFile.path)}`)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 z-40 overflow-auto body-bg">
|
||||
<AppHeader showToolbar={false} project={loaderData?.project}>
|
||||
@ -257,26 +292,18 @@ export const Settings = () => {
|
||||
))}
|
||||
</select>
|
||||
</SettingsSection>
|
||||
{location.pathname.includes(paths.FILE) && (
|
||||
<SettingsSection
|
||||
title="Onboarding"
|
||||
description="Replay the onboarding process"
|
||||
<SettingsSection
|
||||
title="Onboarding"
|
||||
description="Replay the onboarding process"
|
||||
>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={restartOnboarding}
|
||||
icon={{ icon: faArrowRotateBack }}
|
||||
>
|
||||
<ActionButton
|
||||
Element="button"
|
||||
onClick={() => {
|
||||
send({
|
||||
type: 'Set Onboarding Status',
|
||||
data: { onboardingStatus: '' },
|
||||
})
|
||||
navigate(dotDotSlash(1) + paths.ONBOARDING.INDEX)
|
||||
}}
|
||||
icon={{ icon: faArrowRotateBack }}
|
||||
>
|
||||
Replay Onboarding
|
||||
</ActionButton>
|
||||
</SettingsSection>
|
||||
)}
|
||||
Replay Onboarding
|
||||
</ActionButton>
|
||||
</SettingsSection>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -20,6 +20,8 @@ import { KCLError } from './lang/errors'
|
||||
import { deferExecution } from 'lib/utils'
|
||||
import { bracket } from 'lib/exampleKcl'
|
||||
import { engineCommandManager } from './lang/std/engineConnection'
|
||||
import { DefaultPlanes } from './wasm-lib/kcl/bindings/DefaultPlanes'
|
||||
import { initDefaultPlanes } from './hooks/useAppMode'
|
||||
|
||||
export type Selection = {
|
||||
type: 'default' | 'line-end' | 'line-mid'
|
||||
@ -182,6 +184,10 @@ export interface StoreState {
|
||||
}) => void
|
||||
isExecuting: boolean
|
||||
setIsExecuting: (isExecuting: boolean) => void
|
||||
defaultPlanes: DefaultPlanes | null
|
||||
setDefaultPlanes: (defaultPlanes: DefaultPlanes) => void
|
||||
currentPlane: string | null
|
||||
setCurrentPlane: (currentPlane: string) => void
|
||||
|
||||
showHomeMenu: boolean
|
||||
setHomeShowMenu: (showMenu: boolean) => void
|
||||
@ -222,10 +228,20 @@ export const useStore = create<StoreState>()(
|
||||
}
|
||||
},
|
||||
executeCode: async (code, force) => {
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
const result = await executeCode({
|
||||
code: code || get().code,
|
||||
lastAst: get().ast,
|
||||
engineCommandManager: engineCommandManager,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
force,
|
||||
})
|
||||
if (!result.isChange) {
|
||||
@ -332,11 +348,20 @@ export const useStore = create<StoreState>()(
|
||||
executeAst: async (ast) => {
|
||||
const _ast = ast || get().ast
|
||||
if (!get().isStreamReady) return
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
set({ isExecuting: true })
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
})
|
||||
set({
|
||||
programMemory,
|
||||
@ -349,10 +374,20 @@ export const useStore = create<StoreState>()(
|
||||
const _ast = ast || get().ast
|
||||
if (!get().isStreamReady) return
|
||||
|
||||
if (!get().defaultPlanes) {
|
||||
let defaultPlanes = await initDefaultPlanes(
|
||||
engineCommandManager,
|
||||
true
|
||||
)
|
||||
if (!defaultPlanes) return
|
||||
get().setDefaultPlanes(defaultPlanes)
|
||||
}
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager,
|
||||
useFakeExecutor: true,
|
||||
defaultPlanes: get().defaultPlanes!,
|
||||
})
|
||||
set({
|
||||
programMemory,
|
||||
@ -453,6 +488,10 @@ export const useStore = create<StoreState>()(
|
||||
},
|
||||
isExecuting: false,
|
||||
setIsExecuting: (isExecuting) => set({ isExecuting }),
|
||||
defaultPlanes: null,
|
||||
setDefaultPlanes: (defaultPlanes) => set({ defaultPlanes }),
|
||||
currentPlane: null,
|
||||
setCurrentPlane: (currentPlane) => set({ currentPlane }),
|
||||
|
||||
// tauri specific app settings
|
||||
defaultDir: {
|
||||
@ -512,11 +551,13 @@ async function executeCode({
|
||||
engineCommandManager,
|
||||
code,
|
||||
lastAst,
|
||||
defaultPlanes,
|
||||
force,
|
||||
}: {
|
||||
code: string
|
||||
lastAst: Program
|
||||
engineCommandManager: EngineCommandManager
|
||||
defaultPlanes: DefaultPlanes
|
||||
force?: boolean
|
||||
}): Promise<
|
||||
| {
|
||||
@ -566,6 +607,7 @@ async function executeCode({
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes,
|
||||
})
|
||||
return {
|
||||
ast,
|
||||
@ -579,10 +621,12 @@ async function executeCode({
|
||||
async function executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
defaultPlanes,
|
||||
useFakeExecutor = false,
|
||||
}: {
|
||||
ast: Program
|
||||
engineCommandManager: EngineCommandManager
|
||||
defaultPlanes: DefaultPlanes
|
||||
useFakeExecutor?: boolean
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
@ -605,7 +649,8 @@ async function executeAst({
|
||||
root: defaultProgramMemory,
|
||||
return: null,
|
||||
},
|
||||
engineCommandManager
|
||||
engineCommandManager,
|
||||
defaultPlanes
|
||||
))
|
||||
|
||||
await engineCommandManager.waitForAllCommands(ast, programMemory)
|
||||
|
10
src/wasm-lib/Cargo.lock
generated
@ -1390,7 +1390,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kcl-lib"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-recursion",
|
||||
@ -1426,9 +1426,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "kittycad"
|
||||
version = "0.2.28"
|
||||
version = "0.2.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35b2f9302648dbb06fd7121687f9505fc3179eba84111a06d76b246e3158f5dc"
|
||||
checksum = "539b323537b877fc8dd130362b8f1af9af8051c19208bb8bfd816ab7c330f2bb"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -2217,9 +2217,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.21"
|
||||
version = "0.11.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78fdbab6a7e1d7b13cc8ff10197f47986b41c639300cc3c8158cac7847c9bbef"
|
||||
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
|
||||
dependencies = [
|
||||
"base64 0.21.4",
|
||||
"bytes",
|
||||
|
@ -11,7 +11,7 @@ crate-type = ["cdylib"]
|
||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||
gloo-utils = "0.2.0"
|
||||
kcl-lib = { path = "kcl" }
|
||||
kittycad = { version = "0.2.27", default-features = false, features = ["js"] }
|
||||
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
|
||||
serde_json = "1.0.107"
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
wasm-bindgen = "0.2.87"
|
||||
@ -20,9 +20,9 @@ wasm-bindgen-futures = "0.4.37"
|
||||
[dev-dependencies]
|
||||
anyhow = "1"
|
||||
image = "0.24.7"
|
||||
kittycad = "0.2.27"
|
||||
kittycad = "0.2.31"
|
||||
pretty_assertions = "1.4.0"
|
||||
reqwest = { version = "0.11.21", default-features = false }
|
||||
reqwest = { version = "0.11.22", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.6.1"
|
||||
uuid = { version = "1.4.1", features = ["v4", "js", "serde"] }
|
||||
|
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "kcl-lib"
|
||||
description = "KittyCAD Language"
|
||||
version = "0.1.32"
|
||||
version = "0.1.33"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
@ -15,7 +15,7 @@ clap = { version = "4.4.6", features = ["cargo", "derive", "env", "unicode"], op
|
||||
dashmap = "5.5.3"
|
||||
derive-docs = { version = "0.1.4" }
|
||||
#derive-docs = { path = "../derive-docs" }
|
||||
kittycad = { version = "0.2.27", default-features = false, features = ["js"] }
|
||||
kittycad = { version = "0.2.31", default-features = false, features = ["js"] }
|
||||
lazy_static = "1.4.0"
|
||||
parse-display = "0.8.2"
|
||||
schemars = { version = "0.8", features = ["impl_json_schema", "url", "uuid1"] }
|
||||
@ -36,7 +36,7 @@ web-sys = { version = "0.3.64", features = ["console"] }
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
bson = { version = "2.7.0", features = ["uuid-1", "chrono"] }
|
||||
futures = { version = "0.3.28" }
|
||||
reqwest = { version = "0.11.21", default-features = false }
|
||||
reqwest = { version = "0.11.22", default-features = false }
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
tokio-tungstenite = { version = "0.20.0", features = ["rustls-tls-native-roots"] }
|
||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||
|
@ -1 +1 @@
|
||||
enum-variant-size-threshold = 24
|
||||
enum-variant-size-threshold = 48
|
||||
|
@ -31,6 +31,8 @@ pub async fn modify_ast_for_sketch(
|
||||
program: &mut Program,
|
||||
// The name of the sketch.
|
||||
sketch_name: &str,
|
||||
// The type of plane the sketch is on. `XY` or `XZ`, etc
|
||||
plane: crate::executor::PlaneType,
|
||||
// The ID of the parent sketch.
|
||||
sketch_id: uuid::Uuid,
|
||||
) -> Result<String, KclError> {
|
||||
@ -153,10 +155,11 @@ pub async fn modify_ast_for_sketch(
|
||||
y: (first_control_points.points[1].y - first_control_points.points[0].y),
|
||||
z: (first_control_points.points[1].z - first_control_points.points[0].z),
|
||||
};
|
||||
let sketch = create_start_sketch_at(
|
||||
let sketch = create_start_sketch_on(
|
||||
sketch_name,
|
||||
[first_control_points.points[0].x, first_control_points.points[0].y],
|
||||
[start_sketch_at_end.x, start_sketch_at_end.y],
|
||||
plane,
|
||||
additional_lines,
|
||||
)?;
|
||||
|
||||
@ -174,19 +177,24 @@ pub async fn modify_ast_for_sketch(
|
||||
}
|
||||
|
||||
/// Create a pipe expression that starts a sketch at the given point and draws a line to the given point.
|
||||
fn create_start_sketch_at(
|
||||
fn create_start_sketch_on(
|
||||
name: &str,
|
||||
start: [f64; 2],
|
||||
end: [f64; 2],
|
||||
plane: crate::executor::PlaneType,
|
||||
additional_lines: Vec<[f64; 2]>,
|
||||
) -> Result<VariableDeclarator, KclError> {
|
||||
let start_sketch_at = CallExpression::new(
|
||||
"startSketchAt",
|
||||
vec![ArrayExpression::new(vec![
|
||||
Literal::new(round_before_recast(start[0]).into()).into(),
|
||||
Literal::new(round_before_recast(start[1]).into()).into(),
|
||||
])
|
||||
.into()],
|
||||
let start_sketch_on = CallExpression::new("startSketchOn", vec![Literal::new(plane.to_string().into()).into()])?;
|
||||
let start_profile_at = CallExpression::new(
|
||||
"startProfileAt",
|
||||
vec![
|
||||
ArrayExpression::new(vec![
|
||||
Literal::new(round_before_recast(start[0]).into()).into(),
|
||||
Literal::new(round_before_recast(start[1]).into()).into(),
|
||||
])
|
||||
.into(),
|
||||
PipeSubstitution::new().into(),
|
||||
],
|
||||
)?;
|
||||
|
||||
// Keep track of where we are so we can close the sketch if we need to.
|
||||
@ -209,7 +217,7 @@ fn create_start_sketch_at(
|
||||
],
|
||||
)?;
|
||||
|
||||
let mut pipe_body = vec![start_sketch_at.into(), initial_line.into()];
|
||||
let mut pipe_body = vec![start_sketch_on.into(), start_profile_at.into(), initial_line.into()];
|
||||
|
||||
for (index, line) in additional_lines.iter().enumerate() {
|
||||
current_position.x += line[0];
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Data types for the AST.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, fmt::Write};
|
||||
|
||||
use anyhow::Result;
|
||||
use parse_display::{Display, FromStr};
|
||||
@ -10,9 +10,8 @@ use serde_json::Map;
|
||||
use tower_lsp::lsp_types::{CompletionItem, CompletionItemKind, DocumentSymbol, Range as LspRange, SymbolKind};
|
||||
|
||||
use crate::{
|
||||
engine::EngineConnection,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
||||
executor::{ExecutorContext, MemoryItem, Metadata, PipeInfo, ProgramMemory, SourceRange, UserVal},
|
||||
parser::PIPE_OPERATOR,
|
||||
};
|
||||
|
||||
@ -29,73 +28,74 @@ pub struct Program {
|
||||
impl Program {
|
||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
let indentation = options.get_indentation(indentation_level);
|
||||
let result = self
|
||||
.body
|
||||
.iter()
|
||||
.map(|statement| match statement.clone() {
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
expression_statement
|
||||
.expression
|
||||
.recast(options, indentation_level, false)
|
||||
}
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration
|
||||
.declarations
|
||||
.iter()
|
||||
.map(|declaration| {
|
||||
format!(
|
||||
"{}{} {} = {}",
|
||||
indentation,
|
||||
variable_declaration.kind,
|
||||
declaration.id.name,
|
||||
declaration.init.recast(options, 0, false)
|
||||
)
|
||||
})
|
||||
.collect::<String>(),
|
||||
BodyItem::ReturnStatement(return_statement) => {
|
||||
format!(
|
||||
"{}return {}",
|
||||
indentation,
|
||||
return_statement.argument.recast(options, 0, false)
|
||||
)
|
||||
}
|
||||
})
|
||||
.enumerate()
|
||||
.map(|(index, recast_str)| {
|
||||
let start_string = if index == 0 {
|
||||
// We need to indent.
|
||||
if let Some(start) = self.non_code_meta.start.clone() {
|
||||
start.format(&indentation)
|
||||
} else {
|
||||
indentation.to_string()
|
||||
let result =
|
||||
self.body
|
||||
.iter()
|
||||
.map(|statement| match statement.clone() {
|
||||
BodyItem::ExpressionStatement(expression_statement) => {
|
||||
expression_statement
|
||||
.expression
|
||||
.recast(options, indentation_level, false)
|
||||
}
|
||||
} else {
|
||||
// Do nothing, we already applied the indentation elsewhere.
|
||||
String::new()
|
||||
};
|
||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration
|
||||
.declarations
|
||||
.iter()
|
||||
.fold(String::new(), |mut output, declaration| {
|
||||
let _ = write!(
|
||||
output,
|
||||
"{}{} {} = {}",
|
||||
indentation,
|
||||
variable_declaration.kind,
|
||||
declaration.id.name,
|
||||
declaration.init.recast(options, 0, false)
|
||||
);
|
||||
output
|
||||
}),
|
||||
BodyItem::ReturnStatement(return_statement) => {
|
||||
format!(
|
||||
"{}return {}",
|
||||
indentation,
|
||||
return_statement.argument.recast(options, 0, false)
|
||||
)
|
||||
}
|
||||
})
|
||||
.enumerate()
|
||||
.fold(String::new(), |mut output, (index, recast_str)| {
|
||||
let start_string = if index == 0 {
|
||||
// We need to indent.
|
||||
if let Some(start) = self.non_code_meta.start.clone() {
|
||||
start.format(&indentation)
|
||||
} else {
|
||||
indentation.to_string()
|
||||
}
|
||||
} else {
|
||||
// Do nothing, we already applied the indentation elsewhere.
|
||||
String::new()
|
||||
};
|
||||
|
||||
// determine the value of the end string
|
||||
// basically if we are inside a nested function we want to end with a new line
|
||||
let maybe_line_break: String = if index == self.body.len() - 1 && indentation_level == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
"\n".to_string()
|
||||
};
|
||||
// determine the value of the end string
|
||||
// basically if we are inside a nested function we want to end with a new line
|
||||
let maybe_line_break: String = if index == self.body.len() - 1 && indentation_level == 0 {
|
||||
String::new()
|
||||
} else {
|
||||
"\n".to_string()
|
||||
};
|
||||
|
||||
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
|
||||
Some(custom_white_space_or_comment) => custom_white_space_or_comment.format(&indentation),
|
||||
None => String::new(),
|
||||
};
|
||||
let end_string = if custom_white_space_or_comment.is_empty() {
|
||||
maybe_line_break
|
||||
} else {
|
||||
custom_white_space_or_comment
|
||||
};
|
||||
let custom_white_space_or_comment = match self.non_code_meta.non_code_nodes.get(&index) {
|
||||
Some(custom_white_space_or_comment) => custom_white_space_or_comment.format(&indentation),
|
||||
None => String::new(),
|
||||
};
|
||||
let end_string = if custom_white_space_or_comment.is_empty() {
|
||||
maybe_line_break
|
||||
} else {
|
||||
custom_white_space_or_comment
|
||||
};
|
||||
|
||||
format!("{}{}{}", start_string, recast_str, end_string)
|
||||
})
|
||||
.collect::<String>()
|
||||
.trim()
|
||||
.to_string();
|
||||
let _ = write!(output, "{}{}{}", start_string, recast_str, end_string);
|
||||
output
|
||||
})
|
||||
.trim()
|
||||
.to_string();
|
||||
|
||||
// Insert a final new line if the user wants it.
|
||||
if options.insert_final_newline {
|
||||
@ -631,7 +631,7 @@ impl BinaryPart {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -646,13 +646,13 @@ impl BinaryPart {
|
||||
Ok(value.clone())
|
||||
}
|
||||
BinaryPart::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, &mut new_pipe_info, engine).await
|
||||
binary_expression.get_result(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::CallExpression(call_expression) => {
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, &mut new_pipe_info, engine).await
|
||||
unary_expression.get_result(memory, &mut new_pipe_info, ctx).await
|
||||
}
|
||||
BinaryPart::MemberExpression(member_expression) => member_expression.get_result(memory),
|
||||
}
|
||||
@ -879,7 +879,7 @@ impl CallExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let fn_name = self.callee.name.clone();
|
||||
|
||||
@ -893,7 +893,7 @@ impl CallExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -901,15 +901,11 @@ impl CallExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeExpression not implemented here: {:?}", pipe_expression),
|
||||
@ -941,12 +937,12 @@ impl CallExpression {
|
||||
match &self.function {
|
||||
Function::StdLib { func } => {
|
||||
// Attempt to call the function.
|
||||
let args = crate::std::Args::new(fn_args, self.into(), engine.clone());
|
||||
let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
|
||||
let result = func.std_lib_fn()(args).await?;
|
||||
if pipe_info.is_in_pipe {
|
||||
pipe_info.index += 1;
|
||||
pipe_info.previous_results.push(result);
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
@ -954,7 +950,7 @@ impl CallExpression {
|
||||
Function::InMemory => {
|
||||
let func = memory.get(&fn_name, self.into())?;
|
||||
let result = func
|
||||
.call_fn(fn_args, memory.clone(), engine.clone())
|
||||
.call_fn(fn_args, memory.clone(), ctx.clone())
|
||||
.await?
|
||||
.ok_or_else(|| {
|
||||
KclError::UndefinedValue(KclErrorDetails {
|
||||
@ -969,7 +965,7 @@ impl CallExpression {
|
||||
pipe_info.index += 1;
|
||||
pipe_info.previous_results.push(result);
|
||||
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &pipe_info.body.clone(), pipe_info, self.into(), ctx).await
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
@ -1507,7 +1503,7 @@ impl ArrayExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let mut results = Vec::with_capacity(self.elements.len());
|
||||
|
||||
@ -1519,7 +1515,7 @@ impl ArrayExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -1527,16 +1523,12 @@ impl ArrayExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
|
||||
@ -1663,7 +1655,7 @@ impl ObjectExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
let mut object = Map::new();
|
||||
for property in &self.properties {
|
||||
@ -1674,7 +1666,7 @@ impl ObjectExpression {
|
||||
value.clone()
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
binary_expression.get_result(memory, pipe_info, engine).await?
|
||||
binary_expression.get_result(memory, pipe_info, ctx).await?
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
@ -1682,16 +1674,12 @@ impl ObjectExpression {
|
||||
// THIS IS IMPORTANT.
|
||||
let mut new_pipe_info = pipe_info.clone();
|
||||
new_pipe_info.is_in_pipe = false;
|
||||
call_expression.execute(memory, &mut new_pipe_info, engine).await?
|
||||
call_expression.execute(memory, &mut new_pipe_info, ctx).await?
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
unary_expression.get_result(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
object_expression.execute(memory, pipe_info, engine).await?
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, engine).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, engine).await?,
|
||||
Value::UnaryExpression(unary_expression) => unary_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::ObjectExpression(object_expression) => object_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::ArrayExpression(array_expression) => array_expression.execute(memory, pipe_info, ctx).await?,
|
||||
Value::PipeExpression(pipe_expression) => pipe_expression.get_result(memory, pipe_info, ctx).await?,
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("PipeSubstitution not implemented here: {:?}", pipe_substitution),
|
||||
@ -2109,7 +2097,7 @@ impl BinaryExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -2119,12 +2107,12 @@ impl BinaryExpression {
|
||||
|
||||
let left_json_value = self
|
||||
.left
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
let right_json_value = self
|
||||
.right
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?;
|
||||
|
||||
@ -2283,7 +2271,7 @@ impl UnaryExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// We DO NOT set this gloablly because if we did and this was called inside a pipe it would
|
||||
// stop the execution of the pipe.
|
||||
@ -2294,7 +2282,7 @@ impl UnaryExpression {
|
||||
let num = parse_json_number_as_f64(
|
||||
&self
|
||||
.argument
|
||||
.get_result(memory, &mut new_pipe_info, engine)
|
||||
.get_result(memory, &mut new_pipe_info, ctx)
|
||||
.await?
|
||||
.get_json_value()?,
|
||||
self.into(),
|
||||
@ -2427,12 +2415,12 @@ impl PipeExpression {
|
||||
&self,
|
||||
memory: &mut ProgramMemory,
|
||||
pipe_info: &mut PipeInfo,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
// Reset the previous results.
|
||||
pipe_info.previous_results = vec![];
|
||||
pipe_info.index = 0;
|
||||
execute_pipe_body(memory, &self.body, pipe_info, self.into(), engine).await
|
||||
execute_pipe_body(memory, &self.body, pipe_info, self.into(), ctx).await
|
||||
}
|
||||
|
||||
/// Rename all identifiers that have the old name to the new given name.
|
||||
@ -2449,7 +2437,7 @@ async fn execute_pipe_body(
|
||||
body: &[Value],
|
||||
pipe_info: &mut PipeInfo,
|
||||
source_range: SourceRange,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<MemoryItem, KclError> {
|
||||
if pipe_info.index == body.len() {
|
||||
pipe_info.is_in_pipe = false;
|
||||
@ -2474,15 +2462,15 @@ async fn execute_pipe_body(
|
||||
|
||||
match expression {
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, pipe_info, engine).await?;
|
||||
let result = binary_expression.get_result(memory, pipe_info, ctx).await?;
|
||||
pipe_info.previous_results.push(result);
|
||||
pipe_info.index += 1;
|
||||
execute_pipe_body(memory, body, pipe_info, source_range, engine).await
|
||||
execute_pipe_body(memory, body, pipe_info, source_range, ctx).await
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
pipe_info.is_in_pipe = true;
|
||||
pipe_info.body = body.to_vec();
|
||||
call_expression.execute(memory, pipe_info, engine).await
|
||||
call_expression.execute(memory, pipe_info, ctx).await
|
||||
}
|
||||
_ => {
|
||||
// Return an error this should not happen.
|
||||
@ -2775,7 +2763,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_get_lsp_symbols() {
|
||||
let code = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
|
||||
let code = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0000000000, 5.0000000000], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -2796,25 +2785,10 @@ show(part001)"#;
|
||||
assert_eq!(symbols.len(), 7);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_std_and_non_stdlib() {
|
||||
let some_program_string = r#"{"body":[{"type":"VariableDeclaration","start":0,"end":0,"declarations":[{"type":"VariableDeclarator","start":0,"end":0,"id":{"type":"Identifier","start":0,"end":0,"name":"part001"},"init":{"type":"PipeExpression","start":0,"end":0,"body":[{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"startSketchAt"},"function":{"type":"StdLib","func":{"name":"startSketchAt","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"ry"},"function":{"type":"InMemory"},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":90,"raw":"90"},{"type":"PipeSubstitution","start":0,"end":0}]},{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"line"},"function":{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Literal","start":0,"end":0,"value":"default","raw":"default"},{"type":"PipeSubstitution","start":0,"end":0}]}],"nonCodeMeta":{"nonCodeNodes":{},"start":null}}}],"kind":"const"},{"type":"ExpressionStatement","start":0,"end":0,"expression":{"type":"CallExpression","start":0,"end":0,"callee":{"type":"Identifier","start":0,"end":0,"name":"show"},"function":{"type":"StdLib","func":{"name":"show","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{}},"args":[],"unpublished":false,"deprecated":false}},"optional":false,"arguments":[{"type":"Identifier","start":0,"end":0,"name":"part001"}]}}],"start":0,"end":0,"nonCodeMeta":{"nonCodeNodes":{},"start":null}}"#;
|
||||
let some_program: crate::ast::types::Program = serde_json::from_str(some_program_string).unwrap();
|
||||
|
||||
let recasted = some_program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt('default')
|
||||
|> ry(90, %)
|
||||
|> line('default', %)
|
||||
show(part001)
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
@ -2824,7 +2798,8 @@ show(part001)
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
"#
|
||||
@ -2833,7 +2808,8 @@ show(part001)
|
||||
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation_and_inline_comment() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %) // hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
let tokens = crate::token::lexer(some_program_string);
|
||||
@ -2843,7 +2819,8 @@ show(part001)
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %) // hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
"#
|
||||
@ -2851,7 +2828,8 @@ show(part001)
|
||||
}
|
||||
#[test]
|
||||
fn test_recast_with_bad_indentation_and_line_comment() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
// hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)"#;
|
||||
@ -2862,7 +2840,8 @@ show(part001)
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const part001 = startSketchAt([0.0, 5.0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
// hello world
|
||||
|> line([0.6804562304, 0.9087880491], %)
|
||||
@ -2904,7 +2883,8 @@ show(part001)
|
||||
#[test]
|
||||
fn test_recast_lots_of_comments() {
|
||||
let some_program_string = r#"// comment at start
|
||||
const mySk1 = startSketchAt([0, 0])
|
||||
const mySk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
// comment here
|
||||
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
|
||||
@ -2926,7 +2906,8 @@ const mySk1 = startSketchAt([0, 0])
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"// comment at start
|
||||
const mySk1 = startSketchAt([0, 0])
|
||||
const mySk1 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo([1, 1], %)
|
||||
// comment here
|
||||
|> lineTo({ to: [0, 1], tag: 'myTag' }, %)
|
||||
@ -2946,7 +2927,8 @@ a comment between pipe expression statements */
|
||||
|
||||
#[test]
|
||||
fn test_recast_multiline_object() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([-0.01, -0.08])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-0.01, -0.08], %)
|
||||
|> line({ to: [0.62, 4.15], tag: 'seg01' }, %)
|
||||
|> line([2.77, -1.24], %)
|
||||
|> angledLineThatIntersects({
|
||||
@ -3034,7 +3016,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -3059,7 +3042,8 @@ const myVar2 = 5
|
||||
const myVar3 = 6
|
||||
const myAng = 40
|
||||
const myAng2 = 134
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [1, 3.82], tag: 'seg01' }, %) // ln-should-get-tag
|
||||
|> angledLineToX([
|
||||
-angleToMatchLengthX('seg01', myVar, %),
|
||||
@ -3087,7 +3071,8 @@ const part001 = startSketchAt([0, 0])
|
||||
|
||||
#[test]
|
||||
fn test_recast_after_rename_std() {
|
||||
let some_program_string = r#"const part001 = startSketchAt([0.0000000000, 5.0000000000])
|
||||
let some_program_string = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0.0000000000, 5.0000000000], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -3109,7 +3094,8 @@ show(part001)"#;
|
||||
let recasted = program.recast(&Default::default(), 0);
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"const mySuperCoolPart = startSketchAt([0.0, 5.0])
|
||||
r#"const mySuperCoolPart = startSketchOn('XY')
|
||||
|> startProfileAt([0.0, 5.0], %)
|
||||
|> line([0.4900857016, -0.0240763666], %)
|
||||
|
||||
const part002 = "part002"
|
||||
@ -3153,7 +3139,8 @@ show(mySuperCoolPart)
|
||||
const l = 8
|
||||
const h = 10
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -3172,7 +3159,8 @@ show(firstExtrude)"#;
|
||||
const l = 8
|
||||
const h = 10
|
||||
|
||||
const firstExtrude = startSketchAt([0, 0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
|
@ -3,13 +3,15 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use anyhow::Result;
|
||||
use kittycad::types::{Color, ModelingCmd, Point3D};
|
||||
use parse_display::{Display, FromStr};
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tower_lsp::lsp_types::{Position as LspPosition, Range as LspRange};
|
||||
|
||||
use crate::{
|
||||
ast::types::{BodyItem, Function, FunctionExpression, Value},
|
||||
engine::EngineConnection,
|
||||
engine::{EngineConnection, EngineManager},
|
||||
errors::{KclError, KclErrorDetails},
|
||||
};
|
||||
|
||||
@ -101,6 +103,7 @@ impl ProgramReturn {
|
||||
#[serde(tag = "type")]
|
||||
pub enum MemoryItem {
|
||||
UserVal(UserVal),
|
||||
Plane(Box<Plane>),
|
||||
SketchGroup(Box<SketchGroup>),
|
||||
ExtrudeGroup(Box<ExtrudeGroup>),
|
||||
#[ts(skip)]
|
||||
@ -115,6 +118,166 @@ pub enum MemoryItem {
|
||||
},
|
||||
}
|
||||
|
||||
/// A plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct Plane {
|
||||
/// The id of the plane.
|
||||
pub id: uuid::Uuid,
|
||||
// The code for the plane either a string or custom.
|
||||
pub value: PlaneType,
|
||||
/// Origin of the plane.
|
||||
pub origin: Point3d,
|
||||
/// What should the plane’s X axis be?
|
||||
pub x_axis: Point3d,
|
||||
/// What should the plane’s Y axis be?
|
||||
pub y_axis: Point3d,
|
||||
/// The z-axis (normal).
|
||||
pub z_axis: Point3d,
|
||||
#[serde(rename = "__meta")]
|
||||
pub meta: Vec<Metadata>,
|
||||
}
|
||||
|
||||
/// Type for a plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[display(style = "camelCase")]
|
||||
pub enum PlaneType {
|
||||
#[serde(rename = "XY", alias = "xy")]
|
||||
#[display("XY")]
|
||||
XY,
|
||||
#[serde(rename = "XZ", alias = "xz")]
|
||||
#[display("XZ")]
|
||||
XZ,
|
||||
#[serde(rename = "YZ", alias = "yz")]
|
||||
#[display("YZ")]
|
||||
YZ,
|
||||
/// A custom plane.
|
||||
#[serde(rename = "Custom")]
|
||||
#[display("Custom")]
|
||||
Custom,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct DefaultPlanes {
|
||||
pub xy: uuid::Uuid,
|
||||
pub xz: uuid::Uuid,
|
||||
pub yz: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl DefaultPlanes {
|
||||
pub async fn new(engine: &EngineConnection) -> Result<Self, KclError> {
|
||||
// Create new default planes.
|
||||
let default_size = 60.0;
|
||||
let default_origin = Point3D { x: 0.0, y: 0.0, z: 0.0 };
|
||||
|
||||
// Create xy plane.
|
||||
let xy = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
xy,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin.clone(),
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.7,
|
||||
g: 0.28,
|
||||
b: 0.28,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: xy,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create yz plane.
|
||||
let yz = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
yz,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin.clone(),
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.28,
|
||||
g: 0.7,
|
||||
b: 0.28,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: yz,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Create xz plane.
|
||||
let xz = uuid::Uuid::new_v4();
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
xz,
|
||||
SourceRange::default(),
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: default_origin,
|
||||
size: default_size,
|
||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 0.0, z: 1.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
// Set the color.
|
||||
engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
ModelingCmd::PlaneSetColor {
|
||||
color: Color {
|
||||
r: 0.28,
|
||||
g: 0.28,
|
||||
b: 0.7,
|
||||
a: 0.4,
|
||||
},
|
||||
plane_id: xz,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Self { xy, xz, yz })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
@ -140,7 +303,7 @@ pub type MemoryFunction =
|
||||
memory: ProgramMemory,
|
||||
expression: Box<FunctionExpression>,
|
||||
metadata: Vec<Metadata>,
|
||||
engine: EngineConnection,
|
||||
ctx: ExecutorContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>;
|
||||
|
||||
fn force_memory_function<
|
||||
@ -149,7 +312,7 @@ fn force_memory_function<
|
||||
ProgramMemory,
|
||||
Box<FunctionExpression>,
|
||||
Vec<Metadata>,
|
||||
EngineConnection,
|
||||
ExecutorContext,
|
||||
) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Option<ProgramReturn>, KclError>>>>,
|
||||
>(
|
||||
f: F,
|
||||
@ -165,6 +328,7 @@ impl From<MemoryItem> for Vec<SourceRange> {
|
||||
MemoryItem::ExtrudeGroup(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::ExtrudeTransform(e) => e.meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::Function { meta, .. } => meta.iter().map(|m| m.source_range).collect(),
|
||||
MemoryItem::Plane(p) => p.meta.iter().map(|m| m.source_range).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -187,11 +351,11 @@ impl MemoryItem {
|
||||
&self,
|
||||
args: Vec<MemoryItem>,
|
||||
memory: ProgramMemory,
|
||||
engine: EngineConnection,
|
||||
ctx: ExecutorContext,
|
||||
) -> Result<Option<ProgramReturn>, KclError> {
|
||||
if let MemoryItem::Function { func, expression, meta } = &self {
|
||||
if let Some(func) = func {
|
||||
func(args, memory, expression.clone(), meta.clone(), engine).await
|
||||
func(args, memory, expression.clone(), meta.clone(), ctx).await
|
||||
} else {
|
||||
Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Not a function: {:?}", expression),
|
||||
@ -222,6 +386,8 @@ pub struct SketchGroup {
|
||||
pub position: Position,
|
||||
/// The rotation of the sketch group.
|
||||
pub rotation: Rotation,
|
||||
/// The plane id of the sketch group.
|
||||
pub plane_id: Option<uuid::Uuid>,
|
||||
/// Metadata.
|
||||
#[serde(rename = "__meta")]
|
||||
pub meta: Vec<Metadata>,
|
||||
@ -414,6 +580,18 @@ pub struct Point3d {
|
||||
pub z: f64,
|
||||
}
|
||||
|
||||
impl Point3d {
|
||||
pub fn new(x: f64, y: f64, z: f64) -> Self {
|
||||
Self { x, y, z }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Point3d> for kittycad::types::Point3D {
|
||||
fn from(p: Point3d) -> Self {
|
||||
Self { x: p.x, y: p.y, z: p.z }
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
@ -593,12 +771,19 @@ impl Default for PipeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/// The executor context.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExecutorContext {
|
||||
pub engine: EngineConnection,
|
||||
pub planes: DefaultPlanes,
|
||||
}
|
||||
|
||||
/// Execute a AST's program.
|
||||
pub async fn execute(
|
||||
program: crate::ast::types::Program,
|
||||
memory: &mut ProgramMemory,
|
||||
options: BodyType,
|
||||
engine: &EngineConnection,
|
||||
ctx: &ExecutorContext,
|
||||
) -> Result<ProgramMemory, KclError> {
|
||||
let mut pipe_info = PipeInfo::default();
|
||||
|
||||
@ -617,23 +802,23 @@ pub async fn execute(
|
||||
args.push(memory_item.clone());
|
||||
}
|
||||
Value::CallExpression(call_expr) => {
|
||||
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => {
|
||||
let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
args.push(result);
|
||||
}
|
||||
// We do nothing for the rest.
|
||||
@ -651,7 +836,7 @@ pub async fn execute(
|
||||
|
||||
memory.return_ = Some(ProgramReturn::Arguments(call_expr.arguments.clone()));
|
||||
} else if let Some(func) = memory.clone().root.get(&fn_name) {
|
||||
let result = func.call_fn(args.clone(), memory.clone(), engine.clone()).await?;
|
||||
let result = func.call_fn(args.clone(), memory.clone(), ctx.clone()).await?;
|
||||
|
||||
memory.return_ = result;
|
||||
} else {
|
||||
@ -677,7 +862,7 @@ pub async fn execute(
|
||||
memory.add(&var_name, value.clone(), source_range)?;
|
||||
}
|
||||
Value::BinaryExpression(binary_expression) => {
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = binary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::FunctionExpression(function_expression) => {
|
||||
@ -686,7 +871,7 @@ pub async fn execute(
|
||||
memory: ProgramMemory,
|
||||
function_expression: Box<FunctionExpression>,
|
||||
_metadata: Vec<Metadata>,
|
||||
engine: EngineConnection| {
|
||||
ctx: ExecutorContext| {
|
||||
Box::pin(async move {
|
||||
let mut fn_memory = memory.clone();
|
||||
|
||||
@ -714,7 +899,7 @@ pub async fn execute(
|
||||
function_expression.body.clone(),
|
||||
&mut fn_memory,
|
||||
BodyType::Block,
|
||||
&engine,
|
||||
&ctx,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -733,11 +918,11 @@ pub async fn execute(
|
||||
)?;
|
||||
}
|
||||
Value::CallExpression(call_expression) => {
|
||||
let result = call_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::PipeExpression(pipe_expression) => {
|
||||
let result = pipe_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = pipe_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::PipeSubstitution(pipe_substitution) => {
|
||||
@ -750,11 +935,11 @@ pub async fn execute(
|
||||
}));
|
||||
}
|
||||
Value::ArrayExpression(array_expression) => {
|
||||
let result = array_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = array_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::ObjectExpression(object_expression) => {
|
||||
let result = object_expression.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = object_expression.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::MemberExpression(member_expression) => {
|
||||
@ -762,7 +947,7 @@ pub async fn execute(
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
Value::UnaryExpression(unary_expression) => {
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = unary_expression.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.add(&var_name, result, source_range)?;
|
||||
}
|
||||
}
|
||||
@ -770,11 +955,11 @@ pub async fn execute(
|
||||
}
|
||||
BodyItem::ReturnStatement(return_statement) => match &return_statement.argument {
|
||||
Value::BinaryExpression(bin_expr) => {
|
||||
let result = bin_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = bin_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::UnaryExpression(unary_expr) => {
|
||||
let result = unary_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = unary_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::Identifier(identifier) => {
|
||||
@ -785,15 +970,15 @@ pub async fn execute(
|
||||
memory.return_ = Some(ProgramReturn::Value(literal.into()));
|
||||
}
|
||||
Value::ArrayExpression(array_expr) => {
|
||||
let result = array_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = array_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::ObjectExpression(obj_expr) => {
|
||||
let result = obj_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = obj_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::CallExpression(call_expr) => {
|
||||
let result = call_expr.execute(memory, &mut pipe_info, engine).await?;
|
||||
let result = call_expr.execute(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::MemberExpression(member_expr) => {
|
||||
@ -801,7 +986,7 @@ pub async fn execute(
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::PipeExpression(pipe_expr) => {
|
||||
let result = pipe_expr.get_result(memory, &mut pipe_info, engine).await?;
|
||||
let result = pipe_expr.get_result(memory, &mut pipe_info, ctx).await?;
|
||||
memory.return_ = Some(ProgramReturn::Value(result));
|
||||
}
|
||||
Value::PipeSubstitution(_) => {}
|
||||
@ -825,7 +1010,9 @@ mod tests {
|
||||
let program = parser.ast()?;
|
||||
let mut mem: ProgramMemory = Default::default();
|
||||
let engine = EngineConnection::new().await?;
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &engine).await?;
|
||||
let planes = DefaultPlanes::new(&engine).await?;
|
||||
let ctx = ExecutorContext { engine, planes };
|
||||
let memory = execute(program, &mut mem, BodyType::Root, &ctx).await?;
|
||||
|
||||
Ok(memory)
|
||||
}
|
||||
@ -849,7 +1036,8 @@ const newVar = myVar + 1"#;
|
||||
async fn test_execute_angled_line_that_intersects() {
|
||||
let ast_fn = |offset: &str| -> String {
|
||||
format!(
|
||||
r#"const part001 = startSketchAt([0, 0])
|
||||
r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> lineTo({{to:[2, 2], tag: "yo"}}, %)
|
||||
|> lineTo([3, 1], %)
|
||||
|> angledLineThatIntersects({{
|
||||
@ -896,7 +1084,8 @@ const yo = 5 + 6
|
||||
|
||||
const abc = 3
|
||||
const identifierGuy = 5
|
||||
const part001 = startSketchAt([-1.2, 4.83])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-1.2, 4.83], %)
|
||||
|> line([2.8, 0], %)
|
||||
|> angledLine([100 + 100, 3.01], %)
|
||||
|> angledLine([abc, 3.02], %)
|
||||
@ -913,7 +1102,8 @@ show(part001)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_pipe_substitutions_unary() {
|
||||
let ast = r#"const myVar = 3
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
||||
|> line([
|
||||
min(segLen('seg01', %), myVar),
|
||||
@ -928,7 +1118,8 @@ show(part001)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_pipe_substitutions() {
|
||||
let ast = r#"const myVar = 3
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line({ to: [3, 4], tag: 'seg01' }, %)
|
||||
|> line([
|
||||
min(segLen('seg01', %), myVar),
|
||||
@ -951,7 +1142,8 @@ const halfArmAngle = armAngle / 2
|
||||
const arrExpShouldNotBeIncluded = [1, 2, 3]
|
||||
const objExpShouldNotBeIncluded = { a: 1, b: 2, c: 3 }
|
||||
|
||||
const part001 = startSketchAt([0, 0])
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> yLineTo(1, %)
|
||||
|> xLine(3.84, %) // selection-range-7ish-before-this
|
||||
|
||||
@ -972,7 +1164,8 @@ fn thing = () => {
|
||||
return -8
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing()], %)
|
||||
@ -994,7 +1187,8 @@ fn thing = (x) => {
|
||||
return -x
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing(8)], %)
|
||||
@ -1016,7 +1210,8 @@ fn thing = (x) => {
|
||||
return [0, -x]
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line(thing(8), %)
|
||||
@ -1042,7 +1237,8 @@ fn thing = (x) => {
|
||||
return other_thing(x)
|
||||
}
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, thing(8)], %)
|
||||
@ -1057,7 +1253,8 @@ show(firstExtrude)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_execute_with_function_sketch() {
|
||||
let ast = r#"fn box = (h, l, w) => {
|
||||
const myBox = startSketchAt([0,0])
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -1077,7 +1274,8 @@ show(fnBox)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_period() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj.start)
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj.start, %)
|
||||
|> line([0, obj.l], %)
|
||||
|> line([obj.w, 0], %)
|
||||
|> line([0, -obj.l], %)
|
||||
@ -1097,7 +1295,8 @@ show(thisBox)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_brace() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj["start"])
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj["start"], %)
|
||||
|> line([0, obj["l"]], %)
|
||||
|> line([obj["w"], 0], %)
|
||||
|> line([0, -obj["l"]], %)
|
||||
@ -1117,7 +1316,8 @@ show(thisBox)
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_object_with_function_mix_period_brace() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj["start"])
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj["start"], %)
|
||||
|> line([0, obj["l"]], %)
|
||||
|> line([obj["w"], 0], %)
|
||||
|> line([10 - obj["w"], -obj.l], %)
|
||||
@ -1138,7 +1338,8 @@ show(thisBox)
|
||||
#[ignore] // ignore til we get loops
|
||||
async fn test_execute_with_function_sketch_loop_objects() {
|
||||
let ast = r#"fn box = (obj) => {
|
||||
let myBox = startSketchAt(obj.start)
|
||||
let myBox = startSketchOn('XY')
|
||||
|> startProfileAt(obj.start, %)
|
||||
|> line([0, obj.l], %)
|
||||
|> line([obj.w, 0], %)
|
||||
|> line([0, -obj.l], %)
|
||||
@ -1160,7 +1361,8 @@ for var in [{start: [0,0], l: 6, w: 10, h: 3}, {start: [-10,-10], l: 3, w: 5, h:
|
||||
#[ignore] // ignore til we get loops
|
||||
async fn test_execute_with_function_sketch_loop_array() {
|
||||
let ast = r#"fn box = (h, l, w, start) => {
|
||||
const myBox = startSketchAt([0,0])
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -1182,7 +1384,8 @@ for var in [[3, 6, 10, [0,0]], [1.5, 3, 5, [-10,-10]]] {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_get_member_of_array_with_function() {
|
||||
let ast = r#"fn box = (array) => {
|
||||
let myBox = startSketchAt(array[0])
|
||||
let myBox =startSketchOn('XY')
|
||||
|> startProfileAt(array[0], %)
|
||||
|> line([0, array[1]], %)
|
||||
|> line([array[2], 0], %)
|
||||
|> line([0, -array[1]], %)
|
||||
@ -1256,7 +1459,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
fn thickness = () => { return 0.56 }
|
||||
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness()], %)
|
||||
@ -1280,7 +1484,8 @@ const leg2 = 8 // inches
|
||||
const thickness_squared = distance * p * FOS * 6 / sigmaAllow
|
||||
const thickness = 0.56 // inches. App does not support square root function yet
|
||||
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
@ -1300,7 +1505,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness_squared = (distance * p * FOS * 6 / (sigmaAllow - width))
|
||||
const thickness = 0.32 // inches. App does not support square root function yet
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
@ -1324,7 +1530,8 @@ const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness_squared = distance * p * FOS * 6 / (sigmaAllow - width)
|
||||
const thickness = 0.32 // inches. App does not support square root function yet
|
||||
const bracket = startSketchAt([0,0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|
@ -552,7 +552,7 @@ impl Parser {
|
||||
_previous_keys: Option<Vec<ObjectKeyInfo>>,
|
||||
has_opening_brace: bool,
|
||||
) -> Result<Vec<ObjectKeyInfo>, KclError> {
|
||||
let previous_keys = _previous_keys.unwrap_or(vec![]);
|
||||
let previous_keys = _previous_keys.unwrap_or_default();
|
||||
let next_token = self.next_meaningful_token(index, None)?;
|
||||
if next_token.index == self.tokens.len() - 1 {
|
||||
return Ok(previous_keys);
|
||||
@ -1918,28 +1918,6 @@ const key = 'c'"#,
|
||||
31,
|
||||
);
|
||||
assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output);
|
||||
let tokens = crate::token::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
a comment
|
||||
spanning a few lines */
|
||||
|> lineTo({ to: [1,0], tag: "rightPath" }, %)
|
||||
|> close(%)"#,
|
||||
);
|
||||
let parser = Parser::new(tokens);
|
||||
let index = 57;
|
||||
let expected_output = (
|
||||
Some(NonCodeNode {
|
||||
start: 106,
|
||||
end: 166,
|
||||
value: NonCodeValue::BlockComment {
|
||||
value: "this is\n a comment\n spanning a few lines".to_string(),
|
||||
},
|
||||
}),
|
||||
59,
|
||||
);
|
||||
assert_eq!(parser.make_non_code_node(index).unwrap(), expected_output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -2070,7 +2048,8 @@ const key = 'c'"#,
|
||||
fn test_next_meaningful_token() {
|
||||
let _offset = 1;
|
||||
let tokens = crate::token::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
r#"const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
a comment
|
||||
@ -2082,10 +2061,10 @@ const key = 'c'"#,
|
||||
let index = 17;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 49,
|
||||
end: 50,
|
||||
value: "(".to_string(),
|
||||
token_type: TokenType::Number,
|
||||
start: 60,
|
||||
end: 61,
|
||||
value: "0".to_string(),
|
||||
}),
|
||||
index: 18,
|
||||
non_code_node: None,
|
||||
@ -2095,9 +2074,9 @@ const key = 'c'"#,
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 50,
|
||||
end: 51,
|
||||
value: "{".to_string(),
|
||||
start: 61,
|
||||
end: 62,
|
||||
value: "]".to_string(),
|
||||
}),
|
||||
index: 19,
|
||||
non_code_node: None,
|
||||
@ -2106,10 +2085,10 @@ const key = 'c'"#,
|
||||
let index = 21;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Colon,
|
||||
start: 54,
|
||||
end: 55,
|
||||
value: ":".to_string(),
|
||||
token_type: TokenType::Operator,
|
||||
start: 64,
|
||||
end: 65,
|
||||
value: "%".to_string(),
|
||||
}),
|
||||
index: 22,
|
||||
non_code_node: None,
|
||||
@ -2118,10 +2097,10 @@ const key = 'c'"#,
|
||||
let index = 24;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Number,
|
||||
start: 57,
|
||||
end: 58,
|
||||
value: "0".to_string(),
|
||||
token_type: TokenType::Operator,
|
||||
start: 69,
|
||||
end: 71,
|
||||
value: "|>".to_string(),
|
||||
}),
|
||||
index: 25,
|
||||
non_code_node: None,
|
||||
@ -2130,324 +2109,12 @@ const key = 'c'"#,
|
||||
let index = 25;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 58,
|
||||
end: 59,
|
||||
value: ",".to_string(),
|
||||
token_type: TokenType::Word,
|
||||
start: 72,
|
||||
end: 78,
|
||||
value: "lineTo".to_string(),
|
||||
}),
|
||||
index: 26,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 28;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 61,
|
||||
end: 62,
|
||||
value: "]".to_string(),
|
||||
}),
|
||||
index: 29,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 29;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 62,
|
||||
end: 63,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 30,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 32;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Colon,
|
||||
start: 67,
|
||||
end: 68,
|
||||
value: ":".to_string(),
|
||||
}),
|
||||
index: 33,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 37;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 79,
|
||||
end: 80,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 38,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 40;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 82,
|
||||
end: 83,
|
||||
value: ")".to_string(),
|
||||
}),
|
||||
index: 41,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 45;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 95,
|
||||
end: 96,
|
||||
value: "(".to_string(),
|
||||
}),
|
||||
index: 46,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 46;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 96,
|
||||
end: 97,
|
||||
value: "[".to_string(),
|
||||
}),
|
||||
index: 47,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 47;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Number,
|
||||
start: 97,
|
||||
end: 98,
|
||||
value: "1".to_string(),
|
||||
}),
|
||||
index: 48,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 48;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 98,
|
||||
end: 99,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 49,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 51;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 101,
|
||||
end: 102,
|
||||
value: "]".to_string(),
|
||||
}),
|
||||
index: 52,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 52;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 102,
|
||||
end: 103,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 53,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 55;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 105,
|
||||
end: 106,
|
||||
value: ")".to_string(),
|
||||
}),
|
||||
index: 56,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 62;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 175,
|
||||
end: 176,
|
||||
value: "(".to_string(),
|
||||
}),
|
||||
index: 63,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 63;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 176,
|
||||
end: 177,
|
||||
value: "{".to_string(),
|
||||
}),
|
||||
index: 64,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 66;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Colon,
|
||||
start: 180,
|
||||
end: 181,
|
||||
value: ":".to_string(),
|
||||
}),
|
||||
index: 67,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 69;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Number,
|
||||
start: 183,
|
||||
end: 184,
|
||||
value: "1".to_string(),
|
||||
}),
|
||||
index: 70,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 70;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 184,
|
||||
end: 185,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 71,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 71;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Number,
|
||||
start: 185,
|
||||
end: 186,
|
||||
value: "0".to_string(),
|
||||
}),
|
||||
index: 72,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 72;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 186,
|
||||
end: 187,
|
||||
value: "]".to_string(),
|
||||
}),
|
||||
index: 73,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 73;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 187,
|
||||
end: 188,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 74,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 76;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Colon,
|
||||
start: 192,
|
||||
end: 193,
|
||||
value: ":".to_string(),
|
||||
}),
|
||||
index: 77,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 81;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Comma,
|
||||
start: 207,
|
||||
end: 208,
|
||||
value: ",".to_string(),
|
||||
}),
|
||||
index: 82,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 84;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 210,
|
||||
end: 211,
|
||||
value: ")".to_string(),
|
||||
}),
|
||||
index: 85,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 89;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 222,
|
||||
end: 223,
|
||||
value: "(".to_string(),
|
||||
}),
|
||||
index: 90,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 90;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Operator,
|
||||
start: 223,
|
||||
end: 224,
|
||||
value: "%".to_string(),
|
||||
}),
|
||||
index: 91,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
let index = 91;
|
||||
let expected_output = TokenReturnWithNonCode {
|
||||
token: Some(Token {
|
||||
token_type: TokenType::Brace,
|
||||
start: 224,
|
||||
end: 225,
|
||||
value: ")".to_string(),
|
||||
}),
|
||||
index: 92,
|
||||
index: 27,
|
||||
non_code_node: None,
|
||||
};
|
||||
assert_eq!(parser.next_meaningful_token(index, None).unwrap(), expected_output);
|
||||
@ -2456,7 +2123,8 @@ const key = 'c'"#,
|
||||
#[test]
|
||||
fn test_find_closing_brace() {
|
||||
let tokens = crate::token::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
r#"const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
a comment
|
||||
@ -2465,11 +2133,10 @@ const key = 'c'"#,
|
||||
|> close(%)"#,
|
||||
);
|
||||
let parser = Parser::new(tokens);
|
||||
assert_eq!(parser.find_closing_brace(7, 0, "").unwrap(), 13);
|
||||
assert_eq!(parser.find_closing_brace(18, 0, "").unwrap(), 41);
|
||||
assert_eq!(parser.find_closing_brace(46, 0, "").unwrap(), 56);
|
||||
assert_eq!(parser.find_closing_brace(63, 0, "").unwrap(), 85);
|
||||
assert_eq!(parser.find_closing_brace(90, 0, "").unwrap(), 92);
|
||||
assert_eq!(parser.find_closing_brace(7, 0, "").unwrap(), 9);
|
||||
assert_eq!(parser.find_closing_brace(14, 0, "").unwrap(), 23);
|
||||
assert_eq!(parser.find_closing_brace(29, 0, "").unwrap(), 47);
|
||||
assert_eq!(parser.find_closing_brace(57, 0, "").unwrap(), 62);
|
||||
|
||||
let basic = "( hey )";
|
||||
let parser = Parser::new(crate::token::lexer(basic));
|
||||
@ -2490,7 +2157,8 @@ const key = 'c'"#,
|
||||
#[test]
|
||||
fn test_is_call_expression() {
|
||||
let tokens = crate::token::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
r#"const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
a comment
|
||||
@ -2501,8 +2169,8 @@ const key = 'c'"#,
|
||||
let parser = Parser::new(tokens);
|
||||
|
||||
assert_eq!(parser.is_call_expression(4).unwrap(), None);
|
||||
assert_eq!(parser.is_call_expression(6).unwrap(), Some(13));
|
||||
assert_eq!(parser.is_call_expression(15).unwrap(), None);
|
||||
assert_eq!(parser.is_call_expression(6).unwrap(), Some(9));
|
||||
assert_eq!(parser.is_call_expression(9).unwrap(), None);
|
||||
assert_eq!(parser.is_call_expression(43).unwrap(), None);
|
||||
assert_eq!(parser.is_call_expression(60).unwrap(), None);
|
||||
assert_eq!(parser.is_call_expression(87).unwrap(), None);
|
||||
@ -2511,7 +2179,8 @@ const key = 'c'"#,
|
||||
#[test]
|
||||
fn test_find_next_declaration_keyword() {
|
||||
let tokens = crate::token::lexer(
|
||||
r#"const mySketch = startSketchAt([0,0])
|
||||
r#"const mySketch = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
||||
|> lineTo([1, 1], %) /* this is
|
||||
a comment
|
||||
@ -2522,7 +2191,10 @@ const key = 'c'"#,
|
||||
let parser = Parser::new(tokens);
|
||||
assert_eq!(
|
||||
parser.find_next_declaration_keyword(4).unwrap(),
|
||||
TokenReturn { token: None, index: 92 }
|
||||
TokenReturn {
|
||||
token: None,
|
||||
index: 102
|
||||
}
|
||||
);
|
||||
|
||||
let tokens = crate::token::lexer(
|
||||
@ -2986,7 +2658,8 @@ show(mySk1)"#;
|
||||
#[test]
|
||||
fn test_parse_half_pipe_small() {
|
||||
let tokens = crate::token::lexer(
|
||||
"const secondExtrude = startSketchAt([0,0])
|
||||
"const secondExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|",
|
||||
);
|
||||
let parser = Parser::new(tokens);
|
||||
@ -3067,7 +2740,8 @@ const height = [obj["a"] -1, 0]"#,
|
||||
let tokens = crate::token::lexer(
|
||||
"const height = 10
|
||||
|
||||
const firstExtrude = startSketchAt([0,0])
|
||||
const firstExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, 8], %)
|
||||
|> line([20, 0], %)
|
||||
|> line([0, -8], %)
|
||||
@ -3076,7 +2750,8 @@ const firstExtrude = startSketchAt([0,0])
|
||||
|
||||
show(firstExtrude)
|
||||
|
||||
const secondExtrude = startSketchAt([0,0])
|
||||
const secondExtrude = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|",
|
||||
);
|
||||
let parser = Parser::new(tokens);
|
||||
@ -3457,7 +3132,8 @@ thing(false)
|
||||
#[test]
|
||||
fn test_member_expression_sketch_group() {
|
||||
let some_program_string = r#"fn cube = (pos, scale) => {
|
||||
const sg = startSketchAt(pos)
|
||||
const sg = startSketchOn('XY')
|
||||
|> startProfileAt(pos, %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
@ -3499,7 +3175,8 @@ let other_thing = 2 * cos(3)"#;
|
||||
#[test]
|
||||
fn test_negative_arguments() {
|
||||
let some_program_string = r#"fn box = (p, h, l, w) => {
|
||||
const myBox = startSketchAt(p)
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt(p, %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
|
@ -17,9 +17,9 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
ast::types::parse_json_number_as_f64,
|
||||
engine::{EngineConnection, EngineManager},
|
||||
engine::EngineManager,
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{ExtrudeGroup, MemoryItem, Metadata, SketchGroup, SourceRange},
|
||||
executor::{ExecutorContext, ExtrudeGroup, MemoryItem, Metadata, Plane, SketchGroup, SourceRange},
|
||||
};
|
||||
|
||||
pub type StdFn = fn(Args) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<MemoryItem, KclError>>>>;
|
||||
@ -59,6 +59,8 @@ impl StdLib {
|
||||
Box::new(crate::std::sketch::AngledLineOfYLength),
|
||||
Box::new(crate::std::sketch::AngledLineThatIntersects),
|
||||
Box::new(crate::std::sketch::StartSketchAt),
|
||||
Box::new(crate::std::sketch::StartSketchOn),
|
||||
Box::new(crate::std::sketch::StartProfileAt),
|
||||
Box::new(crate::std::sketch::Close),
|
||||
Box::new(crate::std::sketch::Arc),
|
||||
Box::new(crate::std::sketch::TangentalArc),
|
||||
@ -109,15 +111,15 @@ impl Default for StdLib {
|
||||
pub struct Args {
|
||||
pub args: Vec<MemoryItem>,
|
||||
pub source_range: SourceRange,
|
||||
engine: EngineConnection,
|
||||
pub ctx: ExecutorContext,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn new(args: Vec<MemoryItem>, source_range: SourceRange, engine: EngineConnection) -> Self {
|
||||
pub fn new(args: Vec<MemoryItem>, source_range: SourceRange, ctx: ExecutorContext) -> Self {
|
||||
Self {
|
||||
args,
|
||||
source_range,
|
||||
engine,
|
||||
ctx,
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +128,7 @@ impl Args {
|
||||
id: uuid::Uuid,
|
||||
cmd: kittycad::types::ModelingCmd,
|
||||
) -> Result<OkWebSocketResponseData, KclError> {
|
||||
self.engine.send_modeling_cmd(id, self.source_range, cmd).await
|
||||
self.ctx.engine.send_modeling_cmd(id, self.source_range, cmd).await
|
||||
}
|
||||
|
||||
fn make_user_val_from_json(&self, j: serde_json::Value) -> Result<MemoryItem, KclError> {
|
||||
@ -308,6 +310,44 @@ impl Args {
|
||||
Ok((data, sketch_group))
|
||||
}
|
||||
|
||||
fn get_data_and_plane<T: serde::de::DeserializeOwned>(&self) -> Result<(T, Box<Plane>), KclError> {
|
||||
let first_value = self
|
||||
.args
|
||||
.first()
|
||||
.ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a struct as the first argument, found `{:?}`", self.args),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?
|
||||
.get_json_value()?;
|
||||
|
||||
let data: T = serde_json::from_value(first_value).map_err(|e| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Failed to deserialize struct from JSON: {}", e),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let second_value = self.args.get(1).ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a Plane as the second argument, found `{:?}`", self.args),
|
||||
source_ranges: vec![self.source_range],
|
||||
})
|
||||
})?;
|
||||
|
||||
let plane = if let MemoryItem::Plane(p) = second_value {
|
||||
p.clone()
|
||||
} else {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: format!("Expected a Plane as the second argument, found `{:?}`", self.args),
|
||||
source_ranges: vec![self.source_range],
|
||||
}));
|
||||
};
|
||||
|
||||
Ok((data, plane))
|
||||
}
|
||||
|
||||
fn get_segment_name_to_number_sketch_group(&self) -> Result<(String, f64, Box<SketchGroup>), KclError> {
|
||||
// Iterate over our args, the first argument should be a UserVal with a string value.
|
||||
// The second argument should be a number.
|
||||
|
@ -9,7 +9,9 @@ use serde::{Deserialize, Serialize};
|
||||
use super::utils::Angle;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
executor::{BasePath, GeoMeta, MemoryItem, Path, Point2d, Position, Rotation, SketchGroup},
|
||||
executor::{
|
||||
BasePath, GeoMeta, MemoryItem, Path, Plane, PlaneType, Point2d, Point3d, Position, Rotation, SketchGroup,
|
||||
},
|
||||
std::{
|
||||
utils::{arc_angles, arc_center_and_end, get_x_component, get_y_component, intersection_with_parallel_line},
|
||||
Args,
|
||||
@ -649,11 +651,202 @@ pub async fn start_sketch_at(args: Args) -> Result<MemoryItem, KclError> {
|
||||
Ok(MemoryItem::SketchGroup(sketch_group))
|
||||
}
|
||||
|
||||
/// Start a sketch at a given point.
|
||||
/// Start a sketch at a given point on the 'XY' plane.
|
||||
#[stdlib {
|
||||
name = "startSketchAt",
|
||||
}]
|
||||
async fn inner_start_sketch_at(data: LineData, args: Args) -> Result<Box<SketchGroup>, KclError> {
|
||||
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
|
||||
let xy_plane = PlaneData::XY;
|
||||
let plane = inner_start_sketch_on(xy_plane, args.clone()).await?;
|
||||
let sketch_group = inner_start_profile_at(data, plane, args).await?;
|
||||
Ok(sketch_group)
|
||||
}
|
||||
|
||||
/// Data for a plane.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum PlaneData {
|
||||
/// The XY plane.
|
||||
#[serde(rename = "XY", alias = "xy")]
|
||||
XY,
|
||||
/// The opposite side of the XY plane.
|
||||
#[serde(rename = "-XY", alias = "-xy")]
|
||||
NegXY,
|
||||
/// The XZ plane.
|
||||
#[serde(rename = "XZ", alias = "xz")]
|
||||
XZ,
|
||||
/// The opposite side of the XZ plane.
|
||||
#[serde(rename = "-XZ", alias = "-xz")]
|
||||
NegXZ,
|
||||
/// The YZ plane.
|
||||
#[serde(rename = "YZ", alias = "yz")]
|
||||
YZ,
|
||||
/// The opposite side of the YZ plane.
|
||||
#[serde(rename = "-YZ", alias = "-yz")]
|
||||
NegYZ,
|
||||
/// A defined plane.
|
||||
Plane {
|
||||
/// Origin of the plane.
|
||||
origin: Box<Point3d>,
|
||||
/// What should the plane’s X axis be?
|
||||
x_axis: Box<Point3d>,
|
||||
/// What should the plane’s Y axis be?
|
||||
y_axis: Box<Point3d>,
|
||||
/// The z-axis (normal).
|
||||
z_axis: Box<Point3d>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<PlaneData> for Plane {
|
||||
fn from(value: PlaneData) -> Self {
|
||||
let id = uuid::Uuid::new_v4();
|
||||
match value {
|
||||
PlaneData::XY => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
z_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
value: PlaneType::XY,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::NegXY => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
z_axis: Point3d::new(0.0, 0.0, -1.0),
|
||||
value: PlaneType::XY,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::XZ => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
value: PlaneType::XZ,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::NegXZ => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(0.0, -1.0, 0.0),
|
||||
value: PlaneType::XZ,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::YZ => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(1.0, 0.0, 0.0),
|
||||
value: PlaneType::YZ,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::NegYZ => Plane {
|
||||
id,
|
||||
origin: Point3d::new(0.0, 0.0, 0.0),
|
||||
x_axis: Point3d::new(0.0, 1.0, 0.0),
|
||||
y_axis: Point3d::new(0.0, 0.0, 1.0),
|
||||
z_axis: Point3d::new(-1.0, 0.0, 0.0),
|
||||
value: PlaneType::YZ,
|
||||
meta: vec![],
|
||||
},
|
||||
PlaneData::Plane {
|
||||
origin,
|
||||
x_axis,
|
||||
y_axis,
|
||||
z_axis,
|
||||
} => Plane {
|
||||
id,
|
||||
origin: *origin,
|
||||
x_axis: *x_axis,
|
||||
y_axis: *y_axis,
|
||||
z_axis: *z_axis,
|
||||
value: PlaneType::Custom,
|
||||
meta: vec![],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a sketch on a specific plane.
|
||||
pub async fn start_sketch_on(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let data: PlaneData = args.get_data()?;
|
||||
|
||||
let plane = inner_start_sketch_on(data, args).await?;
|
||||
Ok(MemoryItem::Plane(plane))
|
||||
}
|
||||
|
||||
/// Start a sketch at a given point.
|
||||
#[stdlib {
|
||||
name = "startSketchOn",
|
||||
}]
|
||||
async fn inner_start_sketch_on(data: PlaneData, args: Args) -> Result<Box<Plane>, KclError> {
|
||||
let mut plane: Plane = data.clone().into();
|
||||
|
||||
plane.id = match data {
|
||||
PlaneData::XY | PlaneData::NegXY => args.ctx.planes.xy,
|
||||
PlaneData::XZ | PlaneData::NegXZ => args.ctx.planes.xz,
|
||||
PlaneData::YZ | PlaneData::NegYZ => args.ctx.planes.yz,
|
||||
PlaneData::Plane {
|
||||
origin,
|
||||
x_axis,
|
||||
y_axis,
|
||||
z_axis: _,
|
||||
} => {
|
||||
let id = uuid::Uuid::new_v4();
|
||||
// Create the plane.
|
||||
args.send_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::MakePlane {
|
||||
clobber: false,
|
||||
origin: (*origin).into(),
|
||||
size: 60.0,
|
||||
x_axis: (*x_axis).into(),
|
||||
y_axis: (*y_axis).into(),
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
id
|
||||
}
|
||||
};
|
||||
|
||||
// Enter sketch mode on the plane.
|
||||
args.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
ModelingCmd::SketchModeEnable {
|
||||
animated: false,
|
||||
ortho: false,
|
||||
plane_id: plane.id,
|
||||
// We pass in the normal for the plane here.
|
||||
disable_camera_with_plane: Some(plane.z_axis.clone().into()),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(Box::new(plane))
|
||||
}
|
||||
|
||||
/// Start a profile at a given point.
|
||||
pub async fn start_profile_at(args: Args) -> Result<MemoryItem, KclError> {
|
||||
let (data, plane): (LineData, Box<Plane>) = args.get_data_and_plane()?;
|
||||
|
||||
let sketch_group = inner_start_profile_at(data, plane, args).await?;
|
||||
Ok(MemoryItem::SketchGroup(sketch_group))
|
||||
}
|
||||
|
||||
/// Start a profile at a given point.
|
||||
#[stdlib {
|
||||
name = "startProfileAt",
|
||||
}]
|
||||
async fn inner_start_profile_at(data: LineData, plane: Box<Plane>, args: Args) -> Result<Box<SketchGroup>, KclError> {
|
||||
let to = match &data {
|
||||
LineData::PointWithTag { to, .. } => *to,
|
||||
LineData::Point(to) => *to,
|
||||
@ -694,6 +887,7 @@ async fn inner_start_sketch_at(data: LineData, args: Args) -> Result<Box<SketchG
|
||||
id: path_id,
|
||||
position: Position([0.0, 0.0, 0.0]),
|
||||
rotation: Rotation([0.0, 0.0, 0.0, 1.0]),
|
||||
plane_id: Some(plane.id),
|
||||
value: vec![],
|
||||
start: current_path,
|
||||
meta: vec![args.source_range.into()],
|
||||
@ -728,6 +922,13 @@ async fn inner_close(sketch_group: Box<SketchGroup>, args: Args) -> Result<Box<S
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Exit sketch mode, since if we were in a plane we'd want to disable the sketch mode after.
|
||||
if sketch_group.plane_id.is_some() {
|
||||
// We were on a plane, disable the sketch mode.
|
||||
args.send_modeling_cmd(uuid::Uuid::new_v4(), ModelingCmd::SketchModeDisable {})
|
||||
.await?;
|
||||
}
|
||||
|
||||
let mut new_sketch_group = sketch_group.clone();
|
||||
new_sketch_group.value.push(Path::ToPoint {
|
||||
base: BasePath {
|
||||
@ -1199,7 +1400,7 @@ mod tests {
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
use crate::std::sketch::LineData;
|
||||
use crate::std::sketch::{LineData, PlaneData};
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_line_data() {
|
||||
@ -1221,4 +1422,23 @@ mod tests {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_plane_data() {
|
||||
let data = PlaneData::XY;
|
||||
let mut str_json = serde_json::to_string(&data).unwrap();
|
||||
assert_eq!(str_json, "\"XY\"");
|
||||
|
||||
str_json = "\"YZ\"".to_string();
|
||||
let data: PlaneData = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, PlaneData::YZ);
|
||||
|
||||
str_json = "\"-YZ\"".to_string();
|
||||
let data: PlaneData = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, PlaneData::NegYZ);
|
||||
|
||||
str_json = "\"-xz\"".to_string();
|
||||
let data: PlaneData = serde_json::from_str(&str_json).unwrap();
|
||||
assert_eq!(data, PlaneData::NegXZ);
|
||||
}
|
||||
}
|
||||
|
@ -1434,13 +1434,13 @@ const things = "things"
|
||||
fn test_kitt() {
|
||||
let program = include_str!("../../../tests/executor/inputs/kittycad_svg.kcl");
|
||||
let actual = lexer(program).unwrap();
|
||||
assert_eq!(actual.len(), 5088);
|
||||
assert_eq!(actual.len(), 5098);
|
||||
}
|
||||
#[test]
|
||||
fn test_pipes_on_pipes() {
|
||||
let program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||
let actual = lexer(program).unwrap();
|
||||
assert_eq!(actual.len(), 17836);
|
||||
assert_eq!(actual.len(), 17846);
|
||||
}
|
||||
#[test]
|
||||
fn test_lexer_negative_word() {
|
||||
|
@ -16,16 +16,21 @@ pub async fn execute_wasm(
|
||||
program_str: &str,
|
||||
memory_str: &str,
|
||||
manager: kcl_lib::engine::conn_wasm::EngineCommandManager,
|
||||
planes_str: &str,
|
||||
) -> Result<JsValue, String> {
|
||||
// deserialize the ast from a stringified json
|
||||
|
||||
use kcl_lib::executor::ExecutorContext;
|
||||
let program: kcl_lib::ast::types::Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||
let planes: kcl_lib::executor::DefaultPlanes = serde_json::from_str(planes_str).map_err(|e| e.to_string())?;
|
||||
let mut mem: kcl_lib::executor::ProgramMemory = serde_json::from_str(memory_str).map_err(|e| e.to_string())?;
|
||||
|
||||
let engine = kcl_lib::engine::EngineConnection::new(manager)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
let ctx = ExecutorContext { engine, planes };
|
||||
|
||||
let memory = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &engine)
|
||||
let memory = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &ctx)
|
||||
.await
|
||||
.map_err(String::from)?;
|
||||
// The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
|
||||
@ -40,11 +45,14 @@ pub async fn modify_ast_for_sketch_wasm(
|
||||
manager: kcl_lib::engine::conn_wasm::EngineCommandManager,
|
||||
program_str: &str,
|
||||
sketch_name: &str,
|
||||
plane_type: &str,
|
||||
sketch_id: &str,
|
||||
) -> Result<JsValue, String> {
|
||||
// deserialize the ast from a stringified json
|
||||
let mut program: kcl_lib::ast::types::Program = serde_json::from_str(program_str).map_err(|e| e.to_string())?;
|
||||
|
||||
let plane: kcl_lib::executor::PlaneType = serde_json::from_str(plane_type).map_err(|e| e.to_string())?;
|
||||
|
||||
let mut engine = kcl_lib::engine::EngineConnection::new(manager)
|
||||
.await
|
||||
.map_err(|e| format!("{:?}", e))?;
|
||||
@ -53,6 +61,7 @@ pub async fn modify_ast_for_sketch_wasm(
|
||||
&mut engine,
|
||||
&mut program,
|
||||
sketch_name,
|
||||
plane,
|
||||
uuid::Uuid::parse_str(sketch_id).map_err(|e| e.to_string())?,
|
||||
)
|
||||
.await
|
||||
|
@ -1,4 +1,5 @@
|
||||
const svg = startSketchAt([0, 0])
|
||||
const svg = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|
||||
|> lineTo([2.52, -26.04], %) // MoveAbsolute
|
||||
|> lineTo([2.52, -25.2], %) // VerticalLineAbsolute
|
||||
|
@ -1,4 +1,5 @@
|
||||
const svg = startSketchAt([0, 0])
|
||||
const svg = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|
||||
|> lineTo([22.687663, -2.7664351], %) // MoveRelative
|
||||
|> lineTo([15.687664000000002, -5.7664351], %) // MoveRelative
|
||||
|
@ -37,10 +37,13 @@ async fn execute_and_snapshot(code: &str) -> Result<image::DynamicImage> {
|
||||
let program = parser.ast()?;
|
||||
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
|
||||
let engine = kcl_lib::engine::EngineConnection::new(ws).await?;
|
||||
let _ = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &engine).await?;
|
||||
let planes = kcl_lib::executor::DefaultPlanes::new(&engine).await?;
|
||||
let ctx = kcl_lib::executor::ExecutorContext { engine, planes };
|
||||
let _ = kcl_lib::executor::execute(program, &mut mem, kcl_lib::executor::BodyType::Root, &ctx).await?;
|
||||
|
||||
// Send a snapshot request to the engine.
|
||||
let resp = engine
|
||||
let resp = ctx
|
||||
.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
kcl_lib::executor::SourceRange::default(),
|
||||
@ -68,7 +71,8 @@ async fn execute_and_snapshot(code: &str) -> Result<image::DynamicImage> {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_execute_with_function_sketch() {
|
||||
let code = r#"fn box = (h, l, w) => {
|
||||
const myBox = startSketchAt([0,0])
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt([0,0], %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -89,7 +93,8 @@ show(fnBox)"#;
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_execute_with_function_sketch_with_position() {
|
||||
let code = r#"fn box = (p, h, l, w) => {
|
||||
const myBox = startSketchAt(p)
|
||||
const myBox = startSketchOn('XY')
|
||||
|> startProfileAt(p, %)
|
||||
|> line([0, l], %)
|
||||
|> line([w, 0], %)
|
||||
|> line([0, -l], %)
|
||||
@ -107,7 +112,8 @@ show(box([0,0], 3, 6, 10))"#;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_execute_with_angled_line() {
|
||||
let code = r#"const part001 = startSketchAt([4.83, 12.56])
|
||||
let code = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([4.83, 12.56], %)
|
||||
|> line([15.1, 2.48], %)
|
||||
|> line({ to: [3.15, -9.85], tag: 'seg01' }, %)
|
||||
|> line([-15.17, -4.1], %)
|
||||
@ -133,7 +139,8 @@ const FOS = 2
|
||||
const leg1 = 5 // inches
|
||||
const leg2 = 8 // inches
|
||||
const thickness = sqrt(distance * p * FOS * 6 / sigmaAllow / width) // inches
|
||||
const bracket = startSketchAt([0, 0])
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, leg1], %)
|
||||
|> line([leg2, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
@ -185,7 +192,8 @@ show(bracket)"#;
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_execute_engine_error_return() {
|
||||
let code = r#"const part001 = startSketchAt([5.5229, 5.25217])
|
||||
let code = r#"const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([5.5229, 5.25217], %)
|
||||
|> line([10.50433, -1.19122], %)
|
||||
|> line([8.01362, -5.48731], %)
|
||||
|> line([-1.02877, -6.76825], %)
|
||||
@ -197,7 +205,7 @@ async fn serial_test_execute_engine_error_return() {
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.err().unwrap().to_string(),
|
||||
r#"engine: KclErrorDetails { source_ranges: [SourceRange([193, 206])], message: "Modeling command failed: Some([ApiError { error_code: BadRequest, message: \"The path is not closed. Solid2D construction requires a closed path!\" }])" }"#,
|
||||
r#"engine: KclErrorDetails { source_ranges: [SourceRange([222, 235])], message: "Modeling command failed: Some([ApiError { error_code: BadRequest, message: \"The path is not closed. Solid2D construction requires a closed path!\" }])" }"#,
|
||||
);
|
||||
}
|
||||
|
||||
@ -221,7 +229,8 @@ async fn serial_test_execute_kittycad_svg() {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_member_expression_sketch_group() {
|
||||
let code = r#"fn cube = (pos, scale) => {
|
||||
const sg = startSketchAt(pos)
|
||||
const sg = startSketchOn('XY')
|
||||
|> startProfileAt(pos, %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
@ -252,7 +261,8 @@ async fn test_close_arc() {
|
||||
const radius = 40
|
||||
const height = 3
|
||||
|
||||
const body = startSketchAt([center[0]+radius, center[1]])
|
||||
const body = startSketchOn('XY')
|
||||
|> startProfileAt([center[0]+radius, center[1]], %)
|
||||
|> arc({angle_end: 360, angle_start: 0, radius: radius}, %)
|
||||
|> close(%)
|
||||
|> extrude(height, %)
|
||||
@ -270,7 +280,8 @@ const height = 10
|
||||
const length = 12
|
||||
|
||||
fn box = (sk1, sk2, scale) => {
|
||||
const boxSketch = startSketchAt([sk1, sk2])
|
||||
const boxSketch = startSketchOn('XY')
|
||||
|> startProfileAt([sk1, sk2], %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
@ -326,3 +337,89 @@ async fn test_basic_tangental_arc_to() {
|
||||
let result = execute_and_snapshot(code).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/tangental_arc_to.png", &result, 1.0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_different_planes_same_drawing() {
|
||||
let code = r#"const width = 5
|
||||
const height = 10
|
||||
const length = 12
|
||||
|
||||
fn box = (sk1, sk2, scale, plane) => {
|
||||
const boxsketch = startSketchOn(plane)
|
||||
|> startProfileAt([sk1, sk2], %)
|
||||
|> line([0, scale], %)
|
||||
|> line([scale, 0], %)
|
||||
|> line([0, -scale], %)
|
||||
|> close(%)
|
||||
|> extrude(scale, %)
|
||||
return boxsketch
|
||||
}
|
||||
|
||||
box(0, 0, 5, 'xy')
|
||||
box(10, 23, 8, 'xz')
|
||||
box(30, 43, 18, '-xy')
|
||||
let thing = box(-12, -15, 10, 'yz')
|
||||
box(-20, -5, 10, 'xy')"#;
|
||||
|
||||
let result = execute_and_snapshot(code).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/different_planes_same_drawing.png", &result, 1.0);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_lots_of_planes() {
|
||||
let code = r#"const sigmaAllow = 15000 // psi
|
||||
const width = 11 // inch
|
||||
const p = 150 // Force on shelf - lbs
|
||||
const distance = 12 // inches
|
||||
const FOS = 2
|
||||
const thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))
|
||||
const filletR = thickness * 2
|
||||
const shelfMountL = 9
|
||||
const wallMountL = 8
|
||||
|
||||
const bracket = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([0, wallMountL], %)
|
||||
|> tangentalArc({ radius: filletR, offset: 90 }, %)
|
||||
|> line([-shelfMountL, 0], %)
|
||||
|> line([0, -thickness], %)
|
||||
|> line([shelfMountL, 0], %)
|
||||
|> tangentalArc({
|
||||
radius: filletR - thickness,
|
||||
offset: -90
|
||||
}, %)
|
||||
|> line([0, -wallMountL], %)
|
||||
|> close(%)
|
||||
|> extrude(width, %)
|
||||
|
||||
show(bracket)
|
||||
const part001 = startSketchOn('XY')
|
||||
|> startProfileAt([-15.53, -10.28], %)
|
||||
|> line([10.49, -2.08], %)
|
||||
|> line([10.42, 8.47], %)
|
||||
|> line([-19.16, 5.1], %)
|
||||
|> close(%)
|
||||
|> extrude(4, %)
|
||||
|
||||
const part002 = startSketchOn('-XZ')
|
||||
|> startProfileAt([-9.35, 19.18], %)
|
||||
|> line([32.14, -2.47], %)
|
||||
|> line([8.39, -3.73], %)
|
||||
|> close(%)
|
||||
|
||||
const part003 = startSketchOn('-XZ')
|
||||
|> startProfileAt([13.82, 16.51], %)
|
||||
|> line([-6.24, -30.82], %)
|
||||
|> line([8.39, -3.73], %)
|
||||
|> close(%)
|
||||
|
||||
const part004 = startSketchOn('YZ')
|
||||
|> startProfileAt([19.04, 20.22], %)
|
||||
|> line([9.44, -30.16], %)
|
||||
|> line([8.39, -3.73], %)
|
||||
|> close(%)
|
||||
"#;
|
||||
|
||||
let result = execute_and_snapshot(code).await.unwrap();
|
||||
twenty_twenty::assert_image("tests/executor/outputs/lots_of_planes.png", &result, 1.0);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 88 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
BIN
src/wasm-lib/tests/executor/outputs/lots_of_planes.png
Normal file
After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 69 KiB After Width: | Height: | Size: 69 KiB |
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 78 KiB |
Before Width: | Height: | Size: 71 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
@ -1,14 +1,14 @@
|
||||
use anyhow::Result;
|
||||
use kcl_lib::{
|
||||
ast::{modify::modify_ast_for_sketch, types::Program},
|
||||
engine::{EngineConnection, EngineManager},
|
||||
executor::{MemoryItem, SourceRange},
|
||||
engine::EngineManager,
|
||||
executor::{ExecutorContext, MemoryItem, PlaneType, SourceRange},
|
||||
};
|
||||
use kittycad::types::{ModelingCmd, Point3D};
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
/// Setup the engine and parse code for an ast.
|
||||
async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uuid::Uuid)> {
|
||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid::Uuid)> {
|
||||
let user_agent = concat!(env!("CARGO_PKG_NAME"), ".rs/", env!("CARGO_PKG_VERSION"),);
|
||||
let http_client = reqwest::Client::builder()
|
||||
.user_agent(user_agent)
|
||||
@ -38,8 +38,9 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
let program = parser.ast()?;
|
||||
let mut mem: kcl_lib::executor::ProgramMemory = Default::default();
|
||||
let engine = kcl_lib::engine::EngineConnection::new(ws).await?;
|
||||
let memory =
|
||||
kcl_lib::executor::execute(program.clone(), &mut mem, kcl_lib::executor::BodyType::Root, &engine).await?;
|
||||
let planes = kcl_lib::executor::DefaultPlanes::new(&engine).await?;
|
||||
let ctx = ExecutorContext { engine, planes };
|
||||
let memory = kcl_lib::executor::execute(program.clone(), &mut mem, kcl_lib::executor::BodyType::Root, &ctx).await?;
|
||||
|
||||
// We need to get the sketch ID.
|
||||
// Get the sketch group ID from memory.
|
||||
@ -49,7 +50,7 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
let sketch_id = sketch_group.id;
|
||||
|
||||
let plane_id = uuid::Uuid::new_v4();
|
||||
engine
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
plane_id,
|
||||
SourceRange::default(),
|
||||
@ -59,6 +60,7 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
size: 60.0,
|
||||
x_axis: Point3D { x: 1.0, y: 0.0, z: 0.0 },
|
||||
y_axis: Point3D { x: 0.0, y: 1.0, z: 0.0 },
|
||||
hide: Some(true),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
@ -66,7 +68,7 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
// Enter sketch mode.
|
||||
// We can't get control points without being in sketch mode.
|
||||
// You can however get path info without sketch mode.
|
||||
engine
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
@ -74,13 +76,14 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
animated: false,
|
||||
ortho: true,
|
||||
plane_id,
|
||||
disable_camera_with_plane: Some(Point3D { x: 0.0, y: 0.0, z: 1.0 }),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Enter edit mode.
|
||||
// We can't get control points of an existing sketch without being in edit mode.
|
||||
engine
|
||||
ctx.engine
|
||||
.send_modeling_cmd(
|
||||
uuid::Uuid::new_v4(),
|
||||
SourceRange::default(),
|
||||
@ -88,14 +91,15 @@ async fn setup(code: &str, name: &str) -> Result<(EngineConnection, Program, uui
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok((engine, program, sketch_id))
|
||||
Ok((ctx, program, sketch_id))
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn serial_test_modify_sketch_part001() {
|
||||
let name = "part001";
|
||||
let code = format!(
|
||||
r#"const {} = startSketchAt([8.41, 5.78])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([8.41, 5.78], %)
|
||||
|> line([7.37, -11.0], %)
|
||||
|> line([-8.69, -3.75], %)
|
||||
|> line([-5.0, 4.25], %)
|
||||
@ -103,9 +107,9 @@ async fn serial_test_modify_sketch_part001() {
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let new_code = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id)
|
||||
let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -119,7 +123,8 @@ async fn serial_test_modify_sketch_part001() {
|
||||
async fn serial_test_modify_sketch_part002() {
|
||||
let name = "part002";
|
||||
let code = format!(
|
||||
r#"const {} = startSketchAt([8.41, 5.78])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([8.41, 5.78], %)
|
||||
|> line([7.42, -8.62], %)
|
||||
|> line([-6.38, -3.51], %)
|
||||
|> line([-3.77, 3.56], %)
|
||||
@ -127,9 +132,9 @@ async fn serial_test_modify_sketch_part002() {
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let new_code = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id)
|
||||
let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -144,7 +149,8 @@ async fn serial_test_modify_sketch_part002() {
|
||||
async fn serial_test_modify_close_sketch() {
|
||||
let name = "part002";
|
||||
let code = format!(
|
||||
r#"const {} = startSketchAt([7.91, 3.89])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([7.91, 3.89], %)
|
||||
|> line([7.42, -8.62], %)
|
||||
|> line([-6.38, -3.51], %)
|
||||
|> line([-3.77, 3.56], %)
|
||||
@ -153,9 +159,9 @@ async fn serial_test_modify_close_sketch() {
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let new_code = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id)
|
||||
let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -169,7 +175,8 @@ async fn serial_test_modify_close_sketch() {
|
||||
async fn serial_test_modify_line_to_close_sketch() {
|
||||
let name = "part002";
|
||||
let code = format!(
|
||||
r#"const {} = startSketchAt([7.91, 3.89])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([7.91, 3.89], %)
|
||||
|> line([7.42, -8.62], %)
|
||||
|> line([-6.38, -3.51], %)
|
||||
|> line([-3.77, 3.56], %)
|
||||
@ -178,9 +185,9 @@ async fn serial_test_modify_line_to_close_sketch() {
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let new_code = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id)
|
||||
let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -188,7 +195,8 @@ async fn serial_test_modify_line_to_close_sketch() {
|
||||
assert_eq!(
|
||||
new_code,
|
||||
format!(
|
||||
r#"const {} = startSketchAt([7.91, 3.89])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([7.91, 3.89], %)
|
||||
|> line([7.42, -8.62], %)
|
||||
|> line([-6.38, -3.51], %)
|
||||
|> line([-3.77, 3.56], %)
|
||||
@ -204,7 +212,8 @@ async fn serial_test_modify_with_constraint() {
|
||||
let name = "part002";
|
||||
let code = format!(
|
||||
r#"const thing = 12
|
||||
const {} = startSketchAt([7.91, 3.89])
|
||||
const {} = startSketchOn("XY")
|
||||
|> startProfileAt([7.91, 3.89], %)
|
||||
|> line([7.42, -8.62], %)
|
||||
|> line([-6.38, -3.51], %)
|
||||
|> line([-3.77, 3.56], %)
|
||||
@ -213,14 +222,14 @@ const {} = startSketchAt([7.91, 3.89])
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let result = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id).await;
|
||||
let result = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await;
|
||||
|
||||
assert!(result.is_err());
|
||||
assert_eq!(
|
||||
result.unwrap_err().to_string(),
|
||||
r#"engine: KclErrorDetails { source_ranges: [SourceRange([159, 164])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"#
|
||||
r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"#
|
||||
);
|
||||
}
|
||||
|
||||
@ -228,7 +237,8 @@ const {} = startSketchAt([7.91, 3.89])
|
||||
async fn serial_test_modify_line_should_close_sketch() {
|
||||
let name = "part003";
|
||||
let code = format!(
|
||||
r#"const {} = startSketchAt([13.69, 3.8])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([13.69, 3.8], %)
|
||||
|> line([4.23, -11.79], %)
|
||||
|> line([-10.7, -1.16], %)
|
||||
|> line([-3.72, 8.69], %)
|
||||
@ -237,9 +247,9 @@ async fn serial_test_modify_line_should_close_sketch() {
|
||||
name
|
||||
);
|
||||
|
||||
let (mut engine, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let (mut ctx, program, sketch_id) = setup(&code, name).await.unwrap();
|
||||
let mut new_program = program.clone();
|
||||
let new_code = modify_ast_for_sketch(&mut engine, &mut new_program, name, sketch_id)
|
||||
let new_code = modify_ast_for_sketch(&mut ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -247,7 +257,8 @@ async fn serial_test_modify_line_should_close_sketch() {
|
||||
assert_eq!(
|
||||
new_code,
|
||||
format!(
|
||||
r#"const {} = startSketchAt([13.69, 3.8])
|
||||
r#"const {} = startSketchOn("XY")
|
||||
|> startProfileAt([13.69, 3.8], %)
|
||||
|> line([4.23, -11.79], %)
|
||||
|> line([-10.7, -1.16], %)
|
||||
|> line([-3.72, 8.69], %)
|
||||
|
@ -1,6 +1,9 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
"paths": {
|
||||
"/*": ["src/*"]
|
||||
},
|
||||
"types": ["vite/client", "@types/wicg-file-system-access"],
|
||||
"target": "esnext",
|
||||
"lib": [
|
||||
|
16
yarn.lock
@ -1530,10 +1530,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@juggle/resize-observer/-/resize-observer-3.4.0.tgz#08d6c5e20cf7e4cc02fd181c4b0c225cd31dbb60"
|
||||
integrity sha512-dfLbk+PwWvFzSxwk3n5ySL0hfBog779o8h68wK/7/APo/7cgyWp5jcXockbxdk5kFRkbeXWm4Fbi9FrdN381sA==
|
||||
|
||||
"@kittycad/lib@^0.0.40":
|
||||
version "0.0.40"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.40.tgz#0ba00c642e76648fb7cb1337e799b9d24724312d"
|
||||
integrity sha512-R8sQKLWe3lQC7l7cyY49oFgeiMvRh8+bCaaoLiIVYT+YiE9TaS+uwwF1+sR7MiX6YZp/YCBBPFGj4Ci0VHC9Bg==
|
||||
"@kittycad/lib@^0.0.43":
|
||||
version "0.0.43"
|
||||
resolved "https://registry.yarnpkg.com/@kittycad/lib/-/lib-0.0.43.tgz#b93c0961200cf327f5ac6491be1ca01f9edc321c"
|
||||
integrity sha512-Pe/PQfZ8BWEDOm4dkY4tcPcuCSh2mQPe/W8RvJ6PQNTlB7bmjAj0234pVyG+8zrKKsJC9nq4ye7CZoaXEfUSTg==
|
||||
dependencies:
|
||||
node-fetch "3.3.2"
|
||||
openapi-types "^12.0.0"
|
||||
@ -4814,10 +4814,10 @@ queue-microtask@^1.2.2:
|
||||
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
|
||||
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
|
||||
|
||||
re-resizable@^6.9.9:
|
||||
version "6.9.9"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.9.tgz#99e8b31c67a62115dc9c5394b7e55892265be216"
|
||||
integrity sha512-l+MBlKZffv/SicxDySKEEh42hR6m5bAHfNu3Tvxks2c4Ah+ldnWjfnVRwxo/nxF27SsUsxDS0raAzFuJNKABXA==
|
||||
re-resizable@^6.9.11:
|
||||
version "6.9.11"
|
||||
resolved "https://registry.yarnpkg.com/re-resizable/-/re-resizable-6.9.11.tgz#f356e27877f12d926d076ab9ad9ff0b95912b475"
|
||||
integrity sha512-a3hiLWck/NkmyLvGWUuvkAmN1VhwAz4yOhS6FdMTaxCUVN9joIWkT11wsO68coG/iEYuwn+p/7qAmfQzRhiPLQ==
|
||||
|
||||
react-base16-styling@^0.6.0:
|
||||
version "0.6.0"
|
||||
|