Compare commits
27 Commits
with-our-p
...
jtran/fix-
Author | SHA1 | Date | |
---|---|---|---|
0c2f63b399 | |||
363ae10658 | |||
ac4a6c84cf | |||
c6fad2e2dc | |||
013cb10961 | |||
6261083cb1 | |||
2b0ba37ed0 | |||
96174f3cf6 | |||
aed62ff912 | |||
9334d64608 | |||
4fa7d2d8c8 | |||
3e615dfdbc | |||
c9860af29f | |||
23a42f0195 | |||
a77fa639f3 | |||
0a5ad7c95b | |||
4a654523d2 | |||
73a7e2bfd6 | |||
eb0850fea9 | |||
029f76f273 | |||
28b5f7080c | |||
5b1dcfecd6 | |||
f89d191425 | |||
2f4e4b62a8 | |||
5ebd5c8dbb | |||
a9ceaf2678 | |||
c8afd3399b |
@ -1,3 +1,3 @@
|
|||||||
[codespell]
|
[codespell]
|
||||||
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
ignore-words-list: crate,everytime,inout,co-ordinate,ot,nwo,atleast,ue,afterall
|
||||||
skip: **/target,node_modules,build,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./src/lib/machine-api.d.ts,./packages/codemirror-lang-kcl/test/all.test.ts
|
skip: **/target,node_modules,build,dist,./out,**/Cargo.lock,./docs/kcl/*.md,.yarn.lock,**/yarn.lock,./openapi/*.json,./packages/codemirror-lang-kcl/test/all.test.ts,tsconfig.tsbuildinfo
|
||||||
|
12
.eslintrc
12
.eslintrc
@ -5,16 +5,24 @@
|
|||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"css-modules",
|
"css-modules",
|
||||||
|
"jest",
|
||||||
|
"react",
|
||||||
"suggest-no-throw",
|
"suggest-no-throw",
|
||||||
|
"@typescript-eslint"
|
||||||
],
|
],
|
||||||
"extends": [
|
"extends": [
|
||||||
"react-app",
|
|
||||||
"react-app/jest",
|
|
||||||
"plugin:css-modules/recommended"
|
"plugin:css-modules/recommended"
|
||||||
],
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"@typescript-eslint/no-floating-promises": "error",
|
"@typescript-eslint/no-floating-promises": "error",
|
||||||
"@typescript-eslint/no-misused-promises": "error",
|
"@typescript-eslint/no-misused-promises": "error",
|
||||||
|
"no-restricted-globals": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"name": "isNaN",
|
||||||
|
"message": "Use Number.isNaN() instead."
|
||||||
|
}
|
||||||
|
],
|
||||||
"semi": [
|
"semi": [
|
||||||
"error",
|
"error",
|
||||||
"never"
|
"never"
|
||||||
|
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
2
.github/ci-cd-scripts/playwright-electron.sh
vendored
@ -21,7 +21,7 @@ if [[ ! -f "test-results/.last-run.json" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
retry=1
|
retry=1
|
||||||
max_retrys=4
|
max_retrys=5
|
||||||
|
|
||||||
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
# retry failed tests, doing our own retries because using inbuilt playwright retries causes connection issues
|
||||||
while [[ $retry -le $max_retrys ]]; do
|
while [[ $retry -le $max_retrys ]]; do
|
||||||
|
@ -24,3 +24,5 @@ once fixed in engine will just start working here with no language changes.
|
|||||||
chamfer cases work currently.
|
chamfer cases work currently.
|
||||||
|
|
||||||
- **Appearance**: Changing the appearance on a loft does not work.
|
- **Appearance**: Changing the appearance on a loft does not work.
|
||||||
|
|
||||||
|
- **Helix**: Currently sweeping a helix does not work.
|
||||||
|
File diff suppressed because one or more lines are too long
43
docs/kcl/helixRevolutions.md
Normal file
43
docs/kcl/helixRevolutions.md
Normal file
File diff suppressed because one or more lines are too long
@ -48,6 +48,7 @@ layout: manual
|
|||||||
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
* [`getOppositeEdge`](kcl/getOppositeEdge)
|
||||||
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
* [`getPreviousAdjacentEdge`](kcl/getPreviousAdjacentEdge)
|
||||||
* [`helix`](kcl/helix)
|
* [`helix`](kcl/helix)
|
||||||
|
* [`helixRevolutions`](kcl/helixRevolutions)
|
||||||
* [`hole`](kcl/hole)
|
* [`hole`](kcl/hole)
|
||||||
* [`hollow`](kcl/hollow)
|
* [`hollow`](kcl/hollow)
|
||||||
* [`import`](kcl/import)
|
* [`import`](kcl/import)
|
||||||
|
2066
docs/kcl/std.json
2066
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
@ -1,19 +1,19 @@
|
|||||||
---
|
---
|
||||||
title: "AxisOrEdgeReference"
|
title: "Axis2dOrEdgeReference"
|
||||||
excerpt: "Axis or tagged edge."
|
excerpt: "A 2D axis or tagged edge."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis or tagged edge.
|
A 2D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
**This schema accepts any of the following:**
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
Axis and origin.
|
2D axis and origin.
|
||||||
|
|
||||||
[`AxisAndOrigin`](/docs/kcl/types/AxisAndOrigin)
|
[`AxisAndOrigin2d`](/docs/kcl/types/AxisAndOrigin2d)
|
||||||
|
|
||||||
|
|
||||||
|
|
42
docs/kcl/types/Axis3dOrEdgeReference.md
Normal file
42
docs/kcl/types/Axis3dOrEdgeReference.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "Axis3dOrEdgeReference"
|
||||||
|
excerpt: "A 3D axis or tagged edge."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis or tagged edge.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
3D axis and origin.
|
||||||
|
|
||||||
|
[`AxisAndOrigin3d`](/docs/kcl/types/AxisAndOrigin3d)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Tagged edge.
|
||||||
|
|
||||||
|
[`EdgeReference`](/docs/kcl/types/EdgeReference)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "AxisAndOrigin"
|
title: "AxisAndOrigin2d"
|
||||||
excerpt: "Axis and origin."
|
excerpt: "A 2D axis and origin."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Axis and origin.
|
A 2D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
105
docs/kcl/types/AxisAndOrigin3d.md
Normal file
105
docs/kcl/types/AxisAndOrigin3d.md
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
---
|
||||||
|
title: "AxisAndOrigin3d"
|
||||||
|
excerpt: "A 3D axis and origin."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A 3D axis and origin.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts exactly one of the following:**
|
||||||
|
|
||||||
|
X-axis.
|
||||||
|
|
||||||
|
**enum:** `X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Y-axis.
|
||||||
|
|
||||||
|
**enum:** `Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Z-axis.
|
||||||
|
|
||||||
|
**enum:** `Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the X-axis.
|
||||||
|
|
||||||
|
**enum:** `-X`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Y-axis.
|
||||||
|
|
||||||
|
**enum:** `-Y`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
Flip the Z-axis.
|
||||||
|
|
||||||
|
**enum:** `-Z`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `custom` |`object`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/Helix.md
Normal file
25
docs/kcl/types/Helix.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "Helix"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
|||||||
---
|
---
|
||||||
title: "HelixData"
|
title: "HelixData"
|
||||||
excerpt: "Data for helices."
|
excerpt: "Data for a helix."
|
||||||
layout: manual
|
layout: manual
|
||||||
---
|
---
|
||||||
|
|
||||||
Data for helices.
|
Data for a helix.
|
||||||
|
|
||||||
**Type:** `object`
|
**Type:** `object`
|
||||||
|
|
||||||
@ -19,6 +19,8 @@ Data for helices.
|
|||||||
| `revolutions` |`number`| Number of revolutions. | No |
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
| `angleStart` |`number`| Start angle (in degrees). | No |
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
| `length` |`number`| Length of the helix. | No |
|
||||||
|
| `radius` |`number`| Radius of the helix. | No |
|
||||||
|
| `axis` |[`Axis3dOrEdgeReference`](/docs/kcl/types/Axis3dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
24
docs/kcl/types/HelixRevolutionsData.md
Normal file
24
docs/kcl/types/HelixRevolutionsData.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "HelixRevolutionsData"
|
||||||
|
excerpt: "Data for helix revolutions."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
Data for helix revolutions.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? The default is `false`. | No |
|
||||||
|
| `length` |`number`| Length of the helix. If this argument is not provided, the height of the solid is used. | No |
|
||||||
|
|
||||||
|
|
25
docs/kcl/types/HelixValue.md
Normal file
25
docs/kcl/types/HelixValue.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
title: "HelixValue"
|
||||||
|
excerpt: "A helix."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
@ -285,6 +285,27 @@ An solid is a collection of extrude surfaces.
|
|||||||
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
| `value` |`[` [`Solid`](/docs/kcl/types/Solid) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A helix.
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: [`Helix`](/docs/kcl/types/Helix)| | No |
|
||||||
|
| `value` |`string`| The id of the helix. | No |
|
||||||
|
| `revolutions` |`number`| Number of revolutions. | No |
|
||||||
|
| `angleStart` |`number`| Start angle (in degrees). | No |
|
||||||
|
| `ccw` |`boolean`| Is the helix rotation counter clockwise? | No |
|
||||||
|
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||||
|
|
||||||
|
|
||||||
----
|
----
|
||||||
Data for an imported geometry.
|
Data for an imported geometry.
|
||||||
|
|
||||||
|
@ -16,6 +16,6 @@ Data for a mirror.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis to use as mirror. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis to use as mirror. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ Data for revolution surfaces.
|
|||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
| `angle` |`number` (**maximum:** 360.0) (**minimum:** -360.0)| Angle to revolve (in degrees). Default is 360. | No |
|
||||||
| `axis` |[`AxisOrEdgeReference`](/docs/kcl/types/AxisOrEdgeReference)| Axis of revolution. | No |
|
| `axis` |[`Axis2dOrEdgeReference`](/docs/kcl/types/Axis2dOrEdgeReference)| Axis of revolution. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
| `tolerance` |`number`| Tolerance for the revolve operation. | No |
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ Data for a sweep.
|
|||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
|----------|------|-------------|----------|
|
|----------|------|-------------|----------|
|
||||||
| `path` |[`Sketch`](/docs/kcl/types/Sketch)| The path to sweep along. | No |
|
| `path` |[`SweepPath`](/docs/kcl/types/SweepPath)| The path to sweep along. | No |
|
||||||
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
| `sectional` |`boolean`| If true, the sweep will be broken up into sub-sweeps (extrusions, revolves, sweeps) based on the trajectory path components. | No |
|
||||||
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
| `tolerance` |`number`| Tolerance for the sweep operation. | No |
|
||||||
|
|
||||||
|
42
docs/kcl/types/SweepPath.md
Normal file
42
docs/kcl/types/SweepPath.md
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: "SweepPath"
|
||||||
|
excerpt: "A path to sweep along."
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**This schema accepts any of the following:**
|
||||||
|
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Sketch`](/docs/kcl/types/Sketch)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
A path to sweep along.
|
||||||
|
|
||||||
|
[`Helix`](/docs/kcl/types/Helix)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -54,26 +54,23 @@ async function doBasicSketch(
|
|||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator).toContainText(
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
)
|
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
|
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
}
|
}
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(500)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
commonPoints.startAt
|
|
||||||
}, sketch001)
|
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||||
} else {
|
} else {
|
||||||
@ -82,10 +79,8 @@ async function doBasicSketch(
|
|||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
if (openPanes.includes('code')) {
|
if (openPanes.includes('code')) {
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
commonPoints.startAt
|
|
||||||
}, sketch001)
|
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||||
@ -142,10 +137,8 @@ async function doBasicSketch(
|
|||||||
|
|
||||||
// Open the code pane.
|
// Open the code pane.
|
||||||
await u.openKclCodePanel()
|
await u.openKclCodePanel()
|
||||||
await expect(u.codeLocator)
|
await expect(u.codeLocator).toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
commonPoints.startAt
|
|
||||||
}, sketch001)
|
|
||||||
|> xLine(${commonPoints.num1}, %, $seg01)
|
|> xLine(${commonPoints.num1}, %, $seg01)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> xLine(-segLen(seg01), %)`)
|
|> xLine(-segLen(seg01), %)`)
|
||||||
|
@ -38,7 +38,8 @@ test.describe('Can create sketches on all planes and their back sides', () => {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const code = `sketch001 = startSketchOn('${plane}')profile001 = startProfileAt([0.9, -1.22], sketch001)`
|
const code = `sketch001 = startSketchOn('${plane}')
|
||||||
|
|> startProfileAt([0.9, -1.22], %)`
|
||||||
|
|
||||||
await u.openDebugPanel()
|
await u.openDebugPanel()
|
||||||
|
|
||||||
|
@ -9,15 +9,13 @@ import {
|
|||||||
sendCustomCmd,
|
sendCustomCmd,
|
||||||
} from '../test-utils'
|
} from '../test-utils'
|
||||||
|
|
||||||
type MouseParams = {
|
type mouseParams = {
|
||||||
pixelDiff?: number
|
pixelDiff?: number
|
||||||
shouldDbClick?: boolean
|
|
||||||
delay?: number
|
|
||||||
}
|
}
|
||||||
type MouseDragToParams = MouseParams & {
|
type mouseDragToParams = mouseParams & {
|
||||||
fromPoint: { x: number; y: number }
|
fromPoint: { x: number; y: number }
|
||||||
}
|
}
|
||||||
type MouseDragFromParams = MouseParams & {
|
type mouseDragFromParams = mouseParams & {
|
||||||
toPoint: { x: number; y: number }
|
toPoint: { x: number; y: number }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,17 +26,18 @@ type SceneSerialised = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClickHandler = (clickParams?: MouseParams) => Promise<void | boolean>
|
type ClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type MoveHandler = (moveParams?: MouseParams) => Promise<void | boolean>
|
type MoveHandler = (moveParams?: mouseParams) => Promise<void | boolean>
|
||||||
type DblClickHandler = (clickParams?: MouseParams) => Promise<void | boolean>
|
type DblClickHandler = (clickParams?: mouseParams) => Promise<void | boolean>
|
||||||
type DragToHandler = (dragParams: MouseDragToParams) => Promise<void | boolean>
|
type DragToHandler = (dragParams: mouseDragToParams) => Promise<void | boolean>
|
||||||
type DragFromHandler = (
|
type DragFromHandler = (
|
||||||
dragParams: MouseDragFromParams
|
dragParams: mouseDragFromParams
|
||||||
) => Promise<void | boolean>
|
) => Promise<void | boolean>
|
||||||
|
|
||||||
export class SceneFixture {
|
export class SceneFixture {
|
||||||
public page: Page
|
public page: Page
|
||||||
|
public streamWrapper!: Locator
|
||||||
|
public loadingIndicator!: Locator
|
||||||
private exeIndicator!: Locator
|
private exeIndicator!: Locator
|
||||||
|
|
||||||
constructor(page: Page) {
|
constructor(page: Page) {
|
||||||
@ -66,6 +65,8 @@ export class SceneFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
|
|
||||||
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
this.exeIndicator = page.getByTestId('model-state-indicator-execution-done')
|
||||||
|
this.streamWrapper = page.getByTestId('stream')
|
||||||
|
this.loadingIndicator = this.streamWrapper.getByTestId('loading')
|
||||||
}
|
}
|
||||||
|
|
||||||
makeMouseHelpers = (
|
makeMouseHelpers = (
|
||||||
@ -74,26 +75,17 @@ export class SceneFixture {
|
|||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
): [ClickHandler, MoveHandler, DblClickHandler] =>
|
||||||
[
|
[
|
||||||
(clickParams?: MouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
() =>
|
() => this.page.mouse.click(x, y),
|
||||||
clickParams?.shouldDbClick
|
|
||||||
? this.page.mouse.dblclick(x, y, {
|
|
||||||
delay: clickParams?.delay || 0,
|
|
||||||
})
|
|
||||||
: this.page.mouse.click(x, y, {
|
|
||||||
delay: clickParams?.delay || 0,
|
|
||||||
}),
|
|
||||||
clickParams.pixelDiff
|
clickParams.pixelDiff
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return clickParams?.shouldDbClick
|
return this.page.mouse.click(x, y)
|
||||||
? this.page.mouse.dblclick(x, y, { delay: clickParams?.delay || 0 })
|
|
||||||
: this.page.mouse.click(x, y, { delay: clickParams?.delay || 0 })
|
|
||||||
},
|
},
|
||||||
(moveParams?: MouseParams) => {
|
(moveParams?: mouseParams) => {
|
||||||
if (moveParams?.pixelDiff) {
|
if (moveParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -103,7 +95,7 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
return this.page.mouse.move(x, y, { steps })
|
return this.page.mouse.move(x, y, { steps })
|
||||||
},
|
},
|
||||||
(clickParams?: MouseParams) => {
|
(clickParams?: mouseParams) => {
|
||||||
if (clickParams?.pixelDiff) {
|
if (clickParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -120,7 +112,7 @@ export class SceneFixture {
|
|||||||
{ steps }: { steps: number } = { steps: 20 }
|
{ steps }: { steps: number } = { steps: 20 }
|
||||||
): [DragToHandler, DragFromHandler] =>
|
): [DragToHandler, DragFromHandler] =>
|
||||||
[
|
[
|
||||||
(dragToParams: MouseDragToParams) => {
|
(dragToParams: mouseDragToParams) => {
|
||||||
if (dragToParams?.pixelDiff) {
|
if (dragToParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -137,7 +129,7 @@ export class SceneFixture {
|
|||||||
targetPosition: { x, y },
|
targetPosition: { x, y },
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
(dragFromParams: MouseDragFromParams) => {
|
(dragFromParams: mouseDragFromParams) => {
|
||||||
if (dragFromParams?.pixelDiff) {
|
if (dragFromParams?.pixelDiff) {
|
||||||
return doAndWaitForImageDiff(
|
return doAndWaitForImageDiff(
|
||||||
this.page,
|
this.page,
|
||||||
@ -225,7 +217,7 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
|
|
||||||
expectPixelColor = async (
|
expectPixelColor = async (
|
||||||
colour: [number, number, number] | [number, number, number][],
|
colour: [number, number, number],
|
||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) => {
|
) => {
|
||||||
@ -247,36 +239,22 @@ export class SceneFixture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isColourArray(
|
|
||||||
colour: [number, number, number] | [number, number, number][]
|
|
||||||
): colour is [number, number, number][] {
|
|
||||||
return Array.isArray(colour[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function expectPixelColor(
|
export async function expectPixelColor(
|
||||||
page: Page,
|
page: Page,
|
||||||
colour: [number, number, number] | [number, number, number][],
|
colour: [number, number, number],
|
||||||
coords: { x: number; y: number },
|
coords: { x: number; y: number },
|
||||||
diff: number
|
diff: number
|
||||||
) {
|
) {
|
||||||
let finalValue = colour
|
let finalValue = colour
|
||||||
await expect
|
await expect
|
||||||
.poll(
|
.poll(async () => {
|
||||||
async () => {
|
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
||||||
const pixel = (await getPixelRGBs(page)(coords, 1))[0]
|
if (!pixel) return null
|
||||||
if (!pixel) return null
|
finalValue = pixel
|
||||||
finalValue = pixel
|
return pixel.every(
|
||||||
if (!isColourArray(colour)) {
|
(channel, index) => Math.abs(channel - colour[index]) < diff
|
||||||
return pixel.every(
|
)
|
||||||
(channel, index) => Math.abs(channel - colour[index]) < diff
|
})
|
||||||
)
|
|
||||||
}
|
|
||||||
return colour.some((c) =>
|
|
||||||
c.every((channel, index) => Math.abs(pixel[index] - channel) < diff)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
{ timeout: 10_000 }
|
|
||||||
)
|
|
||||||
.toBeTruthy()
|
.toBeTruthy()
|
||||||
.catch((cause) => {
|
.catch((cause) => {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -14,14 +14,12 @@ export class ToolbarFixture {
|
|||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
loftButton!: Locator
|
loftButton!: Locator
|
||||||
|
sweepButton!: Locator
|
||||||
shellButton!: Locator
|
shellButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
tangentialArcBtn!: Locator
|
|
||||||
circleBtn!: Locator
|
|
||||||
rectangleBtn!: Locator
|
rectangleBtn!: Locator
|
||||||
lengthConstraintBtn!: Locator
|
|
||||||
exitSketchBtn!: Locator
|
exitSketchBtn!: Locator
|
||||||
editSketchBtn!: Locator
|
editSketchBtn!: Locator
|
||||||
fileTreeBtn!: Locator
|
fileTreeBtn!: Locator
|
||||||
@ -43,14 +41,12 @@ export class ToolbarFixture {
|
|||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
this.loftButton = page.getByTestId('loft')
|
this.loftButton = page.getByTestId('loft')
|
||||||
|
this.sweepButton = page.getByTestId('sweep')
|
||||||
this.shellButton = page.getByTestId('shell')
|
this.shellButton = page.getByTestId('shell')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
this.tangentialArcBtn = page.getByTestId('tangential-arc')
|
|
||||||
this.circleBtn = page.getByTestId('circle-center')
|
|
||||||
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
this.rectangleBtn = page.getByTestId('corner-rectangle')
|
||||||
this.lengthConstraintBtn = page.getByTestId('constraint-length')
|
|
||||||
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
this.exitSketchBtn = page.getByTestId('sketch-exit')
|
||||||
this.editSketchBtn = page.getByText('Edit Sketch')
|
this.editSketchBtn = page.getByText('Edit Sketch')
|
||||||
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
this.fileTreeBtn = page.locator('[id="files-button-holder"]')
|
||||||
@ -109,15 +105,6 @@ export class ToolbarFixture {
|
|||||||
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
await expect(this.exeIndicator).toBeVisible({ timeout: 15_000 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
selectCenterRectangle = async () => {
|
|
||||||
await this.page
|
|
||||||
.getByRole('button', { name: 'caret down Corner rectangle:' })
|
|
||||||
.click()
|
|
||||||
await expect(
|
|
||||||
this.page.getByTestId('dropdown-center-rectangle')
|
|
||||||
).toBeVisible()
|
|
||||||
await this.page.getByTestId('dropdown-center-rectangle').click()
|
|
||||||
}
|
|
||||||
|
|
||||||
async closePane(paneId: SidebarType) {
|
async closePane(paneId: SidebarType) {
|
||||||
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
|
return closePane(this.page, paneId + SIDEBAR_BUTTON_SUFFIX)
|
||||||
|
@ -437,7 +437,7 @@ test.describe('Onboarding tests', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
test(
|
test.fixme(
|
||||||
'Restarting onboarding on desktop takes one attempt',
|
'Restarting onboarding on desktop takes one attempt',
|
||||||
{
|
{
|
||||||
appSettings: {
|
appSettings: {
|
||||||
|
@ -218,13 +218,18 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
]}, %)`,
|
]}, %)`,
|
||||||
|
|
||||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet:
|
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||||
'startProfileAt([205.96, 254.59], sketch002)',
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
|> angledLine([
|
||||||
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
105.26
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
], %, $rectangleSegmentB001)
|
||||||
|>close(%)`,
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -244,15 +249,19 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
}, %)`,
|
}, %)`,
|
||||||
|
|
||||||
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
afterRectangle1stClickSnippet:
|
afterRectangle1stClickSnippet: 'startProfileAt([-209.64, 255.28], %)',
|
||||||
'startProfileAt([-209.64, 255.28], sketch003)',
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.56],%,$rectangleSegmentA003)
|
|> angledLine([
|
||||||
|>angledLine([segAng(rectangleSegmentA003)-90,106.84],%)
|
segAng(rectangleSegmentA003) - 90,
|
||||||
|>angledLine([segAng(rectangleSegmentA003),-segLen(rectangleSegmentA003)],%)
|
106.84
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
], %, $rectangleSegmentB002)
|
||||||
|>close(%)`,
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003),
|
||||||
|
-segLen(rectangleSegmentA003)
|
||||||
|
], %, $rectangleSegmentC002)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
clickCoords: { x: 677, y: 87 },
|
clickCoords: { x: 677, y: 87 },
|
||||||
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
cameraPos: { x: -6200, y: 1500, z: 6200 },
|
||||||
@ -264,14 +273,19 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
getNextAdjacentEdge(seg02)
|
getNextAdjacentEdge(seg02)
|
||||||
]
|
]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet: 'sketch004 = startSketchOn(extrude001, seg05)',
|
afterChamferSelectSnippet: 'sketch003 = startSketchOn(extrude001, seg04)',
|
||||||
afterRectangle1stClickSnippet:
|
afterRectangle1stClickSnippet: 'startProfileAt([75.8, 317.2], %)',
|
||||||
'startProfileAt([82.57, 322.96], sketch004)',
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.16],%,$rectangleSegmentA004)
|
|> angledLine([
|
||||||
|>angledLine([segAng(rectangleSegmentA004)-90,103.07],%)
|
segAng(rectangleSegmentA003) - 90,
|
||||||
|>angledLine([segAng(rectangleSegmentA004),-segLen(rectangleSegmentA004)],%)
|
106.84
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)|
|
], %, $rectangleSegmentB002)
|
||||||
>close(%)`,
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA003),
|
||||||
|
-segLen(rectangleSegmentA003)
|
||||||
|
], %, $rectangleSegmentC002)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`,
|
||||||
})
|
})
|
||||||
/// last one
|
/// last one
|
||||||
await sketchOnAChamfer({
|
await sketchOnAChamfer({
|
||||||
@ -283,97 +297,104 @@ test.describe('verify sketch on chamfer works', () => {
|
|||||||
tags = [getNextAdjacentEdge(yo)]
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
}, %)`,
|
}, %)`,
|
||||||
afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
|
afterChamferSelectSnippet: 'sketch005 = startSketchOn(extrude001, seg06)',
|
||||||
afterRectangle1stClickSnippet:
|
afterRectangle1stClickSnippet: 'startProfileAt([-23.43, 19.69], %)',
|
||||||
'startProfileAt([-23.43, 19.69], sketch005)',
|
afterRectangle2ndClickSnippet: `angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,9.1],%,$rectangleSegmentA005)
|
|
||||||
|>angledLine([segAng(rectangleSegmentA005)-90,84.07],%)
|
|> angledLine([
|
||||||
|>angledLine([segAng(rectangleSegmentA005),-segLen(rectangleSegmentA005)],%)
|
segAng(rectangleSegmentA005) - 90,
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
84.07
|
||||||
|>close(%)`,
|
], %, $rectangleSegmentB004)
|
||||||
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA005),
|
||||||
|
-segLen(rectangleSegmentA005)
|
||||||
|
], %, $rectangleSegmentC004)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`,
|
||||||
})
|
})
|
||||||
|
|
||||||
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
await test.step('verify at the end of the test that final code is what is expected', async () => {
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
|
||||||
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
|> startProfileAt([75.8, 317.2], %) // [$startCapTag, $EndCapTag]
|
||||||
|> angledLine([
|
|> angledLine([0, 268.43], %, $rectangleSegmentA001)
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|> angledLine([
|
||||||
217.26
|
segAng(rectangleSegmentA001) - 90,
|
||||||
], %, $seg01)
|
217.26
|
||||||
|> angledLine([
|
], %, $seg01)
|
||||||
segAng(rectangleSegmentA001),
|
|> angledLine([
|
||||||
-segLen(rectangleSegmentA001)
|
segAng(rectangleSegmentA001),
|
||||||
], %, $yo)
|
-segLen(rectangleSegmentA001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg02)
|
], %, $yo)
|
||||||
|> close(%)
|
|> lineTo([profileStartX(%), profileStartY(%)], %, $seg02)
|
||||||
extrude001 = extrude(100, sketch001)
|
|> close(%)
|
||||||
|> chamfer({
|
extrude001 = extrude(100, sketch001)
|
||||||
length = 30,
|
|> chamfer({
|
||||||
tags = [getOppositeEdge(seg01)]
|
length = 30,
|
||||||
}, %, $seg03)
|
tags = [getOppositeEdge(seg01)]
|
||||||
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|
}, %, $seg03)
|
||||||
|> chamfer({
|
|> chamfer({ length = 30, tags = [seg01] }, %, $seg04)
|
||||||
length = 30,
|
|> chamfer({
|
||||||
tags = [getNextAdjacentEdge(seg02)]
|
length = 30,
|
||||||
}, %, $seg05)
|
tags = [getNextAdjacentEdge(seg02)]
|
||||||
|> chamfer({
|
}, %, $seg05)
|
||||||
length = 30,
|
|> chamfer({
|
||||||
tags = [getNextAdjacentEdge(yo)]
|
length = 30,
|
||||||
}, %, $seg06)
|
tags = [getNextAdjacentEdge(yo)]
|
||||||
sketch005 = startSketchOn(extrude001, seg06)
|
}, %, $seg06)
|
||||||
profile004 = startProfileAt([-23.43, 19.69], sketch005)
|
sketch005 = startSketchOn(extrude001, seg06)
|
||||||
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
|> startProfileAt([-23.43,19.69], %)
|
||||||
|> angledLine([
|
|> angledLine([0, 9.1], %, $rectangleSegmentA005)
|
||||||
segAng(rectangleSegmentA005) - 90,
|
|> angledLine([
|
||||||
84.07
|
segAng(rectangleSegmentA005) - 90,
|
||||||
], %)
|
84.07
|
||||||
|> angledLine([
|
], %, $rectangleSegmentB004)
|
||||||
segAng(rectangleSegmentA005),
|
|> angledLine([
|
||||||
-segLen(rectangleSegmentA005)
|
segAng(rectangleSegmentA005),
|
||||||
], %)
|
-segLen(rectangleSegmentA005)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
], %, $rectangleSegmentC004)
|
||||||
|> close(%)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
sketch004 = startSketchOn(extrude001, seg05)
|
|> close(%)
|
||||||
profile003 = startProfileAt([82.57, 322.96], sketch004)
|
sketch004 = startSketchOn(extrude001, seg05)
|
||||||
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
|> startProfileAt([82.57,322.96], %)
|
||||||
|> angledLine([
|
|> angledLine([0, 11.16], %, $rectangleSegmentA004)
|
||||||
segAng(rectangleSegmentA004) - 90,
|
|> angledLine([
|
||||||
103.07
|
segAng(rectangleSegmentA004) - 90,
|
||||||
], %)
|
103.07
|
||||||
|> angledLine([
|
], %, $rectangleSegmentB003)
|
||||||
segAng(rectangleSegmentA004),
|
|> angledLine([
|
||||||
-segLen(rectangleSegmentA004)
|
segAng(rectangleSegmentA004),
|
||||||
], %)
|
-segLen(rectangleSegmentA004)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
], %, $rectangleSegmentC003)
|
||||||
|> close(%)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
sketch003 = startSketchOn(extrude001, seg04)
|
|> close(%)
|
||||||
profile002 = startProfileAt([-209.64, 255.28], sketch003)
|
sketch003 = startSketchOn(extrude001, seg04)
|
||||||
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
|> startProfileAt([-209.64,255.28], %)
|
||||||
|> angledLine([
|
|> angledLine([0, 11.56], %, $rectangleSegmentA003)
|
||||||
segAng(rectangleSegmentA003) - 90,
|
|> angledLine([
|
||||||
106.84
|
segAng(rectangleSegmentA003) - 90,
|
||||||
], %)
|
106.84
|
||||||
|> angledLine([
|
], %, $rectangleSegmentB002)
|
||||||
segAng(rectangleSegmentA003),
|
|> angledLine([
|
||||||
-segLen(rectangleSegmentA003)
|
segAng(rectangleSegmentA003),
|
||||||
], %)
|
-segLen(rectangleSegmentA003)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
], %, $rectangleSegmentC002)
|
||||||
|> close(%)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
|> close(%)
|
||||||
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> startProfileAt([205.96,254.59], %)
|
||||||
|> angledLine([
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
segAng(rectangleSegmentA002) - 90,
|
|> angledLine([
|
||||||
105.26
|
segAng(rectangleSegmentA002) - 90,
|
||||||
], %)
|
105.26
|
||||||
|> angledLine([
|
], %, $rectangleSegmentB001)
|
||||||
segAng(rectangleSegmentA002),
|
|> angledLine([
|
||||||
-segLen(rectangleSegmentA002)
|
segAng(rectangleSegmentA002),
|
||||||
], %)
|
-segLen(rectangleSegmentA002)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
], %, $rectangleSegmentC001)
|
||||||
|> close(%)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
`,
|
|> close(%)
|
||||||
|
`,
|
||||||
{ shouldNormalise: true }
|
{ shouldNormalise: true }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -416,13 +437,18 @@ profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|||||||
]}, extrude001)`,
|
]}, extrude001)`,
|
||||||
beforeChamferSnippetEnd: '}, extrude001)',
|
beforeChamferSnippetEnd: '}, extrude001)',
|
||||||
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
afterChamferSelectSnippet: 'sketch002 = startSketchOn(extrude001, seg03)',
|
||||||
afterRectangle1stClickSnippet:
|
afterRectangle1stClickSnippet: 'startProfileAt([205.96, 254.59], %)',
|
||||||
'startProfileAt([205.96, 254.59], sketch002)',
|
afterRectangle2ndClickSnippet: `angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
afterRectangle2ndClickSnippet: `angledLine([0,11.39],%,$rectangleSegmentA002)
|
|> angledLine([
|
||||||
|>angledLine([segAng(rectangleSegmentA002)-90,105.26],%)
|
segAng(rectangleSegmentA002) - 90,
|
||||||
|>angledLine([segAng(rectangleSegmentA002),-segLen(rectangleSegmentA002)],%)
|
105.26
|
||||||
|>lineTo([profileStartX(%),profileStartY(%)],%)
|
], %, $rectangleSegmentB001)
|
||||||
|>close(%)`,
|
|> angledLine([
|
||||||
|
segAng(rectangleSegmentA002),
|
||||||
|
-segLen(rectangleSegmentA002)
|
||||||
|
], %, $rectangleSegmentC001)
|
||||||
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|
|> close(%)`,
|
||||||
})
|
})
|
||||||
await editor.expectEditor.toContain(
|
await editor.expectEditor.toContain(
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
@ -452,16 +478,16 @@ chamf = chamfer({
|
|||||||
]
|
]
|
||||||
}, %)
|
}, %)
|
||||||
sketch002 = startSketchOn(extrude001, seg03)
|
sketch002 = startSketchOn(extrude001, seg03)
|
||||||
profile001 = startProfileAt([205.96, 254.59], sketch002)
|
|> startProfileAt([205.96, 254.59], %)
|
||||||
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
|> angledLine([0, 11.39], %, $rectangleSegmentA002)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002) - 90,
|
segAng(rectangleSegmentA002) - 90,
|
||||||
105.26
|
105.26
|
||||||
], %)
|
], %, $rectangleSegmentB001)
|
||||||
|> angledLine([
|
|> angledLine([
|
||||||
segAng(rectangleSegmentA002),
|
segAng(rectangleSegmentA002),
|
||||||
-segLen(rectangleSegmentA002)
|
-segLen(rectangleSegmentA002)
|
||||||
], %)
|
], %, $rectangleSegmentC001)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`,
|
`,
|
||||||
@ -529,10 +555,10 @@ test(`Verify axis, origin, and horizontal snapping`, async ({
|
|||||||
|
|
||||||
const expectedCodeSnippets = {
|
const expectedCodeSnippets = {
|
||||||
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
sketchOnXzPlane: `sketch001 = startSketchOn('XZ')`,
|
||||||
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], sketch001)`,
|
pointAtOrigin: `startProfileAt([${originSloppy.kcl[0]}, ${originSloppy.kcl[1]}], %)`,
|
||||||
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
segmentOnXAxis: `xLine(${xAxisSloppy.kcl[0]}, %)`,
|
||||||
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], sketch001)`,
|
afterSegmentDraggedOffYAxis: `startProfileAt([${offYAxis.kcl[0]}, ${offYAxis.kcl[1]}], %)`,
|
||||||
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], sketch001)`,
|
afterSegmentDraggedOnYAxis: `startProfileAt([${yAxisSloppy.kcl[0]}, ${yAxisSloppy.kcl[1]}], %)`,
|
||||||
}
|
}
|
||||||
|
|
||||||
await test.step(`Start a sketch on the XZ plane`, async () => {
|
await test.step(`Start a sketch on the XZ plane`, async () => {
|
||||||
@ -730,6 +756,17 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete offset plane via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation(
|
||||||
|
'Offset Plane',
|
||||||
|
0
|
||||||
|
)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([50, 51, 96], testPoint, 15)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const loftPointAndClickCases = [
|
const loftPointAndClickCases = [
|
||||||
@ -825,6 +862,173 @@ loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete loft via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Loft', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: merge with above test. Right now we're not able to delete a loft
|
||||||
|
// right after creation via selection for some reason, so we go with a new instance
|
||||||
|
test('Loft and offset plane deletion via selection', async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
loft001 = loft([sketch001, sketch002])
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x, testPoint.y + 80)
|
||||||
|
|
||||||
|
await test.step(`Delete loft`, async () => {
|
||||||
|
// Check for loft
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
await clickOnSketch1()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for sketch 1
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete sketch002', async () => {
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await clickOnSketch2()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for plane001
|
||||||
|
await scene.expectPixelColor([228, 228, 228], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete plane001', async () => {
|
||||||
|
await page.waitForTimeout(1000)
|
||||||
|
await clickOnSketch2()
|
||||||
|
await expect(page.locator('.cm-activeLine')).toHaveText(`
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
`)
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
// Check for sketch 1
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test(`Sweep point-and-click`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('YZ')
|
||||||
|
|> circle({
|
||||||
|
center = [0, 0],
|
||||||
|
radius = 500
|
||||||
|
}, %)
|
||||||
|
sketch002 = startSketchOn('XZ')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> xLine(-500, %)
|
||||||
|
|> tangentialArcTo([-2000, 500], %)
|
||||||
|
`
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 700, y: 250 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(testPoint.x - 50, testPoint.y)
|
||||||
|
const sweepDeclaration = 'sweep001 = sweep({ path = sketch002 }, sketch001)'
|
||||||
|
|
||||||
|
await test.step(`Look for sketch001`, async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow`, async () => {
|
||||||
|
await toolbar.sweepButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
currentArgKey: 'profile',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '',
|
||||||
|
Profile: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'profile',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
|
await clickOnSketch1()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
currentArgKey: 'path',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '',
|
||||||
|
Profile: '1 face',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'path',
|
||||||
|
stage: 'arguments',
|
||||||
|
})
|
||||||
|
await clickOnSketch2()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
commandName: 'Sweep',
|
||||||
|
headerArguments: {
|
||||||
|
Path: '1 face',
|
||||||
|
Profile: '1 face',
|
||||||
|
},
|
||||||
|
stage: 'review',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await scene.expectPixelColor([135, 64, 73], testPoint, 15)
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(sweepDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [sweepDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step('Delete sweep via feature tree selection', async () => {
|
||||||
|
await toolbar.openPane('feature-tree')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Sweep', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await toolbar.closePane('feature-tree')
|
||||||
|
await scene.expectPixelColor([53, 53, 53], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -1004,4 +1208,104 @@ extrude001 = extrude(40, sketch001)
|
|||||||
})
|
})
|
||||||
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
await scene.expectPixelColor([49, 49, 49], testPoint, 15)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
await test.step('Delete shell via feature tree selection', async () => {
|
||||||
|
await editor.closePane()
|
||||||
|
const operationButton = await toolbar.getFeatureTreeOperation('Shell', 0)
|
||||||
|
await operationButton.click({ button: 'left' })
|
||||||
|
await page.keyboard.press('Backspace')
|
||||||
|
await scene.expectPixelColor([99, 99, 99], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const shellSketchOnFacesCases = [
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||||
|
|> extrude(100, %)
|
||||||
|
|
||||||
|
sketch002 = startSketchOn(sketch001, 'END')
|
||||||
|
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||||
|
|> extrude(50, %)
|
||||||
|
`,
|
||||||
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 100 }, %)
|
||||||
|
extrude001 = extrude(100, sketch001)
|
||||||
|
|
||||||
|
sketch002 = startSketchOn(extrude001, 'END')
|
||||||
|
|> circle({ center = [0, 0], radius = 50 }, %)
|
||||||
|
extrude002 = extrude(50, sketch002)
|
||||||
|
`,
|
||||||
|
]
|
||||||
|
shellSketchOnFacesCases.forEach((initialCode, index) => {
|
||||||
|
const hasExtrudesInPipe = index === 0
|
||||||
|
test(`Shell point-and-click sketch on face (extrudes in pipes: ${hasExtrudesInPipe})`, async ({
|
||||||
|
context,
|
||||||
|
page,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
await context.addInitScript((initialCode) => {
|
||||||
|
localStorage.setItem('persistCode', initialCode)
|
||||||
|
}, initialCode)
|
||||||
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
|
await homePage.goToModelingScene()
|
||||||
|
await scene.waitForExecutionDone()
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 550, y: 295 }
|
||||||
|
const [clickOnCap] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const shellDeclaration = `shell001 = shell({ faces = ['end'], thickness = 5 }, ${
|
||||||
|
hasExtrudesInPipe ? 'sketch002' : 'extrude002'
|
||||||
|
})`
|
||||||
|
|
||||||
|
await test.step(`Look for the grey of the shape`, async () => {
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([128, 128, 128], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow, selecting a cap and keeping default thickness`, async () => {
|
||||||
|
await toolbar.shellButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '',
|
||||||
|
Thickness: '',
|
||||||
|
},
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await clickOnCap()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await page.waitForTimeout(500)
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: {
|
||||||
|
Selection: '1 cap',
|
||||||
|
Thickness: '5',
|
||||||
|
},
|
||||||
|
commandName: 'Shell',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await toolbar.openPane('code')
|
||||||
|
await editor.expectEditor.toContain(shellDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [shellDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
await scene.expectPixelColor([73, 73, 73], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -115,7 +115,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'yyyyyyyyy open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project with errors, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -199,7 +199,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'aaayyyyyyyy open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
'open a file in a project works and renders, open another file in different project that is empty, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -276,7 +276,7 @@ test(
|
|||||||
)
|
)
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'nooooooooooooo open a file in a project works and renders, open empty file, it should clear the scene',
|
'open a file in a project works and renders, open empty file, it should clear the scene',
|
||||||
{ tag: '@electron' },
|
{ tag: '@electron' },
|
||||||
async ({ context, page }, testInfo) => {
|
async ({ context, page }, testInfo) => {
|
||||||
await context.folderSetupFn(async (dir) => {
|
await context.folderSetupFn(async (dir) => {
|
||||||
@ -1885,3 +1885,48 @@ test.fixme(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
test(
|
||||||
|
'project name with foreign characters should open',
|
||||||
|
{ tag: '@electron' },
|
||||||
|
async ({ context, page }, testInfo) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const bracketDir = path.join(dir, 'اَلْعَرَبِيَّةُ')
|
||||||
|
await fsp.mkdir(bracketDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('focusrite_scarlett_mounting_braket.kcl'),
|
||||||
|
path.join(bracketDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
|
||||||
|
await fsp.writeFile(path.join(bracketDir, 'empty.kcl'), '')
|
||||||
|
})
|
||||||
|
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 500 })
|
||||||
|
const u = await getUtils(page)
|
||||||
|
|
||||||
|
page.on('console', console.log)
|
||||||
|
|
||||||
|
const pointOnModel = { x: 630, y: 280 }
|
||||||
|
|
||||||
|
await test.step('Opening the اَلْعَرَبِيَّةُ project should load the stream', async () => {
|
||||||
|
// expect to see the text bracket
|
||||||
|
await expect(page.getByText('اَلْعَرَبِيَّةُ')).toBeVisible()
|
||||||
|
|
||||||
|
await page.getByText('اَلْعَرَبِيَّةُ').click()
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
page.getByRole('button', { name: 'Start Sketch' })
|
||||||
|
).toBeEnabled({
|
||||||
|
timeout: 20_000,
|
||||||
|
})
|
||||||
|
|
||||||
|
// gray at this pixel means the stream has loaded in the most
|
||||||
|
// user way we can verify it (pixel color)
|
||||||
|
await expect
|
||||||
|
.poll(() => u.getGreatestPixDiff(pointOnModel, [85, 85, 85]), {
|
||||||
|
timeout: 10_000,
|
||||||
|
})
|
||||||
|
.toBeLessThan(15)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
@ -614,6 +614,38 @@ extrude001 = extrude(50, sketch001)
|
|||||||
await expect(gizmo).toBeVisible()
|
await expect(gizmo).toBeVisible()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test(`Refreshing the app doesn't cause the stream to pause on long-executing files`, async ({
|
||||||
|
context,
|
||||||
|
homePage,
|
||||||
|
scene,
|
||||||
|
toolbar,
|
||||||
|
viewport,
|
||||||
|
}) => {
|
||||||
|
await context.folderSetupFn(async (dir) => {
|
||||||
|
const legoDir = path.join(dir, 'lego')
|
||||||
|
await fsp.mkdir(legoDir, { recursive: true })
|
||||||
|
await fsp.copyFile(
|
||||||
|
executorInputPath('lego.kcl'),
|
||||||
|
path.join(legoDir, 'main.kcl')
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Test setup`, async () => {
|
||||||
|
await homePage.openProject('lego')
|
||||||
|
await toolbar.closePane('code')
|
||||||
|
})
|
||||||
|
await test.step(`Waiting for the loading spinner to disappear`, async () => {
|
||||||
|
await scene.loadingIndicator.waitFor({ state: 'detached' })
|
||||||
|
})
|
||||||
|
await test.step(`The part should start loading quickly, not waiting until execution is complete`, async () => {
|
||||||
|
await scene.expectPixelColor(
|
||||||
|
[143, 143, 143],
|
||||||
|
{ x: (viewport?.width ?? 1200) / 2, y: (viewport?.height ?? 500) / 2 },
|
||||||
|
15
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
async function clickExportButton(page: Page) {
|
async function clickExportButton(page: Page) {
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -451,7 +451,8 @@ test(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
code += `
|
||||||
|
|> startProfileAt([7.19, -9.7], %)`
|
||||||
await expect(page.locator('.cm-content')).toHaveText(code)
|
await expect(page.locator('.cm-content')).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -473,10 +474,6 @@ test(
|
|||||||
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
.getByRole('button', { name: 'arc Tangential Arc', exact: true })
|
||||||
.click()
|
.click()
|
||||||
|
|
||||||
// click to continue profile
|
|
||||||
await page.mouse.move(813, 392, { steps: 10 })
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
await page.mouse.move(startXPx + PUR * 30, 500 - PUR * 20, { steps: 10 })
|
||||||
|
|
||||||
await page.waitForTimeout(1000)
|
await page.waitForTimeout(1000)
|
||||||
@ -599,7 +596,8 @@ test(
|
|||||||
mask: [page.getByTestId('model-state-indicator')],
|
mask: [page.getByTestId('model-state-indicator')],
|
||||||
})
|
})
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content')).toHaveText(
|
||||||
`sketch001 = startSketchOn('XZ')profile001 = circle({ center = [14.44, -2.44], radius = 1 }, sketch001)`
|
`sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [14.44, -2.44], radius = 1 }, %)`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -643,7 +641,8 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `profile001 = startProfileAt([7.19, -9.7], sketch001)`
|
code += `
|
||||||
|
|> startProfileAt([7.19, -9.7], %)`
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -661,10 +660,6 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// click to continue profile
|
|
||||||
await page.mouse.click(813, 392)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
@ -751,7 +746,8 @@ test.describe(
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
code += `profile001 = startProfileAt([182.59, -246.32], sketch001)`
|
code += `
|
||||||
|
|> startProfileAt([182.59, -246.32], %)`
|
||||||
await expect(u.codeLocator).toHaveText(code)
|
await expect(u.codeLocator).toHaveText(code)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
@ -769,10 +765,6 @@ test.describe(
|
|||||||
.click()
|
.click()
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
// click to continue profile
|
|
||||||
await page.mouse.click(813, 392)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 30, 500 - PUR * 20)
|
||||||
|
|
||||||
code += `
|
code += `
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
Binary file not shown.
After Width: | Height: | Size: 47 KiB |
@ -1,7 +1,6 @@
|
|||||||
import { test, expect } from './zoo-test'
|
import { test, expect } from './zoo-test'
|
||||||
|
|
||||||
import { commonPoints, getUtils } from './test-utils'
|
import { commonPoints, getUtils } from './test-utils'
|
||||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
|
||||||
import { uuidv4 } from 'lib/utils'
|
|
||||||
|
|
||||||
test.describe('Test network and connection issues', () => {
|
test.describe('Test network and connection issues', () => {
|
||||||
test('simulate network down and network little widget', async ({
|
test('simulate network down and network little widget', async ({
|
||||||
@ -111,17 +110,18 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content'))
|
||||||
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
|
|
||||||
// Expect the network to be up
|
// Expect the network to be up
|
||||||
await expect(networkToggle).toContainText('Connected')
|
await expect(networkToggle).toContainText('Connected')
|
||||||
@ -168,9 +168,7 @@ test.describe('Test network and connection issues', () => {
|
|||||||
await page.mouse.click(100, 100)
|
await page.mouse.click(100, 100)
|
||||||
|
|
||||||
// select a line
|
// select a line
|
||||||
await page
|
await page.getByText(`startProfileAt(${commonPoints.startAt}, %)`).click()
|
||||||
.getByText(`startProfileAt(${commonPoints.startAt}, sketch001)`)
|
|
||||||
.click()
|
|
||||||
|
|
||||||
// enter sketch again
|
// enter sketch again
|
||||||
await u.doAndWaitForCmd(
|
await u.doAndWaitForCmd(
|
||||||
@ -184,36 +182,11 @@ test.describe('Test network and connection issues', () => {
|
|||||||
|
|
||||||
await page.waitForTimeout(150)
|
await page.waitForTimeout(150)
|
||||||
|
|
||||||
const camCommand: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_look_at',
|
|
||||||
center: { x: 109, y: 0, z: -152 },
|
|
||||||
vantage: { x: 115, y: -505, z: -152 },
|
|
||||||
up: { x: 0, y: 0, z: 1 },
|
|
||||||
},
|
|
||||||
}
|
|
||||||
const updateCamCommand: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: {
|
|
||||||
type: 'default_camera_get_settings',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
await u.sendCustomCmd(camCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
await u.sendCustomCmd(updateCamCommand)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// click to continue profile
|
|
||||||
await page.mouse.click(1007, 400)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
// Ensure we can continue sketching
|
// Ensure we can continue sketching
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
await expect.poll(u.normalisedEditorCode)
|
await expect.poll(u.normalisedEditorCode)
|
||||||
.toBe(`sketch001 = startSketchOn('XZ')
|
.toBe(`sketch001 = startSketchOn('XZ')
|
||||||
profile001 = startProfileAt([12.34, -12.34], sketch001)
|
|> startProfileAt([12.34, -12.34], %)
|
||||||
|> xLine(12.34, %)
|
|> xLine(12.34, %)
|
||||||
|> line([-12.34, 12.34], %)
|
|> line([-12.34, 12.34], %)
|
||||||
|
|
||||||
@ -223,7 +196,7 @@ profile001 = startProfileAt([12.34, -12.34], sketch001)
|
|||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode)
|
await expect.poll(u.normalisedEditorCode)
|
||||||
.toBe(`sketch001 = startSketchOn('XZ')
|
.toBe(`sketch001 = startSketchOn('XZ')
|
||||||
profile001 = startProfileAt([12.34, -12.34], sketch001)
|
|> startProfileAt([12.34, -12.34], %)
|
||||||
|> xLine(12.34, %)
|
|> xLine(12.34, %)
|
||||||
|> line([-12.34, 12.34], %)
|
|> line([-12.34, 12.34], %)
|
||||||
|> xLine(-12.34, %)
|
|> xLine(-12.34, %)
|
||||||
|
@ -19,7 +19,7 @@ test.describe('Testing constraints', () => {
|
|||||||
|> line([20, 0], %)
|
|> line([20, 0], %)
|
||||||
|> line([0, 20], %)
|
|> line([0, 20], %)
|
||||||
|> xLine(-20, %)
|
|> xLine(-20, %)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -69,34 +69,33 @@ test.describe('Testing selections', () => {
|
|||||||
const startXPx = 600
|
const startXPx = 600
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 10, 500 - PUR * 10)
|
||||||
await expect(page.locator('.cm-content')).toHaveText(
|
await expect(page.locator('.cm-content'))
|
||||||
`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)`
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
)
|
|> startProfileAt(${commonPoints.startAt}, %)`)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 10)
|
||||||
|
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${commonPoints.startAt}, sketch001)
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
|> xLine(${commonPoints.num1}, %)`)
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
|
|> xLine(${commonPoints.num1}, %)`)
|
||||||
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
await page.mouse.click(startXPx + PUR * 20, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
commonPoints.startAt
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
}, sketch001)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)`)
|
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
await page.mouse.click(startXPx, 500 - PUR * 20)
|
await page.mouse.click(startXPx, 500 - PUR * 20)
|
||||||
await expect(page.locator('.cm-content'))
|
await expect(page.locator('.cm-content'))
|
||||||
.toHaveText(`sketch001 = startSketchOn('XZ')profile001 = startProfileAt(${
|
.toHaveText(`sketch001 = startSketchOn('XZ')
|
||||||
commonPoints.startAt
|
|> startProfileAt(${commonPoints.startAt}, %)
|
||||||
}, sketch001)
|
|> xLine(${commonPoints.num1}, %)
|
||||||
|> xLine(${commonPoints.num1}, %)
|
|> yLine(${commonPoints.num1 + 0.01}, %)
|
||||||
|> yLine(${commonPoints.num1 + 0.01}, %)
|
|> xLine(${commonPoints.num2 * -1}, %)`)
|
||||||
|> xLine(${commonPoints.num2 * -1}, %)`)
|
|
||||||
|
|
||||||
// deselect line tool
|
// deselect line tool
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
@ -264,88 +263,66 @@ test.describe('Testing selections', () => {
|
|||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
'persistCode',
|
'persistCode',
|
||||||
`sketch001 = startSketchOn('XZ')
|
`sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([-79.26, 95.04], %)
|
|> startProfileAt([-79.26, 95.04], %)
|
||||||
|> line([112.54, 127.64], %, $seg02)
|
|> line([112.54, 127.64], %, $seg02)
|
||||||
|> line([170.36, -121.61], %, $seg01)
|
|> line([170.36, -121.61], %, $seg01)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude001 = extrude(50, sketch001)
|
extrude001 = extrude(50, sketch001)
|
||||||
sketch005 = startSketchOn(extrude001, 'END')
|
sketch005 = startSketchOn(extrude001, 'END')
|
||||||
|> startProfileAt([23.24, 136.52], %)
|
|> startProfileAt([23.24, 136.52], %)
|
||||||
|> line([-8.44, 36.61], %)
|
|> line([-8.44, 36.61], %)
|
||||||
|> line([49.4, 2.05], %)
|
|> line([49.4, 2.05], %)
|
||||||
|> line([29.69, -46.95], %)
|
|> line([29.69, -46.95], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch003 = startSketchOn(extrude001, seg01)
|
sketch003 = startSketchOn(extrude001, seg01)
|
||||||
|> startProfileAt([21.23, 17.81], %)
|
|> startProfileAt([21.23, 17.81], %)
|
||||||
|> line([51.97, 21.32], %)
|
|> line([51.97, 21.32], %)
|
||||||
|> line([4.07, -22.75], %)
|
|> line([4.07, -22.75], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
sketch002 = startSketchOn(extrude001, seg02)
|
sketch002 = startSketchOn(extrude001, seg02)
|
||||||
|> startProfileAt([-100.54, 16.99], %)
|
|> startProfileAt([-100.54, 16.99], %)
|
||||||
|> line([0, 20.03], %)
|
|> line([0, 20.03], %)
|
||||||
|> line([62.61, 0], %, $seg03)
|
|> line([62.61, 0], %, $seg03)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude002 = extrude(50, sketch002)
|
extrude002 = extrude(50, sketch002)
|
||||||
sketch004 = startSketchOn(extrude002, seg03)
|
sketch004 = startSketchOn(extrude002, seg03)
|
||||||
|> startProfileAt([57.07, 134.77], %)
|
|> startProfileAt([57.07, 134.77], %)
|
||||||
|> line([-4.72, 22.84], %)
|
|> line([-4.72, 22.84], %)
|
||||||
|> line([28.8, 6.71], %)
|
|> line([28.8, 6.71], %)
|
||||||
|> line([9.19, -25.33], %)
|
|> line([9.19, -25.33], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
extrude003 = extrude(20, sketch004)
|
extrude003 = extrude(20, sketch004)
|
||||||
pipeLength = 40
|
pipeLength = 40
|
||||||
pipeSmallDia = 10
|
pipeSmallDia = 10
|
||||||
pipeLargeDia = 20
|
pipeLargeDia = 20
|
||||||
thickness = 0.5
|
thickness = 0.5
|
||||||
part009 = startSketchOn('XY')
|
part009 = startSketchOn('XY')
|
||||||
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
|> startProfileAt([pipeLargeDia - (thickness / 2), 38], %)
|
||||||
|> line([thickness, 0], %)
|
|> line([thickness, 0], %)
|
||||||
|> line([0, -1], %)
|
|> line([0, -1], %)
|
||||||
|> angledLineToX({
|
|> angledLineToX({
|
||||||
angle = 60,
|
angle = 60,
|
||||||
to = pipeSmallDia + thickness
|
to = pipeSmallDia + thickness
|
||||||
}, %)
|
}, %)
|
||||||
|> line([0, -pipeLength], %)
|
|> line([0, -pipeLength], %)
|
||||||
|> angledLineToX({
|
|> angledLineToX({
|
||||||
angle = -60,
|
angle = -60,
|
||||||
to = pipeLargeDia + thickness
|
to = pipeLargeDia + thickness
|
||||||
}, %)
|
}, %)
|
||||||
|> line([0, -1], %)
|
|> line([0, -1], %)
|
||||||
|> line([-thickness, 0], %)
|
|> line([-thickness, 0], %)
|
||||||
|> line([0, 1], %)
|
|> line([0, 1], %)
|
||||||
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
|> angledLineToX({ angle = 120, to = pipeSmallDia }, %)
|
||||||
|> line([0, pipeLength], %)
|
|> line([0, pipeLength], %)
|
||||||
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
|> angledLineToX({ angle = 60, to = pipeLargeDia }, %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
rev = revolve({ axis = 'y' }, part009)
|
rev = revolve({ axis: 'y' }, part009)
|
||||||
sketch006 = startSketchOn('XY')
|
`
|
||||||
profile001 = circle({
|
|
||||||
center = [42.91, -70.42],
|
|
||||||
radius = 17.96
|
|
||||||
}, sketch006)
|
|
||||||
profile002 = startProfileAt([86.92, -63.81], sketch006)
|
|
||||||
|> angledLine([0, 63.81], %, $rectangleSegmentA001)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001) - 90,
|
|
||||||
17.05
|
|
||||||
], %)
|
|
||||||
|> angledLine([
|
|
||||||
segAng(rectangleSegmentA001),
|
|
||||||
-segLen(rectangleSegmentA001)
|
|
||||||
], %)
|
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|
||||||
|> close(%)
|
|
||||||
profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|
||||||
|> line([26.95, 24.21], %)
|
|
||||||
|> line([20.91, -28.61], %)
|
|
||||||
|> line([32.46, 18.71], %)
|
|
||||||
|
|
||||||
`
|
|
||||||
)
|
)
|
||||||
}, KCL_DEFAULT_LENGTH)
|
}, KCL_DEFAULT_LENGTH)
|
||||||
await page.setBodyDimensions({ width: 1000, height: 500 })
|
await page.setBodyDimensions({ width: 1000, height: 500 })
|
||||||
@ -377,10 +354,9 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
})
|
})
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
const revolve = { x: 635, y: 253 }
|
const revolve = { x: 646, y: 248 }
|
||||||
const parentExtrude = { x: 915, y: 133 }
|
const parentExtrude = { x: 915, y: 133 }
|
||||||
const solid2d = { x: 770, y: 167 }
|
const solid2d = { x: 770, y: 167 }
|
||||||
const individualProfile = { x: 694, y: 432 }
|
|
||||||
|
|
||||||
// DELETE REVOLVE
|
// DELETE REVOLVE
|
||||||
await page.mouse.click(revolve.x, revolve.y)
|
await page.mouse.click(revolve.x, revolve.y)
|
||||||
@ -413,25 +389,25 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch005 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 0, y = -50, z = 0 },
|
origin = { x = 0, y = -50, z = 0 },
|
||||||
x_axis = { x = 1, y = 0, z = 0 },
|
xAxis = { x = 1, y = 0, z = 0 },
|
||||||
y_axis = { x = 0, y = 0, z = 1 },
|
yAxis = { x = 0, y = 0, z = 1 },
|
||||||
z_axis = { x = 0, y = -1, z = 0 }
|
zAxis = { x = 0, y = -1, z = 0 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch003 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 116.53, y = 0, z = 163.25 },
|
origin = { x = 116.53, y = 0, z = 163.25 },
|
||||||
x_axis = { x = -0.81, y = 0, z = 0.58 },
|
xAxis = { x = -0.81, y = 0, z = 0.58 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = 0.58, y = 0, z = 0.81 }
|
zAxis = { x = 0.58, y = 0, z = 0.81 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
await expect(u.codeLocator).toContainText(`sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = -91.74, y = 0, z = 80.89 },
|
origin = { x = -91.74, y = 0, z = 80.89 },
|
||||||
x_axis = { x = -0.66, y = 0, z = -0.75 },
|
xAxis = { x = -0.66, y = 0, z = -0.75 },
|
||||||
y_axis = { x = 0, y = -1, z = 0 },
|
yAxis = { x = 0, y = -1, z = 0 },
|
||||||
z_axis = { x = -0.75, y = 0, z = 0.66 }
|
zAxis = { x = -0.75, y = 0, z = 0.66 }
|
||||||
}
|
}
|
||||||
})`)
|
})`)
|
||||||
|
|
||||||
@ -446,20 +422,6 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
||||||
await page.waitForTimeout(200)
|
await page.waitForTimeout(200)
|
||||||
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
|
await expect(u.codeLocator).not.toContainText(`sketch005 = startSketchOn({`)
|
||||||
|
|
||||||
// Delete a single profile
|
|
||||||
await page.mouse.click(individualProfile.x, individualProfile.y)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
const codeToBeDeletedSnippet =
|
|
||||||
'profile003 = startProfileAt([40.16, -120.48], sketch006)'
|
|
||||||
await expect(page.locator('.cm-activeLine')).toHaveText(
|
|
||||||
' |> line([20.91, -28.61], %)'
|
|
||||||
)
|
|
||||||
await u.clearCommandLogs()
|
|
||||||
await page.keyboard.press('Backspace')
|
|
||||||
await u.expectCmdLog('[data-message-type="execution-done"]', 10_000)
|
|
||||||
await page.waitForTimeout(200)
|
|
||||||
await expect(u.codeLocator).not.toContainText(codeToBeDeletedSnippet)
|
|
||||||
})
|
})
|
||||||
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
test("Deleting solid that the AST mod can't handle results in a toast message", async ({
|
||||||
page,
|
page,
|
||||||
@ -1311,15 +1273,12 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
|
|
||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
const firstClickCoords = { x: 650, y: 200 } as const
|
|
||||||
// Place a point because the line tool will exit if no points are pressed
|
// Place a point because the line tool will exit if no points are pressed
|
||||||
await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
|
await page.mouse.click(650, 200)
|
||||||
await page.waitForTimeout(600)
|
await page.waitForTimeout(600)
|
||||||
|
|
||||||
// Code before exiting the tool
|
// Code before exiting the tool
|
||||||
let previousCodeContent = (
|
let previousCodeContent = await page.locator('.cm-content').innerText()
|
||||||
await page.locator('.cm-content').innerText()
|
|
||||||
).replace(/\s+/g, '')
|
|
||||||
|
|
||||||
// deselect the line tool by clicking it
|
// deselect the line tool by clicking it
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
@ -1331,23 +1290,14 @@ profile003 = startProfileAt([40.16, -120.48], sketch006)
|
|||||||
await page.mouse.click(750, 200)
|
await page.mouse.click(750, 200)
|
||||||
await page.waitForTimeout(100)
|
await page.waitForTimeout(100)
|
||||||
|
|
||||||
await expect
|
// expect no change
|
||||||
.poll(async () => {
|
await expect(page.locator('.cm-content')).toHaveText(previousCodeContent)
|
||||||
let str = await page.locator('.cm-content').innerText()
|
|
||||||
str = str.replace(/\s+/g, '')
|
|
||||||
return str
|
|
||||||
})
|
|
||||||
.toBe(previousCodeContent)
|
|
||||||
|
|
||||||
// select line tool again
|
// select line tool again
|
||||||
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
await page.getByRole('button', { name: 'line Line', exact: true }).click()
|
||||||
|
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
|
||||||
// Click to continue profile
|
|
||||||
await page.mouse.click(firstClickCoords.x, firstClickCoords.y)
|
|
||||||
await page.waitForTimeout(100)
|
|
||||||
|
|
||||||
// line tool should work as expected again
|
// line tool should work as expected again
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
await expect(page.locator('.cm-content')).not.toHaveText(
|
await expect(page.locator('.cm-content')).not.toHaveText(
|
||||||
|
@ -205,13 +205,8 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
// Draw a line
|
// Draw a line
|
||||||
await page.mouse.move(700, 200, { steps: 5 })
|
await page.mouse.move(700, 200, { steps: 5 })
|
||||||
await page.mouse.click(700, 200)
|
await page.mouse.click(700, 200)
|
||||||
|
await page.mouse.move(800, 250, { steps: 5 })
|
||||||
const secondMousePosition = { x: 800, y: 250 }
|
await page.mouse.click(800, 250)
|
||||||
|
|
||||||
await page.mouse.move(secondMousePosition.x, secondMousePosition.y, {
|
|
||||||
steps: 5,
|
|
||||||
})
|
|
||||||
await page.mouse.click(secondMousePosition.x, secondMousePosition.y)
|
|
||||||
// Unequip line tool
|
// Unequip line tool
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
// Make sure we didn't pop out of sketch mode.
|
// Make sure we didn't pop out of sketch mode.
|
||||||
@ -220,17 +215,9 @@ test('First escape in tool pops you out of tool, second exits sketch mode', asyn
|
|||||||
// Equip arc tool
|
// Equip arc tool
|
||||||
await page.keyboard.press('a')
|
await page.keyboard.press('a')
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
await expect(arcButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
// click in the same position again to continue the profile
|
|
||||||
await page.mouse.move(secondMousePosition.x, secondMousePosition.y, {
|
|
||||||
steps: 5,
|
|
||||||
})
|
|
||||||
await page.mouse.click(secondMousePosition.x, secondMousePosition.y)
|
|
||||||
|
|
||||||
await page.mouse.move(1000, 100, { steps: 5 })
|
await page.mouse.move(1000, 100, { steps: 5 })
|
||||||
await page.mouse.click(1000, 100)
|
await page.mouse.click(1000, 100)
|
||||||
await page.keyboard.press('Escape')
|
await page.keyboard.press('Escape')
|
||||||
await expect(arcButton).toHaveAttribute('aria-pressed', 'false')
|
|
||||||
await page.keyboard.press('l')
|
await page.keyboard.press('l')
|
||||||
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
await expect(lineButton).toHaveAttribute('aria-pressed', 'true')
|
||||||
|
|
||||||
@ -532,9 +519,9 @@ extrude001 = extrude(5 + 7, sketch001)`
|
|||||||
|
|
||||||
await expect.poll(u.normalisedEditorCode).toContain(
|
await expect.poll(u.normalisedEditorCode).toContain(
|
||||||
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
u.normalisedCode(`sketch002 = startSketchOn(extrude001, seg01)
|
||||||
profile001 = startProfileAt([-12.88, 6.66], sketch002)
|
|> startProfileAt([-12.94, 6.6], %)
|
||||||
|> line([2.71, -0.22], %)
|
|> line([2.45, -0.2], %)
|
||||||
|> line([-2.87, -1.38], %)
|
|> line([-2.6, -1.25], %)
|
||||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||||
|> close(%)
|
|> close(%)
|
||||||
`)
|
`)
|
||||||
@ -550,8 +537,9 @@ profile001 = startProfileAt([-12.88, 6.66], sketch002)
|
|||||||
await page.getByText('startProfileAt([-12').click()
|
await page.getByText('startProfileAt([-12').click()
|
||||||
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
await expect(page.getByRole('button', { name: 'Edit Sketch' })).toBeVisible()
|
||||||
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
await page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||||
await page.waitForTimeout(500)
|
await page.waitForTimeout(400)
|
||||||
await page.setViewportSize({ width: 1200, height: 1200 })
|
await page.waitForTimeout(150)
|
||||||
|
await page.setBodyDimensions({ width: 1200, height: 1200 })
|
||||||
await u.openAndClearDebugPanel()
|
await u.openAndClearDebugPanel()
|
||||||
await u.updateCamPosition([452, -152, 1166])
|
await u.updateCamPosition([452, -152, 1166])
|
||||||
await u.closeDebugPanel()
|
await u.closeDebugPanel()
|
||||||
|
18
flake.lock
generated
18
flake.lock
generated
@ -2,11 +2,11 @@
|
|||||||
"nodes": {
|
"nodes": {
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721933792,
|
"lastModified": 1736320768,
|
||||||
"narHash": "sha256-zYVwABlQnxpbaHMfX6Wt9jhyQstFYwN2XjleOJV3VVg=",
|
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "2122a9b35b35719ad9a395fe783eabb092df01b1",
|
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -18,11 +18,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1718428119,
|
"lastModified": 1728538411,
|
||||||
"narHash": "sha256-WdWDpNaq6u1IPtxtYHHWpl5BmabtpmLnMAx0RdJ/vo8=",
|
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e6cea36f83499eb4e9cd184c8a8e823296b50ad5",
|
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -43,11 +43,11 @@
|
|||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_2"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1721960387,
|
"lastModified": 1736476219,
|
||||||
"narHash": "sha256-o21ax+745ETGXrcgc/yUuLw1SI77ymp3xEpJt+w/kks=",
|
"narHash": "sha256-+qyv3QqdZCdZ3cSO/cbpEY6tntyYjfe1bB12mdpNFaY=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "9cbf831c5b20a53354fc12758abd05966f9f1699",
|
"rev": "de30cc5963da22e9742bbbbb9a3344570ed237b9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
1
interface.d.ts
vendored
1
interface.d.ts
vendored
@ -93,5 +93,6 @@ export interface IElectronAPI {
|
|||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
electron: IElectronAPI
|
electron: IElectronAPI
|
||||||
|
openExternalLink: (e: React.MouseEvent<HTMLAnchorElement>) => void
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
package.json
12
package.json
@ -26,7 +26,7 @@
|
|||||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||||
"@headlessui/react": "^1.7.19",
|
"@headlessui/react": "^1.7.19",
|
||||||
"@headlessui/tailwindcss": "^0.2.0",
|
"@headlessui/tailwindcss": "^0.2.0",
|
||||||
"@kittycad/lib": "2.0.12",
|
"@kittycad/lib": "2.0.13",
|
||||||
"@lezer/highlight": "^1.2.1",
|
"@lezer/highlight": "^1.2.1",
|
||||||
"@lezer/lr": "^1.4.1",
|
"@lezer/lr": "^1.4.1",
|
||||||
"@react-hook/resize-observer": "^2.0.1",
|
"@react-hook/resize-observer": "^2.0.1",
|
||||||
@ -91,8 +91,8 @@
|
|||||||
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
"build:wasm": "yarn wasm-prep && cd src/wasm-lib && wasm-pack build --release --target web --out-dir pkg && cargo test -p kcl-lib export_bindings && cd ../.. && yarn isomorphic-copy-wasm && yarn fmt",
|
||||||
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
"remove-importmeta": "sed -i 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\"; sed -i '' 's/import.meta.url/window.location.origin/g' \"./src/wasm-lib/pkg/wasm_lib.js\" || echo \"sed for both mac and linux\"",
|
||||||
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
"wasm-prep": "rimraf src/wasm-lib/pkg && mkdirp src/wasm-lib/pkg && rimraf src/wasm-lib/kcl/bindings",
|
||||||
"lint-fix": "eslint --fix src e2e packages/codemirror-lsp-client",
|
"lint-fix": "eslint --fix --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||||
"lint": "eslint --max-warnings 0 src e2e packages/codemirror-lsp-client",
|
"lint": "eslint --max-warnings 0 --ext .ts --ext .tsx src e2e packages/codemirror-lsp-client/src",
|
||||||
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
"files:set-version": "echo \"$(jq --arg v \"$VERSION\" '.version=$v' package.json --indent 2)\" > package.json",
|
||||||
"files:set-notes": "./scripts/set-files-notes.sh",
|
"files:set-notes": "./scripts/set-files-notes.sh",
|
||||||
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
"files:flip-to-nightly": "./scripts/flip-files-to-nightly.sh",
|
||||||
@ -171,8 +171,6 @@
|
|||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^9.0.8",
|
||||||
"@types/wicg-file-system-access": "^2023.10.5",
|
"@types/wicg-file-system-access": "^2023.10.5",
|
||||||
"@types/ws": "^8.5.13",
|
"@types/ws": "^8.5.13",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
|
||||||
"@vitejs/plugin-react": "^4.3.0",
|
"@vitejs/plugin-react": "^4.3.0",
|
||||||
"@vitest/web-worker": "^1.5.0",
|
"@vitest/web-worker": "^1.5.0",
|
||||||
"@xstate/cli": "^0.5.17",
|
"@xstate/cli": "^0.5.17",
|
||||||
@ -182,9 +180,10 @@
|
|||||||
"electron-builder": "24.13.3",
|
"electron-builder": "24.13.3",
|
||||||
"electron-notarize": "1.2.2",
|
"electron-notarize": "1.2.2",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
"eslint-config-react-app": "^7.0.1",
|
|
||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
|
"eslint-plugin-jest": "^28.10.0",
|
||||||
|
"eslint-plugin-react": "^7.37.3",
|
||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^16.3.0",
|
"happy-dom": "^16.3.0",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
@ -200,6 +199,7 @@
|
|||||||
"tailwindcss": "^3.4.1",
|
"tailwindcss": "^3.4.1",
|
||||||
"ts-node": "^10.0.0",
|
"ts-node": "^10.0.0",
|
||||||
"typescript": "^5.7.2",
|
"typescript": "^5.7.2",
|
||||||
|
"typescript-eslint": "^8.19.1",
|
||||||
"vite": "^5.4.6",
|
"vite": "^5.4.6",
|
||||||
"vite-plugin-package-version": "^1.1.0",
|
"vite-plugin-package-version": "^1.1.0",
|
||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
|
@ -42,7 +42,7 @@ export default class StreamDemuxer extends Queue<Uint8Array> {
|
|||||||
// try to parse the content-length from the headers
|
// try to parse the content-length from the headers
|
||||||
const length = parseInt(match[1])
|
const length = parseInt(match[1])
|
||||||
|
|
||||||
if (isNaN(length))
|
if (Number.isNaN(length))
|
||||||
return Promise.reject(new Error('invalid content length'))
|
return Promise.reject(new Error('invalid content length'))
|
||||||
|
|
||||||
// slice the headers since we now have the content length
|
// slice the headers since we now have the content length
|
||||||
|
@ -368,13 +368,20 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
sortText,
|
sortText,
|
||||||
filterText,
|
filterText,
|
||||||
}) => {
|
}) => {
|
||||||
|
const detailText = [
|
||||||
|
deprecated ? 'Deprecated' : undefined,
|
||||||
|
labelDetails ? labelDetails.detail : detail,
|
||||||
|
]
|
||||||
|
// Don't let undefined appear.
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(' ')
|
||||||
const completion: Completion & {
|
const completion: Completion & {
|
||||||
filterText: string
|
filterText: string
|
||||||
sortText?: string
|
sortText?: string
|
||||||
apply: string
|
apply: string
|
||||||
} = {
|
} = {
|
||||||
label,
|
label,
|
||||||
detail: labelDetails ? labelDetails.detail : detail,
|
detail: detailText,
|
||||||
apply: label,
|
apply: label,
|
||||||
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
type: kind && CompletionItemKindMap[kind].toLowerCase(),
|
||||||
sortText: sortText ?? label,
|
sortText: sortText ?? label,
|
||||||
@ -382,7 +389,11 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
}
|
}
|
||||||
if (documentation) {
|
if (documentation) {
|
||||||
completion.info = () => {
|
completion.info = () => {
|
||||||
const htmlString = formatMarkdownContents(documentation)
|
const deprecatedHtml = deprecated
|
||||||
|
? '<p><strong>Deprecated</strong></p>'
|
||||||
|
: ''
|
||||||
|
const htmlString =
|
||||||
|
deprecatedHtml + formatMarkdownContents(documentation)
|
||||||
const htmlNode = document.createElement('div')
|
const htmlNode = document.createElement('div')
|
||||||
htmlNode.style.display = 'contents'
|
htmlNode.style.display = 'contents'
|
||||||
htmlNode.innerHTML = htmlString
|
htmlNode.innerHTML = htmlString
|
||||||
|
@ -32,10 +32,9 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
projects: [
|
projects: [
|
||||||
{
|
{
|
||||||
name: 'Google Chrome',
|
name: 'chromium',
|
||||||
use: {
|
use: {
|
||||||
...devices['Desktop Chrome'],
|
...devices['Desktop Chrome'],
|
||||||
channel: 'chrome',
|
|
||||||
contextOptions: {
|
contextOptions: {
|
||||||
/* Chromium is the only one with these permission types */
|
/* Chromium is the only one with these permission types */
|
||||||
permissions: ['clipboard-write', 'clipboard-read'],
|
permissions: ['clipboard-write', 'clipboard-read'],
|
||||||
|
@ -6,6 +6,7 @@ import { useCommandsContext } from 'hooks/useCommandsContext'
|
|||||||
import { useNetworkContext } from 'hooks/useNetworkContext'
|
import { useNetworkContext } from 'hooks/useNetworkContext'
|
||||||
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
import { NetworkHealthState } from 'hooks/useNetworkStatus'
|
||||||
import { ActionButton } from 'components/ActionButton'
|
import { ActionButton } from 'components/ActionButton'
|
||||||
|
import { isSingleCursorInPipe } from 'lang/queryAst'
|
||||||
import { useKclContext } from 'lang/KclProvider'
|
import { useKclContext } from 'lang/KclProvider'
|
||||||
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
|
import { ActionButtonDropdown } from 'components/ActionButtonDropdown'
|
||||||
import { useHotkeys } from 'react-hotkeys-hook'
|
import { useHotkeys } from 'react-hotkeys-hook'
|
||||||
@ -21,7 +22,6 @@ import {
|
|||||||
} from 'lib/toolbar'
|
} from 'lib/toolbar'
|
||||||
import { isDesktop } from 'lib/isDesktop'
|
import { isDesktop } from 'lib/isDesktop'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
import { isCursorInFunctionDefinition } from 'lang/queryAst'
|
|
||||||
|
|
||||||
export function Toolbar({
|
export function Toolbar({
|
||||||
className = '',
|
className = '',
|
||||||
@ -38,12 +38,7 @@ export function Toolbar({
|
|||||||
'!border-transparent hover:!border-chalkboard-20 dark:enabled:hover:!border-primary pressed:!border-primary ui-open:!border-primary'
|
'!border-transparent hover:!border-chalkboard-20 dark:enabled:hover:!border-primary pressed:!border-primary ui-open:!border-primary'
|
||||||
|
|
||||||
const sketchPathId = useMemo(() => {
|
const sketchPathId = useMemo(() => {
|
||||||
if (
|
if (!isSingleCursorInPipe(context.selectionRanges, kclManager.ast))
|
||||||
isCursorInFunctionDefinition(
|
|
||||||
kclManager.ast,
|
|
||||||
context.selectionRanges.graphSelections[0]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
return isCursorInSketchCommandRange(
|
return isCursorInSketchCommandRange(
|
||||||
engineCommandManager.artifactGraph,
|
engineCommandManager.artifactGraph,
|
||||||
|
@ -438,8 +438,6 @@ export async function deleteSegment({
|
|||||||
if (!sketchDetails) return
|
if (!sketchDetails) return
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
pathToNode,
|
pathToNode,
|
||||||
sketchDetails.sketchNodePaths,
|
|
||||||
sketchDetails.planeNodePath,
|
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -696,21 +696,19 @@ export function createProfileStartHandle({
|
|||||||
scale = 1,
|
scale = 1,
|
||||||
theme,
|
theme,
|
||||||
isSelected,
|
isSelected,
|
||||||
size = 12,
|
|
||||||
...rest
|
...rest
|
||||||
}: {
|
}: {
|
||||||
from: Coords2d
|
from: Coords2d
|
||||||
scale?: number
|
scale?: number
|
||||||
theme: Themes
|
theme: Themes
|
||||||
isSelected?: boolean
|
isSelected?: boolean
|
||||||
size?: number
|
|
||||||
} & (
|
} & (
|
||||||
| { isDraft: true }
|
| { isDraft: true }
|
||||||
| { isDraft: false; id: string; pathToNode: PathToNode }
|
| { isDraft: false; id: string; pathToNode: PathToNode }
|
||||||
)) {
|
)) {
|
||||||
const group = new Group()
|
const group = new Group()
|
||||||
|
|
||||||
const geometry = new BoxGeometry(size, size, size) // in pixels scaled later
|
const geometry = new BoxGeometry(12, 12, 12) // in pixels scaled later
|
||||||
const baseColor = getThemeColorForThreeJs(theme)
|
const baseColor = getThemeColorForThreeJs(theme)
|
||||||
const color = isSelected ? 0x0000ff : baseColor
|
const color = isSelected ? 0x0000ff : baseColor
|
||||||
const body = new MeshBasicMaterial({ color })
|
const body = new MeshBasicMaterial({ color })
|
||||||
|
@ -21,6 +21,7 @@ import { ContextMenu, ContextMenuItem } from './ContextMenu'
|
|||||||
import usePlatform from 'hooks/usePlatform'
|
import usePlatform from 'hooks/usePlatform'
|
||||||
import { FileEntry } from 'lib/project'
|
import { FileEntry } from 'lib/project'
|
||||||
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
import { useFileSystemWatcher } from 'hooks/useFileSystemWatcher'
|
||||||
|
import { normalizeLineEndings } from 'lib/codeEditor'
|
||||||
import { reportRejection } from 'lib/trap'
|
import { reportRejection } from 'lib/trap'
|
||||||
|
|
||||||
function getIndentationCSS(level: number) {
|
function getIndentationCSS(level: number) {
|
||||||
@ -187,25 +188,24 @@ const FileTreeItem = ({
|
|||||||
// Because subtrees only render when they are opened, that means this
|
// Because subtrees only render when they are opened, that means this
|
||||||
// only listens when they open. Because this acts like a useEffect, when
|
// only listens when they open. Because this acts like a useEffect, when
|
||||||
// the ReactNodes are destroyed, so is this listener :)
|
// the ReactNodes are destroyed, so is this listener :)
|
||||||
/** Disabling this in favor of faster file writes until we fix file writing **/
|
useFileSystemWatcher(
|
||||||
/* useFileSystemWatcher(
|
async (eventType, path) => {
|
||||||
* async (eventType, path) => {
|
// Prevents a cyclic read / write causing editor problems such as
|
||||||
* // Prevents a cyclic read / write causing editor problems such as
|
// misplaced cursor positions.
|
||||||
* // misplaced cursor positions.
|
if (codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher) {
|
||||||
* if (codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher) {
|
codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher = false
|
||||||
* codeManager.writeCausedByAppCheckedInFileTreeFileSystemWatcher = false
|
return
|
||||||
* return
|
}
|
||||||
* }
|
|
||||||
|
|
||||||
* if (isCurrentFile && eventType === 'change') {
|
if (isCurrentFile && eventType === 'change') {
|
||||||
* let code = await window.electron.readFile(path, { encoding: 'utf-8' })
|
let code = await window.electron.readFile(path, { encoding: 'utf-8' })
|
||||||
* code = normalizeLineEndings(code)
|
code = normalizeLineEndings(code)
|
||||||
* codeManager.updateCodeStateEditor(code)
|
codeManager.updateCodeStateEditor(code)
|
||||||
* }
|
}
|
||||||
* fileSend({ type: 'Refresh' })
|
fileSend({ type: 'Refresh' })
|
||||||
* },
|
},
|
||||||
* [fileOrDir.path]
|
[fileOrDir.path]
|
||||||
* ) */
|
)
|
||||||
|
|
||||||
const showNewTreeEntry =
|
const showNewTreeEntry =
|
||||||
newTreeEntry !== undefined &&
|
newTreeEntry !== undefined &&
|
||||||
|
@ -24,7 +24,7 @@ import { useSetupEngineManager } from 'hooks/useSetupEngineManager'
|
|||||||
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
import { useSettingsAuthContext } from 'hooks/useSettingsAuthContext'
|
||||||
import {
|
import {
|
||||||
isCursorInSketchCommandRange,
|
isCursorInSketchCommandRange,
|
||||||
updateSketchDetailsNodePaths,
|
updatePathToNodeFromMap,
|
||||||
} from 'lang/util'
|
} from 'lang/util'
|
||||||
import {
|
import {
|
||||||
kclManager,
|
kclManager,
|
||||||
@ -64,30 +64,20 @@ import {
|
|||||||
replaceValueAtNodePath,
|
replaceValueAtNodePath,
|
||||||
sketchOnExtrudedFace,
|
sketchOnExtrudedFace,
|
||||||
sketchOnOffsetPlane,
|
sketchOnOffsetPlane,
|
||||||
splitPipedProfile,
|
|
||||||
startSketchOnDefault,
|
startSketchOnDefault,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import {
|
import { PathToNode, Program, parse, recast, resultIsOk } from 'lang/wasm'
|
||||||
PathToNode,
|
|
||||||
Program,
|
|
||||||
VariableDeclaration,
|
|
||||||
parse,
|
|
||||||
recast,
|
|
||||||
resultIsOk,
|
|
||||||
} from 'lang/wasm'
|
|
||||||
import {
|
import {
|
||||||
artifactIsPlaneWithPaths,
|
artifactIsPlaneWithPaths,
|
||||||
doesSketchPipeNeedSplitting,
|
getNodePathFromSourceRange,
|
||||||
getNodeFromPath,
|
isSingleCursorInPipe,
|
||||||
isCursorInFunctionDefinition,
|
|
||||||
traverse,
|
|
||||||
} from 'lang/queryAst'
|
} from 'lang/queryAst'
|
||||||
import { exportFromEngine } from 'lib/exportFromEngine'
|
import { exportFromEngine } from 'lib/exportFromEngine'
|
||||||
import { Models } from '@kittycad/lib/dist/types/src'
|
import { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
import { useLoaderData, useNavigate, useSearchParams } from 'react-router-dom'
|
||||||
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
import { letEngineAnimateAndSyncCamAfter } from 'clientSideScene/CameraControls'
|
||||||
import { err, reportRejection, trap, reject } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
@ -99,10 +89,6 @@ import { useFileContext } from 'hooks/useFileContext'
|
|||||||
import { uuidv4 } from 'lib/utils'
|
import { uuidv4 } from 'lib/utils'
|
||||||
import { IndexLoaderData } from 'lib/types'
|
import { IndexLoaderData } from 'lib/types'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import {
|
|
||||||
getPathsFromArtifact,
|
|
||||||
getPlaneFromArtifact,
|
|
||||||
} from 'lang/std/artifactGraph'
|
|
||||||
import { promptToEditFlow } from 'lib/promptToEdit'
|
import { promptToEditFlow } from 'lib/promptToEdit'
|
||||||
import { kclEditorActor } from 'machines/kclEditorMachine'
|
import { kclEditorActor } from 'machines/kclEditorMachine'
|
||||||
|
|
||||||
@ -171,39 +157,38 @@ export const ModelingMachineProvider = ({
|
|||||||
'enable copilot': () => {
|
'enable copilot': () => {
|
||||||
editorManager.setCopilotEnabled(true)
|
editorManager.setCopilotEnabled(true)
|
||||||
},
|
},
|
||||||
// tsc reports this typing as perfectly fine, but eslint is complaining.
|
'sketch exit execute': ({ context: { store } }) => {
|
||||||
// It's actually nonsensical, so I'm quieting.
|
// TODO: Remove this async callback. For some reason eslint wouldn't
|
||||||
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
// let me disable @typescript-eslint/no-misused-promises for the line.
|
||||||
'sketch exit execute': async ({
|
;(async () => {
|
||||||
context: { store },
|
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
||||||
}): Promise<void> => {
|
await engineCommandManager.sendSceneCommand({
|
||||||
// When cancelling the sketch mode we should disable sketch mode within the engine.
|
type: 'modeling_cmd_req',
|
||||||
await engineCommandManager.sendSceneCommand({
|
cmd_id: uuidv4(),
|
||||||
type: 'modeling_cmd_req',
|
cmd: { type: 'sketch_mode_disable' },
|
||||||
cmd_id: uuidv4(),
|
|
||||||
cmd: { type: 'sketch_mode_disable' },
|
|
||||||
})
|
|
||||||
|
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
|
||||||
|
|
||||||
if (cameraProjection.current === 'perspective') {
|
|
||||||
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
|
||||||
}
|
|
||||||
|
|
||||||
sceneInfra.camControls.syncDirection = 'engineToClient'
|
|
||||||
|
|
||||||
store.videoElement?.pause()
|
|
||||||
|
|
||||||
return kclManager
|
|
||||||
.executeCode()
|
|
||||||
.then(() => {
|
|
||||||
if (engineCommandManager.engineConnection?.idleMode) return
|
|
||||||
|
|
||||||
store.videoElement?.play().catch((e) => {
|
|
||||||
console.warn('Video playing was prevented', e)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.catch(reportRejection)
|
|
||||||
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
|
|
||||||
|
if (cameraProjection.current === 'perspective') {
|
||||||
|
await sceneInfra.camControls.snapToPerspectiveBeforeHandingBackControlToEngine()
|
||||||
|
}
|
||||||
|
|
||||||
|
sceneInfra.camControls.syncDirection = 'engineToClient'
|
||||||
|
|
||||||
|
store.videoElement?.pause()
|
||||||
|
|
||||||
|
return kclManager
|
||||||
|
.executeCode()
|
||||||
|
.then(() => {
|
||||||
|
if (engineCommandManager.engineConnection?.idleMode) return
|
||||||
|
|
||||||
|
store.videoElement?.play().catch((e) => {
|
||||||
|
console.warn('Video playing was prevented', e)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.catch(reportRejection)
|
||||||
|
})().catch(reportRejection)
|
||||||
},
|
},
|
||||||
'Set mouse state': assign(({ context, event }) => {
|
'Set mouse state': assign(({ context, event }) => {
|
||||||
if (event.type !== 'Set mouse state') return {}
|
if (event.type !== 'Set mouse state') return {}
|
||||||
@ -285,6 +270,7 @@ export const ModelingMachineProvider = ({
|
|||||||
cmd_id: uuidv4(),
|
cmd_id: uuidv4(),
|
||||||
cmd: {
|
cmd: {
|
||||||
type: 'default_camera_center_to_selection',
|
type: 'default_camera_center_to_selection',
|
||||||
|
camera_movement: 'vantage',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.catch(reportRejection)
|
.catch(reportRejection)
|
||||||
@ -295,7 +281,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
sketchDetails: {
|
sketchDetails: {
|
||||||
...sketchDetails,
|
...sketchDetails,
|
||||||
sketchEntryNodePath: event.data,
|
sketchPathToNode: event.data,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
@ -410,17 +396,9 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges: setSelections.selection,
|
selectionRanges: setSelections.selection,
|
||||||
sketchDetails: {
|
sketchDetails: {
|
||||||
...sketchDetails,
|
...sketchDetails,
|
||||||
sketchEntryNodePath:
|
sketchPathToNode:
|
||||||
setSelections.updatedSketchEntryNodePath ||
|
setSelections.updatedPathToNode ||
|
||||||
sketchDetails?.sketchEntryNodePath ||
|
sketchDetails?.sketchPathToNode ||
|
||||||
[],
|
|
||||||
sketchNodePaths:
|
|
||||||
setSelections.updatedSketchNodePaths ||
|
|
||||||
sketchDetails?.sketchNodePaths ||
|
|
||||||
[],
|
|
||||||
planeNodePath:
|
|
||||||
setSelections.updatedPlaneNodePath ||
|
|
||||||
sketchDetails?.planeNodePath ||
|
|
||||||
[],
|
[],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -574,12 +552,7 @@ export const ModelingMachineProvider = ({
|
|||||||
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
if (artifactIsPlaneWithPaths(selectionRanges)) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
if (
|
if (!isSingleCursorInPipe(selectionRanges, kclManager.ast))
|
||||||
isCursorInFunctionDefinition(
|
|
||||||
kclManager.ast,
|
|
||||||
selectionRanges.graphSelections[0]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return false
|
return false
|
||||||
return !!isCursorInSketchCommandRange(
|
return !!isCursorInSketchCommandRange(
|
||||||
engineCommandManager.artifactGraph,
|
engineCommandManager.artifactGraph,
|
||||||
@ -610,32 +583,10 @@ export const ModelingMachineProvider = ({
|
|||||||
// this assumes no changes have been made to the sketch besides what we did when entering the sketch
|
// this assumes no changes have been made to the sketch besides what we did when entering the sketch
|
||||||
// i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
|
// i.e. doesn't account for user's adding code themselves, maybe we need store a flag userEditedSinceSketchMode?
|
||||||
const newAst = structuredClone(kclManager.ast)
|
const newAst = structuredClone(kclManager.ast)
|
||||||
const varDecIndex = sketchDetails.planeNodePath[1][0]
|
const varDecIndex = sketchDetails.sketchPathToNode[1][0]
|
||||||
|
|
||||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
|
||||||
newAst,
|
|
||||||
sketchDetails.planeNodePath,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(varDec)) return reject(new Error('No varDec'))
|
|
||||||
const variableName = varDec.node.declaration.id.name
|
|
||||||
let isIdentifierUsed = false
|
|
||||||
traverse(newAst, {
|
|
||||||
enter: (node) => {
|
|
||||||
if (
|
|
||||||
node.type === 'Identifier' &&
|
|
||||||
node.name === variableName
|
|
||||||
) {
|
|
||||||
isIdentifierUsed = true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (isIdentifierUsed) return
|
|
||||||
|
|
||||||
// remove body item at varDecIndex
|
// remove body item at varDecIndex
|
||||||
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
newAst.body = newAst.body.filter((_, i) => i !== varDecIndex)
|
||||||
await kclManager.executeAstMock(newAst)
|
await kclManager.executeAstMock(newAst)
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(newAst)
|
|
||||||
}
|
}
|
||||||
sceneInfra.setCallbacks({
|
sceneInfra.setCallbacks({
|
||||||
onClick: () => {},
|
onClick: () => {},
|
||||||
@ -645,7 +596,7 @@ export const ModelingMachineProvider = ({
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
'animate-to-face': fromPromise(async ({ input }) => {
|
'animate-to-face': fromPromise(async ({ input }) => {
|
||||||
if (!input) return null
|
if (!input) return undefined
|
||||||
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
if (input.type === 'extrudeFace' || input.type === 'offsetPlane') {
|
||||||
const sketched =
|
const sketched =
|
||||||
input.type === 'extrudeFace'
|
input.type === 'extrudeFace'
|
||||||
@ -672,9 +623,7 @@ export const ModelingMachineProvider = ({
|
|||||||
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
await letEngineAnimateAndSyncCamAfter(engineCommandManager, id)
|
||||||
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
sceneInfra.camControls.syncDirection = 'clientToEngine'
|
||||||
return {
|
return {
|
||||||
sketchEntryNodePath: [],
|
sketchPathToNode: pathToNewSketchNode,
|
||||||
planeNodePath: pathToNewSketchNode,
|
|
||||||
sketchNodePaths: [],
|
|
||||||
zAxis: input.zAxis,
|
zAxis: input.zAxis,
|
||||||
yAxis: input.yAxis,
|
yAxis: input.yAxis,
|
||||||
origin: input.position,
|
origin: input.position,
|
||||||
@ -694,9 +643,7 @@ export const ModelingMachineProvider = ({
|
|||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sketchEntryNodePath: [],
|
sketchPathToNode: pathToNode,
|
||||||
planeNodePath: pathToNode,
|
|
||||||
sketchNodePaths: [],
|
|
||||||
zAxis: input.zAxis,
|
zAxis: input.zAxis,
|
||||||
yAxis: input.yAxis,
|
yAxis: input.yAxis,
|
||||||
origin: [0, 0, 0],
|
origin: [0, 0, 0],
|
||||||
@ -704,14 +651,12 @@ export const ModelingMachineProvider = ({
|
|||||||
}),
|
}),
|
||||||
'animate-to-sketch': fromPromise(
|
'animate-to-sketch': fromPromise(
|
||||||
async ({ input: { selectionRanges } }) => {
|
async ({ input: { selectionRanges } }) => {
|
||||||
const sketchPathToNode =
|
const sourceRange =
|
||||||
selectionRanges.graphSelections[0]?.codeRef?.pathToNode
|
selectionRanges.graphSelections[0]?.codeRef?.range
|
||||||
const plane = getPlaneFromArtifact(
|
const sketchPathToNode = getNodePathFromSourceRange(
|
||||||
selectionRanges.graphSelections[0].artifact,
|
kclManager.ast,
|
||||||
engineCommandManager.artifactGraph
|
sourceRange
|
||||||
)
|
)
|
||||||
if (err(plane)) return Promise.reject(plane)
|
|
||||||
|
|
||||||
const info = await getSketchOrientationDetails(
|
const info = await getSketchOrientationDetails(
|
||||||
sketchPathToNode || []
|
sketchPathToNode || []
|
||||||
)
|
)
|
||||||
@ -719,17 +664,8 @@ export const ModelingMachineProvider = ({
|
|||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
info?.sketchDetails?.faceId || ''
|
info?.sketchDetails?.faceId || ''
|
||||||
)
|
)
|
||||||
const sketchPaths = getPathsFromArtifact({
|
|
||||||
artifact: selectionRanges.graphSelections[0].artifact,
|
|
||||||
sketchPathToNode: sketchPathToNode || [],
|
|
||||||
})
|
|
||||||
if (err(sketchPaths)) return Promise.reject(sketchPaths)
|
|
||||||
if (!plane.codeRef)
|
|
||||||
return Promise.reject(new Error('No plane codeRef'))
|
|
||||||
return {
|
return {
|
||||||
sketchEntryNodePath: sketchPathToNode || [],
|
sketchPathToNode: sketchPathToNode || [],
|
||||||
sketchNodePaths: sketchPaths,
|
|
||||||
planeNodePath: plane.codeRef.pathToNode,
|
|
||||||
zAxis: info.sketchDetails.zAxis || null,
|
zAxis: info.sketchDetails.zAxis || null,
|
||||||
yAxis: info.sketchDetails.yAxis || null,
|
yAxis: info.sketchDetails.yAxis || null,
|
||||||
origin: info.sketchDetails.origin.map(
|
origin: info.sketchDetails.origin.map(
|
||||||
@ -741,7 +677,7 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
'Get horizontal info': fromPromise(
|
'Get horizontal info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } =
|
||||||
await applyConstraintHorzVertDistance({
|
await applyConstraintHorzVertDistance({
|
||||||
constraint: 'setHorzDistance',
|
constraint: 'setHorzDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -753,23 +689,13 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -790,15 +716,13 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get vertical info': fromPromise(
|
'Get vertical info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } =
|
||||||
await applyConstraintHorzVertDistance({
|
await applyConstraintHorzVertDistance({
|
||||||
constraint: 'setVertDistance',
|
constraint: 'setVertDistance',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -809,23 +733,13 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -846,9 +760,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -858,15 +770,14 @@ export const ModelingMachineProvider = ({
|
|||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
if (err(info)) return Promise.reject(info)
|
if (err(info)) return Promise.reject(info)
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } = await (info.enabled
|
||||||
await (info.enabled
|
? applyConstraintAngleBetween({
|
||||||
? applyConstraintAngleBetween({
|
selectionRanges,
|
||||||
selectionRanges,
|
})
|
||||||
})
|
: applyConstraintAngleLength({
|
||||||
: applyConstraintAngleLength({
|
selectionRanges,
|
||||||
selectionRanges,
|
angleOrLength: 'setAngle',
|
||||||
angleOrLength: 'setAngle',
|
}))
|
||||||
}))
|
|
||||||
const pResult = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
@ -875,23 +786,13 @@ export const ModelingMachineProvider = ({
|
|||||||
|
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -912,9 +813,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -929,30 +828,20 @@ export const ModelingMachineProvider = ({
|
|||||||
length: lengthValue,
|
length: lengthValue,
|
||||||
})
|
})
|
||||||
if (err(constraintResult)) return Promise.reject(constraintResult)
|
if (err(constraintResult)) return Promise.reject(constraintResult)
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } = constraintResult
|
||||||
constraintResult
|
|
||||||
const pResult = parse(recast(modifiedAst))
|
const pResult = parse(recast(modifiedAst))
|
||||||
if (trap(pResult) || !resultIsOk(pResult))
|
if (trap(pResult) || !resultIsOk(pResult))
|
||||||
return Promise.reject(new Error('Unexpected compilation error'))
|
return Promise.reject(new Error('Unexpected compilation error'))
|
||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -973,15 +862,13 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get perpendicular distance info': fromPromise(
|
'Get perpendicular distance info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } =
|
||||||
await applyConstraintIntersect({
|
await applyConstraintIntersect({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
})
|
})
|
||||||
@ -991,22 +878,13 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1027,15 +905,13 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get ABS X info': fromPromise(
|
'Get ABS X info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } =
|
||||||
await applyConstraintAbsDistance({
|
await applyConstraintAbsDistance({
|
||||||
constraint: 'xAbs',
|
constraint: 'xAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -1046,22 +922,13 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1082,15 +949,13 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
'Get ABS Y info': fromPromise(
|
'Get ABS Y info': fromPromise(
|
||||||
async ({ input: { selectionRanges, sketchDetails } }) => {
|
async ({ input: { selectionRanges, sketchDetails } }) => {
|
||||||
const { modifiedAst, pathToNodeMap, exprInsertIndex } =
|
const { modifiedAst, pathToNodeMap } =
|
||||||
await applyConstraintAbsDistance({
|
await applyConstraintAbsDistance({
|
||||||
constraint: 'yAbs',
|
constraint: 'yAbs',
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -1101,22 +966,13 @@ export const ModelingMachineProvider = ({
|
|||||||
const _modifiedAst = pResult.program
|
const _modifiedAst = pResult.program
|
||||||
if (!sketchDetails)
|
if (!sketchDetails)
|
||||||
return Promise.reject(new Error('No sketch details'))
|
return Promise.reject(new Error('No sketch details'))
|
||||||
|
const updatedPathToNode = updatePathToNodeFromMap(
|
||||||
const {
|
sketchDetails.sketchPathToNode,
|
||||||
updatedSketchEntryNodePath,
|
pathToNodeMap
|
||||||
updatedSketchNodePaths,
|
)
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
})
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
_modifiedAst,
|
_modifiedAst,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1137,9 +993,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
@ -1159,11 +1013,9 @@ export const ModelingMachineProvider = ({
|
|||||||
let result: {
|
let result: {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToReplaced: PathToNode | null
|
pathToReplaced: PathToNode | null
|
||||||
exprInsertIndex: number
|
|
||||||
} = {
|
} = {
|
||||||
modifiedAst: parsed,
|
modifiedAst: parsed,
|
||||||
pathToReplaced: null,
|
pathToReplaced: null,
|
||||||
exprInsertIndex: -1,
|
|
||||||
}
|
}
|
||||||
// If the user provided a constant name,
|
// If the user provided a constant name,
|
||||||
// we need to insert the named constant
|
// we need to insert the named constant
|
||||||
@ -1193,7 +1045,6 @@ export const ModelingMachineProvider = ({
|
|||||||
result = {
|
result = {
|
||||||
modifiedAst: parseResultAfterInsertion.program,
|
modifiedAst: parseResultAfterInsertion.program,
|
||||||
pathToReplaced: astAfterReplacement.pathToReplaced,
|
pathToReplaced: astAfterReplacement.pathToReplaced,
|
||||||
exprInsertIndex: astAfterReplacement.exprInsertIndex,
|
|
||||||
}
|
}
|
||||||
} else if ('valueText' in data.namedValue) {
|
} else if ('valueText' in data.namedValue) {
|
||||||
// If they didn't provide a constant name,
|
// If they didn't provide a constant name,
|
||||||
@ -1224,22 +1075,10 @@ export const ModelingMachineProvider = ({
|
|||||||
parsed = parsed as Node<Program>
|
parsed = parsed as Node<Program>
|
||||||
if (!result.pathToReplaced)
|
if (!result.pathToReplaced)
|
||||||
return Promise.reject(new Error('No path to replaced node'))
|
return Promise.reject(new Error('No path to replaced node'))
|
||||||
const {
|
|
||||||
updatedSketchEntryNodePath,
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
} = updateSketchDetailsNodePaths({
|
|
||||||
sketchEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
exprInsertIndex: result.exprInsertIndex,
|
|
||||||
})
|
|
||||||
|
|
||||||
const updatedAst =
|
const updatedAst =
|
||||||
await sceneEntitiesManager.updateAstAndRejigSketch(
|
await sceneEntitiesManager.updateAstAndRejigSketch(
|
||||||
updatedSketchEntryNodePath,
|
result.pathToReplaced || [],
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
parsed,
|
parsed,
|
||||||
sketchDetails.zAxis,
|
sketchDetails.zAxis,
|
||||||
sketchDetails.yAxis,
|
sketchDetails.yAxis,
|
||||||
@ -1260,140 +1099,7 @@ export const ModelingMachineProvider = ({
|
|||||||
return {
|
return {
|
||||||
selectionType: 'completeSelection',
|
selectionType: 'completeSelection',
|
||||||
selection,
|
selection,
|
||||||
updatedSketchEntryNodePath,
|
updatedPathToNode: result.pathToReplaced,
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedPlaneNodePath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'set-up-draft-circle': fromPromise(
|
|
||||||
async ({ input: { sketchDetails, data } }) => {
|
|
||||||
if (!sketchDetails || !data)
|
|
||||||
return reject('No sketch details or data')
|
|
||||||
await sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
|
||||||
|
|
||||||
const result = await sceneEntitiesManager.setupDraftCircle(
|
|
||||||
sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchDetails.sketchNodePaths,
|
|
||||||
sketchDetails.planeNodePath,
|
|
||||||
sketchDetails.zAxis,
|
|
||||||
sketchDetails.yAxis,
|
|
||||||
sketchDetails.origin,
|
|
||||||
data
|
|
||||||
)
|
|
||||||
if (err(result)) return reject(result)
|
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'set-up-draft-rectangle': fromPromise(
|
|
||||||
async ({ input: { sketchDetails, data } }) => {
|
|
||||||
if (!sketchDetails || !data)
|
|
||||||
return reject('No sketch details or data')
|
|
||||||
await sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
|
||||||
|
|
||||||
const result = await sceneEntitiesManager.setupDraftRectangle(
|
|
||||||
sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchDetails.sketchNodePaths,
|
|
||||||
sketchDetails.planeNodePath,
|
|
||||||
sketchDetails.zAxis,
|
|
||||||
sketchDetails.yAxis,
|
|
||||||
sketchDetails.origin,
|
|
||||||
data
|
|
||||||
)
|
|
||||||
if (err(result)) return reject(result)
|
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'set-up-draft-center-rectangle': fromPromise(
|
|
||||||
async ({ input: { sketchDetails, data } }) => {
|
|
||||||
if (!sketchDetails || !data)
|
|
||||||
return reject('No sketch details or data')
|
|
||||||
await sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
|
||||||
const result = await sceneEntitiesManager.setupDraftCenterRectangle(
|
|
||||||
sketchDetails.sketchEntryNodePath,
|
|
||||||
sketchDetails.sketchNodePaths,
|
|
||||||
sketchDetails.planeNodePath,
|
|
||||||
sketchDetails.zAxis,
|
|
||||||
sketchDetails.yAxis,
|
|
||||||
sketchDetails.origin,
|
|
||||||
data
|
|
||||||
)
|
|
||||||
if (err(result)) return reject(result)
|
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(kclManager.ast)
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'setup-client-side-sketch-segments': fromPromise(
|
|
||||||
async ({ input: { sketchDetails, selectionRanges } }) => {
|
|
||||||
if (!sketchDetails) return
|
|
||||||
if (!sketchDetails.sketchEntryNodePath.length) return
|
|
||||||
if (Object.keys(sceneEntitiesManager.activeSegments).length > 0) {
|
|
||||||
sceneEntitiesManager.tearDownSketch({ removeAxis: false })
|
|
||||||
}
|
|
||||||
sceneInfra.resetMouseListeners()
|
|
||||||
await sceneEntitiesManager.setupSketch({
|
|
||||||
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
forward: sketchDetails.zAxis,
|
|
||||||
up: sketchDetails.yAxis,
|
|
||||||
position: sketchDetails.origin,
|
|
||||||
maybeModdedAst: kclManager.ast,
|
|
||||||
selectionRanges,
|
|
||||||
})
|
|
||||||
sceneInfra.resetMouseListeners()
|
|
||||||
|
|
||||||
sceneEntitiesManager.setupSketchIdleCallbacks({
|
|
||||||
sketchEntryNodePath: sketchDetails?.sketchEntryNodePath || [],
|
|
||||||
forward: sketchDetails.zAxis,
|
|
||||||
up: sketchDetails.yAxis,
|
|
||||||
position: sketchDetails.origin,
|
|
||||||
sketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
planeNodePath: sketchDetails.planeNodePath,
|
|
||||||
})
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
),
|
|
||||||
'split-sketch-pipe-if-needed': fromPromise(
|
|
||||||
async ({ input: { sketchDetails } }) => {
|
|
||||||
if (!sketchDetails) return reject('No sketch details')
|
|
||||||
const existingSketchInfoNoOp = {
|
|
||||||
updatedEntryNodePath: sketchDetails.sketchEntryNodePath,
|
|
||||||
updatedSketchNodePaths: sketchDetails.sketchNodePaths,
|
|
||||||
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
|
||||||
} as const
|
|
||||||
if (
|
|
||||||
!sketchDetails.sketchNodePaths.length &&
|
|
||||||
sketchDetails.planeNodePath.length
|
|
||||||
) {
|
|
||||||
// new sketch, no profiles yet
|
|
||||||
return existingSketchInfoNoOp
|
|
||||||
}
|
|
||||||
const doesNeedSplitting = doesSketchPipeNeedSplitting(
|
|
||||||
kclManager.ast,
|
|
||||||
sketchDetails.sketchEntryNodePath
|
|
||||||
)
|
|
||||||
if (err(doesNeedSplitting)) return reject(doesNeedSplitting)
|
|
||||||
if (!doesNeedSplitting) return existingSketchInfoNoOp
|
|
||||||
|
|
||||||
const splitResult = splitPipedProfile(
|
|
||||||
kclManager.ast,
|
|
||||||
sketchDetails.sketchEntryNodePath
|
|
||||||
)
|
|
||||||
if (err(splitResult)) return reject(splitResult)
|
|
||||||
|
|
||||||
await kclManager.executeAstMock(splitResult.modifiedAst)
|
|
||||||
await codeManager.updateEditorWithAstAndWriteToFile(
|
|
||||||
splitResult.modifiedAst
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
updatedEntryNodePath: splitResult.pathToProfile,
|
|
||||||
updatedSketchNodePaths: [splitResult.pathToProfile],
|
|
||||||
updatedPlaneNodePath: sketchDetails.planeNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -2,12 +2,7 @@ import { SVGProps } from 'react'
|
|||||||
|
|
||||||
export const Spinner = (props: SVGProps<SVGSVGElement>) => {
|
export const Spinner = (props: SVGProps<SVGSVGElement>) => {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg viewBox="0 0 10 10" className={'w-8 h-8'} {...props}>
|
||||||
data-testid="spinner"
|
|
||||||
viewBox="0 0 10 10"
|
|
||||||
className={'w-8 h-8'}
|
|
||||||
{...props}
|
|
||||||
>
|
|
||||||
<circle
|
<circle
|
||||||
cx="5"
|
cx="5"
|
||||||
cy="5"
|
cy="5"
|
||||||
|
@ -218,20 +218,6 @@ export const Stream = () => {
|
|||||||
}
|
}
|
||||||
}, [IDLE, streamState])
|
}, [IDLE, streamState])
|
||||||
|
|
||||||
/**
|
|
||||||
* Play the vid
|
|
||||||
*/
|
|
||||||
useEffect(() => {
|
|
||||||
if (!kclManager.isExecuting) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// execute in the next event loop
|
|
||||||
videoRef.current?.play().catch((e) => {
|
|
||||||
console.warn('Video playing was prevented', e, videoRef.current)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}, [kclManager.isExecuting])
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (
|
if (
|
||||||
typeof window === 'undefined' ||
|
typeof window === 'undefined' ||
|
||||||
@ -243,9 +229,15 @@ export const Stream = () => {
|
|||||||
|
|
||||||
// The browser complains if we try to load a new stream without pausing first.
|
// The browser complains if we try to load a new stream without pausing first.
|
||||||
// Do not immediately play the stream!
|
// Do not immediately play the stream!
|
||||||
|
// we instead use a setTimeout to play the stream in the next event loop
|
||||||
try {
|
try {
|
||||||
videoRef.current.srcObject = mediaStream
|
videoRef.current.srcObject = mediaStream
|
||||||
videoRef.current.pause()
|
videoRef.current.pause()
|
||||||
|
setTimeout(() => {
|
||||||
|
videoRef.current?.play().catch((e) => {
|
||||||
|
console.warn('Video playing was prevented', e, videoRef.current)
|
||||||
|
})
|
||||||
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Attempted to pause stream while play was still loading', e)
|
console.warn('Attempted to pause stream while play was still loading', e)
|
||||||
}
|
}
|
||||||
|
@ -150,4 +150,31 @@ describe('ToastUpdate tests', () => {
|
|||||||
expect(restartButton).toBeEnabled()
|
expect(restartButton).toBeEnabled()
|
||||||
expect(dismissButton).toBeEnabled()
|
expect(dismissButton).toBeEnabled()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Happy path: external links render correctly', () => {
|
||||||
|
const releaseNotesWithBreakingChanges = `
|
||||||
|
## Some markdown release notes
|
||||||
|
- [Zoo](https://zoo.dev/)
|
||||||
|
`
|
||||||
|
const onRestart = vi.fn()
|
||||||
|
const onDismiss = vi.fn()
|
||||||
|
|
||||||
|
render(
|
||||||
|
<ToastUpdate
|
||||||
|
onRestart={onRestart}
|
||||||
|
onDismiss={onDismiss}
|
||||||
|
version={testData.version}
|
||||||
|
releaseNotes={releaseNotesWithBreakingChanges}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Locators and other constants
|
||||||
|
const zooDev = screen.getByText('Zoo', {
|
||||||
|
selector: 'a',
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(zooDev).toHaveAttribute('href', 'https://zoo.dev/')
|
||||||
|
expect(zooDev).toHaveAttribute('target', '_blank')
|
||||||
|
expect(zooDev).toHaveAttribute('onClick')
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import toast from 'react-hot-toast'
|
import toast from 'react-hot-toast'
|
||||||
import { ActionButton } from './ActionButton'
|
import { ActionButton } from './ActionButton'
|
||||||
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
import { openExternalBrowserIfDesktop } from 'lib/openWindow'
|
||||||
import { Marked } from '@ts-stack/markdown'
|
import { escape, Marked, MarkedOptions, unescape } from '@ts-stack/markdown'
|
||||||
import { getReleaseUrl } from 'routes/Settings'
|
import { getReleaseUrl } from 'routes/Settings'
|
||||||
|
import { SafeRenderer } from 'lib/markdown'
|
||||||
|
|
||||||
export function ToastUpdate({
|
export function ToastUpdate({
|
||||||
version,
|
version,
|
||||||
@ -19,6 +20,14 @@ export function ToastUpdate({
|
|||||||
?.toLocaleLowerCase()
|
?.toLocaleLowerCase()
|
||||||
.includes('breaking')
|
.includes('breaking')
|
||||||
|
|
||||||
|
const markedOptions: MarkedOptions = {
|
||||||
|
gfm: true,
|
||||||
|
breaks: true,
|
||||||
|
sanitize: true,
|
||||||
|
unescape,
|
||||||
|
escape,
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="inset-0 z-50 grid place-content-center rounded bg-chalkboard-110/50 shadow-md">
|
<div className="inset-0 z-50 grid place-content-center rounded bg-chalkboard-110/50 shadow-md">
|
||||||
<div className="max-w-3xl min-w-[35rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
<div className="max-w-3xl min-w-[35rem] p-8 rounded bg-chalkboard-10 dark:bg-chalkboard-90">
|
||||||
@ -58,9 +67,8 @@ export function ToastUpdate({
|
|||||||
className="parsed-markdown py-2 px-4 mt-2 border-t border-chalkboard-30 dark:border-chalkboard-60 max-h-60 overflow-y-auto"
|
className="parsed-markdown py-2 px-4 mt-2 border-t border-chalkboard-30 dark:border-chalkboard-60 max-h-60 overflow-y-auto"
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: Marked.parse(releaseNotes, {
|
__html: Marked.parse(releaseNotes, {
|
||||||
gfm: true,
|
renderer: new SafeRenderer(markedOptions),
|
||||||
breaks: true,
|
...markedOptions,
|
||||||
sanitize: true,
|
|
||||||
}),
|
}),
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
|
@ -136,7 +136,6 @@ export async function applyConstraintIntersect({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
}> {
|
||||||
const info = intersectInfo({
|
const info = intersectInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -175,7 +174,6 @@ export async function applyConstraintIntersect({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex: -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
@ -194,7 +192,6 @@ export async function applyConstraintIntersect({
|
|||||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||||
transform2
|
transform2
|
||||||
|
|
||||||
let exprInsertIndex = -1
|
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -207,11 +204,9 @@ export async function applyConstraintIntersect({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
exprInsertIndex = newVariableInsertIndex
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap: _pathToNodeMap,
|
pathToNodeMap: _pathToNodeMap,
|
||||||
exprInsertIndex,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ export function removeConstrainingValuesInfo({
|
|||||||
| Error {
|
| Error {
|
||||||
const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => {
|
const _nodes = selectionRanges.graphSelections.map(({ codeRef }) => {
|
||||||
const tmp = getNodeFromPath<Expr>(kclManager.ast, codeRef.pathToNode)
|
const tmp = getNodeFromPath<Expr>(kclManager.ast, codeRef.pathToNode)
|
||||||
if (tmp instanceof Error) return tmp
|
if (err(tmp)) return tmp
|
||||||
return tmp.node
|
return tmp.node
|
||||||
})
|
})
|
||||||
const _err1 = _nodes.find(err)
|
const _err1 = _nodes.find(err)
|
||||||
|
@ -93,7 +93,6 @@ export async function applyConstraintAbsDistance({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
}> {
|
||||||
const info = absDistanceInfo({
|
const info = absDistanceInfo({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
@ -133,7 +132,6 @@ export async function applyConstraintAbsDistance({
|
|||||||
if (err(transform2)) return Promise.reject(transform2)
|
if (err(transform2)) return Promise.reject(transform2)
|
||||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transform2
|
const { modifiedAst: _modifiedAst, pathToNodeMap } = transform2
|
||||||
|
|
||||||
let exprInsertIndex = -1
|
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -146,9 +144,8 @@ export async function applyConstraintAbsDistance({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
exprInsertIndex = newVariableInsertIndex
|
|
||||||
}
|
}
|
||||||
return { modifiedAst: _modifiedAst, pathToNodeMap, exprInsertIndex }
|
return { modifiedAst: _modifiedAst, pathToNodeMap }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyConstraintAxisAlign({
|
export function applyConstraintAxisAlign({
|
||||||
|
@ -86,7 +86,6 @@ export async function applyConstraintAngleBetween({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
}> {
|
||||||
const info = angleBetweenInfo({ selectionRanges })
|
const info = angleBetweenInfo({ selectionRanges })
|
||||||
if (err(info)) return Promise.reject(info)
|
if (err(info)) return Promise.reject(info)
|
||||||
@ -123,7 +122,6 @@ export async function applyConstraintAngleBetween({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex: -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +141,6 @@ export async function applyConstraintAngleBetween({
|
|||||||
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
const { modifiedAst: _modifiedAst, pathToNodeMap: _pathToNodeMap } =
|
||||||
transformed2
|
transformed2
|
||||||
|
|
||||||
let exprInsertIndex = -1
|
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -156,11 +153,9 @@ export async function applyConstraintAngleBetween({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
exprInsertIndex = newVariableInsertIndex
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap: _pathToNodeMap,
|
pathToNodeMap: _pathToNodeMap,
|
||||||
exprInsertIndex,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,13 +87,15 @@ export function horzVertDistanceInfo({
|
|||||||
export async function applyConstraintHorzVertDistance({
|
export async function applyConstraintHorzVertDistance({
|
||||||
selectionRanges,
|
selectionRanges,
|
||||||
constraint,
|
constraint,
|
||||||
|
// TODO align will always be false (covered by synconous applyConstraintHorzVertAlign), remove it
|
||||||
|
isAlign = false,
|
||||||
}: {
|
}: {
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
constraint: 'setHorzDistance' | 'setVertDistance'
|
constraint: 'setHorzDistance' | 'setVertDistance'
|
||||||
|
isAlign?: false
|
||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
}> {
|
||||||
const info = horzVertDistanceInfo({
|
const info = horzVertDistanceInfo({
|
||||||
selectionRanges: selectionRanges,
|
selectionRanges: selectionRanges,
|
||||||
@ -131,12 +133,13 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
return {
|
return {
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex: -1,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!isExprBinaryPart(valueNode))
|
if (!isExprBinaryPart(valueNode))
|
||||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
||||||
let finalValue = removeDoubleNegatives(valueNode, sign, variableName)
|
let finalValue = isAlign
|
||||||
|
? createLiteral(0)
|
||||||
|
: removeDoubleNegatives(valueNode, sign, variableName)
|
||||||
// transform again but forcing certain values
|
// transform again but forcing certain values
|
||||||
const transformed = transformSecondarySketchLinesTagFirst({
|
const transformed = transformSecondarySketchLinesTagFirst({
|
||||||
ast: kclManager.ast,
|
ast: kclManager.ast,
|
||||||
@ -149,7 +152,6 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
|
|
||||||
if (err(transformed)) return Promise.reject(transformed)
|
if (err(transformed)) return Promise.reject(transformed)
|
||||||
const { modifiedAst: _modifiedAst, pathToNodeMap } = transformed
|
const { modifiedAst: _modifiedAst, pathToNodeMap } = transformed
|
||||||
let exprInsertIndex = -1
|
|
||||||
if (variableName) {
|
if (variableName) {
|
||||||
const newBody = [..._modifiedAst.body]
|
const newBody = [..._modifiedAst.body]
|
||||||
newBody.splice(
|
newBody.splice(
|
||||||
@ -162,12 +164,10 @@ export async function applyConstraintHorzVertDistance({
|
|||||||
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
const index = pathToNode.findIndex((a) => a[0] === 'body') + 1
|
||||||
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
pathToNode[index][0] = Number(pathToNode[index][0]) + 1
|
||||||
})
|
})
|
||||||
exprInsertIndex = newVariableInsertIndex
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,14 +70,10 @@ export async function applyConstraintLength({
|
|||||||
}: {
|
}: {
|
||||||
length: KclCommandValue
|
length: KclCommandValue
|
||||||
selectionRanges: Selections
|
selectionRanges: Selections
|
||||||
}): Promise<{
|
}) {
|
||||||
modifiedAst: Program
|
|
||||||
pathToNodeMap: PathToNodeMap
|
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
|
||||||
const ast = kclManager.ast
|
const ast = kclManager.ast
|
||||||
const angleLength = angleLengthInfo({ selectionRanges })
|
const angleLength = angleLengthInfo({ selectionRanges })
|
||||||
if (err(angleLength)) return Promise.reject(angleLength)
|
if (err(angleLength)) return angleLength
|
||||||
const { transforms } = angleLength
|
const { transforms } = angleLength
|
||||||
|
|
||||||
let distanceExpression: Expr = length.valueAst
|
let distanceExpression: Expr = length.valueAst
|
||||||
@ -98,7 +94,7 @@ export async function applyConstraintLength({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!isExprBinaryPart(distanceExpression)) {
|
if (!isExprBinaryPart(distanceExpression)) {
|
||||||
return Promise.reject('Invalid valueNode, is not a BinaryPart')
|
return new Error('Invalid valueNode, is not a BinaryPart')
|
||||||
}
|
}
|
||||||
|
|
||||||
const retval = transformAstSketchLines({
|
const retval = transformAstSketchLines({
|
||||||
@ -116,12 +112,6 @@ export async function applyConstraintLength({
|
|||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex:
|
|
||||||
'variableName' in length &&
|
|
||||||
length.variableName &&
|
|
||||||
length.insertIndex !== undefined
|
|
||||||
? length.insertIndex
|
|
||||||
: -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,7 +124,6 @@ export async function applyConstraintAngleLength({
|
|||||||
}): Promise<{
|
}): Promise<{
|
||||||
modifiedAst: Program
|
modifiedAst: Program
|
||||||
pathToNodeMap: PathToNodeMap
|
pathToNodeMap: PathToNodeMap
|
||||||
exprInsertIndex: number
|
|
||||||
}> {
|
}> {
|
||||||
const angleLength = angleLengthInfo({ selectionRanges, angleOrLength })
|
const angleLength = angleLengthInfo({ selectionRanges, angleOrLength })
|
||||||
if (err(angleLength)) return Promise.reject(angleLength)
|
if (err(angleLength)) return Promise.reject(angleLength)
|
||||||
@ -219,6 +208,5 @@ export async function applyConstraintAngleLength({
|
|||||||
return {
|
return {
|
||||||
modifiedAst: _modifiedAst,
|
modifiedAst: _modifiedAst,
|
||||||
pathToNodeMap,
|
pathToNodeMap,
|
||||||
exprInsertIndex: variableName ? newVariableInsertIndex : -1,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,8 +10,11 @@ import { AppStreamProvider } from 'AppState'
|
|||||||
import { ToastUpdate } from 'components/ToastUpdate'
|
import { ToastUpdate } from 'components/ToastUpdate'
|
||||||
import { markOnce } from 'lib/performance'
|
import { markOnce } from 'lib/performance'
|
||||||
import { AUTO_UPDATER_TOAST_ID } from 'lib/constants'
|
import { AUTO_UPDATER_TOAST_ID } from 'lib/constants'
|
||||||
|
import { initializeWindowExceptionHandler } from 'lib/exceptions'
|
||||||
|
|
||||||
markOnce('code/willAuth')
|
markOnce('code/willAuth')
|
||||||
|
initializeWindowExceptionHandler()
|
||||||
|
|
||||||
// uncomment for xstate inspector
|
// uncomment for xstate inspector
|
||||||
// import { DEV } from 'env'
|
// import { DEV } from 'env'
|
||||||
// import { inspect } from '@xstate/inspect'
|
// import { inspect } from '@xstate/inspect'
|
||||||
|
@ -378,11 +378,14 @@ export class KclManager {
|
|||||||
// updateArtifactGraph relies on updated executeState/programMemory
|
// updateArtifactGraph relies on updated executeState/programMemory
|
||||||
await this.engineCommandManager.updateArtifactGraph(
|
await this.engineCommandManager.updateArtifactGraph(
|
||||||
this.ast,
|
this.ast,
|
||||||
|
execState.artifactCommands,
|
||||||
execState.artifacts
|
execState.artifacts
|
||||||
)
|
)
|
||||||
this._executeCallback()
|
this._executeCallback()
|
||||||
if (!isInterrupted)
|
if (!isInterrupted) {
|
||||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||||
|
}
|
||||||
|
|
||||||
this.engineCommandManager.addCommandLog({
|
this.engineCommandManager.addCommandLog({
|
||||||
type: 'execution-done',
|
type: 'execution-done',
|
||||||
data: null,
|
data: null,
|
||||||
@ -391,6 +394,24 @@ export class KclManager {
|
|||||||
this._cancelTokens.delete(currentExecutionId)
|
this._cancelTokens.delete(currentExecutionId)
|
||||||
markOnce('code/endExecuteAst')
|
markOnce('code/endExecuteAst')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cleanup function is external and internal to the KclSingleton class.
|
||||||
|
* Since the WASM runtime can panic and the error cannot be caught in executeAst
|
||||||
|
* we need a global exception handler in exceptions.ts
|
||||||
|
* This file will interface with this cleanup as if it caught the original error
|
||||||
|
* to properly restore the TS application state.
|
||||||
|
*/
|
||||||
|
executeAstCleanUp() {
|
||||||
|
this.isExecuting = false
|
||||||
|
this.executeIsStale = null
|
||||||
|
this.engineCommandManager.addCommandLog({
|
||||||
|
type: 'execution-done',
|
||||||
|
data: null,
|
||||||
|
})
|
||||||
|
markOnce('code/endExecuteAst')
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: this always updates the code state and editor.
|
// NOTE: this always updates the code state and editor.
|
||||||
// DO NOT CALL THIS from codemirror ever.
|
// DO NOT CALL THIS from codemirror ever.
|
||||||
async executeAstMock(
|
async executeAstMock(
|
||||||
@ -424,7 +445,6 @@ export class KclManager {
|
|||||||
|
|
||||||
this._logs = logs
|
this._logs = logs
|
||||||
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
||||||
|
|
||||||
this._execState = execState
|
this._execState = execState
|
||||||
this._programMemory = execState.memory
|
this._programMemory = execState.memory
|
||||||
if (!errors.length) {
|
if (!errors.length) {
|
||||||
@ -437,7 +457,7 @@ export class KclManager {
|
|||||||
// problem this solves, but either way we should strive to remove it.
|
// problem this solves, but either way we should strive to remove it.
|
||||||
Array.from(this.engineCommandManager.artifactGraph).forEach(
|
Array.from(this.engineCommandManager.artifactGraph).forEach(
|
||||||
([commandId, artifact]) => {
|
([commandId, artifact]) => {
|
||||||
if (!('codeRef' in artifact && artifact.codeRef)) return
|
if (!('codeRef' in artifact)) return
|
||||||
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||||
this.ast,
|
this.ast,
|
||||||
artifact.codeRef.pathToNode,
|
artifact.codeRef.pathToNode,
|
||||||
|
@ -47,7 +47,7 @@ describe('parsing errors', () => {
|
|||||||
const result = parse(code)
|
const result = parse(code)
|
||||||
if (err(result)) throw result
|
if (err(result)) throw result
|
||||||
const error = result.errors[0]
|
const error = result.errors[0]
|
||||||
expect(error.message).toBe('Unexpected token: (')
|
expect(error.message).toBe('Array is missing a closing bracket(`]`)')
|
||||||
expect(error.sourceRange).toEqual([27, 28, 0])
|
expect(error.sourceRange).toEqual([28, 29, 0])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -153,7 +153,7 @@ export default class CodeManager {
|
|||||||
toast.error('Error saving file, please check file permissions')
|
toast.error('Error saving file, please check file permissions')
|
||||||
reject(err)
|
reject(err)
|
||||||
})
|
})
|
||||||
}, 10)
|
}, 1000)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
safeLSSetItem(PERSIST_CODE_KEY, this.code)
|
safeLSSetItem(PERSIST_CODE_KEY, this.code)
|
||||||
|
@ -10,6 +10,7 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
msg: 'Semantic error',
|
msg: 'Semantic error',
|
||||||
sourceRange: [0, 1, true],
|
sourceRange: [0, 1, true],
|
||||||
operations: [],
|
operations: [],
|
||||||
|
artifactCommands: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '',
|
name: '',
|
||||||
@ -18,6 +19,7 @@ describe('test kclErrToDiagnostic', () => {
|
|||||||
msg: 'Type error',
|
msg: 'Type error',
|
||||||
sourceRange: [4, 5, true],
|
sourceRange: [4, 5, true],
|
||||||
operations: [],
|
operations: [],
|
||||||
|
artifactCommands: [],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
const diagnostics = kclErrorsToDiagnostics(errors)
|
const diagnostics = kclErrorsToDiagnostics(errors)
|
||||||
|
@ -5,7 +5,7 @@ import { posToOffset } from '@kittycad/codemirror-lsp-client'
|
|||||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
|
||||||
import { Text } from '@codemirror/state'
|
import { Text } from '@codemirror/state'
|
||||||
import { EditorView } from 'codemirror'
|
import { EditorView } from 'codemirror'
|
||||||
import { SourceRange } from 'lang/wasm'
|
import { ArtifactCommand, SourceRange } from 'lang/wasm'
|
||||||
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
||||||
|
|
||||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
type ExtractKind<T> = T extends { kind: infer K } ? K : never
|
||||||
@ -14,86 +14,141 @@ export class KCLError extends Error {
|
|||||||
sourceRange: SourceRange
|
sourceRange: SourceRange
|
||||||
msg: string
|
msg: string
|
||||||
operations: Operation[]
|
operations: Operation[]
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
kind: ExtractKind<RustKclError> | 'name',
|
kind: ExtractKind<RustKclError> | 'name',
|
||||||
msg: string,
|
msg: string,
|
||||||
sourceRange: SourceRange,
|
sourceRange: SourceRange,
|
||||||
operations: Operation[]
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
) {
|
) {
|
||||||
super()
|
super()
|
||||||
this.kind = kind
|
this.kind = kind
|
||||||
this.msg = msg
|
this.msg = msg
|
||||||
this.sourceRange = sourceRange
|
this.sourceRange = sourceRange
|
||||||
this.operations = operations
|
this.operations = operations
|
||||||
|
this.artifactCommands = artifactCommands
|
||||||
Object.setPrototypeOf(this, KCLError.prototype)
|
Object.setPrototypeOf(this, KCLError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLLexicalError extends KCLError {
|
export class KCLLexicalError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('lexical', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('lexical', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLInternalError extends KCLError {
|
export class KCLInternalError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('internal', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('internal', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSyntaxError extends KCLError {
|
export class KCLSyntaxError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('syntax', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('syntax', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
Object.setPrototypeOf(this, KCLSyntaxError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLSemanticError extends KCLError {
|
export class KCLSemanticError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('semantic', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('semantic', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
Object.setPrototypeOf(this, KCLSemanticError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLTypeError extends KCLError {
|
export class KCLTypeError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('type', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('type', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
Object.setPrototypeOf(this, KCLTypeError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnimplementedError extends KCLError {
|
export class KCLUnimplementedError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('unimplemented', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('unimplemented', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUnexpectedError extends KCLError {
|
export class KCLUnexpectedError extends KCLError {
|
||||||
constructor(msg: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('unexpected', msg, sourceRange, operations)
|
msg: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super('unexpected', msg, sourceRange, operations, artifactCommands)
|
||||||
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLValueAlreadyDefined extends KCLError {
|
export class KCLValueAlreadyDefined extends KCLError {
|
||||||
constructor(key: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
|
key: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
super(
|
super(
|
||||||
'name',
|
'name',
|
||||||
`Key ${key} was already defined elsewhere`,
|
`Key ${key} was already defined elsewhere`,
|
||||||
sourceRange,
|
sourceRange,
|
||||||
operations
|
operations,
|
||||||
|
artifactCommands
|
||||||
)
|
)
|
||||||
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class KCLUndefinedValueError extends KCLError {
|
export class KCLUndefinedValueError extends KCLError {
|
||||||
constructor(key: string, sourceRange: SourceRange, operations: Operation[]) {
|
constructor(
|
||||||
super('name', `Key ${key} has not been defined`, sourceRange, operations)
|
key: string,
|
||||||
|
sourceRange: SourceRange,
|
||||||
|
operations: Operation[],
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
|
) {
|
||||||
|
super(
|
||||||
|
'name',
|
||||||
|
`Key ${key} has not been defined`,
|
||||||
|
sourceRange,
|
||||||
|
operations,
|
||||||
|
artifactCommands
|
||||||
|
)
|
||||||
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -113,6 +168,7 @@ export function lspDiagnosticsToKclErrors(
|
|||||||
'unexpected',
|
'unexpected',
|
||||||
message,
|
message,
|
||||||
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, true],
|
[posToOffset(doc, range.start)!, posToOffset(doc, range.end)!, true],
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -481,6 +481,7 @@ const theExtrude = startSketchOn('XY')
|
|||||||
'undefined_value',
|
'undefined_value',
|
||||||
'memory item key `myVarZ` is not defined',
|
'memory item key `myVarZ` is not defined',
|
||||||
[129, 135, true],
|
[129, 135, true],
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -67,6 +67,7 @@ export async function executeAst({
|
|||||||
: executor(ast, engineCommandManager))
|
: executor(ast, engineCommandManager))
|
||||||
|
|
||||||
await engineCommandManager.waitForAllCommands()
|
await engineCommandManager.waitForAllCommands()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
errors: [],
|
errors: [],
|
||||||
|
@ -16,7 +16,6 @@ import {
|
|||||||
deleteSegmentFromPipeExpression,
|
deleteSegmentFromPipeExpression,
|
||||||
removeSingleConstraintInfo,
|
removeSingleConstraintInfo,
|
||||||
deleteFromSelection,
|
deleteFromSelection,
|
||||||
splitPipedProfile,
|
|
||||||
} from './modifyAst'
|
} from './modifyAst'
|
||||||
import { enginelessExecutor } from '../lib/testHelpers'
|
import { enginelessExecutor } from '../lib/testHelpers'
|
||||||
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
import { findUsesOfTagInPipe, getNodePathFromSourceRange } from './queryAst'
|
||||||
@ -807,9 +806,9 @@ sketch001 = startSketchOn('XZ')
|
|||||||
sketch002 = startSketchOn({
|
sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 1, y = 2, z = 3 },
|
origin = { x = 1, y = 2, z = 3 },
|
||||||
x_axis = { x = 4, y = 5, z = 6 },
|
xAxis = { x = 4, y = 5, z = 6 },
|
||||||
y_axis = { x = 7, y = 8, z = 9 },
|
yAxis = { x = 7, y = 8, z = 9 },
|
||||||
z_axis = { x = 10, y = 11, z = 12 }
|
zAxis = { x = 10, y = 11, z = 12 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> startProfileAt([-12.55, 2.89], %)
|
|> startProfileAt([-12.55, 2.89], %)
|
||||||
@ -863,9 +862,9 @@ sketch001 = startSketchOn('XZ')
|
|||||||
sketch002 = startSketchOn({
|
sketch002 = startSketchOn({
|
||||||
plane = {
|
plane = {
|
||||||
origin = { x = 1, y = 2, z = 3 },
|
origin = { x = 1, y = 2, z = 3 },
|
||||||
x_axis = { x = 4, y = 5, z = 6 },
|
xAxis = { x = 4, y = 5, z = 6 },
|
||||||
y_axis = { x = 7, y = 8, z = 9 },
|
yAxis = { x = 7, y = 8, z = 9 },
|
||||||
z_axis = { x = 10, y = 11, z = 12 }
|
zAxis = { x = 10, y = 11, z = 12 }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|> startProfileAt([-12.55, 2.89], %)
|
|> startProfileAt([-12.55, 2.89], %)
|
||||||
@ -919,63 +918,3 @@ sketch002 = startSketchOn({
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('Testing splitPipedProfile', () => {
|
|
||||||
it('should split the pipe expression correctly', () => {
|
|
||||||
const codeBefore = `part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([1, 2], %)
|
|
||||||
|> line([3, 4], %)
|
|
||||||
|> line([5, 6], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(5, part001)
|
|
||||||
`
|
|
||||||
|
|
||||||
const expectedCodeAfter = `sketch001 = startSketchOn('XZ')
|
|
||||||
part001 = startProfileAt([1, 2], sketch001)
|
|
||||||
|> line([3, 4], %)
|
|
||||||
|> line([5, 6], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(5, part001)
|
|
||||||
`
|
|
||||||
|
|
||||||
const ast = assertParse(codeBefore)
|
|
||||||
|
|
||||||
const codeOfInterest = `startSketchOn('XZ')`
|
|
||||||
const range: [number, number, boolean] = [
|
|
||||||
codeBefore.indexOf(codeOfInterest),
|
|
||||||
codeBefore.indexOf(codeOfInterest) + codeOfInterest.length,
|
|
||||||
true,
|
|
||||||
]
|
|
||||||
const pathToPipe = getNodePathFromSourceRange(ast, range)
|
|
||||||
|
|
||||||
const result = splitPipedProfile(ast, pathToPipe)
|
|
||||||
|
|
||||||
if (err(result)) throw result
|
|
||||||
|
|
||||||
const newCode = recast(result.modifiedAst)
|
|
||||||
if (err(newCode)) throw newCode
|
|
||||||
expect(newCode.trim()).toBe(expectedCodeAfter.trim())
|
|
||||||
})
|
|
||||||
it('should return error for already split pipe', () => {
|
|
||||||
const codeBefore = `sketch001 = startSketchOn('XZ')
|
|
||||||
part001 = startProfileAt([1, 2], sketch001)
|
|
||||||
|> line([3, 4], %)
|
|
||||||
|> line([5, 6], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(5, part001)
|
|
||||||
`
|
|
||||||
|
|
||||||
const ast = assertParse(codeBefore)
|
|
||||||
|
|
||||||
const codeOfInterest = `startProfileAt([1, 2], sketch001)`
|
|
||||||
const range: [number, number, boolean] = [
|
|
||||||
codeBefore.indexOf(codeOfInterest),
|
|
||||||
codeBefore.indexOf(codeOfInterest) + codeOfInterest.length,
|
|
||||||
true,
|
|
||||||
]
|
|
||||||
const pathToPipe = getNodePathFromSourceRange(ast, range)
|
|
||||||
|
|
||||||
const result = splitPipedProfile(ast, pathToPipe)
|
|
||||||
expect(result instanceof Error).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
@ -29,8 +29,6 @@ import {
|
|||||||
getNodePathFromSourceRange,
|
getNodePathFromSourceRange,
|
||||||
isNodeSafeToReplace,
|
isNodeSafeToReplace,
|
||||||
traverse,
|
traverse,
|
||||||
getBodyIndex,
|
|
||||||
isCallExprWithName,
|
|
||||||
} from './queryAst'
|
} from './queryAst'
|
||||||
import { addTagForSketchOnFace, getConstraintInfo } from './std/sketch'
|
import { addTagForSketchOnFace, getConstraintInfo } from './std/sketch'
|
||||||
import {
|
import {
|
||||||
@ -48,7 +46,6 @@ import { Models } from '@kittycad/lib'
|
|||||||
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
import { ExtrudeFacePlane } from 'machines/modelingMachine'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
import { KclExpressionWithVariable } from 'lib/commandTypes'
|
||||||
import { Artifact, getPathsFromArtifact } from './std/artifactGraph'
|
|
||||||
|
|
||||||
export function startSketchOnDefault(
|
export function startSketchOnDefault(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
@ -81,54 +78,41 @@ export function startSketchOnDefault(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function insertNewStartProfileAt(
|
export function addStartProfileAt(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
sketchEntryNodePath: PathToNode,
|
pathToNode: PathToNode,
|
||||||
sketchNodePaths: PathToNode[],
|
at: [number, number]
|
||||||
planeNodePath: PathToNode,
|
): { modifiedAst: Node<Program>; pathToNode: PathToNode } | Error {
|
||||||
at: [number, number],
|
const _node1 = getNodeFromPath<VariableDeclaration>(
|
||||||
insertType: 'start' | 'end' = 'end'
|
|
||||||
):
|
|
||||||
| {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
updatedSketchNodePaths: PathToNode[]
|
|
||||||
updatedEntryNodePath: PathToNode
|
|
||||||
}
|
|
||||||
| Error {
|
|
||||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
|
||||||
node,
|
node,
|
||||||
planeNodePath,
|
pathToNode,
|
||||||
'VariableDeclarator'
|
'VariableDeclaration'
|
||||||
)
|
)
|
||||||
if (err(varDec)) return varDec
|
if (err(_node1)) return _node1
|
||||||
if (varDec.node.type !== 'VariableDeclarator') return new Error('not a var')
|
const variableDeclaration = _node1.node
|
||||||
|
if (variableDeclaration.type !== 'VariableDeclaration') {
|
||||||
const newExpression = createVariableDeclaration(
|
return new Error('variableDeclaration.init.type !== PipeExpression')
|
||||||
findUniqueName(node, 'profile'),
|
}
|
||||||
createCallExpressionStdLib('startProfileAt', [
|
const _node = { ...node }
|
||||||
createArrayExpression([
|
const init = variableDeclaration.declaration.init
|
||||||
createLiteral(roundOff(at[0])),
|
const startProfileAt = createCallExpressionStdLib('startProfileAt', [
|
||||||
createLiteral(roundOff(at[1])),
|
createArrayExpression([
|
||||||
]),
|
createLiteral(roundOff(at[0])),
|
||||||
createIdentifier(varDec.node.id.name),
|
createLiteral(roundOff(at[1])),
|
||||||
|
]),
|
||||||
|
createPipeSubstitution(),
|
||||||
|
])
|
||||||
|
if (init.type === 'PipeExpression') {
|
||||||
|
init.body.splice(1, 0, startProfileAt)
|
||||||
|
} else {
|
||||||
|
variableDeclaration.declaration.init = createPipeExpression([
|
||||||
|
init,
|
||||||
|
startProfileAt,
|
||||||
])
|
])
|
||||||
)
|
}
|
||||||
const insertIndex = getInsertIndex(sketchNodePaths, planeNodePath, insertType)
|
|
||||||
|
|
||||||
const _node = structuredClone(node)
|
|
||||||
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
|
||||||
_node.body.splice(insertIndex, 0, newExpression)
|
|
||||||
|
|
||||||
const { updatedEntryNodePath, updatedSketchNodePaths } =
|
|
||||||
updateSketchNodePathsWithInsertIndex({
|
|
||||||
insertIndex,
|
|
||||||
insertType,
|
|
||||||
sketchNodePaths,
|
|
||||||
})
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
updatedSketchNodePaths,
|
pathToNode,
|
||||||
updatedEntryNodePath,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +253,7 @@ export function mutateObjExpProp(
|
|||||||
export function extrudeSketch(
|
export function extrudeSketch(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
artifact?: Artifact,
|
shouldPipe = false,
|
||||||
distance: Expr = createLiteral(4)
|
distance: Expr = createLiteral(4)
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
@ -278,14 +262,10 @@ export function extrudeSketch(
|
|||||||
pathToExtrudeArg: PathToNode
|
pathToExtrudeArg: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
const orderedSketchNodePaths = getPathsFromArtifact({
|
|
||||||
artifact: artifact,
|
|
||||||
sketchPathToNode: pathToNode,
|
|
||||||
})
|
|
||||||
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
|
||||||
const _node = structuredClone(node)
|
const _node = structuredClone(node)
|
||||||
const _node1 = getNodeFromPath(_node, pathToNode)
|
const _node1 = getNodeFromPath(_node, pathToNode)
|
||||||
if (err(_node1)) return _node1
|
if (err(_node1)) return _node1
|
||||||
|
const { node: sketchExpression } = _node1
|
||||||
|
|
||||||
// determine if sketchExpression is in a pipeExpression or not
|
// determine if sketchExpression is in a pipeExpression or not
|
||||||
const _node2 = getNodeFromPath<PipeExpression>(
|
const _node2 = getNodeFromPath<PipeExpression>(
|
||||||
@ -294,6 +274,9 @@ export function extrudeSketch(
|
|||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
)
|
)
|
||||||
if (err(_node2)) return _node2
|
if (err(_node2)) return _node2
|
||||||
|
const { node: pipeExpression } = _node2
|
||||||
|
|
||||||
|
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
|
||||||
|
|
||||||
const _node3 = getNodeFromPath<VariableDeclarator>(
|
const _node3 = getNodeFromPath<VariableDeclarator>(
|
||||||
_node,
|
_node,
|
||||||
@ -301,23 +284,49 @@ export function extrudeSketch(
|
|||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(_node3)) return _node3
|
if (err(_node3)) return _node3
|
||||||
const { node: variableDeclarator } = _node3
|
const { node: variableDeclarator, shallowPath: pathToDecleration } = _node3
|
||||||
|
|
||||||
const extrudeCall = createCallExpressionStdLib('extrude', [
|
const extrudeCall = createCallExpressionStdLib('extrude', [
|
||||||
distance,
|
distance,
|
||||||
createIdentifier(variableDeclarator.id.name),
|
shouldPipe
|
||||||
|
? createPipeSubstitution()
|
||||||
|
: createIdentifier(variableDeclarator.id.name),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if (shouldPipe) {
|
||||||
|
const pipeChain = createPipeExpression(
|
||||||
|
isInPipeExpression
|
||||||
|
? [...pipeExpression.body, extrudeCall]
|
||||||
|
: [sketchExpression as any, extrudeCall]
|
||||||
|
)
|
||||||
|
|
||||||
|
variableDeclarator.init = pipeChain
|
||||||
|
const pathToExtrudeArg: PathToNode = [
|
||||||
|
...pathToDecleration,
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['body', ''],
|
||||||
|
[pipeChain.body.length - 1, 'index'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: _node,
|
||||||
|
pathToNode,
|
||||||
|
pathToExtrudeArg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
// We're not creating a pipe expression,
|
||||||
// but rather a separate constant for the extrusion
|
// but rather a separate constant for the extrusion
|
||||||
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.EXTRUDE)
|
||||||
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
const VariableDeclaration = createVariableDeclaration(name, extrudeCall)
|
||||||
|
|
||||||
const lastSketchNodePath =
|
const sketchIndexInPathToNode =
|
||||||
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
pathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
||||||
|
const sketchIndexInBody = pathToDecleration[
|
||||||
console.log('lastSketchNodePath', lastSketchNodePath, orderedSketchNodePaths)
|
sketchIndexInPathToNode
|
||||||
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
][0] as number
|
||||||
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
_node.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||||
|
|
||||||
const pathToExtrudeArg: PathToNode = [
|
const pathToExtrudeArg: PathToNode = [
|
||||||
@ -365,6 +374,37 @@ export function loftSketches(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addSweep(
|
||||||
|
node: Node<Program>,
|
||||||
|
profileDeclarator: VariableDeclarator,
|
||||||
|
pathDeclarator: VariableDeclarator
|
||||||
|
): {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
} {
|
||||||
|
const modifiedAst = structuredClone(node)
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.SWEEP)
|
||||||
|
const sweep = createCallExpressionStdLib('sweep', [
|
||||||
|
createObjectExpression({ path: createIdentifier(pathDeclarator.id.name) }),
|
||||||
|
createIdentifier(profileDeclarator.id.name),
|
||||||
|
])
|
||||||
|
const declaration = createVariableDeclaration(name, sweep)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const pathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declaration', 'VariableDeclaration'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
@ -1140,11 +1180,17 @@ export async function deleteFromSelection(
|
|||||||
((selection?.artifact?.type === 'wall' ||
|
((selection?.artifact?.type === 'wall' ||
|
||||||
selection?.artifact?.type === 'cap') &&
|
selection?.artifact?.type === 'cap') &&
|
||||||
varDec.node.init.type === 'PipeExpression') ||
|
varDec.node.init.type === 'PipeExpression') ||
|
||||||
selection.artifact?.type === 'sweep'
|
selection.artifact?.type === 'sweep' ||
|
||||||
|
selection.artifact?.type === 'plane' ||
|
||||||
|
!selection.artifact // aka expected to be a shell at this point
|
||||||
) {
|
) {
|
||||||
let extrudeNameToDelete = ''
|
let extrudeNameToDelete = ''
|
||||||
let pathToNode: PathToNode | null = null
|
let pathToNode: PathToNode | null = null
|
||||||
if (selection.artifact?.type !== 'sweep') {
|
if (
|
||||||
|
selection.artifact &&
|
||||||
|
selection.artifact.type !== 'sweep' &&
|
||||||
|
selection.artifact.type !== 'plane'
|
||||||
|
) {
|
||||||
const varDecName = varDec.node.id.name
|
const varDecName = varDec.node.id.name
|
||||||
traverse(astClone, {
|
traverse(astClone, {
|
||||||
enter: (node, path) => {
|
enter: (node, path) => {
|
||||||
@ -1160,6 +1206,17 @@ export async function deleteFromSelection(
|
|||||||
pathToNode = path
|
pathToNode = path
|
||||||
extrudeNameToDelete = dec.id.name
|
extrudeNameToDelete = dec.id.name
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
dec.init.type === 'CallExpression' &&
|
||||||
|
dec.init.callee.name === 'loft' &&
|
||||||
|
dec.init.arguments?.[0].type === 'ArrayExpression' &&
|
||||||
|
dec.init.arguments?.[0].elements.some(
|
||||||
|
(a) => a.type === 'Identifier' && a.name === varDecName
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
pathToNode = path
|
||||||
|
extrudeNameToDelete = dec.id.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -1269,17 +1326,17 @@ export async function deleteFromSelection(
|
|||||||
y: roundLiteral(faceDetails.origin.y),
|
y: roundLiteral(faceDetails.origin.y),
|
||||||
z: roundLiteral(faceDetails.origin.z),
|
z: roundLiteral(faceDetails.origin.z),
|
||||||
}),
|
}),
|
||||||
x_axis: createObjectExpression({
|
xAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.x_axis.x),
|
x: roundLiteral(faceDetails.x_axis.x),
|
||||||
y: roundLiteral(faceDetails.x_axis.y),
|
y: roundLiteral(faceDetails.x_axis.y),
|
||||||
z: roundLiteral(faceDetails.x_axis.z),
|
z: roundLiteral(faceDetails.x_axis.z),
|
||||||
}),
|
}),
|
||||||
y_axis: createObjectExpression({
|
yAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.y_axis.x),
|
x: roundLiteral(faceDetails.y_axis.x),
|
||||||
y: roundLiteral(faceDetails.y_axis.y),
|
y: roundLiteral(faceDetails.y_axis.y),
|
||||||
z: roundLiteral(faceDetails.y_axis.z),
|
z: roundLiteral(faceDetails.y_axis.z),
|
||||||
}),
|
}),
|
||||||
z_axis: createObjectExpression({
|
zAxis: createObjectExpression({
|
||||||
x: roundLiteral(faceDetails.z_axis.x),
|
x: roundLiteral(faceDetails.z_axis.x),
|
||||||
y: roundLiteral(faceDetails.z_axis.y),
|
y: roundLiteral(faceDetails.z_axis.y),
|
||||||
z: roundLiteral(faceDetails.z_axis.z),
|
z: roundLiteral(faceDetails.z_axis.z),
|
||||||
@ -1298,8 +1355,7 @@ export async function deleteFromSelection(
|
|||||||
const pipeBody = varDec.node.init.body
|
const pipeBody = varDec.node.init.body
|
||||||
if (
|
if (
|
||||||
pipeBody[0].type === 'CallExpression' &&
|
pipeBody[0].type === 'CallExpression' &&
|
||||||
(pipeBody[0].callee.name === 'startSketchOn' ||
|
pipeBody[0].callee.name === 'startSketchOn'
|
||||||
pipeBody[0].callee.name === 'startProfileAt')
|
|
||||||
) {
|
) {
|
||||||
// remove varDec
|
// remove varDec
|
||||||
const varDecIndex = varDec.shallowPath[1][0] as number
|
const varDecIndex = varDec.shallowPath[1][0] as number
|
||||||
@ -1314,149 +1370,3 @@ export async function deleteFromSelection(
|
|||||||
const nonCodeMetaEmpty = () => {
|
const nonCodeMetaEmpty = () => {
|
||||||
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
return { nonCodeNodes: {}, startNodes: [], start: 0, end: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getInsertIndex(
|
|
||||||
sketchNodePaths: PathToNode[],
|
|
||||||
planeNodePath: PathToNode,
|
|
||||||
insertType: 'start' | 'end'
|
|
||||||
) {
|
|
||||||
let minIndex = 0
|
|
||||||
let maxIndex = 0
|
|
||||||
for (const path of sketchNodePaths) {
|
|
||||||
const index = Number(path[1][0])
|
|
||||||
if (index < minIndex) minIndex = index
|
|
||||||
if (index > maxIndex) maxIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
const insertIndex = !sketchNodePaths.length
|
|
||||||
? Number(planeNodePath[1][0]) + 1
|
|
||||||
: insertType === 'start'
|
|
||||||
? minIndex
|
|
||||||
: maxIndex + 1
|
|
||||||
return insertIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
export function updateSketchNodePathsWithInsertIndex({
|
|
||||||
insertIndex,
|
|
||||||
insertType,
|
|
||||||
sketchNodePaths,
|
|
||||||
}: {
|
|
||||||
insertIndex: number
|
|
||||||
insertType: 'start' | 'end'
|
|
||||||
sketchNodePaths: PathToNode[]
|
|
||||||
}): {
|
|
||||||
updatedEntryNodePath: PathToNode
|
|
||||||
updatedSketchNodePaths: PathToNode[]
|
|
||||||
} {
|
|
||||||
// TODO the rest of this function will not be robust to work for sketches defined within a function declaration
|
|
||||||
const newExpressionPathToNode: PathToNode = [
|
|
||||||
['body', ''],
|
|
||||||
[insertIndex, 'index'],
|
|
||||||
['declaration', 'VariableDeclaration'],
|
|
||||||
['init', 'VariableDeclarator'],
|
|
||||||
]
|
|
||||||
let updatedSketchNodePaths = structuredClone(sketchNodePaths)
|
|
||||||
if (insertType === 'start') {
|
|
||||||
updatedSketchNodePaths = updatedSketchNodePaths.map((path) => {
|
|
||||||
path[1][0] = Number(path[1][0]) + 1
|
|
||||||
return path
|
|
||||||
})
|
|
||||||
updatedSketchNodePaths.unshift(newExpressionPathToNode)
|
|
||||||
} else {
|
|
||||||
updatedSketchNodePaths.push(newExpressionPathToNode)
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
updatedSketchNodePaths,
|
|
||||||
updatedEntryNodePath: newExpressionPathToNode,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* Split the following pipe expression into
|
|
||||||
* ```ts
|
|
||||||
* part001 = startSketchOn('XZ')
|
|
||||||
|> startProfileAt([1, 2], %)
|
|
||||||
|> line([3, 4], %)
|
|
||||||
|> line([5, 6], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(5, part001)
|
|
||||||
```
|
|
||||||
into
|
|
||||||
```ts
|
|
||||||
sketch001 = startSketchOn('XZ')
|
|
||||||
part001 = startProfileAt([1, 2], sketch001)
|
|
||||||
|> line([3, 4], %)
|
|
||||||
|> line([5, 6], %)
|
|
||||||
|> close(%)
|
|
||||||
extrude001 = extrude(5, part001)
|
|
||||||
```
|
|
||||||
Notice that the `startSketchOn` is what gets the new variable name, this is so part001 still has the same data as before
|
|
||||||
making it safe for later code that uses part001 (the extrude in this example)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
export function splitPipedProfile(
|
|
||||||
ast: Program,
|
|
||||||
pathToPipe: PathToNode
|
|
||||||
):
|
|
||||||
| {
|
|
||||||
modifiedAst: Program
|
|
||||||
pathToProfile: PathToNode
|
|
||||||
pathToPlane: PathToNode
|
|
||||||
}
|
|
||||||
| Error {
|
|
||||||
const _ast = structuredClone(ast)
|
|
||||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
|
||||||
_ast,
|
|
||||||
pathToPipe,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(varDec)) return varDec
|
|
||||||
if (
|
|
||||||
varDec.node.type !== 'VariableDeclaration' ||
|
|
||||||
varDec.node.declaration.init.type !== 'PipeExpression'
|
|
||||||
) {
|
|
||||||
return new Error('pathToNode does not point to pipe')
|
|
||||||
}
|
|
||||||
const init = varDec.node.declaration.init
|
|
||||||
const firstCall = init.body[0]
|
|
||||||
if (!isCallExprWithName(firstCall, 'startSketchOn'))
|
|
||||||
return new Error('First call is not startSketchOn')
|
|
||||||
const secondCall = init.body[1]
|
|
||||||
if (!isCallExprWithName(secondCall, 'startProfileAt'))
|
|
||||||
return new Error('Second call is not startProfileAt')
|
|
||||||
|
|
||||||
const varName = varDec.node.declaration.id.name
|
|
||||||
const newVarName = findUniqueName(_ast, 'sketch')
|
|
||||||
const secondCallArgs = structuredClone(secondCall.arguments)
|
|
||||||
secondCallArgs[1] = createIdentifier(newVarName)
|
|
||||||
const firstCallOfNewPipe = createCallExpression(
|
|
||||||
'startProfileAt',
|
|
||||||
secondCallArgs
|
|
||||||
)
|
|
||||||
const newSketch = createVariableDeclaration(
|
|
||||||
newVarName,
|
|
||||||
varDec.node.declaration.init.body[0]
|
|
||||||
)
|
|
||||||
const newProfile = createVariableDeclaration(
|
|
||||||
varName,
|
|
||||||
varDec.node.declaration.init.body.length <= 2
|
|
||||||
? firstCallOfNewPipe
|
|
||||||
: createPipeExpression([
|
|
||||||
firstCallOfNewPipe,
|
|
||||||
...varDec.node.declaration.init.body.slice(2),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
const index = getBodyIndex(pathToPipe)
|
|
||||||
if (err(index)) return index
|
|
||||||
_ast.body.splice(index, 1, newSketch, newProfile)
|
|
||||||
const pathToPlane = structuredClone(pathToPipe)
|
|
||||||
const pathToProfile = structuredClone(pathToPipe)
|
|
||||||
pathToProfile[1][0] = index + 1
|
|
||||||
|
|
||||||
return {
|
|
||||||
modifiedAst: _ast,
|
|
||||||
pathToProfile,
|
|
||||||
pathToPlane,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -275,7 +275,7 @@ const runModifyAstCloneWithEdgeTreatmentAndTag = async (
|
|||||||
const selection: Selections = {
|
const selection: Selections = {
|
||||||
graphSelections: segmentRanges.map((segmentRange) => {
|
graphSelections: segmentRanges.map((segmentRange) => {
|
||||||
const maybeArtifact = [...artifactGraph].find(([, a]) => {
|
const maybeArtifact = [...artifactGraph].find(([, a]) => {
|
||||||
if (!('codeRef' in a && a.codeRef)) return false
|
if (!('codeRef' in a)) return false
|
||||||
return isOverlap(a.codeRef.range, segmentRange)
|
return isOverlap(a.codeRef.range, segmentRange)
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
|
@ -61,19 +61,18 @@ export interface FilletParameters {
|
|||||||
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
export type EdgeTreatmentParameters = ChamferParameters | FilletParameters
|
||||||
|
|
||||||
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
// Apply Edge Treatment (Fillet or Chamfer) To Selection
|
||||||
export function applyEdgeTreatmentToSelection(
|
export async function applyEdgeTreatmentToSelection(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
selection: Selections,
|
selection: Selections,
|
||||||
parameters: EdgeTreatmentParameters
|
parameters: EdgeTreatmentParameters
|
||||||
): void | Error {
|
): Promise<void | Error> {
|
||||||
// 1. clone and modify with edge treatment and tag
|
// 1. clone and modify with edge treatment and tag
|
||||||
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
const result = modifyAstWithEdgeTreatmentAndTag(ast, selection, parameters)
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
const { modifiedAst, pathToEdgeTreatmentNode } = result
|
||||||
|
|
||||||
// 2. update ast
|
// 2. update ast
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
await updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
||||||
updateAstAndFocus(modifiedAst, pathToEdgeTreatmentNode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function modifyAstWithEdgeTreatmentAndTag(
|
export function modifyAstWithEdgeTreatmentAndTag(
|
||||||
@ -291,7 +290,7 @@ export function getPathToExtrudeForSegmentSelection(
|
|||||||
async function updateAstAndFocus(
|
async function updateAstAndFocus(
|
||||||
modifiedAst: Node<Program>,
|
modifiedAst: Node<Program>,
|
||||||
pathToEdgeTreatmentNode: Array<PathToNode>
|
pathToEdgeTreatmentNode: Array<PathToNode>
|
||||||
) {
|
): Promise<void> {
|
||||||
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
const updatedAst = await kclManager.updateAst(modifiedAst, true, {
|
||||||
focusPath: pathToEdgeTreatmentNode,
|
focusPath: pathToEdgeTreatmentNode,
|
||||||
})
|
})
|
||||||
|
@ -5,6 +5,7 @@ import {
|
|||||||
PathToNode,
|
PathToNode,
|
||||||
Expr,
|
Expr,
|
||||||
CallExpression,
|
CallExpression,
|
||||||
|
PipeExpression,
|
||||||
VariableDeclarator,
|
VariableDeclarator,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { Selections } from 'lib/selections'
|
import { Selections } from 'lib/selections'
|
||||||
@ -14,6 +15,7 @@ import {
|
|||||||
createCallExpressionStdLib,
|
createCallExpressionStdLib,
|
||||||
createObjectExpression,
|
createObjectExpression,
|
||||||
createIdentifier,
|
createIdentifier,
|
||||||
|
createPipeExpression,
|
||||||
findUniqueName,
|
findUniqueName,
|
||||||
createVariableDeclaration,
|
createVariableDeclaration,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
@ -22,13 +24,14 @@ import {
|
|||||||
mutateAstWithTagForSketchSegment,
|
mutateAstWithTagForSketchSegment,
|
||||||
getEdgeTagCall,
|
getEdgeTagCall,
|
||||||
} from 'lang/modifyAst/addEdgeTreatment'
|
} from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import { Artifact, getPathsFromArtifact } from 'lang/std/artifactGraph'
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
pathToSketchNode: PathToNode,
|
pathToSketchNode: PathToNode,
|
||||||
|
shouldPipe = false,
|
||||||
angle: Expr = createLiteral(4),
|
angle: Expr = createLiteral(4),
|
||||||
axis: Selections,
|
axisOrEdge: string,
|
||||||
artifact?: Artifact
|
axis: string,
|
||||||
|
edge: Selections
|
||||||
):
|
):
|
||||||
| {
|
| {
|
||||||
modifiedAst: Node<Program>
|
modifiedAst: Node<Program>
|
||||||
@ -36,40 +39,51 @@ export function revolveSketch(
|
|||||||
pathToRevolveArg: PathToNode
|
pathToRevolveArg: PathToNode
|
||||||
}
|
}
|
||||||
| Error {
|
| Error {
|
||||||
const orderedSketchNodePaths = getPathsFromArtifact({
|
|
||||||
artifact: artifact,
|
|
||||||
sketchPathToNode: pathToSketchNode,
|
|
||||||
})
|
|
||||||
if (err(orderedSketchNodePaths)) return orderedSketchNodePaths
|
|
||||||
const clonedAst = structuredClone(ast)
|
const clonedAst = structuredClone(ast)
|
||||||
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
|
const sketchNode = getNodeFromPath(clonedAst, pathToSketchNode)
|
||||||
if (err(sketchNode)) return sketchNode
|
if (err(sketchNode)) return sketchNode
|
||||||
|
|
||||||
// testing code
|
let generatedAxis
|
||||||
const pathToAxisSelection = getNodePathFromSourceRange(
|
|
||||||
clonedAst,
|
|
||||||
axis.graphSelections[0]?.codeRef.range
|
|
||||||
)
|
|
||||||
|
|
||||||
const lineNode = getNodeFromPath<CallExpression>(
|
if (axisOrEdge === 'Edge') {
|
||||||
clonedAst,
|
const pathToAxisSelection = getNodePathFromSourceRange(
|
||||||
pathToAxisSelection,
|
clonedAst,
|
||||||
'CallExpression'
|
edge.graphSelections[0]?.codeRef.range
|
||||||
)
|
)
|
||||||
if (err(lineNode)) return lineNode
|
const lineNode = getNodeFromPath<CallExpression>(
|
||||||
|
clonedAst,
|
||||||
|
pathToAxisSelection,
|
||||||
|
'CallExpression'
|
||||||
|
)
|
||||||
|
if (err(lineNode)) return lineNode
|
||||||
|
|
||||||
// TODO Kevin: What if |> close(%)?
|
const tagResult = mutateAstWithTagForSketchSegment(
|
||||||
// TODO Kevin: What if opposite edge
|
clonedAst,
|
||||||
// TODO Kevin: What if the edge isn't planar to the sketch?
|
pathToAxisSelection
|
||||||
// TODO Kevin: add a tag.
|
)
|
||||||
const tagResult = mutateAstWithTagForSketchSegment(
|
|
||||||
clonedAst,
|
|
||||||
pathToAxisSelection
|
|
||||||
)
|
|
||||||
|
|
||||||
// Have the tag whether it is already created or a new one is generated
|
// Have the tag whether it is already created or a new one is generated
|
||||||
if (err(tagResult)) return tagResult
|
if (err(tagResult)) return tagResult
|
||||||
const { tag } = tagResult
|
const { tag } = tagResult
|
||||||
|
const axisSelection = edge?.graphSelections[0]?.artifact
|
||||||
|
if (!axisSelection) return new Error('Generated axis selection is missing.')
|
||||||
|
generatedAxis = getEdgeTagCall(tag, axisSelection)
|
||||||
|
} else {
|
||||||
|
generatedAxis = createLiteral(axis)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Original Code */
|
||||||
|
const { node: sketchExpression } = sketchNode
|
||||||
|
|
||||||
|
// determine if sketchExpression is in a pipeExpression or not
|
||||||
|
const sketchPipeExpressionNode = getNodeFromPath<PipeExpression>(
|
||||||
|
clonedAst,
|
||||||
|
pathToSketchNode,
|
||||||
|
'PipeExpression'
|
||||||
|
)
|
||||||
|
if (err(sketchPipeExpressionNode)) return sketchPipeExpressionNode
|
||||||
|
const { node: sketchPipeExpression } = sketchPipeExpressionNode
|
||||||
|
const isInPipeExpression = sketchPipeExpression.type === 'PipeExpression'
|
||||||
|
|
||||||
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
|
const sketchVariableDeclaratorNode = getNodeFromPath<VariableDeclarator>(
|
||||||
clonedAst,
|
clonedAst,
|
||||||
@ -77,27 +91,52 @@ export function revolveSketch(
|
|||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
|
if (err(sketchVariableDeclaratorNode)) return sketchVariableDeclaratorNode
|
||||||
const { node: sketchVariableDeclarator } = sketchVariableDeclaratorNode
|
const {
|
||||||
|
node: sketchVariableDeclarator,
|
||||||
|
shallowPath: sketchPathToDecleration,
|
||||||
|
} = sketchVariableDeclaratorNode
|
||||||
|
|
||||||
const axisSelection = axis?.graphSelections[0]?.artifact
|
if (!generatedAxis) return new Error('Generated axis selection is missing.')
|
||||||
|
|
||||||
if (!axisSelection) return new Error('Axis selection is missing.')
|
|
||||||
|
|
||||||
const revolveCall = createCallExpressionStdLib('revolve', [
|
const revolveCall = createCallExpressionStdLib('revolve', [
|
||||||
createObjectExpression({
|
createObjectExpression({
|
||||||
angle: angle,
|
angle: angle,
|
||||||
axis: getEdgeTagCall(tag, axisSelection),
|
axis: generatedAxis,
|
||||||
}),
|
}),
|
||||||
createIdentifier(sketchVariableDeclarator.id.name),
|
createIdentifier(sketchVariableDeclarator.id.name),
|
||||||
])
|
])
|
||||||
|
|
||||||
|
if (shouldPipe) {
|
||||||
|
const pipeChain = createPipeExpression(
|
||||||
|
isInPipeExpression
|
||||||
|
? [...sketchPipeExpression.body, revolveCall]
|
||||||
|
: [sketchExpression as any, revolveCall]
|
||||||
|
)
|
||||||
|
|
||||||
|
sketchVariableDeclarator.init = pipeChain
|
||||||
|
const pathToRevolveArg: PathToNode = [
|
||||||
|
...sketchPathToDecleration,
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['body', ''],
|
||||||
|
[pipeChain.body.length - 1, 'index'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst: clonedAst,
|
||||||
|
pathToSketchNode,
|
||||||
|
pathToRevolveArg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We're not creating a pipe expression,
|
// We're not creating a pipe expression,
|
||||||
// but rather a separate constant for the extrusion
|
// but rather a separate constant for the extrusion
|
||||||
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
const name = findUniqueName(clonedAst, KCL_DEFAULT_CONSTANT_PREFIXES.REVOLVE)
|
||||||
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
const VariableDeclaration = createVariableDeclaration(name, revolveCall)
|
||||||
const lastSketchNodePath =
|
const sketchIndexInPathToNode =
|
||||||
orderedSketchNodePaths[orderedSketchNodePaths.length - 1]
|
sketchPathToDecleration.findIndex((a) => a[0] === 'body') + 1
|
||||||
const sketchIndexInBody = Number(lastSketchNodePath[1][0])
|
const sketchIndexInBody = sketchPathToDecleration[sketchIndexInPathToNode][0]
|
||||||
if (typeof sketchIndexInBody !== 'number')
|
if (typeof sketchIndexInBody !== 'number')
|
||||||
return new Error('expected sketchIndexInBody to be a number')
|
return new Error('expected sketchIndexInBody to be a number')
|
||||||
clonedAst.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
clonedAst.body.splice(sketchIndexInBody + 1, 0, VariableDeclaration)
|
||||||
|
@ -49,17 +49,27 @@ export function addShell({
|
|||||||
return new Error("Couldn't find extrude")
|
return new Error("Couldn't find extrude")
|
||||||
}
|
}
|
||||||
|
|
||||||
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
|
|
||||||
// Get the sketch ref from the selection
|
|
||||||
// TODO: this assumes the segment is piped directly from the sketch, with no intermediate `VariableDeclarator` between.
|
// TODO: this assumes the segment is piped directly from the sketch, with no intermediate `VariableDeclarator` between.
|
||||||
// We must find a technique for these situations that is robust to intermediate declarations
|
// We must find a technique for these situations that is robust to intermediate declarations
|
||||||
const sketchNode = getNodeFromPath<VariableDeclarator>(
|
const extrudeNode = getNodeFromPath<VariableDeclarator>(
|
||||||
modifiedAst,
|
modifiedAst,
|
||||||
graphSelection.codeRef.pathToNode,
|
extrudeLookupResult.pathToExtrudeNode,
|
||||||
'VariableDeclarator'
|
'VariableDeclarator'
|
||||||
)
|
)
|
||||||
if (err(sketchNode)) {
|
const segmentNode = getNodeFromPath<VariableDeclarator>(
|
||||||
return sketchNode
|
modifiedAst,
|
||||||
|
extrudeLookupResult.pathToSegmentNode,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
if (err(extrudeNode) || err(segmentNode)) {
|
||||||
|
return new Error("Couldn't find extrude")
|
||||||
|
}
|
||||||
|
if (extrudeNode.node.init.type === 'CallExpression') {
|
||||||
|
pathToExtrudeNode = extrudeLookupResult.pathToExtrudeNode
|
||||||
|
} else if (segmentNode.node.init.type === 'PipeExpression') {
|
||||||
|
pathToExtrudeNode = extrudeLookupResult.pathToSegmentNode
|
||||||
|
} else {
|
||||||
|
return new Error("Couldn't find extrude")
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedArtifact = graphSelection.artifact
|
const selectedArtifact = graphSelection.artifact
|
||||||
|
@ -33,7 +33,6 @@ import { err, Reason } from 'lib/trap'
|
|||||||
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
import { ArtifactGraph, codeRefFromRange } from './std/artifactGraph'
|
import { ArtifactGraph, codeRefFromRange } from './std/artifactGraph'
|
||||||
import { FunctionExpression } from 'wasm-lib/kcl/bindings/FunctionExpression'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||||
@ -598,13 +597,7 @@ export function findAllPreviousVariables(
|
|||||||
type ReplacerFn = (
|
type ReplacerFn = (
|
||||||
_ast: Node<Program>,
|
_ast: Node<Program>,
|
||||||
varName: string
|
varName: string
|
||||||
) =>
|
) => { modifiedAst: Node<Program>; pathToReplaced: PathToNode } | Error
|
||||||
| {
|
|
||||||
modifiedAst: Node<Program>
|
|
||||||
pathToReplaced: PathToNode
|
|
||||||
exprInsertIndex: number
|
|
||||||
}
|
|
||||||
| Error
|
|
||||||
|
|
||||||
export function isNodeSafeToReplacePath(
|
export function isNodeSafeToReplacePath(
|
||||||
ast: Program,
|
ast: Program,
|
||||||
@ -656,7 +649,7 @@ export function isNodeSafeToReplacePath(
|
|||||||
if (err(_nodeToReplace)) return _nodeToReplace
|
if (err(_nodeToReplace)) return _nodeToReplace
|
||||||
const nodeToReplace = _nodeToReplace.node as any
|
const nodeToReplace = _nodeToReplace.node as any
|
||||||
nodeToReplace[last[0]] = identifier
|
nodeToReplace[last[0]] = identifier
|
||||||
return { modifiedAst: _ast, pathToReplaced, exprInsertIndex: index }
|
return { modifiedAst: _ast, pathToReplaced }
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPipeSub = isTypeInValue(finVal as Expr, 'PipeSubstitution')
|
const hasPipeSub = isTypeInValue(finVal as Expr, 'PipeSubstitution')
|
||||||
@ -775,15 +768,8 @@ export function isLinesParallelAndConstrained(
|
|||||||
if (err(_primarySegment)) return _primarySegment
|
if (err(_primarySegment)) return _primarySegment
|
||||||
const primarySegment = _primarySegment.segment
|
const primarySegment = _primarySegment.segment
|
||||||
|
|
||||||
const _varDec2 = getNodeFromPath(ast, secondaryPath, 'VariableDeclaration')
|
|
||||||
if (err(_varDec2)) return _varDec2
|
|
||||||
const varDec2 = _varDec2.node
|
|
||||||
const varName2 = (varDec2 as VariableDeclaration)?.declaration.id?.name
|
|
||||||
const sg2 = sketchFromKclValue(programMemory?.get(varName2), varName2)
|
|
||||||
if (err(sg2)) return sg2
|
|
||||||
|
|
||||||
const _segment = getSketchSegmentFromSourceRange(
|
const _segment = getSketchSegmentFromSourceRange(
|
||||||
sg2,
|
sg,
|
||||||
secondaryLine?.codeRef?.range
|
secondaryLine?.codeRef?.range
|
||||||
)
|
)
|
||||||
if (err(_segment)) return _segment
|
if (err(_segment)) return _segment
|
||||||
@ -1097,57 +1083,3 @@ export function getObjExprProperty(
|
|||||||
if (index === -1) return null
|
if (index === -1) return null
|
||||||
return { expr: node.properties[index].value, index }
|
return { expr: node.properties[index].value, index }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCursorInFunctionDefinition(
|
|
||||||
ast: Node<Program>,
|
|
||||||
selectionRanges: Selection
|
|
||||||
): boolean {
|
|
||||||
if (!selectionRanges?.codeRef?.pathToNode) return false
|
|
||||||
const node = getNodeFromPath<FunctionExpression>(
|
|
||||||
ast,
|
|
||||||
selectionRanges.codeRef.pathToNode,
|
|
||||||
'FunctionExpression'
|
|
||||||
)
|
|
||||||
if (err(node)) return false
|
|
||||||
if (node.node.type === 'FunctionExpression') return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBodyIndex(pathToNode: PathToNode): number | Error {
|
|
||||||
const index = Number(pathToNode[1][0])
|
|
||||||
if (Number.isInteger(index)) return index
|
|
||||||
return new Error('Expected number index')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isCallExprWithName(
|
|
||||||
expr: Expr | CallExpression,
|
|
||||||
name: string
|
|
||||||
): expr is CallExpression {
|
|
||||||
if (expr.type === 'CallExpression' && expr.callee.type === 'Identifier') {
|
|
||||||
return expr.callee.name === name
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
export function doesSketchPipeNeedSplitting(
|
|
||||||
ast: Node<Program>,
|
|
||||||
pathToPipe: PathToNode
|
|
||||||
): boolean | Error {
|
|
||||||
const varDec = getNodeFromPath<VariableDeclarator>(
|
|
||||||
ast,
|
|
||||||
pathToPipe,
|
|
||||||
'VariableDeclarator'
|
|
||||||
)
|
|
||||||
if (err(varDec)) return varDec
|
|
||||||
if (varDec.node.type !== 'VariableDeclarator') return new Error('Not a var')
|
|
||||||
const pipeExpression = varDec.node.init
|
|
||||||
if (pipeExpression.type !== 'PipeExpression') return false
|
|
||||||
const [firstPipe, secondPipe] = pipeExpression.body
|
|
||||||
if (!firstPipe || !secondPipe) return false
|
|
||||||
if (
|
|
||||||
isCallExprWithName(firstPipe, 'startSketchOn') &&
|
|
||||||
isCallExprWithName(secondPipe, 'startProfileAt')
|
|
||||||
)
|
|
||||||
return true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -212,19 +212,6 @@ Map {
|
|||||||
"type": "wall",
|
"type": "wall",
|
||||||
},
|
},
|
||||||
"UUID-10" => {
|
"UUID-10" => {
|
||||||
"codeRef": {
|
|
||||||
"pathToNode": [
|
|
||||||
[
|
|
||||||
"body",
|
|
||||||
"",
|
|
||||||
],
|
|
||||||
],
|
|
||||||
"range": [
|
|
||||||
312,
|
|
||||||
344,
|
|
||||||
true,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
"edgeCutEdgeIds": [],
|
"edgeCutEdgeIds": [],
|
||||||
"id": "UUID",
|
"id": "UUID",
|
||||||
"pathIds": [
|
"pathIds": [
|
||||||
|
@ -3,11 +3,11 @@ import {
|
|||||||
assertParse,
|
assertParse,
|
||||||
initPromise,
|
initPromise,
|
||||||
Program,
|
Program,
|
||||||
|
ArtifactCommand,
|
||||||
ExecState,
|
ExecState,
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import {
|
import {
|
||||||
OrderedCommand,
|
|
||||||
ResponseMap,
|
ResponseMap,
|
||||||
createArtifactGraph,
|
createArtifactGraph,
|
||||||
filterArtifacts,
|
filterArtifacts,
|
||||||
@ -115,7 +115,7 @@ sketch002 = startSketchOn(offsetPlane001)
|
|||||||
|> line([6.78, 15.01], %)
|
|> line([6.78, 15.01], %)
|
||||||
`
|
`
|
||||||
|
|
||||||
// add more code snippets here and use `getCommands` to get the orderedCommands and responseMap for more tests
|
// add more code snippets here and use `getCommands` to get the artifactCommands and responseMap for more tests
|
||||||
const codeToWriteCacheFor = {
|
const codeToWriteCacheFor = {
|
||||||
exampleCode1,
|
exampleCode1,
|
||||||
sketchOnFaceOnFaceEtc,
|
sketchOnFaceOnFaceEtc,
|
||||||
@ -127,7 +127,7 @@ type CodeKey = keyof typeof codeToWriteCacheFor
|
|||||||
|
|
||||||
type CacheShape = {
|
type CacheShape = {
|
||||||
[key in CodeKey]: {
|
[key in CodeKey]: {
|
||||||
orderedCommands: OrderedCommand[]
|
artifactCommands: ArtifactCommand[]
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
execStateArtifacts: ExecState['artifacts']
|
execStateArtifacts: ExecState['artifacts']
|
||||||
}
|
}
|
||||||
@ -159,7 +159,7 @@ beforeAll(async () => {
|
|||||||
await kclManager.executeAst({ ast })
|
await kclManager.executeAst({ ast })
|
||||||
|
|
||||||
cacheToWriteToFileTemp[codeKey] = {
|
cacheToWriteToFileTemp[codeKey] = {
|
||||||
orderedCommands: engineCommandManager.orderedCommands,
|
artifactCommands: kclManager.execState.artifactCommands,
|
||||||
responseMap: engineCommandManager.responseMap,
|
responseMap: engineCommandManager.responseMap,
|
||||||
execStateArtifacts: kclManager.execState.artifacts,
|
execStateArtifacts: kclManager.execState.artifacts,
|
||||||
}
|
}
|
||||||
@ -186,14 +186,14 @@ describe('testing createArtifactGraph', () => {
|
|||||||
it('setup', () => {
|
it('setup', () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCodeOffsetPlanes')
|
} = getCommands('exampleCodeOffsetPlanes')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({
|
theMap = createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -237,14 +237,14 @@ describe('testing createArtifactGraph', () => {
|
|||||||
it('setup', () => {
|
it('setup', () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCode1')
|
} = getCommands('exampleCode1')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({
|
theMap = createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -338,14 +338,14 @@ describe('testing createArtifactGraph', () => {
|
|||||||
it(`setup`, () => {
|
it(`setup`, () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
} = getCommands('exampleCodeNo3D')
|
} = getCommands('exampleCodeNo3D')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({
|
theMap = createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -409,14 +409,14 @@ describe('capture graph of sketchOnFaceOnFace...', () => {
|
|||||||
it('setup', async () => {
|
it('setup', async () => {
|
||||||
// putting this logic in here because describe blocks runs before beforeAll has finished
|
// putting this logic in here because describe blocks runs before beforeAll has finished
|
||||||
const {
|
const {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast: _ast,
|
ast: _ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
} = getCommands('sketchOnFaceOnFaceEtc')
|
} = getCommands('sketchOnFaceOnFaceEtc')
|
||||||
ast = _ast
|
ast = _ast
|
||||||
theMap = createArtifactGraph({
|
theMap = createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -439,11 +439,11 @@ function getCommands(
|
|||||||
const file = fs.readFileSync(fullPath, 'utf-8')
|
const file = fs.readFileSync(fullPath, 'utf-8')
|
||||||
const parsed: CacheShape = JSON.parse(file)
|
const parsed: CacheShape = JSON.parse(file)
|
||||||
// these either already exist from the last run, or were created in
|
// these either already exist from the last run, or were created in
|
||||||
const orderedCommands = parsed[codeKey].orderedCommands
|
const artifactCommands = parsed[codeKey].artifactCommands
|
||||||
const responseMap = parsed[codeKey].responseMap
|
const responseMap = parsed[codeKey].responseMap
|
||||||
const execStateArtifacts = parsed[codeKey].execStateArtifacts
|
const execStateArtifacts = parsed[codeKey].execStateArtifacts
|
||||||
return {
|
return {
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -672,10 +672,10 @@ async function GraphTheGraph(
|
|||||||
|
|
||||||
describe('testing getArtifactsToUpdate', () => {
|
describe('testing getArtifactsToUpdate', () => {
|
||||||
it('should return an array of artifacts to update', () => {
|
it('should return an array of artifacts to update', () => {
|
||||||
const { orderedCommands, responseMap, ast, execStateArtifacts } =
|
const { artifactCommands, responseMap, ast, execStateArtifacts } =
|
||||||
getCommands('exampleCode1')
|
getCommands('exampleCode1')
|
||||||
const map = createArtifactGraph({
|
const map = createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -683,11 +683,14 @@ describe('testing getArtifactsToUpdate', () => {
|
|||||||
const getArtifact = (id: string) => map.get(id)
|
const getArtifact = (id: string) => map.get(id)
|
||||||
const currentPlaneId = 'UUID-1'
|
const currentPlaneId = 'UUID-1'
|
||||||
const getUpdateObjects = (type: Models['ModelingCmd_type']['type']) => {
|
const getUpdateObjects = (type: Models['ModelingCmd_type']['type']) => {
|
||||||
|
const artifactCommand = artifactCommands.find(
|
||||||
|
(a) => a.command.type === type
|
||||||
|
)
|
||||||
|
if (!artifactCommand) {
|
||||||
|
throw new Error(`No artifactCommand found for ${type}`)
|
||||||
|
}
|
||||||
const artifactsToUpdate = getArtifactsToUpdate({
|
const artifactsToUpdate = getArtifactsToUpdate({
|
||||||
orderedCommand: orderedCommands.find(
|
artifactCommand,
|
||||||
(a) =>
|
|
||||||
a.command.type === 'modeling_cmd_req' && a.command.cmd.type === type
|
|
||||||
)!,
|
|
||||||
responseMap,
|
responseMap,
|
||||||
getArtifact,
|
getArtifact,
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
@ -820,10 +823,6 @@ describe('testing getArtifactsToUpdate', () => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'wall',
|
type: 'wall',
|
||||||
codeRef: {
|
|
||||||
pathToNode: [['body', '']],
|
|
||||||
range: [312, 344, true],
|
|
||||||
},
|
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
segId: expect.any(String),
|
segId: expect.any(String),
|
||||||
edgeCutEdgeIds: [],
|
edgeCutEdgeIds: [],
|
||||||
|
@ -1,8 +1,14 @@
|
|||||||
import { ExecState, Expr, PathToNode, Program, SourceRange } from 'lang/wasm'
|
import {
|
||||||
|
ArtifactCommand,
|
||||||
|
ExecState,
|
||||||
|
PathToNode,
|
||||||
|
Program,
|
||||||
|
SourceRange,
|
||||||
|
sourceRangeFromRust,
|
||||||
|
} from 'lang/wasm'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
import { getNodePathFromSourceRange } from 'lang/queryAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
import { engineCommandManager, kclManager } from 'lib/singletons'
|
|
||||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||||
|
|
||||||
export type ArtifactId = string
|
export type ArtifactId = string
|
||||||
@ -36,14 +42,14 @@ export interface PathArtifact extends BaseArtifact {
|
|||||||
codeRef: CodeRef
|
codeRef: CodeRef
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Solid2DArtifact extends BaseArtifact {
|
interface solid2D extends BaseArtifact {
|
||||||
type: 'solid2D'
|
type: 'solid2D'
|
||||||
pathId: ArtifactId
|
pathId: ArtifactId
|
||||||
}
|
}
|
||||||
export interface PathArtifactRich extends BaseArtifact {
|
export interface PathArtifactRich extends BaseArtifact {
|
||||||
type: 'path'
|
type: 'path'
|
||||||
/** A path must always lie on a plane */
|
/** A path must always lie on a plane */
|
||||||
plane: PlaneArtifact | WallArtifact | CapArtifact
|
plane: PlaneArtifact | WallArtifact
|
||||||
/** A path must always contain 0 or more segments */
|
/** A path must always contain 0 or more segments */
|
||||||
segments: Array<SegmentArtifact>
|
segments: Array<SegmentArtifact>
|
||||||
/** A path may not result in a sweep artifact */
|
/** A path may not result in a sweep artifact */
|
||||||
@ -63,7 +69,7 @@ interface SegmentArtifactRich extends BaseArtifact {
|
|||||||
type: 'segment'
|
type: 'segment'
|
||||||
path: PathArtifact
|
path: PathArtifact
|
||||||
surf: WallArtifact
|
surf: WallArtifact
|
||||||
edges: Array<SweepEdgeArtifact>
|
edges: Array<SweepEdge>
|
||||||
edgeCut?: EdgeCut
|
edgeCut?: EdgeCut
|
||||||
codeRef: CodeRef
|
codeRef: CodeRef
|
||||||
}
|
}
|
||||||
@ -71,7 +77,7 @@ interface SegmentArtifactRich extends BaseArtifact {
|
|||||||
/** A Sweep is a more generic term for extrude, revolve, loft and sweep*/
|
/** A Sweep is a more generic term for extrude, revolve, loft and sweep*/
|
||||||
interface SweepArtifact extends BaseArtifact {
|
interface SweepArtifact extends BaseArtifact {
|
||||||
type: 'sweep'
|
type: 'sweep'
|
||||||
subType: 'extrusion' | 'revolve'
|
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep'
|
||||||
pathId: string
|
pathId: string
|
||||||
surfaceIds: Array<string>
|
surfaceIds: Array<string>
|
||||||
edgeIds: Array<string>
|
edgeIds: Array<string>
|
||||||
@ -79,10 +85,10 @@ interface SweepArtifact extends BaseArtifact {
|
|||||||
}
|
}
|
||||||
interface SweepArtifactRich extends BaseArtifact {
|
interface SweepArtifactRich extends BaseArtifact {
|
||||||
type: 'sweep'
|
type: 'sweep'
|
||||||
subType: 'extrusion' | 'revolve'
|
subType: 'extrusion' | 'revolve' | 'loft' | 'sweep'
|
||||||
path: PathArtifact
|
path: PathArtifact
|
||||||
surfaces: Array<WallArtifact | CapArtifact>
|
surfaces: Array<WallArtifact | CapArtifact>
|
||||||
edges: Array<SweepEdgeArtifact>
|
edges: Array<SweepEdge>
|
||||||
codeRef: CodeRef
|
codeRef: CodeRef
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,9 +98,6 @@ interface WallArtifact extends BaseArtifact {
|
|||||||
edgeCutEdgeIds: Array<ArtifactId>
|
edgeCutEdgeIds: Array<ArtifactId>
|
||||||
sweepId: ArtifactId
|
sweepId: ArtifactId
|
||||||
pathIds: Array<ArtifactId>
|
pathIds: Array<ArtifactId>
|
||||||
// codeRef is for the sketchOnFace plane, not for the wall itself
|
|
||||||
// traverse to the extrude and or segment to get the wall's codeRef
|
|
||||||
codeRef?: CodeRef
|
|
||||||
}
|
}
|
||||||
interface CapArtifact extends BaseArtifact {
|
interface CapArtifact extends BaseArtifact {
|
||||||
type: 'cap'
|
type: 'cap'
|
||||||
@ -102,12 +105,9 @@ interface CapArtifact extends BaseArtifact {
|
|||||||
edgeCutEdgeIds: Array<ArtifactId>
|
edgeCutEdgeIds: Array<ArtifactId>
|
||||||
sweepId: ArtifactId
|
sweepId: ArtifactId
|
||||||
pathIds: Array<ArtifactId>
|
pathIds: Array<ArtifactId>
|
||||||
// codeRef is for the sketchOnFace plane, not for the wall itself
|
|
||||||
// traverse to the extrude and or segment to get the wall's codeRef
|
|
||||||
codeRef?: CodeRef
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SweepEdgeArtifact extends BaseArtifact {
|
interface SweepEdge extends BaseArtifact {
|
||||||
type: 'sweepEdge'
|
type: 'sweepEdge'
|
||||||
segId: ArtifactId
|
segId: ArtifactId
|
||||||
sweepId: ArtifactId
|
sweepId: ArtifactId
|
||||||
@ -137,10 +137,10 @@ export type Artifact =
|
|||||||
| SweepArtifact
|
| SweepArtifact
|
||||||
| WallArtifact
|
| WallArtifact
|
||||||
| CapArtifact
|
| CapArtifact
|
||||||
| SweepEdgeArtifact
|
| SweepEdge
|
||||||
| EdgeCut
|
| EdgeCut
|
||||||
| EdgeCutEdge
|
| EdgeCutEdge
|
||||||
| Solid2DArtifact
|
| solid2D
|
||||||
|
|
||||||
export type ArtifactGraph = Map<ArtifactId, Artifact>
|
export type ArtifactGraph = Map<ArtifactId, Artifact>
|
||||||
|
|
||||||
@ -151,22 +151,18 @@ type OkWebSocketResponseData = Models['OkWebSocketResponseData_type']
|
|||||||
export interface ResponseMap {
|
export interface ResponseMap {
|
||||||
[commandId: string]: OkWebSocketResponseData
|
[commandId: string]: OkWebSocketResponseData
|
||||||
}
|
}
|
||||||
export interface OrderedCommand {
|
|
||||||
command: EngineCommand
|
|
||||||
range: SourceRange
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Creates a graph of artifacts from a list of ordered commands and their responses
|
/** Creates a graph of artifacts from a list of ordered commands and their responses
|
||||||
* muting the Map should happen entirely this function, other functions called within
|
* muting the Map should happen entirely this function, other functions called within
|
||||||
* should return data on how to update the map, and not do so directly.
|
* should return data on how to update the map, and not do so directly.
|
||||||
*/
|
*/
|
||||||
export function createArtifactGraph({
|
export function createArtifactGraph({
|
||||||
orderedCommands,
|
artifactCommands,
|
||||||
responseMap,
|
responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
}: {
|
}: {
|
||||||
orderedCommands: Array<OrderedCommand>
|
artifactCommands: Array<ArtifactCommand>
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
ast: Node<Program>
|
ast: Node<Program>
|
||||||
execStateArtifacts: ExecState['artifacts']
|
execStateArtifacts: ExecState['artifacts']
|
||||||
@ -176,17 +172,15 @@ export function createArtifactGraph({
|
|||||||
/** see docstring for {@link getArtifactsToUpdate} as to why this is needed */
|
/** see docstring for {@link getArtifactsToUpdate} as to why this is needed */
|
||||||
let currentPlaneId = ''
|
let currentPlaneId = ''
|
||||||
|
|
||||||
orderedCommands.forEach((orderedCommand) => {
|
for (const artifactCommand of artifactCommands) {
|
||||||
if (orderedCommand.command?.type === 'modeling_cmd_req') {
|
if (artifactCommand.command.type === 'enable_sketch_mode') {
|
||||||
if (orderedCommand.command.cmd.type === 'enable_sketch_mode') {
|
currentPlaneId = artifactCommand.command.entity_id
|
||||||
currentPlaneId = orderedCommand.command.cmd.entity_id
|
}
|
||||||
}
|
if (artifactCommand.command.type === 'sketch_mode_disable') {
|
||||||
if (orderedCommand.command.cmd.type === 'sketch_mode_disable') {
|
currentPlaneId = ''
|
||||||
currentPlaneId = ''
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const artifactsToUpdate = getArtifactsToUpdate({
|
const artifactsToUpdate = getArtifactsToUpdate({
|
||||||
orderedCommand,
|
artifactCommand,
|
||||||
responseMap,
|
responseMap,
|
||||||
getArtifact: (id: ArtifactId) => myMap.get(id),
|
getArtifact: (id: ArtifactId) => myMap.get(id),
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
@ -197,7 +191,7 @@ export function createArtifactGraph({
|
|||||||
const mergedArtifact = mergeArtifacts(myMap.get(id), artifact)
|
const mergedArtifact = mergeArtifacts(myMap.get(id), artifact)
|
||||||
myMap.set(id, mergedArtifact)
|
myMap.set(id, mergedArtifact)
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
return myMap
|
return myMap
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,14 +232,14 @@ function mergeArtifacts(
|
|||||||
* can remove this.
|
* can remove this.
|
||||||
*/
|
*/
|
||||||
export function getArtifactsToUpdate({
|
export function getArtifactsToUpdate({
|
||||||
orderedCommand: { command, range },
|
artifactCommand,
|
||||||
getArtifact,
|
getArtifact,
|
||||||
responseMap,
|
responseMap,
|
||||||
currentPlaneId,
|
currentPlaneId,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
}: {
|
}: {
|
||||||
orderedCommand: OrderedCommand
|
artifactCommand: ArtifactCommand
|
||||||
responseMap: ResponseMap
|
responseMap: ResponseMap
|
||||||
/** Passing in a getter because we don't wan this function to update the map directly */
|
/** Passing in a getter because we don't wan this function to update the map directly */
|
||||||
getArtifact: (id: ArtifactId) => Artifact | undefined
|
getArtifact: (id: ArtifactId) => Artifact | undefined
|
||||||
@ -256,14 +250,12 @@ export function getArtifactsToUpdate({
|
|||||||
id: ArtifactId
|
id: ArtifactId
|
||||||
artifact: Artifact
|
artifact: Artifact
|
||||||
}> {
|
}> {
|
||||||
|
const range = sourceRangeFromRust(artifactCommand.range)
|
||||||
const pathToNode = getNodePathFromSourceRange(ast, range)
|
const pathToNode = getNodePathFromSourceRange(ast, range)
|
||||||
|
|
||||||
// expect all to be `modeling_cmd_req` as batch commands have
|
const id = artifactCommand.cmdId
|
||||||
// already been expanded before being added to orderedCommands
|
|
||||||
if (command.type !== 'modeling_cmd_req') return []
|
|
||||||
const id = command.cmd_id
|
|
||||||
const response = responseMap[id]
|
const response = responseMap[id]
|
||||||
const cmd = command.cmd
|
const cmd = artifactCommand.command
|
||||||
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
const returnArr: ReturnType<typeof getArtifactsToUpdate> = []
|
||||||
if (!response) return returnArr
|
if (!response) return returnArr
|
||||||
if (cmd.type === 'make_plane' && range[1] !== 0) {
|
if (cmd.type === 'make_plane' && range[1] !== 0) {
|
||||||
@ -297,22 +289,6 @@ export function getArtifactsToUpdate({
|
|||||||
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
|
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
|
||||||
sweepId: existingPlane.sweepId,
|
sweepId: existingPlane.sweepId,
|
||||||
pathIds: existingPlane.pathIds,
|
pathIds: existingPlane.pathIds,
|
||||||
codeRef: existingPlane.codeRef,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]
|
|
||||||
} else if (existingPlane?.type === 'cap') {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
id: currentPlaneId,
|
|
||||||
artifact: {
|
|
||||||
type: 'cap',
|
|
||||||
subType: existingPlane.subType,
|
|
||||||
id: currentPlaneId,
|
|
||||||
edgeCutEdgeIds: existingPlane.edgeCutEdgeIds,
|
|
||||||
sweepId: existingPlane.sweepId,
|
|
||||||
pathIds: existingPlane.pathIds,
|
|
||||||
codeRef: existingPlane.codeRef,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -357,18 +333,6 @@ export function getArtifactsToUpdate({
|
|||||||
pathIds: [id],
|
pathIds: [id],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else if (plane?.type === 'cap') {
|
|
||||||
returnArr.push({
|
|
||||||
id: currentPlaneId,
|
|
||||||
artifact: {
|
|
||||||
type: 'cap',
|
|
||||||
id: currentPlaneId,
|
|
||||||
subType: plane.subType,
|
|
||||||
edgeCutEdgeIds: plane.edgeCutEdgeIds,
|
|
||||||
sweepId: plane.sweepId,
|
|
||||||
pathIds: [id],
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return returnArr
|
return returnArr
|
||||||
} else if (cmd.type === 'extend_path' || cmd.type === 'close_path') {
|
} else if (cmd.type === 'extend_path' || cmd.type === 'close_path') {
|
||||||
@ -413,7 +377,11 @@ export function getArtifactsToUpdate({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
return returnArr
|
return returnArr
|
||||||
} else if (cmd.type === 'extrude' || cmd.type === 'revolve') {
|
} else if (
|
||||||
|
cmd.type === 'extrude' ||
|
||||||
|
cmd.type === 'revolve' ||
|
||||||
|
cmd.type === 'sweep'
|
||||||
|
) {
|
||||||
const subType = cmd.type === 'extrude' ? 'extrusion' : cmd.type
|
const subType = cmd.type === 'extrude' ? 'extrusion' : cmd.type
|
||||||
returnArr.push({
|
returnArr.push({
|
||||||
id,
|
id,
|
||||||
@ -434,6 +402,33 @@ export function getArtifactsToUpdate({
|
|||||||
artifact: { ...path, sweepId: id },
|
artifact: { ...path, sweepId: id },
|
||||||
})
|
})
|
||||||
return returnArr
|
return returnArr
|
||||||
|
} else if (
|
||||||
|
cmd.type === 'loft' &&
|
||||||
|
response.type === 'modeling' &&
|
||||||
|
response.data.modeling_response.type === 'loft'
|
||||||
|
) {
|
||||||
|
returnArr.push({
|
||||||
|
id,
|
||||||
|
artifact: {
|
||||||
|
type: 'sweep',
|
||||||
|
subType: 'loft',
|
||||||
|
id,
|
||||||
|
// TODO: make sure to revisit this choice, don't think it matters for now
|
||||||
|
pathId: cmd.section_ids[0],
|
||||||
|
surfaceIds: [],
|
||||||
|
edgeIds: [],
|
||||||
|
codeRef: { range, pathToNode },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
for (const sectionId of cmd.section_ids) {
|
||||||
|
const path = getArtifact(sectionId)
|
||||||
|
if (path?.type === 'path')
|
||||||
|
returnArr.push({
|
||||||
|
id: sectionId,
|
||||||
|
artifact: { ...path, sweepId: id },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return returnArr
|
||||||
} else if (
|
} else if (
|
||||||
cmd.type === 'solid3d_get_extrusion_face_info' &&
|
cmd.type === 'solid3d_get_extrusion_face_info' &&
|
||||||
response?.type === 'modeling' &&
|
response?.type === 'modeling' &&
|
||||||
@ -448,33 +443,16 @@ export function getArtifactsToUpdate({
|
|||||||
const path = getArtifact(seg.pathId)
|
const path = getArtifact(seg.pathId)
|
||||||
if (path?.type === 'path' && seg?.type === 'segment') {
|
if (path?.type === 'path' && seg?.type === 'segment') {
|
||||||
lastPath = path
|
lastPath = path
|
||||||
const extraArtifact = Object.values(execStateArtifacts).find(
|
|
||||||
(a) => a?.type === 'StartSketchOnFace' && a.faceId === face_id
|
|
||||||
)
|
|
||||||
const sketchOnFaceSourceRange = extraArtifact?.sourceRange
|
|
||||||
const wallArtifact: Artifact = {
|
|
||||||
type: 'wall',
|
|
||||||
id: face_id,
|
|
||||||
segId: curve_id,
|
|
||||||
edgeCutEdgeIds: [],
|
|
||||||
sweepId: path.sweepId,
|
|
||||||
pathIds: [],
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sketchOnFaceSourceRange) {
|
|
||||||
const range: SourceRange = [
|
|
||||||
sketchOnFaceSourceRange[0],
|
|
||||||
sketchOnFaceSourceRange[1],
|
|
||||||
true,
|
|
||||||
]
|
|
||||||
wallArtifact.codeRef = {
|
|
||||||
range,
|
|
||||||
pathToNode: getNodePathFromSourceRange(ast, range),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
returnArr.push({
|
returnArr.push({
|
||||||
id: face_id,
|
id: face_id,
|
||||||
artifact: wallArtifact,
|
artifact: {
|
||||||
|
type: 'wall',
|
||||||
|
id: face_id,
|
||||||
|
segId: curve_id,
|
||||||
|
edgeCutEdgeIds: [],
|
||||||
|
sweepId: path.sweepId,
|
||||||
|
pathIds: [],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
returnArr.push({
|
returnArr.push({
|
||||||
id: curve_id,
|
id: curve_id,
|
||||||
@ -498,33 +476,16 @@ export function getArtifactsToUpdate({
|
|||||||
if ((cap === 'top' || cap === 'bottom') && face_id) {
|
if ((cap === 'top' || cap === 'bottom') && face_id) {
|
||||||
const path = lastPath
|
const path = lastPath
|
||||||
if (path?.type === 'path') {
|
if (path?.type === 'path') {
|
||||||
const extraArtifact = Object.values(execStateArtifacts).find(
|
|
||||||
(a) => a?.type === 'StartSketchOnFace' && a.faceId === face_id
|
|
||||||
)
|
|
||||||
const sketchOnFaceSourceRange = extraArtifact?.sourceRange
|
|
||||||
const capArtifact: Artifact = {
|
|
||||||
type: 'cap',
|
|
||||||
id: face_id,
|
|
||||||
subType: cap === 'bottom' ? 'start' : 'end',
|
|
||||||
edgeCutEdgeIds: [],
|
|
||||||
sweepId: path.sweepId,
|
|
||||||
pathIds: [],
|
|
||||||
}
|
|
||||||
if (sketchOnFaceSourceRange) {
|
|
||||||
const range: SourceRange = [
|
|
||||||
sketchOnFaceSourceRange[0],
|
|
||||||
sketchOnFaceSourceRange[1],
|
|
||||||
true,
|
|
||||||
]
|
|
||||||
|
|
||||||
capArtifact.codeRef = {
|
|
||||||
range,
|
|
||||||
pathToNode: getNodePathFromSourceRange(ast, range),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
returnArr.push({
|
returnArr.push({
|
||||||
id: face_id,
|
id: face_id,
|
||||||
artifact: capArtifact,
|
artifact: {
|
||||||
|
type: 'cap',
|
||||||
|
id: face_id,
|
||||||
|
subType: cap === 'bottom' ? 'start' : 'end',
|
||||||
|
edgeCutEdgeIds: [],
|
||||||
|
sweepId: path.sweepId,
|
||||||
|
pathIds: [],
|
||||||
|
},
|
||||||
})
|
})
|
||||||
const sweep = getArtifact(path.sweepId)
|
const sweep = getArtifact(path.sweepId)
|
||||||
if (sweep?.type !== 'sweep') return
|
if (sweep?.type !== 'sweep') return
|
||||||
@ -808,7 +769,7 @@ export function getCapCodeRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getSolid2dCodeRef(
|
export function getSolid2dCodeRef(
|
||||||
solid2D: Solid2DArtifact,
|
solid2D: solid2D,
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): CodeRef | Error {
|
): CodeRef | Error {
|
||||||
const path = getArtifactOfTypes(
|
const path = getArtifactOfTypes(
|
||||||
@ -832,7 +793,7 @@ export function getWallCodeRef(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getSweepEdgeCodeRef(
|
export function getSweepEdgeCodeRef(
|
||||||
edge: SweepEdgeArtifact,
|
edge: SweepEdge,
|
||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): CodeRef | Error {
|
): CodeRef | Error {
|
||||||
const seg = getArtifactOfTypes(
|
const seg = getArtifactOfTypes(
|
||||||
@ -947,205 +908,6 @@ export function codeRefFromRange(range: SourceRange, ast: Program): CodeRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlaneFromPath(
|
|
||||||
path: PathArtifact,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
const plane = getArtifactOfTypes(
|
|
||||||
{ key: path.planeId, types: ['plane', 'wall', 'cap'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(plane)) return plane
|
|
||||||
return plane
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPlaneFromSegment(
|
|
||||||
segment: SegmentArtifact,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
const path = getArtifactOfTypes(
|
|
||||||
{ key: segment.pathId, types: ['path'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(path)) return path
|
|
||||||
return getPlaneFromPath(path, graph)
|
|
||||||
}
|
|
||||||
function getPlaneFromSolid2D(
|
|
||||||
solid2D: Solid2DArtifact,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
const path = getArtifactOfTypes(
|
|
||||||
{ key: solid2D.pathId, types: ['path'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(path)) return path
|
|
||||||
return getPlaneFromPath(path, graph)
|
|
||||||
}
|
|
||||||
function getPlaneFromCap(
|
|
||||||
cap: CapArtifact,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
const sweep = getArtifactOfTypes(
|
|
||||||
{ key: cap.sweepId, types: ['sweep'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(sweep)) return sweep
|
|
||||||
const path = getArtifactOfTypes({ key: sweep.pathId, types: ['path'] }, graph)
|
|
||||||
if (err(path)) return path
|
|
||||||
return getPlaneFromPath(path, graph)
|
|
||||||
}
|
|
||||||
function getPlaneFromWall(
|
|
||||||
wall: WallArtifact,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
const sweep = getArtifactOfTypes(
|
|
||||||
{ key: wall.sweepId, types: ['sweep'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(sweep)) return sweep
|
|
||||||
const path = getArtifactOfTypes({ key: sweep.pathId, types: ['path'] }, graph)
|
|
||||||
if (err(path)) return path
|
|
||||||
return getPlaneFromPath(path, graph)
|
|
||||||
}
|
|
||||||
function getPlaneFromSweepEdge(edge: SweepEdgeArtifact, graph: ArtifactGraph) {
|
|
||||||
const sweep = getArtifactOfTypes(
|
|
||||||
{ key: edge.sweepId, types: ['sweep'] },
|
|
||||||
graph
|
|
||||||
)
|
|
||||||
if (err(sweep)) return sweep
|
|
||||||
const path = getArtifactOfTypes({ key: sweep.pathId, types: ['path'] }, graph)
|
|
||||||
if (err(path)) return path
|
|
||||||
return getPlaneFromPath(path, graph)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPlaneFromArtifact(
|
|
||||||
artifact: Artifact | undefined,
|
|
||||||
graph: ArtifactGraph
|
|
||||||
): PlaneArtifact | WallArtifact | CapArtifact | Error {
|
|
||||||
if (!artifact) return new Error(`Artifact is undefined`)
|
|
||||||
if (artifact.type === 'plane') return artifact
|
|
||||||
if (artifact.type === 'path') return getPlaneFromPath(artifact, graph)
|
|
||||||
if (artifact.type === 'segment') return getPlaneFromSegment(artifact, graph)
|
|
||||||
if (artifact.type === 'solid2D') return getPlaneFromSolid2D(artifact, graph)
|
|
||||||
if (artifact.type === 'cap') return getPlaneFromCap(artifact, graph)
|
|
||||||
if (artifact.type === 'wall') return getPlaneFromWall(artifact, graph)
|
|
||||||
if (artifact.type === 'sweepEdge')
|
|
||||||
return getPlaneFromSweepEdge(artifact, graph)
|
|
||||||
return new Error(`Artifact type ${artifact.type} does not have a plane`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const isExprSafe = (index: number): boolean => {
|
|
||||||
const expr = kclManager.ast.body?.[index]
|
|
||||||
if (!expr) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (expr.type === 'ImportStatement' || expr.type === 'ReturnStatement') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (expr.type === 'VariableDeclaration') {
|
|
||||||
const init = expr.declaration?.init
|
|
||||||
if (!init) return false
|
|
||||||
if (init.type === 'CallExpression') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (init.type === 'BinaryExpression' && isNodeSafe(init)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (init.type === 'Literal' || init.type === 'MemberExpression') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
const onlyConsecutivePaths = (
|
|
||||||
orderedNodePaths: PathToNode[],
|
|
||||||
originalPath: PathToNode
|
|
||||||
): PathToNode[] => {
|
|
||||||
const originalIndex = Number(
|
|
||||||
orderedNodePaths.find(
|
|
||||||
(path) => path[1][0] === originalPath[1][0]
|
|
||||||
)?.[1]?.[0] || 0
|
|
||||||
)
|
|
||||||
|
|
||||||
const minIndex = Number(orderedNodePaths[0][1][0])
|
|
||||||
const maxIndex = Number(orderedNodePaths[orderedNodePaths.length - 1][1][0])
|
|
||||||
const pathIndexMap: any = {}
|
|
||||||
orderedNodePaths.forEach((path) => {
|
|
||||||
const bodyIndex = Number(path[1][0])
|
|
||||||
pathIndexMap[bodyIndex] = path
|
|
||||||
})
|
|
||||||
const safePaths: PathToNode[] = []
|
|
||||||
|
|
||||||
// traverse expressions in either direction from the profile selected
|
|
||||||
// when the user entered sketch mode
|
|
||||||
for (let i = originalIndex; i <= maxIndex; i++) {
|
|
||||||
if (pathIndexMap[i]) {
|
|
||||||
safePaths.push(pathIndexMap[i])
|
|
||||||
} else if (!isExprSafe(i)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (let i = originalIndex - 1; i >= minIndex; i--) {
|
|
||||||
if (pathIndexMap[i]) {
|
|
||||||
safePaths.unshift(pathIndexMap[i])
|
|
||||||
} else if (!isExprSafe(i)) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return safePaths
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPathsFromPlaneArtifact(planeArtifact: PlaneArtifact) {
|
|
||||||
const nodePaths: PathToNode[] = []
|
|
||||||
for (const pathId of planeArtifact.pathIds) {
|
|
||||||
const path = engineCommandManager.artifactGraph.get(pathId)
|
|
||||||
if (!path) continue
|
|
||||||
if ('codeRef' in path && path.codeRef) {
|
|
||||||
// TODO should figure out why upstream the path is bad
|
|
||||||
const isNodePathBad = path.codeRef.pathToNode.length < 2
|
|
||||||
nodePaths.push(
|
|
||||||
isNodePathBad
|
|
||||||
? getNodePathFromSourceRange(kclManager.ast, path.codeRef.range)
|
|
||||||
: path.codeRef.pathToNode
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return onlyConsecutivePaths(nodePaths, nodePaths[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPathsFromArtifact({
|
|
||||||
sketchPathToNode,
|
|
||||||
artifact,
|
|
||||||
}: {
|
|
||||||
sketchPathToNode: PathToNode
|
|
||||||
artifact?: Artifact
|
|
||||||
}): PathToNode[] | Error {
|
|
||||||
const plane = getPlaneFromArtifact(
|
|
||||||
artifact,
|
|
||||||
engineCommandManager.artifactGraph
|
|
||||||
)
|
|
||||||
if (err(plane)) return plane
|
|
||||||
const paths = getArtifactsOfTypes(
|
|
||||||
{ keys: plane.pathIds, types: ['path'] },
|
|
||||||
engineCommandManager.artifactGraph
|
|
||||||
)
|
|
||||||
let nodePaths = [...paths.values()]
|
|
||||||
.map((path) => path.codeRef.pathToNode)
|
|
||||||
.sort((a, b) => Number(a[1][0]) - Number(b[1][0]))
|
|
||||||
return onlyConsecutivePaths(nodePaths, sketchPathToNode)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNodeSafe(node: Expr): boolean {
|
|
||||||
if (node.type === 'Literal' || node.type === 'MemberExpression') {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (node.type === 'BinaryExpression') {
|
|
||||||
return isNodeSafe(node.left) && isNodeSafe(node.right)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an artifact from a code source range
|
* Get an artifact from a code source range
|
||||||
*/
|
*/
|
||||||
@ -1154,7 +916,7 @@ export function getArtifactFromRange(
|
|||||||
artifactGraph: ArtifactGraph
|
artifactGraph: ArtifactGraph
|
||||||
): Artifact | null {
|
): Artifact | null {
|
||||||
for (const artifact of artifactGraph.values()) {
|
for (const artifact of artifactGraph.values()) {
|
||||||
if ('codeRef' in artifact && artifact.codeRef) {
|
if ('codeRef' in artifact) {
|
||||||
const match =
|
const match =
|
||||||
artifact.codeRef?.range[0] === range[0] &&
|
artifact.codeRef?.range[0] === range[0] &&
|
||||||
artifact.codeRef.range[1] === range[1]
|
artifact.codeRef.range[1] === range[1]
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 560 KiB After Width: | Height: | Size: 568 KiB |
@ -1,11 +1,10 @@
|
|||||||
import {
|
import {
|
||||||
|
ArtifactCommand,
|
||||||
defaultRustSourceRange,
|
defaultRustSourceRange,
|
||||||
defaultSourceRange,
|
|
||||||
ExecState,
|
ExecState,
|
||||||
Program,
|
Program,
|
||||||
RustSourceRange,
|
RustSourceRange,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
sourceRangeFromRust,
|
|
||||||
} from 'lang/wasm'
|
} from 'lang/wasm'
|
||||||
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
import { VITE_KC_API_WS_MODELING_URL, VITE_KC_DEV_TOKEN } from 'env'
|
||||||
import { Models } from '@kittycad/lib'
|
import { Models } from '@kittycad/lib'
|
||||||
@ -21,7 +20,6 @@ import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
|||||||
import {
|
import {
|
||||||
ArtifactGraph,
|
ArtifactGraph,
|
||||||
EngineCommand,
|
EngineCommand,
|
||||||
OrderedCommand,
|
|
||||||
ResponseMap,
|
ResponseMap,
|
||||||
createArtifactGraph,
|
createArtifactGraph,
|
||||||
} from 'lang/std/artifactGraph'
|
} from 'lang/std/artifactGraph'
|
||||||
@ -1305,7 +1303,7 @@ export enum EngineCommandManagerEvents {
|
|||||||
*
|
*
|
||||||
* As commands are send their state is tracked in {@link pendingCommands} and clear as soon as we receive a response.
|
* As commands are send their state is tracked in {@link pendingCommands} and clear as soon as we receive a response.
|
||||||
*
|
*
|
||||||
* Also all commands that are sent are kept track of in {@link orderedCommands} and their responses are kept in {@link responseMap}
|
* Also all commands that are sent are kept track of in WASM artifactCommands and their responses are kept in {@link responseMap}
|
||||||
* Both of these data structures are used to process the {@link artifactGraph}.
|
* Both of these data structures are used to process the {@link artifactGraph}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -1331,12 +1329,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
[commandId: string]: PendingMessage
|
[commandId: string]: PendingMessage
|
||||||
} = {}
|
} = {}
|
||||||
/**
|
/**
|
||||||
* The orderedCommands array of all the the commands sent to the engine, un-folded from batches, and made into one long
|
* A map of the responses to the WASM artifactCommands, when processing the commands into the artifactGraph, this response map allow
|
||||||
* list of the individual commands, this is used to process all the commands into the artifactGraph
|
|
||||||
*/
|
|
||||||
orderedCommands: Array<OrderedCommand> = []
|
|
||||||
/**
|
|
||||||
* A map of the responses to the {@link orderedCommands}, when processing the commands into the artifactGraph, this response map allow
|
|
||||||
* us to look up the response by command id
|
* us to look up the response by command id
|
||||||
*/
|
*/
|
||||||
responseMap: ResponseMap = {}
|
responseMap: ResponseMap = {}
|
||||||
@ -1832,7 +1825,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
async startNewSession() {
|
async startNewSession() {
|
||||||
this.orderedCommands = []
|
|
||||||
this.responseMap = {}
|
this.responseMap = {}
|
||||||
await this.initPlanes()
|
await this.initPlanes()
|
||||||
}
|
}
|
||||||
@ -2075,28 +2067,6 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
isSceneCommand,
|
isSceneCommand,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (message.command.type === 'modeling_cmd_req') {
|
|
||||||
this.orderedCommands.push({
|
|
||||||
command: message.command,
|
|
||||||
range: sourceRangeFromRust(message.range),
|
|
||||||
})
|
|
||||||
} else if (message.command.type === 'modeling_cmd_batch_req') {
|
|
||||||
message.command.requests.forEach((req) => {
|
|
||||||
const cmdId = req.cmd_id || ''
|
|
||||||
const range = cmdId
|
|
||||||
? sourceRangeFromRust(message.idToRangeMap[cmdId])
|
|
||||||
: defaultSourceRange()
|
|
||||||
const cmd: EngineCommand = {
|
|
||||||
type: 'modeling_cmd_req',
|
|
||||||
cmd_id: req.cmd_id,
|
|
||||||
cmd: req.cmd,
|
|
||||||
}
|
|
||||||
this.orderedCommands.push({
|
|
||||||
command: cmd,
|
|
||||||
range,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.engineConnection?.send(message.command)
|
this.engineConnection?.send(message.command)
|
||||||
return promise
|
return promise
|
||||||
}
|
}
|
||||||
@ -2119,10 +2089,11 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
}
|
}
|
||||||
updateArtifactGraph(
|
updateArtifactGraph(
|
||||||
ast: Node<Program>,
|
ast: Node<Program>,
|
||||||
|
artifactCommands: ArtifactCommand[],
|
||||||
execStateArtifacts: ExecState['artifacts']
|
execStateArtifacts: ExecState['artifacts']
|
||||||
) {
|
) {
|
||||||
this.artifactGraph = createArtifactGraph({
|
this.artifactGraph = createArtifactGraph({
|
||||||
orderedCommands: this.orderedCommands,
|
artifactCommands,
|
||||||
responseMap: this.responseMap,
|
responseMap: this.responseMap,
|
||||||
ast,
|
ast,
|
||||||
execStateArtifacts,
|
execStateArtifacts,
|
||||||
@ -2219,11 +2190,7 @@ export class EngineCommandManager extends EventTarget {
|
|||||||
commandTypeToTarget: string
|
commandTypeToTarget: string
|
||||||
): string | undefined {
|
): string | undefined {
|
||||||
for (const [artifactId, artifact] of this.artifactGraph) {
|
for (const [artifactId, artifact] of this.artifactGraph) {
|
||||||
if (
|
if ('codeRef' in artifact && isOverlap(range, artifact.codeRef.range)) {
|
||||||
'codeRef' in artifact &&
|
|
||||||
artifact.codeRef &&
|
|
||||||
isOverlap(range, artifact.codeRef.range)
|
|
||||||
) {
|
|
||||||
if (commandTypeToTarget === artifact.type) return artifactId
|
if (commandTypeToTarget === artifact.type) return artifactId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -297,20 +297,14 @@ export const lineTo: SketchLineHelper = {
|
|||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const to = segmentInput.to
|
const to = segmentInput.to
|
||||||
const _node = structuredClone(node)
|
const _node = { ...node }
|
||||||
const nodeMeta = getNodeFromPath<PipeExpression>(
|
const nodeMeta = getNodeFromPath<PipeExpression>(
|
||||||
_node,
|
_node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
'PipeExpression'
|
'PipeExpression'
|
||||||
)
|
)
|
||||||
if (err(nodeMeta)) return nodeMeta
|
if (err(nodeMeta)) return nodeMeta
|
||||||
const varDec = getNodeFromPath<VariableDeclaration>(
|
const { node: pipe } = nodeMeta
|
||||||
_node,
|
|
||||||
pathToNode,
|
|
||||||
'VariableDeclaration'
|
|
||||||
)
|
|
||||||
if (err(varDec)) return varDec
|
|
||||||
const dec = varDec.node.declaration
|
|
||||||
|
|
||||||
const newVals: [Expr, Expr] = [
|
const newVals: [Expr, Expr] = [
|
||||||
createLiteral(roundOff(to[0], 2)),
|
createLiteral(roundOff(to[0], 2)),
|
||||||
@ -339,20 +333,14 @@ export const lineTo: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { callExp, valueUsedInTransform } = result
|
const { callExp, valueUsedInTransform } = result
|
||||||
if (dec.init.type === 'PipeExpression') {
|
pipe.body[callIndex] = callExp
|
||||||
dec.init.body[callIndex] = callExp
|
|
||||||
} else {
|
|
||||||
dec.init = callExp
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
valueUsedInTransform: valueUsedInTransform,
|
valueUsedInTransform: valueUsedInTransform,
|
||||||
}
|
}
|
||||||
} else if (dec.init.type === 'PipeExpression') {
|
|
||||||
dec.init.body = [...dec.init.body, newLine]
|
|
||||||
} else {
|
} else {
|
||||||
dec.init = createPipeExpression([dec.init, newLine])
|
pipe.body = [...pipe.body, newLine]
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
@ -675,11 +663,11 @@ export const xLine: SketchLineHelper = {
|
|||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { from, to } = segmentInput
|
const { from, to } = segmentInput
|
||||||
const _node = structuredClone(node)
|
const _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const varDec = getNode<VariableDeclaration>('VariableDeclaration')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
if (err(varDec)) return varDec
|
if (err(_node1)) return _node1
|
||||||
const dec = varDec.node.declaration
|
const { node: pipe } = _node1
|
||||||
|
|
||||||
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
const newVal = createLiteral(roundOff(to[0] - from[0], 2))
|
||||||
|
|
||||||
@ -694,11 +682,7 @@ export const xLine: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { callExp, valueUsedInTransform } = result
|
const { callExp, valueUsedInTransform } = result
|
||||||
if (dec.init.type === 'PipeExpression') {
|
pipe.body[callIndex] = callExp
|
||||||
dec.init.body[callIndex] = callExp
|
|
||||||
} else {
|
|
||||||
dec.init = callExp
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -710,11 +694,7 @@ export const xLine: SketchLineHelper = {
|
|||||||
newVal,
|
newVal,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
if (dec.init.type === 'PipeExpression') {
|
pipe.body = [...pipe.body, newLine]
|
||||||
dec.init.body = [...dec.init.body, newLine]
|
|
||||||
} else {
|
|
||||||
dec.init = createPipeExpression([dec.init, newLine])
|
|
||||||
}
|
|
||||||
return { modifiedAst: _node, pathToNode }
|
return { modifiedAst: _node, pathToNode }
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
@ -751,11 +731,11 @@ export const yLine: SketchLineHelper = {
|
|||||||
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
add: ({ node, pathToNode, segmentInput, replaceExistingCallback }) => {
|
||||||
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
if (segmentInput.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||||
const { from, to } = segmentInput
|
const { from, to } = segmentInput
|
||||||
const _node = structuredClone(node)
|
const _node = { ...node }
|
||||||
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
const getNode = getNodeFromPathCurry(_node, pathToNode)
|
||||||
const varDec = getNode<VariableDeclaration>('VariableDeclaration')
|
const _node1 = getNode<PipeExpression>('PipeExpression')
|
||||||
if (err(varDec)) return varDec
|
if (err(_node1)) return _node1
|
||||||
const dec = varDec.node.declaration
|
const { node: pipe } = _node1
|
||||||
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
const newVal = createLiteral(roundOff(to[1] - from[1], 2))
|
||||||
if (replaceExistingCallback) {
|
if (replaceExistingCallback) {
|
||||||
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
const { index: callIndex } = splitPathAtPipeExpression(pathToNode)
|
||||||
@ -768,11 +748,7 @@ export const yLine: SketchLineHelper = {
|
|||||||
])
|
])
|
||||||
if (err(result)) return result
|
if (err(result)) return result
|
||||||
const { callExp, valueUsedInTransform } = result
|
const { callExp, valueUsedInTransform } = result
|
||||||
if (dec.init.type === 'PipeExpression') {
|
pipe.body[callIndex] = callExp
|
||||||
dec.init.body[callIndex] = callExp
|
|
||||||
} else {
|
|
||||||
dec.init = callExp
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
modifiedAst: _node,
|
modifiedAst: _node,
|
||||||
pathToNode,
|
pathToNode,
|
||||||
@ -784,11 +760,7 @@ export const yLine: SketchLineHelper = {
|
|||||||
newVal,
|
newVal,
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
])
|
])
|
||||||
if (dec.init.type === 'PipeExpression') {
|
pipe.body = [...pipe.body, newLine]
|
||||||
dec.init.body = [...dec.init.body, newLine]
|
|
||||||
} else {
|
|
||||||
dec.init = createPipeExpression([dec.init, newLine])
|
|
||||||
}
|
|
||||||
return { modifiedAst: _node, pathToNode }
|
return { modifiedAst: _node, pathToNode }
|
||||||
},
|
},
|
||||||
updateArgs: ({ node, pathToNode, input }) => {
|
updateArgs: ({ node, pathToNode, input }) => {
|
||||||
@ -2173,6 +2145,8 @@ function addTagToChamfer(
|
|||||||
if (err(variableDec)) return variableDec
|
if (err(variableDec)) return variableDec
|
||||||
const isPipeExpression = pipeExpr.node.type === 'PipeExpression'
|
const isPipeExpression = pipeExpr.node.type === 'PipeExpression'
|
||||||
|
|
||||||
|
console.log('pipeExpr', pipeExpr, variableDec)
|
||||||
|
// const callExpr = isPipeExpression ? pipeExpr.node.body[pipeIndex] : variableDec.node.init
|
||||||
const callExpr = isPipeExpression
|
const callExpr = isPipeExpression
|
||||||
? pipeExpr.node.body[pipeIndex]
|
? pipeExpr.node.body[pipeIndex]
|
||||||
: variableDec.node.init
|
: variableDec.node.init
|
||||||
@ -2253,6 +2227,7 @@ function addTagToChamfer(
|
|||||||
if (isPipeExpression) {
|
if (isPipeExpression) {
|
||||||
pipeExpr.node.body.splice(pipeIndex, 0, newExpressionToInsert)
|
pipeExpr.node.body.splice(pipeIndex, 0, newExpressionToInsert)
|
||||||
} else {
|
} else {
|
||||||
|
console.log('yo', createPipeExpression([newExpressionToInsert, callExpr]))
|
||||||
callExpr.arguments[1] = createPipeSubstitution()
|
callExpr.arguments[1] = createPipeSubstitution()
|
||||||
variableDec.node.init = createPipeExpression([
|
variableDec.node.init = createPipeExpression([
|
||||||
newExpressionToInsert,
|
newExpressionToInsert,
|
||||||
|
@ -9,47 +9,20 @@ import {
|
|||||||
import { ArtifactGraph, filterArtifacts } from 'lang/std/artifactGraph'
|
import { ArtifactGraph, filterArtifacts } from 'lang/std/artifactGraph'
|
||||||
import { isOverlap } from 'lib/utils'
|
import { isOverlap } from 'lib/utils'
|
||||||
|
|
||||||
/**
|
export function updatePathToNodeFromMap(
|
||||||
* Updates pathToNode body indices to account for the insertion of an expression
|
oldPath: PathToNode,
|
||||||
* PathToNode expression is after the insertion index, that the body index is incremented
|
pathToNodeMap: { [key: number]: PathToNode }
|
||||||
* Negative insertion index means no insertion
|
|
||||||
*/
|
|
||||||
export function updatePathToNodePostExprInjection(
|
|
||||||
pathToNode: PathToNode,
|
|
||||||
exprInsertIndex: number
|
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
if (exprInsertIndex < 0) return pathToNode
|
const updatedPathToNode = structuredClone(oldPath)
|
||||||
const bodyIndex = Number(pathToNode[1][0])
|
let max = 0
|
||||||
if (bodyIndex < exprInsertIndex) return pathToNode
|
Object.values(pathToNodeMap).forEach((path) => {
|
||||||
const clone = structuredClone(pathToNode)
|
const index = Number(path[1][0])
|
||||||
clone[1][0] = bodyIndex + 1
|
if (index > max) {
|
||||||
return clone
|
max = index
|
||||||
}
|
}
|
||||||
|
})
|
||||||
export function updateSketchDetailsNodePaths({
|
updatedPathToNode[1][0] = max
|
||||||
sketchEntryNodePath,
|
return updatedPathToNode
|
||||||
sketchNodePaths,
|
|
||||||
planeNodePath,
|
|
||||||
exprInsertIndex,
|
|
||||||
}: {
|
|
||||||
sketchEntryNodePath: PathToNode
|
|
||||||
sketchNodePaths: Array<PathToNode>
|
|
||||||
planeNodePath: PathToNode
|
|
||||||
exprInsertIndex: number
|
|
||||||
}) {
|
|
||||||
return {
|
|
||||||
updatedSketchEntryNodePath: updatePathToNodePostExprInjection(
|
|
||||||
sketchEntryNodePath,
|
|
||||||
exprInsertIndex
|
|
||||||
),
|
|
||||||
updatedSketchNodePaths: sketchNodePaths.map((path) =>
|
|
||||||
updatePathToNodePostExprInjection(path, exprInsertIndex)
|
|
||||||
),
|
|
||||||
updatedPlaneNodePath: updatePathToNodePostExprInjection(
|
|
||||||
planeNodePath,
|
|
||||||
exprInsertIndex
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isCursorInSketchCommandRange(
|
export function isCursorInSketchCommandRange(
|
||||||
@ -58,7 +31,7 @@ export function isCursorInSketchCommandRange(
|
|||||||
): string | false {
|
): string | false {
|
||||||
const overlappingEntries = filterArtifacts(
|
const overlappingEntries = filterArtifacts(
|
||||||
{
|
{
|
||||||
types: ['segment', 'path', 'plane'],
|
types: ['segment', 'path'],
|
||||||
predicate: (artifact) => {
|
predicate: (artifact) => {
|
||||||
return selectionRanges.graphSelections.some(
|
return selectionRanges.graphSelections.some(
|
||||||
(selection) =>
|
(selection) =>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import init, {
|
import {
|
||||||
|
init,
|
||||||
parse_wasm,
|
parse_wasm,
|
||||||
recast_wasm,
|
recast_wasm,
|
||||||
execute,
|
execute,
|
||||||
@ -16,7 +17,9 @@ import init, {
|
|||||||
default_project_settings,
|
default_project_settings,
|
||||||
base64_decode,
|
base64_decode,
|
||||||
clear_scene_and_bust_cache,
|
clear_scene_and_bust_cache,
|
||||||
} from '../wasm-lib/pkg/wasm_lib'
|
reloadModule,
|
||||||
|
} from 'lib/wasm_lib_wrapper'
|
||||||
|
|
||||||
import { KCLError } from './errors'
|
import { KCLError } from './errors'
|
||||||
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
||||||
import { EngineCommandManager } from './std/engineConnection'
|
import { EngineCommandManager } from './std/engineConnection'
|
||||||
@ -47,8 +50,10 @@ import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
|||||||
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
||||||
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||||
|
import { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||||
|
|
||||||
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||||
|
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/ArtifactCommand'
|
||||||
export type { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
export type { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||||
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||||
@ -148,6 +153,7 @@ export const wasmUrl = () => {
|
|||||||
// Initialise the wasm module.
|
// Initialise the wasm module.
|
||||||
const initialise = async () => {
|
const initialise = async () => {
|
||||||
try {
|
try {
|
||||||
|
await reloadModule()
|
||||||
const fullUrl = wasmUrl()
|
const fullUrl = wasmUrl()
|
||||||
const input = await fetch(fullUrl)
|
const input = await fetch(fullUrl)
|
||||||
const buffer = await input.arrayBuffer()
|
const buffer = await input.arrayBuffer()
|
||||||
@ -227,6 +233,7 @@ export const parse = (code: string | Error): ParseResult | Error => {
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -252,6 +259,7 @@ export interface ExecState {
|
|||||||
memory: ProgramMemory
|
memory: ProgramMemory
|
||||||
operations: Operation[]
|
operations: Operation[]
|
||||||
artifacts: { [key in ArtifactId]?: Artifact }
|
artifacts: { [key in ArtifactId]?: Artifact }
|
||||||
|
artifactCommands: ArtifactCommand[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -263,6 +271,7 @@ export function emptyExecState(): ExecState {
|
|||||||
memory: ProgramMemory.empty(),
|
memory: ProgramMemory.empty(),
|
||||||
operations: [],
|
operations: [],
|
||||||
artifacts: {},
|
artifacts: {},
|
||||||
|
artifactCommands: [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,6 +280,7 @@ function execStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
|||||||
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
||||||
operations: execOutcome.operations,
|
operations: execOutcome.operations,
|
||||||
artifacts: execOutcome.artifacts,
|
artifacts: execOutcome.artifacts,
|
||||||
|
artifactCommands: execOutcome.artifactCommands,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -512,6 +522,10 @@ export const executor = async (
|
|||||||
if (programMemoryOverride !== null && err(programMemoryOverride))
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
return Promise.reject(programMemoryOverride)
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||||
|
return Promise.reject(programMemoryOverride)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let jsAppSettings = default_app_settings()
|
let jsAppSettings = default_app_settings()
|
||||||
if (!TEST) {
|
if (!TEST) {
|
||||||
@ -537,7 +551,8 @@ export const executor = async (
|
|||||||
parsed.error.kind,
|
parsed.error.kind,
|
||||||
parsed.error.msg,
|
parsed.error.msg,
|
||||||
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
||||||
parsed.operations
|
parsed.operations,
|
||||||
|
parsed.artifactCommands
|
||||||
)
|
)
|
||||||
|
|
||||||
return Promise.reject(kclError)
|
return Promise.reject(kclError)
|
||||||
@ -597,6 +612,7 @@ export const modifyAstForSketch = async (
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -666,6 +682,7 @@ export function programMemoryInit(): ProgramMemory | Error {
|
|||||||
parsed.kind,
|
parsed.kind,
|
||||||
parsed.msg,
|
parsed.msg,
|
||||||
sourceRangeFromRust(parsed.sourceRanges[0]),
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
||||||
|
[],
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,10 @@ export type ModelingCommandSchema = {
|
|||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||||
distance: KclCommandValue
|
distance: KclCommandValue
|
||||||
}
|
}
|
||||||
|
Sweep: {
|
||||||
|
path: Selections
|
||||||
|
profile: Selections
|
||||||
|
}
|
||||||
Loft: {
|
Loft: {
|
||||||
selection: Selections
|
selection: Selections
|
||||||
}
|
}
|
||||||
@ -47,7 +51,9 @@ export type ModelingCommandSchema = {
|
|||||||
Revolve: {
|
Revolve: {
|
||||||
selection: Selections
|
selection: Selections
|
||||||
angle: KclCommandValue
|
angle: KclCommandValue
|
||||||
axis: Selections
|
axisOrEdge: string
|
||||||
|
axis: string
|
||||||
|
edge: Selections
|
||||||
}
|
}
|
||||||
Fillet: {
|
Fillet: {
|
||||||
// todo
|
// todo
|
||||||
@ -290,6 +296,33 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Sweep: {
|
||||||
|
description:
|
||||||
|
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||||
|
icon: 'sweep',
|
||||||
|
status: 'development',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
profile: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['solid2D'],
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
multiple: false,
|
||||||
|
// TODO: add dry-run validation
|
||||||
|
warningMessage:
|
||||||
|
'The sweep workflow is new and under tested. Please break it and report issues.',
|
||||||
|
},
|
||||||
|
path: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['segment', 'path'],
|
||||||
|
required: true,
|
||||||
|
skip: true,
|
||||||
|
multiple: false,
|
||||||
|
// TODO: add dry-run validation
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
Loft: {
|
Loft: {
|
||||||
description: 'Create a 3D body by blending between two or more sketches',
|
description: 'Create a 3D body by blending between two or more sketches',
|
||||||
icon: 'loft',
|
icon: 'loft',
|
||||||
@ -324,10 +357,10 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
|
||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
|
status: 'development',
|
||||||
needsReview: true,
|
needsReview: true,
|
||||||
args: {
|
args: {
|
||||||
selection: {
|
selection: {
|
||||||
@ -336,9 +369,34 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
multiple: false, // TODO: multiple selection
|
multiple: false, // TODO: multiple selection
|
||||||
required: true,
|
required: true,
|
||||||
skip: true,
|
skip: true,
|
||||||
|
warningMessage:
|
||||||
|
'The revolve workflow is new and under tested. Please break it and report issues.',
|
||||||
|
},
|
||||||
|
axisOrEdge: {
|
||||||
|
inputType: 'options',
|
||||||
|
required: true,
|
||||||
|
defaultValue: 'Axis',
|
||||||
|
options: [
|
||||||
|
{ name: 'Axis', isCurrent: true, value: 'Axis' },
|
||||||
|
{ name: 'Edge', isCurrent: false, value: 'Edge' },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
axis: {
|
axis: {
|
||||||
required: true,
|
required: (commandContext) =>
|
||||||
|
['Axis'].includes(
|
||||||
|
commandContext.argumentsToSubmit.axisOrEdge as string
|
||||||
|
),
|
||||||
|
inputType: 'options',
|
||||||
|
options: [
|
||||||
|
{ name: 'X Axis', isCurrent: true, value: 'X' },
|
||||||
|
{ name: 'Y Axis', isCurrent: false, value: 'Y' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
edge: {
|
||||||
|
required: (commandContext) =>
|
||||||
|
['Edge'].includes(
|
||||||
|
commandContext.argumentsToSubmit.axisOrEdge as string
|
||||||
|
),
|
||||||
inputType: 'selection',
|
inputType: 'selection',
|
||||||
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
selectionTypes: ['segment', 'sweepEdge', 'edgeCutEdge'],
|
||||||
multiple: false,
|
multiple: false,
|
||||||
|
@ -68,7 +68,7 @@ export const revolveAxisValidator = async ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sketchSelection = artifact.pathId
|
const sketchSelection = artifact.pathId
|
||||||
let edgeSelection = data.axis.graphSelections[0].artifact?.id
|
let edgeSelection = data.edge.graphSelections[0].artifact?.id
|
||||||
|
|
||||||
if (!sketchSelection) {
|
if (!sketchSelection) {
|
||||||
return 'Unable to revolve, sketch is missing'
|
return 'Unable to revolve, sketch is missing'
|
||||||
@ -101,7 +101,7 @@ export const revolveAxisValidator = async ({
|
|||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
// return error message for the toast
|
// return error message for the toast
|
||||||
return 'Unable to revolve with selected axis'
|
return 'Unable to revolve with selected edge'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,6 +53,7 @@ export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
|||||||
SKETCH: 'sketch',
|
SKETCH: 'sketch',
|
||||||
EXTRUDE: 'extrude',
|
EXTRUDE: 'extrude',
|
||||||
LOFT: 'loft',
|
LOFT: 'loft',
|
||||||
|
SWEEP: 'sweep',
|
||||||
SHELL: 'shell',
|
SHELL: 'shell',
|
||||||
SEGMENT: 'seg',
|
SEGMENT: 'seg',
|
||||||
REVOLVE: 'revolve',
|
REVOLVE: 'revolve',
|
||||||
|
@ -15,6 +15,7 @@ import {
|
|||||||
StateMachineCommandSetSchema,
|
StateMachineCommandSetSchema,
|
||||||
} from './commandTypes'
|
} from './commandTypes'
|
||||||
import { DEV } from 'env'
|
import { DEV } from 'env'
|
||||||
|
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||||
|
|
||||||
interface CreateMachineCommandProps<
|
interface CreateMachineCommandProps<
|
||||||
T extends AnyStateMachine,
|
T extends AnyStateMachine,
|
||||||
@ -84,7 +85,7 @@ export function createMachineCommand<
|
|||||||
} else if ('status' in commandConfig) {
|
} else if ('status' in commandConfig) {
|
||||||
const { status } = commandConfig
|
const { status } = commandConfig
|
||||||
if (status === 'inactive') return null
|
if (status === 'inactive') return null
|
||||||
if (status === 'development' && !DEV) return null
|
if (status === 'development' && !(DEV || IS_NIGHTLY_OR_DEBUG)) return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const icon = ('icon' in commandConfig && commandConfig.icon) || undefined
|
const icon = ('icon' in commandConfig && commandConfig.icon) || undefined
|
||||||
|
51
src/lib/exceptions.ts
Normal file
51
src/lib/exceptions.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { kclManager } from 'lib/singletons'
|
||||||
|
import { reloadModule, getModule } from 'lib/wasm_lib_wrapper'
|
||||||
|
import toast from 'react-hot-toast'
|
||||||
|
import { reportRejection } from './trap'
|
||||||
|
|
||||||
|
let initialized = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WASM/Rust runtime can panic and the original try/catch/finally blocks will not trigger
|
||||||
|
* on the await promise. The interface will killed. This means we need to catch the error at
|
||||||
|
* the global/DOM level. This will have to interface with whatever controlflow that needs to be picked up
|
||||||
|
* within the error branch in the typescript to cover the application state.
|
||||||
|
*/
|
||||||
|
export const initializeWindowExceptionHandler = () => {
|
||||||
|
if (window && !initialized) {
|
||||||
|
window.addEventListener('error', (event) => {
|
||||||
|
void (async () => {
|
||||||
|
if (matchImportExportErrorCrash(event.message)) {
|
||||||
|
// do global singleton cleanup
|
||||||
|
kclManager.executeAstCleanUp()
|
||||||
|
toast.error(
|
||||||
|
'You have hit a KCL execution bug! Put your KCL code in a github issue to help us resolve this bug.'
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
await reloadModule()
|
||||||
|
await getModule().default()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Failed to initialize wasm_lib')
|
||||||
|
console.error(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})().catch(reportRejection)
|
||||||
|
})
|
||||||
|
// Make sure we only initialize this event listener once
|
||||||
|
initialized = true
|
||||||
|
} else {
|
||||||
|
console.error(
|
||||||
|
`Failed to initialize, window: ${window}, initialized:${initialized}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifically match a substring of the message error to detect an import export runtime issue
|
||||||
|
* when the WASM runtime panics
|
||||||
|
*/
|
||||||
|
const matchImportExportErrorCrash = (message: string): boolean => {
|
||||||
|
// called `Result::unwrap_throw()` on an `Err` value
|
||||||
|
const substringError = '`Result::unwrap_throw()` on an `Err` value'
|
||||||
|
return message.indexOf(substringError) !== -1 ? true : false
|
||||||
|
}
|
2
src/lib/machine-api.d.ts
vendored
2
src/lib/machine-api.d.ts
vendored
@ -155,7 +155,7 @@ export interface components {
|
|||||||
color?: string | null
|
color?: string | null
|
||||||
/** @description The material that the filament is made of. */
|
/** @description The material that the filament is made of. */
|
||||||
material: components['schemas']['FilamentMaterial']
|
material: components['schemas']['FilamentMaterial']
|
||||||
/** @description The name of the filament, this is likely specfic to the manufacturer. */
|
/** @description The name of the filament, this is likely specific to the manufacturer. */
|
||||||
name?: string | null
|
name?: string | null
|
||||||
}
|
}
|
||||||
/** @description The material that the filament is made of. */
|
/** @description The material that the filament is made of. */
|
||||||
|
52
src/lib/markdown.ts
Normal file
52
src/lib/markdown.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import { MarkedOptions, Renderer, unescape } from '@ts-stack/markdown'
|
||||||
|
import { openExternalBrowserIfDesktop } from './openWindow'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main goal of this custom renderer is to prevent links from changing the current location
|
||||||
|
* this is specially important for the desktop app.
|
||||||
|
*/
|
||||||
|
export class SafeRenderer extends Renderer {
|
||||||
|
constructor(options: MarkedOptions) {
|
||||||
|
super(options)
|
||||||
|
|
||||||
|
// Attach a global function for non-react anchor elements that need safe navigation
|
||||||
|
window.openExternalLink = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
|
openExternalBrowserIfDesktop()(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extended from https://github.com/ts-stack/markdown/blob/c5c1925c1153ca2fe9051c356ef0ddc60b3e1d6a/packages/markdown/src/renderer.ts#L116
|
||||||
|
link(href: string, title: string, text: string): string {
|
||||||
|
if (this.options.sanitize) {
|
||||||
|
let prot: string
|
||||||
|
|
||||||
|
try {
|
||||||
|
prot = decodeURIComponent(unescape(href))
|
||||||
|
.replace(/[^\w:]/g, '')
|
||||||
|
.toLowerCase()
|
||||||
|
} catch (e) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
// eslint-disable-next-line no-script-url
|
||||||
|
prot.indexOf('javascript:') === 0 ||
|
||||||
|
prot.indexOf('vbscript:') === 0 ||
|
||||||
|
prot.indexOf('data:') === 0
|
||||||
|
) {
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let out =
|
||||||
|
'<a onclick="openExternalLink(event)" target="_blank" href="' + href + '"'
|
||||||
|
|
||||||
|
if (title) {
|
||||||
|
out += ' title="' + title + '"'
|
||||||
|
}
|
||||||
|
|
||||||
|
out += '>' + text + '</a>'
|
||||||
|
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ import {
|
|||||||
*/
|
*/
|
||||||
export const getRectangleCallExpressions = (
|
export const getRectangleCallExpressions = (
|
||||||
rectangleOrigin: [number, number],
|
rectangleOrigin: [number, number],
|
||||||
tag: string
|
tags: [string, string, string]
|
||||||
) => [
|
) => [
|
||||||
createCallExpressionStdLib('angledLine', [
|
createCallExpressionStdLib('angledLine', [
|
||||||
createArrayExpression([
|
createArrayExpression([
|
||||||
@ -37,28 +37,30 @@ export const getRectangleCallExpressions = (
|
|||||||
createLiteral(0), // This will be the width of the rectangle
|
createLiteral(0), // This will be the width of the rectangle
|
||||||
]),
|
]),
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
createTagDeclarator(tag),
|
createTagDeclarator(tags[0]),
|
||||||
]),
|
]),
|
||||||
createCallExpressionStdLib('angledLine', [
|
createCallExpressionStdLib('angledLine', [
|
||||||
createArrayExpression([
|
createArrayExpression([
|
||||||
createBinaryExpression([
|
createBinaryExpression([
|
||||||
createCallExpressionStdLib('segAng', [createIdentifier(tag)]),
|
createCallExpressionStdLib('segAng', [createIdentifier(tags[0])]),
|
||||||
'+',
|
'+',
|
||||||
createLiteral(90),
|
createLiteral(90),
|
||||||
]), // 90 offset from the previous line
|
]), // 90 offset from the previous line
|
||||||
createLiteral(0), // This will be the height of the rectangle
|
createLiteral(0), // This will be the height of the rectangle
|
||||||
]),
|
]),
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
|
createTagDeclarator(tags[1]),
|
||||||
]),
|
]),
|
||||||
createCallExpressionStdLib('angledLine', [
|
createCallExpressionStdLib('angledLine', [
|
||||||
createArrayExpression([
|
createArrayExpression([
|
||||||
createCallExpressionStdLib('segAng', [createIdentifier(tag)]), // same angle as the first line
|
createCallExpressionStdLib('segAng', [createIdentifier(tags[0])]), // same angle as the first line
|
||||||
createUnaryExpression(
|
createUnaryExpression(
|
||||||
createCallExpressionStdLib('segLen', [createIdentifier(tag)]),
|
createCallExpressionStdLib('segLen', [createIdentifier(tags[0])]),
|
||||||
'-'
|
'-'
|
||||||
), // negative height
|
), // negative height
|
||||||
]),
|
]),
|
||||||
createPipeSubstitution(),
|
createPipeSubstitution(),
|
||||||
|
createTagDeclarator(tags[2]),
|
||||||
]),
|
]),
|
||||||
createCallExpressionStdLib('lineTo', [
|
createCallExpressionStdLib('lineTo', [
|
||||||
createArrayExpression([
|
createArrayExpression([
|
||||||
@ -83,12 +85,12 @@ export function updateRectangleSketch(
|
|||||||
y: number,
|
y: number,
|
||||||
tag: string
|
tag: string
|
||||||
) {
|
) {
|
||||||
;((pipeExpression.body[1] as CallExpression)
|
;((pipeExpression.body[2] as CallExpression)
|
||||||
.arguments[0] as ArrayExpression) = createArrayExpression([
|
.arguments[0] as ArrayExpression) = createArrayExpression([
|
||||||
createLiteral(x >= 0 ? 0 : 180),
|
createLiteral(x >= 0 ? 0 : 180),
|
||||||
createLiteral(Math.abs(x)),
|
createLiteral(Math.abs(x)),
|
||||||
])
|
])
|
||||||
;((pipeExpression.body[2] as CallExpression)
|
;((pipeExpression.body[3] as CallExpression)
|
||||||
.arguments[0] as ArrayExpression) = createArrayExpression([
|
.arguments[0] as ArrayExpression) = createArrayExpression([
|
||||||
createBinaryExpression([
|
createBinaryExpression([
|
||||||
createCallExpressionStdLib('segAng', [createIdentifier(tag)]),
|
createCallExpressionStdLib('segAng', [createIdentifier(tag)]),
|
||||||
@ -118,7 +120,7 @@ export function updateCenterRectangleSketch(
|
|||||||
let startY = originY - Math.abs(deltaY)
|
let startY = originY - Math.abs(deltaY)
|
||||||
|
|
||||||
// pipeExpression.body[1] is startProfileAt
|
// pipeExpression.body[1] is startProfileAt
|
||||||
let callExpression = pipeExpression.body[0]
|
let callExpression = pipeExpression.body[1]
|
||||||
if (isCallExpression(callExpression)) {
|
if (isCallExpression(callExpression)) {
|
||||||
const arrayExpression = callExpression.arguments[0]
|
const arrayExpression = callExpression.arguments[0]
|
||||||
if (isArrayExpression(arrayExpression)) {
|
if (isArrayExpression(arrayExpression)) {
|
||||||
@ -132,7 +134,7 @@ export function updateCenterRectangleSketch(
|
|||||||
const twoX = deltaX * 2
|
const twoX = deltaX * 2
|
||||||
const twoY = deltaY * 2
|
const twoY = deltaY * 2
|
||||||
|
|
||||||
callExpression = pipeExpression.body[1]
|
callExpression = pipeExpression.body[2]
|
||||||
if (isCallExpression(callExpression)) {
|
if (isCallExpression(callExpression)) {
|
||||||
const arrayExpression = callExpression.arguments[0]
|
const arrayExpression = callExpression.arguments[0]
|
||||||
if (isArrayExpression(arrayExpression)) {
|
if (isArrayExpression(arrayExpression)) {
|
||||||
@ -146,7 +148,7 @@ export function updateCenterRectangleSketch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
callExpression = pipeExpression.body[2]
|
callExpression = pipeExpression.body[3]
|
||||||
if (isCallExpression(callExpression)) {
|
if (isCallExpression(callExpression)) {
|
||||||
const arrayExpression = callExpression.arguments[0]
|
const arrayExpression = callExpression.arguments[0]
|
||||||
if (isArrayExpression(arrayExpression)) {
|
if (isArrayExpression(arrayExpression)) {
|
||||||
|
@ -278,19 +278,18 @@ export function getEventForSegmentSelection(
|
|||||||
}
|
}
|
||||||
if (!id || !group) return null
|
if (!id || !group) return null
|
||||||
const artifact = engineCommandManager.artifactGraph.get(id)
|
const artifact = engineCommandManager.artifactGraph.get(id)
|
||||||
if (!artifact) return null
|
const codeRefs = getCodeRefsByArtifactId(
|
||||||
const node = getNodeFromPath<Expr>(kclManager.ast, group.userData.pathToNode)
|
id,
|
||||||
if (err(node)) return null
|
engineCommandManager.artifactGraph
|
||||||
|
)
|
||||||
|
if (!artifact || !codeRefs) return null
|
||||||
return {
|
return {
|
||||||
type: 'Set selection',
|
type: 'Set selection',
|
||||||
data: {
|
data: {
|
||||||
selectionType: 'singleCodeCursor',
|
selectionType: 'singleCodeCursor',
|
||||||
selection: {
|
selection: {
|
||||||
artifact,
|
artifact,
|
||||||
codeRef: {
|
codeRef: codeRefs[0],
|
||||||
pathToNode: group?.userData?.pathToNode,
|
|
||||||
range: [node.node.start, node.node.end, true],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -569,7 +568,8 @@ export function getSelectionTypeDisplayText(
|
|||||||
const selectionsByType = getSelectionCountByType(selection)
|
const selectionsByType = getSelectionCountByType(selection)
|
||||||
if (selectionsByType === 'none') return null
|
if (selectionsByType === 'none') return null
|
||||||
|
|
||||||
return [...selectionsByType.entries()]
|
return selectionsByType
|
||||||
|
.entries()
|
||||||
.map(
|
.map(
|
||||||
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
// Hack for showing "face" instead of "extrude-wall" in command bar text
|
||||||
([type, count]) =>
|
([type, count]) =>
|
||||||
@ -578,6 +578,7 @@ export function getSelectionTypeDisplayText(
|
|||||||
.replace('solid2D', 'face')
|
.replace('solid2D', 'face')
|
||||||
.replace('segment', 'face')}${count > 1 ? 's' : ''}`
|
.replace('segment', 'face')}${count > 1 ? 's' : ''}`
|
||||||
)
|
)
|
||||||
|
.toArray()
|
||||||
.join(', ')
|
.join(', ')
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,7 +588,7 @@ export function canSubmitSelectionArg(
|
|||||||
) {
|
) {
|
||||||
return (
|
return (
|
||||||
selectionsByType !== 'none' &&
|
selectionsByType !== 'none' &&
|
||||||
[...selectionsByType.entries()].every(([type, count]) => {
|
selectionsByType.entries().every(([type, count]) => {
|
||||||
const foundIndex = argument.selectionTypes.findIndex((s) => s === type)
|
const foundIndex = argument.selectionTypes.findIndex((s) => s === type)
|
||||||
return (
|
return (
|
||||||
foundIndex !== -1 &&
|
foundIndex !== -1 &&
|
||||||
@ -610,7 +611,7 @@ export function codeToIdSelections(
|
|||||||
// TODO #868: loops over all artifacts will become inefficient at a large scale
|
// TODO #868: loops over all artifacts will become inefficient at a large scale
|
||||||
const overlappingEntries = Array.from(engineCommandManager.artifactGraph)
|
const overlappingEntries = Array.from(engineCommandManager.artifactGraph)
|
||||||
.map(([id, artifact]) => {
|
.map(([id, artifact]) => {
|
||||||
if (!('codeRef' in artifact && artifact.codeRef)) return null
|
if (!('codeRef' in artifact)) return null
|
||||||
return isOverlap(artifact.codeRef.range, selection.range)
|
return isOverlap(artifact.codeRef.range, selection.range)
|
||||||
? {
|
? {
|
||||||
artifact,
|
artifact,
|
||||||
@ -861,6 +862,7 @@ export function updateSelections(
|
|||||||
JSON.stringify(pathToNode)
|
JSON.stringify(pathToNode)
|
||||||
) {
|
) {
|
||||||
artifact = a
|
artifact = a
|
||||||
|
console.log('found artifact', a)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,13 @@ import { CustomIconName } from 'components/CustomIcon'
|
|||||||
import { DEV } from 'env'
|
import { DEV } from 'env'
|
||||||
import { commandBarMachine } from 'machines/commandBarMachine'
|
import { commandBarMachine } from 'machines/commandBarMachine'
|
||||||
import {
|
import {
|
||||||
|
canRectangleOrCircleTool,
|
||||||
|
isClosedSketch,
|
||||||
isEditingExistingSketch,
|
isEditingExistingSketch,
|
||||||
modelingMachine,
|
modelingMachine,
|
||||||
pipeHasCircle,
|
pipeHasCircle,
|
||||||
} from 'machines/modelingMachine'
|
} from 'machines/modelingMachine'
|
||||||
|
import { IS_NIGHTLY_OR_DEBUG } from 'routes/Settings'
|
||||||
import { EventFrom, StateFrom } from 'xstate'
|
import { EventFrom, StateFrom } from 'xstate'
|
||||||
|
|
||||||
export type ToolbarModeName = 'modeling' | 'sketching'
|
export type ToolbarModeName = 'modeling' | 'sketching'
|
||||||
@ -70,7 +73,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
icon: 'sketch',
|
icon: 'sketch',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
title: ({ sketchPathId }) =>
|
title: ({ sketchPathId }) =>
|
||||||
sketchPathId ? 'Edit Sketch' : 'Start Sketch',
|
`${sketchPathId ? 'Edit' : 'Start'} Sketch`,
|
||||||
showTitle: true,
|
showTitle: true,
|
||||||
hotkey: 'S',
|
hotkey: 'S',
|
||||||
description: 'Start drawing a 2D sketch',
|
description: 'Start drawing a 2D sketch',
|
||||||
@ -101,7 +104,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Revolve', groupId: 'modeling' },
|
data: { name: 'Revolve', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'revolve',
|
icon: 'revolve',
|
||||||
status: DEV ? 'available' : 'kcl-only',
|
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||||
title: 'Revolve',
|
title: 'Revolve',
|
||||||
hotkey: 'R',
|
hotkey: 'R',
|
||||||
description:
|
description:
|
||||||
@ -116,17 +119,21 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'sweep',
|
id: 'sweep',
|
||||||
onClick: () => console.error('Sweep not yet implemented'),
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Sweep', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
icon: 'sweep',
|
icon: 'sweep',
|
||||||
status: 'unavailable',
|
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||||
title: 'Sweep',
|
title: 'Sweep',
|
||||||
hotkey: 'W',
|
hotkey: 'W',
|
||||||
description:
|
description:
|
||||||
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
'Create a 3D body by moving a sketch region along an arbitrary path.',
|
||||||
links: [
|
links: [
|
||||||
{
|
{
|
||||||
label: 'GitHub discussion',
|
label: 'KCL docs',
|
||||||
url: 'https://github.com/KittyCAD/modeling-app/discussions/498',
|
url: 'https://zoo.dev/docs/kcl/sweep',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -159,7 +166,7 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
data: { name: 'Fillet', groupId: 'modeling' },
|
data: { name: 'Fillet', groupId: 'modeling' },
|
||||||
}),
|
}),
|
||||||
icon: 'fillet3d',
|
icon: 'fillet3d',
|
||||||
status: DEV ? 'available' : 'kcl-only',
|
status: DEV || IS_NIGHTLY_OR_DEBUG ? 'available' : 'kcl-only',
|
||||||
title: 'Fillet',
|
title: 'Fillet',
|
||||||
hotkey: 'F',
|
hotkey: 'F',
|
||||||
description: 'Round the edges of a 3D solid.',
|
description: 'Round the edges of a 3D solid.',
|
||||||
@ -330,14 +337,22 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
{
|
{
|
||||||
id: 'line',
|
id: 'line',
|
||||||
onClick: ({ modelingState, modelingSend }) => {
|
onClick: ({ modelingState, modelingSend }) => {
|
||||||
modelingSend({
|
if (modelingState.matches({ Sketch: { 'Line tool': 'No Points' } })) {
|
||||||
type: 'change tool',
|
// Exit the sketch state if there are no points and they press ESC
|
||||||
data: {
|
modelingSend({
|
||||||
tool: !modelingState.matches({ Sketch: 'Line tool' })
|
type: 'Cancel',
|
||||||
? 'line'
|
})
|
||||||
: 'none',
|
} else {
|
||||||
},
|
// Exit the tool if there are points and they press ESC
|
||||||
})
|
modelingSend({
|
||||||
|
type: 'change tool',
|
||||||
|
data: {
|
||||||
|
tool: !modelingState.matches({ Sketch: 'Line tool' })
|
||||||
|
? 'line'
|
||||||
|
: 'none',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
icon: 'line',
|
icon: 'line',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
@ -348,7 +363,8 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
}) ||
|
}) ||
|
||||||
state.matches({
|
state.matches({
|
||||||
Sketch: { 'Circle tool': 'Awaiting Radius' },
|
Sketch: { 'Circle tool': 'Awaiting Radius' },
|
||||||
}),
|
}) ||
|
||||||
|
isClosedSketch(state.context),
|
||||||
title: 'Line',
|
title: 'Line',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
|
state.matches({ Sketch: 'Line tool' }) ? ['Esc', 'L'] : 'L',
|
||||||
@ -428,7 +444,10 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
icon: 'circle',
|
icon: 'circle',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
title: 'Center circle',
|
title: 'Center circle',
|
||||||
disabled: (state) => state.matches('Sketch no face'),
|
disabled: (state) =>
|
||||||
|
state.matches('Sketch no face') ||
|
||||||
|
(!canRectangleOrCircleTool(state.context) &&
|
||||||
|
!state.matches({ Sketch: 'Circle tool' })),
|
||||||
isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
|
isActive: (state) => state.matches({ Sketch: 'Circle tool' }),
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Circle tool' }) ? ['Esc', 'C'] : 'C',
|
state.matches({ Sketch: 'Circle tool' }) ? ['Esc', 'C'] : 'C',
|
||||||
@ -476,7 +495,10 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
}),
|
}),
|
||||||
icon: 'rectangle',
|
icon: 'rectangle',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
disabled: (state) => state.matches('Sketch no face'),
|
disabled: (state) =>
|
||||||
|
state.matches('Sketch no face') ||
|
||||||
|
(!canRectangleOrCircleTool(state.context) &&
|
||||||
|
!state.matches({ Sketch: 'Rectangle tool' })),
|
||||||
title: 'Corner rectangle',
|
title: 'Corner rectangle',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Rectangle tool' }) ? ['Esc', 'R'] : 'R',
|
state.matches({ Sketch: 'Rectangle tool' }) ? ['Esc', 'R'] : 'R',
|
||||||
@ -499,7 +521,10 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
}),
|
}),
|
||||||
icon: 'arc',
|
icon: 'arc',
|
||||||
status: 'available',
|
status: 'available',
|
||||||
disabled: (state) => state.matches('Sketch no face'),
|
disabled: (state) =>
|
||||||
|
state.matches('Sketch no face') ||
|
||||||
|
(!canRectangleOrCircleTool(state.context) &&
|
||||||
|
!state.matches({ Sketch: 'Center Rectangle tool' })),
|
||||||
title: 'Center rectangle',
|
title: 'Center rectangle',
|
||||||
hotkey: (state) =>
|
hotkey: (state) =>
|
||||||
state.matches({ Sketch: 'Center Rectangle tool' })
|
state.matches({ Sketch: 'Center Rectangle tool' })
|
||||||
|
@ -97,7 +97,3 @@ export function trap<T>(
|
|||||||
})
|
})
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export function reject(errOrString: Error | string): Promise<never> {
|
|
||||||
return Promise.reject(errOrString)
|
|
||||||
}
|
|
||||||
|
@ -153,7 +153,10 @@ export function toSync<F extends AsyncFn<F>>(
|
|||||||
) => void | PromiseLike<void | null | undefined> | null | undefined
|
) => void | PromiseLike<void | null | undefined> | null | undefined
|
||||||
): (...args: Parameters<F>) => void {
|
): (...args: Parameters<F>) => void {
|
||||||
return (...args: Parameters<F>) => {
|
return (...args: Parameters<F>) => {
|
||||||
fn(...args).catch(onReject)
|
void fn(...args).catch((...args) => {
|
||||||
|
console.error(...args)
|
||||||
|
return onReject(...args)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -342,7 +345,7 @@ export function onDragNumberCalculation(text: string, e: MouseEvent) {
|
|||||||
)
|
)
|
||||||
const newVal = roundOff(addition, precision)
|
const newVal = roundOff(addition, precision)
|
||||||
|
|
||||||
if (isNaN(newVal)) {
|
if (Number.isNaN(newVal)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
108
src/lib/wasm_lib_wrapper.ts
Normal file
108
src/lib/wasm_lib_wrapper.ts
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/**
|
||||||
|
* This wrapper file is to enable reloading of the wasm_lib.js file.
|
||||||
|
* When the wasm instance bricks there is no API or interface to restart,
|
||||||
|
* restore, or re init the WebAssembly instance. The entire application would need
|
||||||
|
* to restart.
|
||||||
|
* A way to bypass this is by reloading the entire .js file so the global wasm variable
|
||||||
|
* gets reinitialized and we do not use that old reference
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
parse_wasm as ParseWasm,
|
||||||
|
recast_wasm as RecastWasm,
|
||||||
|
execute as Execute,
|
||||||
|
kcl_lint as KclLint,
|
||||||
|
modify_ast_for_sketch_wasm as ModifyAstForSketch,
|
||||||
|
is_points_ccw as IsPointsCcw,
|
||||||
|
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
||||||
|
program_memory_init as ProgramMemoryInit,
|
||||||
|
make_default_planes as MakeDefaultPlanes,
|
||||||
|
coredump as CoreDump,
|
||||||
|
toml_stringify as TomlStringify,
|
||||||
|
default_app_settings as DefaultAppSettings,
|
||||||
|
parse_app_settings as ParseAppSettings,
|
||||||
|
parse_project_settings as ParseProjectSettings,
|
||||||
|
default_project_settings as DefaultProjectSettings,
|
||||||
|
base64_decode as Base64Decode,
|
||||||
|
clear_scene_and_bust_cache as ClearSceneAndBustCache,
|
||||||
|
} from '../wasm-lib/pkg/wasm_lib'
|
||||||
|
|
||||||
|
type ModuleType = typeof import('../wasm-lib/pkg/wasm_lib')
|
||||||
|
|
||||||
|
// Stores the result of the import of the wasm_lib file
|
||||||
|
let data: ModuleType
|
||||||
|
|
||||||
|
// Imports the .js file again which will clear the old import
|
||||||
|
// This allows us to reinitialize the wasm instance
|
||||||
|
export async function reloadModule() {
|
||||||
|
data = await import(`../wasm-lib/pkg/wasm_lib`)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getModule(): ModuleType {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function init(module_or_path: any) {
|
||||||
|
return await getModule().default(module_or_path)
|
||||||
|
}
|
||||||
|
export const parse_wasm: typeof ParseWasm = (...args) => {
|
||||||
|
return getModule().parse_wasm(...args)
|
||||||
|
}
|
||||||
|
export const recast_wasm: typeof RecastWasm = (...args) => {
|
||||||
|
return getModule().recast_wasm(...args)
|
||||||
|
}
|
||||||
|
export const execute: typeof Execute = (...args) => {
|
||||||
|
return getModule().execute(...args)
|
||||||
|
}
|
||||||
|
export const kcl_lint: typeof KclLint = (...args) => {
|
||||||
|
return getModule().kcl_lint(...args)
|
||||||
|
}
|
||||||
|
export const modify_ast_for_sketch_wasm: typeof ModifyAstForSketch = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().modify_ast_for_sketch_wasm(...args)
|
||||||
|
}
|
||||||
|
export const is_points_ccw: typeof IsPointsCcw = (...args) => {
|
||||||
|
return getModule().is_points_ccw(...args)
|
||||||
|
}
|
||||||
|
export const get_tangential_arc_to_info: typeof GetTangentialArcToInfo = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().get_tangential_arc_to_info(...args)
|
||||||
|
}
|
||||||
|
export const program_memory_init: typeof ProgramMemoryInit = (...args) => {
|
||||||
|
return getModule().program_memory_init(...args)
|
||||||
|
}
|
||||||
|
export const make_default_planes: typeof MakeDefaultPlanes = (...args) => {
|
||||||
|
return getModule().make_default_planes(...args)
|
||||||
|
}
|
||||||
|
export const coredump: typeof CoreDump = (...args) => {
|
||||||
|
return getModule().coredump(...args)
|
||||||
|
}
|
||||||
|
export const toml_stringify: typeof TomlStringify = (...args) => {
|
||||||
|
return getModule().toml_stringify(...args)
|
||||||
|
}
|
||||||
|
export const default_app_settings: typeof DefaultAppSettings = (...args) => {
|
||||||
|
return getModule().default_app_settings(...args)
|
||||||
|
}
|
||||||
|
export const parse_app_settings: typeof ParseAppSettings = (...args) => {
|
||||||
|
return getModule().parse_app_settings(...args)
|
||||||
|
}
|
||||||
|
export const parse_project_settings: typeof ParseProjectSettings = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().parse_project_settings(...args)
|
||||||
|
}
|
||||||
|
export const default_project_settings: typeof DefaultProjectSettings = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().default_project_settings(...args)
|
||||||
|
}
|
||||||
|
export const base64_decode: typeof Base64Decode = (...args) => {
|
||||||
|
return getModule().base64_decode(...args)
|
||||||
|
}
|
||||||
|
export const clear_scene_and_bust_cache: typeof ClearSceneAndBustCache = (
|
||||||
|
...args
|
||||||
|
) => {
|
||||||
|
return getModule().clear_scene_and_bust_cache(...args)
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -41,13 +41,13 @@ export default function Export() {
|
|||||||
export to almost any CAD software.
|
export to almost any CAD software.
|
||||||
</p>
|
</p>
|
||||||
<p className="my-4">
|
<p className="my-4">
|
||||||
Our teammate David is working on the file format, check out{' '}
|
Our teammate Katie is working on the file format, check out{' '}
|
||||||
<a
|
<a
|
||||||
href="https://www.youtube.com/watch?v=8SuW0qkYCZo"
|
href="https://github.com/KhronosGroup/glTF/pull/2343"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer noopener"
|
rel="noreferrer noopener"
|
||||||
>
|
>
|
||||||
his talk with the Metaverse Standards Forum
|
her standards proposal on GitHub
|
||||||
</a>
|
</a>
|
||||||
!
|
!
|
||||||
</p>
|
</p>
|
||||||
|
@ -32,6 +32,8 @@ export const PACKAGE_NAME = isDesktop()
|
|||||||
|
|
||||||
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1
|
export const IS_NIGHTLY = PACKAGE_NAME.indexOf('-nightly') > -1
|
||||||
|
|
||||||
|
export const IS_NIGHTLY_OR_DEBUG = IS_NIGHTLY || APP_VERSION === '0.0.0'
|
||||||
|
|
||||||
export function getReleaseUrl(version: string = APP_VERSION) {
|
export function getReleaseUrl(version: string = APP_VERSION) {
|
||||||
return `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
return `https://github.com/KittyCAD/modeling-app/releases/tag/${
|
||||||
IS_NIGHTLY ? 'nightly-' : ''
|
IS_NIGHTLY ? 'nightly-' : ''
|
||||||
|
9
src/wasm-lib/Cargo.lock
generated
9
src/wasm-lib/Cargo.lock
generated
@ -1819,9 +1819,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds"
|
name = "kittycad-modeling-cmds"
|
||||||
version = "0.2.79"
|
version = "0.2.86"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "10a9cab4476455be70ea57643c31444068b056d091bd348cab6044c0d8ad7fcc"
|
checksum = "65e34a8eeb4fff5167666d1f2bc36c95d08ab3a0f736a02c8d33a8cde21cfd8d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -1839,6 +1839,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"ts-rs",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1856,9 +1857,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad-modeling-cmds-macros-impl"
|
name = "kittycad-modeling-cmds-macros-impl"
|
||||||
version = "0.1.12"
|
version = "0.1.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6607507a8a0e4273b943179f0a3ef8e90712308d1d3095246040c29cfdbf985b"
|
checksum = "fdb4ee23cc996aa2dca7584d410e8826e08161e1ac4335bb646d5ede33f37cb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user